/* ============================================================
   FONTS - self-hosted. Nothing here touches the network.

   Cormorant Garamond (SIL OFL, see fonts/OFL.txt) carries everything,
   including the wordmark and the card labels, as in the comp.

   Kovanov is bundled at fonts/kovanov-regular.woff2 but is NOT declared
   here, so nothing downloads it. To use it, restore the @font-face
   below and point --font-mark at it in tokens.css. Note its licence
   before you do: the file carries no licence text, only a contact
   address, and self-hosting a webfont needs an explicit webfont grant.

   @font-face {
     font-family: 'Kovanov'; font-style: normal;
     font-weight: 400; font-display: swap;
     src: url("../fonts/kovanov-regular.7482df96c896.woff2") format('woff2');
   }
   ============================================================ */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-latin.ec4225ec161b.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-latin-ext.e1f7cd82031b.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}
/* ============================================================
   IU MIEN WAAC - MAIN STYLESHEET
   Layout and components. Colors/fonts live in tokens.css.
     1. Base            6. Editorial passage
     2. Shell + aura    7. Alliance
     3. Masthead        8. Footer
     4. Hero            9. Inner-page components
     5. The doors      10. Learn / culture, responsive, reduced motion
   ============================================================ */

/* ---------- 1. BASE ---------- */
* { box-sizing: border-box; }

/* The glows (.aura, .hero__arch, .site-footer__glow) are deliberately
   wider than the viewport so their light bleeds off the edges. Clipping
   has to happen on html as well as body, or the document still reports
   horizontal overflow. `clip` rather than `hidden`: hidden would turn
   these into scroll containers and break anything sticky inside them. */
html {
  /* The type scale is entirely rem-based, so the root size is the single
     lever that scales all of it. --ui-scale drives this and the shell
     together - see tokens.css. */
  font-size: calc(16px * var(--ui-scale));
  scroll-behavior: smooth;
  overflow-x: hidden;   /* fallback for anything without `clip` */
  overflow-x: clip;
}

body {
  position: relative;
  margin: 0;
  background: var(--surface-0);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  font-weight: 500;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  overflow-x: clip;
}

img, svg { max-width: 100%; display: block; }
/* Links carry the wordmark's red - the same one the initiative's name
   arrives at in the masthead. Note the cost, measured: #bf1a1f is 3.07:1
   on a card, under the 4.5:1 that normal-size text needs. Links keep
   their underline so they stay findable regardless, and the red is
   5.45:1 against the body text around them so it reads as a link and not
   as emphasis. If you want it to pass, --wordmark-link is the one token
   to change; #f22127 is the nearest red of the same hue that clears it. */
a { color: var(--wordmark-link); }
button, input, select, textarea { font-family: inherit; }

h1, h2, h3, .display { font-family: var(--font-display); font-weight: 600; }
h1 { font-size: clamp(1.9rem, 4vw, 3rem); margin: 0.2em 0; }
h2 { font-size: clamp(1.4rem, 2.6vw, 2rem); }

.wrap {
  /* Never wider than the rail the masthead uses, so inner-page content
     lines up with the chrome and never runs into the shell's wall. */
  max-width: min(var(--max-width), var(--rail));
  margin: 0 auto; padding: 0 20px;
}
.muted { color: var(--text-soft); }
.small { font-size: var(--fs-xs); font-weight: 600; }
.mien { font-family: var(--font-mien); font-weight: 600; letter-spacing: 0.01em; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); border: 0;
}

.skip-link {
  position: absolute; left: 50%; top: 0; z-index: 200;
  transform: translate(-50%, -120%);
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--edge-3);
  border-radius: 0 0 12px 12px;
  padding: 10px 20px; text-decoration: none;
  font-size: var(--fs-sm); font-weight: 600;
}
.skip-link:focus { transform: translate(-50%, 0); }

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
  border-radius: 4px;
}

.todo { outline: 2px dashed var(--red-tassel); outline-offset: 3px; position: relative; }
.todo::after {
  content: "replace with your content";
  position: absolute; top: -0.7em; right: 8px;
  background: var(--red-tassel); color: #fff;
  font-size: var(--fs-xs); font-weight: 600; padding: 1px 8px; border-radius: 999px;
}

/* ---------- 2. SHELL + AURA ----------
   The page is itself a card, as in the comp. The masthead, the
   photograph and the card row all share --rail inside it, so their
   edges line up straight down the page. */
.aura {
  position: absolute; z-index: 0;
  pointer-events: none;
  left: 50%; transform: translateX(-50%);
}
.aura--top {
  top: -190px;
  width: min(1700px, 130vw);
  height: 760px;
  background:
    radial-gradient(50% 55% at 50% 32%, rgba(232, 236, 244, 0.11), transparent 70%),
    radial-gradient(28% 38% at 50% 20%, rgba(255, 255, 255, 0.05), transparent 72%);
}

/* The shell is now width and nothing else. It used to carry its own
   fill and a drawn edge - a 1px line across the top and down both sides,
   with rounded top corners - which put a hard-edged rectangle over the
   page. Its fill was --surface-0, the same colour as the page behind it,
   so the only thing that rectangle ever did was announce itself.

   The light at the top of the page comes from .aura--top instead, which
   is a radial gradient wider than the shell and fades out in every
   direction, so the top of the page now falls away into black with no
   boundary to find. */
.shell {
  position: relative;
  z-index: 1;
  width: var(--shell);
  margin: 0 auto;
}

/* ---------- 3. MASTHEAD ----------
   Static, and exactly as wide as the photograph and the card row. */
.masthead {
  position: static; z-index: 60;
  width: var(--rail);
  margin: 0 auto;
  padding: 18px 0 0;
}

.masthead__frame {
  display: flex; align-items: center; gap: clamp(12px, 2vw, 26px);
  /* The bar is read against the photograph directly below it, so it has
     to carry enough height to hold its own - at 11px it sat at a tenth
     of the picture and read as a strip above the page rather than the
     head of it. */
  padding: 10px clamp(16px, 1.6vw, 26px);
  background: rgba(12, 14, 19, 0.72);
  border: 1px solid var(--edge-2);
  border-radius: var(--radius-card);
}

.masthead__brand {
  flex: 0 0 auto; color: var(--text); text-decoration: none;
  display: flex; align-items: center; gap: clamp(10px, 1vw, 16px);
}
/* The embroidered emblem. Its own black ground is close to the frame's,
   so a hairline edge and a small radius are all it needs to sit as a patch.
   It is exactly as tall as the name beside it (both lines at the name's
   1.08 line height), so it never makes the banner taller than the text. */
/* --banner-name is the size of "Iu Mien" in the banner, on the root so the inner pages'
   heading can be sized from it (.page-head, below). Each breakpoint restates it. */
:root {
  --banner-name: clamp(1.5rem, 1.95vw, 1.9rem);
  --banner-sub: clamp(0.9375rem, 1.08vw, 1.05rem);
}
.masthead__frame {
  --name-top: var(--banner-name);
  --name-sub: var(--banner-sub);
}
.masthead__logo {
  flex: none; display: block;
  width: calc((var(--name-top) + var(--name-sub)) * 1.08); height: auto;
  border-radius: 8px;
  border: 1px solid var(--edge-2);
}
.masthead__name { display: block; min-width: 0; line-height: 1.08; }
.masthead__name-top {
  display: block;
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: var(--name-top); letter-spacing: 0.01em;
  white-space: nowrap;
}
.masthead__name-sub {
  display: block;
  font-size: var(--name-sub); font-weight: 600;
  letter-spacing: 0.13em; text-transform: uppercase;
  white-space: nowrap;
  /* The wordmark's red, the colour "Iu Mien Waac" is set in, everywhere
     the landing page is not - no grey, no arrival, just the name in its
     own colour from the moment the page loads. The landing page brings
     it up from grey instead; see below. */
  color: var(--wordmark);
  transition: color var(--dur-fast) var(--ease-out);
}
.masthead__brand:hover .masthead__name-sub { color: var(--wordmark-lift); }

/* The landing page only. The initiative's name starts in the grey the
   nav links are set in and comes up into the wordmark's red, on exactly
   the schedule the photograph uses to come up into colour - the same
   duration, the same easing, started by the same flag. js/home.js sets
   both classes and removes them once the name has arrived, handing it
   back to the rule above; every other page shows it red from the start.

   The transition lives on the bloomed rule only: going INTO the grey has
   to be instant, or the name visibly fades down before it fades up. */
body.is-arriving .masthead__name-sub { color: var(--text-soft); transition: none; }
body.is-arriving.is-bloomed .masthead__name-sub {
  color: var(--wordmark);
  transition: color var(--dur-cine) var(--ease-out);
}

.masthead__nav { margin-left: auto; }
/* One glass bar holding five equal buttons: every column is as wide as
   the widest label, so the buttons line up as a set rather than hugging
   their words. Rounded, but not a pill. */
.masthead__nav > ul {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  align-items: stretch; gap: 4px;
  list-style: none; margin: 0; padding: 3px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
  border-radius: var(--radius-track);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
  -webkit-backdrop-filter: blur(var(--glass-blur));
  backdrop-filter: blur(var(--glass-blur));
}
.masthead__nav a {
  display: block; position: relative;
  text-align: center;
  color: var(--text-soft); text-decoration: none;
  padding: 5px clamp(12px, 1.4vw, 18px);
  border-radius: var(--radius-button);
  /* Up to a step and a third above the shared label size: these five
     words are the whole navigation and were reading as fine print beside
     the wordmark. --fs-sm is used for labels elsewhere and should not
     move on their account, so the increase lives here.

     It has to be a clamp rather than a flat multiplier. The five links
     sit on one row between the brand and the account controls, and just
     above the 860px breakpoint that row has almost no slack - a flat
     1.35x overflowed the frame by 113px at 880. */
  font-size: clamp(var(--fs-sm), 1.12vw, calc(var(--fs-sm) * 1.35));
  font-weight: 600;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
/* The current page looks exactly like a hovered link. */
.masthead__nav a:hover,
.masthead__nav a.is-active { color: var(--text); background: rgba(255, 255, 255, 0.07); }

/* The account control and the menu button as one group, so flexbox can
   never wrap them apart or put slack between them. */
.masthead__controls {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
}
/* The menu button: three dots stacked upright (⋮) and no box, lighter than a boxed three-bar icon beside the
   embroidered emblem. Open, they draw together into one dot (aria-expanded carries the state
   for assistive tech). --dot and --dot-gap size the dots; the phone/tablet block below
   scales them with the logo. */
.masthead__toggle {
  display: none;
  --dot: 4.5px; --dot-gap: 6px;
  position: relative;
  width: 44px; height: 44px; padding: 0;
  background: transparent; color: #fff; /* always white, open or closed */
  border: 0; border-radius: 12px;
  cursor: pointer;
}
.masthead__toggle:focus-visible { outline: 2px solid var(--edge-hover); outline-offset: 2px; }
.masthead__toggle-bars {
  position: relative; display: block;
  width: var(--dot); height: calc(var(--dot) * 3 + var(--dot-gap) * 2);
}
.masthead__toggle-bars i {
  position: absolute; left: 0; top: calc(50% - var(--dot) / 2);
  width: var(--dot); height: var(--dot);
  border-radius: 999px; background: currentColor;
  transition: top var(--dur-fast) var(--ease-out);
}
.masthead__toggle-bars i:first-child { top: 0; }
.masthead__toggle-bars i:last-child { top: calc(100% - var(--dot)); }
/* Open: the top and bottom dots slide in and merge into the middle one, so a single dot marks
   the open menu. */
.masthead__toggle[aria-expanded="true"] .masthead__toggle-bars i { top: calc(50% - var(--dot) / 2); }

/* ---------- 4. HERO ----------
   The hero scrolls as one piece. The photograph used to drift up by the
   scroll progress while the wordmark beneath it stayed, which opened a
   gap between the search field and the title; before that it also faded.
   Both are gone. */
.hero {
  position: relative;
  /* The photograph sits close under the banner at every width, as it
     does on a phone. */
  padding: 12px 0 0;
  text-align: center;
}

.hero__stage {
  position: relative; z-index: 2;
  width: var(--shell-hero);
  margin: 0 auto;
}

.hero__frame {
  position: relative;
  width: 100%;
  margin: 0;
  aspect-ratio: 1658 / 1069;
  border-radius: var(--radius-hero);
  border: 1px solid var(--edge-2);
  overflow: hidden;
  isolation: isolate;
}

.hero__valley {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; object-fit: cover;
  /* rest state: the archival black-and-white print */
  filter: grayscale(1) contrast(1.06) brightness(0.9);
  transform: scale(1.035);
  transition: filter var(--dur-cine) var(--ease-out),
              transform 4200ms var(--ease-out);
}
.hero.is-bloomed .hero__valley {
  filter: grayscale(0) contrast(1) brightness(1);
  transform: scale(1);
}

/* She starts high and settles down into place, then leaves a little
   faster than the rest of the hero as it scrolls away. */
.hero__woman {
  position: absolute; z-index: 2;
  left: 38.06%;
  width: 25.99%;
  bottom: 2.99%;
  height: auto;
  transform: translate3d(0, -7%, 0);
  opacity: 0;
  transition: transform 3400ms var(--ease-out),
              opacity 1400ms var(--ease-out);
}
/* Once she has settled she stays at full strength; scrolling does not
   thin her out. */
.hero.is-bloomed .hero__woman {
  transform: translate3d(0, 0, 0);
  opacity: 1;
}

.hero__vignette {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background:
    linear-gradient(to top, var(--surface-0) 0%, rgba(3, 4, 7, 0.6) 7%, transparent 22%),
    radial-gradient(120% 110% at 50% 40%, transparent 58%, rgba(0, 0, 0, 0.5) 100%);
}

.hero__search {
  position: relative; z-index: 4;
  display: flex; align-items: center; gap: 10px;
  width: 69.4%;
  margin: -3.6rem auto 0;
  padding: 6px 14px 6px 26px;
  background: #fbfaf8;
  border-radius: var(--radius-field);
  /* the field is itself a light source, as in the comp */
  box-shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.95),
              0 0 0 1px rgba(255, 255, 255, 0.22),
              0 0 26px -2px rgba(255, 255, 255, 0.30),
              0 0 70px 6px rgba(232, 236, 244, 0.14);
  transition: box-shadow var(--dur-mid) var(--ease-out);
}
.hero__search:focus-within {
  box-shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.95),
              0 0 0 1px rgba(255, 255, 255, 0.42),
              0 0 30px 0 rgba(255, 255, 255, 0.42),
              0 0 90px 10px rgba(232, 236, 244, 0.20);
}
.hero__search input {
  flex: 1; min-width: 0;
  border: 0; background: transparent;
  padding: 14px 0;
  font-size: var(--fs-lg); font-style: italic; font-weight: 500;
  color: #17151a;
}
.hero__search input::placeholder { color: #545056; opacity: 1; }
.hero__search input:focus { outline: none; }
/* a plain dark-grey magnifier, no button chrome */
.hero__search button {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  background: transparent; color: #4a4750;
  transition: color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.hero__search button svg { width: 21px; height: 21px; }
.hero__search button:hover { color: #17151a; background: rgba(0, 0, 0, 0.06); }
.hero__search button:focus-visible { outline-color: #4a4750; }

/* THE LIGHT. It rises from under the photograph and the search field,
   arches up onto the wordmark, and falls away down onto the cards. */
.hero__arch {
  position: absolute; z-index: 1; pointer-events: none;
  left: 50%; top: 62%;
  width: min(1150px, 90%);
  height: 620px;
  transform: translateX(-50%);
  background:
    radial-gradient(44% 34% at 50% 24%, rgba(255, 255, 255, 0.11), transparent 72%),
    radial-gradient(64% 52% at 50% 32%, rgba(232, 236, 244, 0.075), transparent 76%);
}

.hero__mark {
  position: relative; z-index: 2;
  font-family: var(--font-mark); font-style: italic; font-weight: 600;
  font-size: var(--fs-mark);
  line-height: 0.98; letter-spacing: 0.005em;
  color: var(--wordmark);
  /* The same space as from the subline down to the cards (.doors), so
     the title sits evenly between the search field and the card row. */
  margin: clamp(13px, 1.6vw, 23px) 0 0;
  /* the one piece of coloured light on the site, and only just. The glow is the shadowed
     tassel red (--red-deep, #8e2620) rather than the brighter --wordmark-lift, so the name
     reads in the same deep red as "ONLINE LANGUAGE INITIATIVE" instead of a hotter one. */
  text-shadow: 0 0 26px rgba(142, 38, 32, 0.45),
               0 0 60px rgba(142, 38, 32, 0.22);
}
.hero__sub {
  position: relative; z-index: 2;
  /* A step above --fs-lg. It is the line that says what the project is,
     read once under the wordmark, and it was sitting at the size of
     ordinary secondary copy. */
  font-size: calc(var(--fs-lg) * 1.15); font-weight: 700; font-style: italic;
  color: var(--text-soft);
  margin: 8px auto 0; max-width: 34em; padding: 0 20px;
}

/* ---------- 5. THE DOORS ---------- */
.doors {
  --doors-gap: clamp(14px, 2.2vw, 32px);
  position: relative;
  /* The lamp bar below the cards used to stand in for this space. It is
     gone, so the padding carries it directly: the bar's own top margin
     plus the 46px it stood in. That reproduced the old spacing exactly,
     which turned out to be more than the row needs now that there is no
     object sitting in it - hence the 0.6, taking 40% off. 137px to
     82px at 1512. */
  /* No padding below: the gap from the cards to the editorial section's
     hairline is the section's own top padding, set to match the space
     from that hairline down to the photograph. */
  padding: clamp(13px, 1.6vw, 23px) 0 0;
}
/* the hero's light continuing down onto the three cards */
.doors__spot {
  position: absolute; z-index: 0; pointer-events: none;
  left: 50%; top: -190px;
  width: min(1080px, 92%); height: 520px;
  transform: translateX(-50%);
  background: radial-gradient(52% 48% at 50% 50%, rgba(232, 236, 244, 0.10), transparent 74%);
}

.doors__row {
  position: relative; z-index: 1;
  /* minmax(0, 1fr), not 1fr. A bare 1fr will not shrink below its card's
     min-content width, and the cards carry hidden width: Translate
     reserves room for "Faan waac", Learn stacks "hoqc Mien waac" under its
     label. Where the row runs narrow those widths win, the columns come
     out unequal, and aspect-ratio turns that into unequal heights. */
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--doors-gap);
  width: var(--shell-cards);
  margin: 0 auto; padding: 0;
  list-style: none;
}

.door {
  --near: 0;
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 499 / 748;
  padding: 32px 26px;
  text-align: center; text-decoration: none;
  color: var(--text);
  border: 1px solid var(--edge-2);
  border-radius: var(--radius-card);
  /* the page's own color as the fill, with the spotlight from above
     landing along the top edge */
  background:
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.075) 0%,
      rgba(255, 255, 255, 0.022) 26%,
      rgba(255, 255, 255, 0.004) 56%,
      rgba(255, 255, 255, 0) 100%),
    var(--surface-0);
  transition: box-shadow var(--dur-mid) var(--ease-out);
  box-shadow: 0 0 calc(26px + 46px * var(--near)) -18px
              rgba(255, 255, 255, calc(0.05 + 0.30 * var(--near)));
}

/* The rim. A 1px ring painted with a radial gradient that follows the
   pointer, so the edge nearest the cursor lights up - tracked from
   anywhere on the page, not only while hovering. The two-layer mask
   is what confines the paint to the ring itself. */
.door__rim {
  position: absolute; inset: 0; z-index: 3;
  border-radius: inherit; pointer-events: none;
  padding: 1px;
  background: radial-gradient(260px circle at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, calc(0.30 + 0.70 * var(--near))) 0%,
      rgba(255, 255, 255, calc(0.14 + 0.26 * var(--near))) 55%,
      rgba(255, 255, 255, 0.10) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}

.door__label {
  position: relative; z-index: 4;
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: clamp(2rem, 3.1vw, 3rem);
  line-height: 1.1;
}
/* the Dictionary label needs a backing, since the words bloom behind it */
.door--cipher .door__label {
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.92), 0 0 6px rgba(0, 0, 0, 0.8);
}

/* ---- the labels' own behaviour under the cursor ----
   Nothing here changes a label's size or family: all three titles stay
   set in the same type. White is the resting state and both return to
   it. */

.door__char {
  display: inline-block;
  white-space: pre;                 /* keeps the space in "faan waac" */
}

/* Learn: a wipe between two words, driven by how far across the card the
   pointer has travelled. --wipe runs 0% -> 100% from js/doors.js.

   The two layers never overprint because each is clipped to the opposite
   side of the wipe line: the English is cut away from the left exactly
   where the Iu Mien is revealed. "Learn" is the same bright white as the
   other card labels; the Iu Mien that replaces it arrives in the
   wordmark's red, the colour "Iu Mien Waac" is set in above the cards,
   so the language names itself in its own colour. */
.door__label--reveal {
  display: grid;
  position: relative;
}
.door__label--reveal > * {
  grid-area: 1 / 1;
  /* In a narrow card "hoqc Mien waac" wraps to two lines and makes the
     cell two lines tall. Centred, "Learn" sits in the middle of it, on
     the same line as the other two cards' labels; stretched, it rode on
     the top line, half a line above them. */
  align-self: center;
  color: #fff;
}
.door__reveal-base {
  /* hidden on the left, where the other word has already arrived */
  clip-path: inset(0 0 0 var(--wipe, 0%));
}
.door__reveal-top {
  /* shown only on the left, up to the wipe line */
  clip-path: inset(0 calc(100% - var(--wipe, 0%)) 0 0);
  /* the wordmark red, carrying the same glow it has in the hero so it
     stays legible against the near-black card */
  color: var(--wordmark);
  text-shadow: 0 0 22px rgba(211, 26, 30, 0.42),
               0 0 52px rgba(211, 26, 30, 0.20);
}
/* the edge of the wipe, so the change reads as a moving line rather
   than letters blinking in and out */
.door__label--reveal::after {
  content: "";
  position: absolute;
  top: -0.1em; bottom: -0.1em;
  left: var(--wipe, 0%);
  width: 1px;
  background: rgba(255, 255, 255, 0.7);
  opacity: var(--wipe-edge, 0);
  transition: opacity var(--dur-fast) var(--ease-out);
  pointer-events: none;
}

/* Translate: the label resolves letter by letter into "Faan waac", its
   own name in Iu Mien, and snaps straight back on the way out. Reserving the width of the
   wider of the two words stops the card's centre shifting while the
   letters change over - js/doors.js measures it. */
.door__label--scramble {
  display: inline-block;
  min-width: min(var(--scramble-w, 0px), 100%);
}
/* The Iu Mien word's letters carry the wordmark's red - the same red as
   "Iu Mien Waac" above the cards and the Learn card's reveal - and the
   finished word holds it for as long as the pointer is on the card. */
.door__label--mien .door__char {
  color: var(--wordmark);
  text-shadow: 0 0 22px rgba(211, 26, 30, 0.42),
               0 0 52px rgba(211, 26, 30, 0.20);
}
/* But only once a letter has landed. Until then the slot still shows
   the outgoing word's letter, in the ordinary label white, and the red
   spreads along the word as each letter lands. Must follow the rule
   above: same specificity, so source order is what settles it. */
.door__char--pending,
.door__label--mien .door__char--pending {
  color: var(--text);
  text-shadow: none;
}

/* ---- the Dictionary card's word bloom ----
   Iu Mien words revealed through a pointer-tracked mask, set in the
   same face as the label and a step smaller. js/home.js reshuffles
   the arrangement each frame. */
.door--cipher { --mx: 50%; --my: 50%; }

.door__words {
  position: absolute; inset: 0; z-index: 1;
  border-radius: inherit; overflow: hidden;
  /* No padding, and justified: the block has to reach all four edges of
     the card, so nothing bare is left to find under the mask. The rows
     run past the bottom edge and overflow trims them. */
  padding: 0;
  text-align: justify;
  font-family: var(--font-mark); font-style: italic; font-weight: 600;
  font-size: clamp(1.05rem, 1.55vw, 1.5rem);
  line-height: 1.32;
  color: rgba(255, 255, 255, 0.94);
  word-spacing: 0.4em;
  opacity: 0;
  transition: opacity var(--dur-mid) var(--ease-out);
  -webkit-mask-image: radial-gradient(circle 215px at var(--mx) var(--my),
      #000 0%, rgba(0, 0, 0, 0.62) 46%, transparent 74%);
          mask-image: radial-gradient(circle 215px at var(--mx) var(--my),
      #000 0%, rgba(0, 0, 0, 0.62) 46%, transparent 74%);
}

/* Embers in the word bloom. Roughly one word in seven comes up in the
   wordmark's red rather than white - the same red as "Iu Mien Waac"
   above the cards. js/home.js picks them one per run of seven words, so
   they stay spread across the block instead of clumping the way a flat
   random draw does. */
.door__word--ember {
  color: var(--wordmark);
  text-shadow: 0 0 14px rgba(211, 26, 30, 0.40);
}
.door--cipher:hover .door__words,
.door--cipher:focus-visible .door__words { opacity: 1; }

/* ---- the same card row, used inside an inner page ----
   Full-sized: the Learn hub carries nothing but these three, so they get
   the home page's proportions rather than being squeezed. Only the width
   and the surrounding space differ - the fill, the rim and the pointer
   light are the shared .door. */
.doors--inline {
  --doors-gap: clamp(14px, 2.2vw, 32px);
  padding: 0 0 clamp(30px, 4vw, 52px);
}
.doors--inline .doors__row {
  /* The home page's row width, so Learn's cards come out the same size as
     the home page's; never wider than the content column. */
  width: min(100%, var(--shell-cards));
}
/* Three tall cards side by side get narrow in a content column, so the
   labels wrap hard. Give them a little more room to breathe than the
   home page's, and let a long name break across lines cleanly. */
.doors--inline .door { padding: 30px 22px; }
.doors--inline .door__label {
  font-size: clamp(1.3rem, 2vw, 1.9rem);
  text-wrap: balance;
}

/* A card with nowhere to go yet: no link and no pointer cursor. The
   label is as bright as its neighbours, so the row reads as three
   equals rather than two and a gap. */
.door--soon {
  cursor: default;
}
.door__note {
  position: relative;
  z-index: 4;
  margin-top: 10px;
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* ---------- REVEAL ---------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
[data-reveal].is-in { opacity: 1; transform: none; }

.rule {
  height: 1px; border: 0;
  margin: 0 auto; width: var(--rail);
  background: linear-gradient(to right, transparent, var(--edge-2) 12%,
              var(--edge-2) 88%, transparent);
}

/* ---------- 6. THE EDITORIAL PASSAGE ----------
   The photograph is pinned and does not move. The copy travels UP ACROSS
   it as you scroll - the two share one grid cell rather than sitting in
   two columns, which is what lets them overlap.

   The pin is `position: sticky` and nothing else. No scroll listener, no
   observer, no library. The media fills the cell (align-self: stretch),
   the cell is as tall as the copy column's min-height, and the figure
   sticks inside that. One variable sets the whole interaction, and it
   degrades to a plain stack wherever sticky is unsupported.

   The overlap, worked out rather than eyeballed: the photograph is 82%
   of the width and right-aligned, so its left edge lands at 18%. The
   copy is 460px wide from the left edge. Overlap = 460 - 18% of the
   container, which at this width is about three fifths of the copy
   block - as asked.

   Copy that sits on a photograph needs a shadow under it or it is not
   readable, so it has one. That is the only effect here. */
.editorial {
  /* The section's height, and therefore how far the photograph stays
     pinned. js/home.js sets it to exactly what the effect needs (the
     photograph plus the copy's travel). Without the script - reduced
     motion, or no JS - it stays 0 and the section is just its content,
     with no pinned stretch of empty page. */
  --editorial-scroll: 0px;
  /* Where the photograph parks itself in the viewport while pinned. */
  --editorial-pin-top: clamp(28px, 10vh, 116px);

  max-width: 1080px;
  margin: 0 auto;
  /* The hairline at the top of this section separates it from the card
     row above. The space above the line equals the space below it - the
     same clamp as .editorial__inner's padding-top - so the line sits
     midway between the cards and the photograph. */
  padding: clamp(30px, 4.5vw, 60px) 20px clamp(40px, 6vw, 72px);
}

.editorial__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);

  /* The scroll distance now lives here rather than on the copy, because
     the copy has to be free to move inside the cell instead of defining
     its height. This is what both sticky children pin against. */
  min-height: var(--editorial-scroll);

  /* the one structural line */
  border-top: 1px solid var(--edge-2);
  padding-top: clamp(30px, 4.5vw, 60px);
}

/* Both children occupy the SAME cell, so the copy can cross the
   photograph instead of sitting beside it. */
.editorial__text,
.editorial__media { grid-area: 1 / 1; }

/* The copy pins alongside the photograph and is then pushed DOWN across
   it as you scroll - scrolling down moves it down the picture, from the
   top edge to the bottom, rather than letting it ride up and out.

   That cannot come from flow, which only ever moves content the other
   way, so js/home.js writes two values: --editorial-p, the 0->1 progress
   through the pinned stretch, and --editorial-travel, the distance from
   the top of the photograph to the bottom. Two custom properties on one
   rAF-throttled listener; the transform itself runs on the compositor. */
.editorial__text {
  z-index: 2;                       /* the copy is always on top */
  /* Wide enough to hold the longest authored line at this size without
     it wrapping and breaking the grouping. Paired with the photograph's
     width below to keep the overlap at about three fifths of the copy. */
  width: min(54%, 560px);
  align-self: start;
  position: sticky;
  top: var(--editorial-pin-top);
  /* Starts one lead above the picture's top edge and finishes one lead
     below its bottom, so the overhang matches at both ends. The hold
     then keeps it there until the rule below comes up to meet it, and
     stops it crossing that rule (js/home.js). */
  transform: translate3d(
      0,
      calc(var(--editorial-p, 0) * var(--editorial-travel, 0px) - var(--editorial-lead, 0px)
           + var(--editorial-hold, 0px)),
      0);
  will-change: transform;
}

.editorial__title {
  /* sits a little above the body copy, and reaches wider than it does */
  margin: 0 0 0.55em;
  width: max-content;
  max-width: 150%;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: clamp(2rem, 3.6vw, 3.05rem);
  line-height: 1.1;
  letter-spacing: 0.002em;
  color: #fff;
  /* The shadow is an impression of the letters, not a cloud behind the
     block: every layer is tight enough to keep the glyph's own shape, so
     it reads as the type pressed into the photograph rather than as a
     dark box sitting under it. */
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 1),
    0 2px 2px rgba(0, 0, 0, 0.96),
    0 3px 6px rgba(0, 0, 0, 0.85);
}

.editorial__para {
  margin: 0 0 1.05em;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(1.32rem, 1.85vw, 1.78rem);
  line-height: 1.52;
  /* white, the whole way down, on every screen */
  color: #fff;
  /* letter-shaped, like the heading - see the note there */
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 1),
    0 2px 2px rgba(0, 0, 0, 0.96),
    0 3px 5px rgba(0, 0, 0, 0.85);
}
.editorial__para:last-child { margin-bottom: 0; }

.editorial__media {
  z-index: 1;
  /* Set against the copy's width so the overlap stays at ~3/5 of the
     copy block: overlap = copyWidth - (1 - thisWidth) x container. */
  width: 78.5%;
  justify-self: end;      /* held to the right, so the copy crosses it */
  align-self: stretch;    /* fills the cell - this is the pin's headroom */
  /* starts below the heading, so the heading rests above the photograph */
  padding-top: clamp(48px, 6vw, 96px);
}

.editorial__sticky {
  position: sticky;
  top: var(--editorial-pin-top);
  margin: 0;
}
.editorial__sticky img {
  width: 100%;
  height: auto;
  border: 1px solid var(--edge-2);
}

/* ---------- 7. ALLIANCE ---------- */
.alliance {
  /* From the rule above down to the heading: the same space as from the
     card row down to the editorial hairline (.editorial's top padding). */
  /* Nothing below: it is the last section before the footer, and the
     footer's own margin is the only gap between them. */
  padding: clamp(30px, 4.5vw, 60px) 20px 0;
  text-align: center;
}
.alliance__head {
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: clamp(2.1rem, 4.4vw, 3.6rem);
  line-height: 1.12;
  color: var(--text);
  margin: 0 0 clamp(34px, 5vw, 64px);
}

.alliance__list {
  list-style: none; margin: 0 auto clamp(40px, 6vw, 76px); padding: 0;
  /* Columns size to their content and the pair is centred, rather than
     two equal halves of a wide block - otherwise each mark floats in
     the middle of its own column and they read as far apart however
     small the gap between the columns is. */
  display: grid; grid-template-columns: repeat(2, auto);
  justify-content: center;
  /* two explicit rows - logo, name - so subgrid below can pin each
     partner's two parts to the same lines as the other's */
  grid-template-rows: auto auto;
  gap: 13px clamp(28px, 4vw, 60px);
  max-width: 820px;
}
/* Subgrid pins the logo and the name of both partners to the same two
   lines, however differently their marks are proportioned.
   Where subgrid is unavailable this falls back to a plain column, and
   the reserved logo height and two-line name below keep it close. */
.partner {
  display: grid;
  grid-row: span 2;
  grid-template-rows: subgrid;
  justify-items: center;
  gap: 13px; text-align: center;
}
@supports not (grid-template-rows: subgrid) {
  .partner { display: flex; flex-direction: column; align-items: center; }
}
.partner__logo-box {
  display: flex; align-items: center; justify-content: center;
  height: clamp(126px, 13.2vw, 182px);
}
/* The logo files are not in the repo yet. Painting them as background
   images means a missing file shows nothing at all rather than a
   broken-image icon, and they appear the moment the files land. */
/* Shown on the page's own ground, with no plaque behind them. Both are
   dark artwork on a transparent field, so the darkest parts sink into
   the background - see the note in the partners README. */
.partner__logo {
  width: clamp(140px, 14vw, 206px);
  height: 100%;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
.partner__logo--mien {
  background-image: url("../img/partners/mien-international-enrichment-network.e7a6d1e7c34d.png");
}
.partner__logo--literacy {
  background-image: url("../img/partners/iu-mien-literacy-projects.ea55ede78324.png");
  /* the literacy mark is a wide wordmark, not a badge */
  width: clamp(224px, 24.5vw, 346px);
}
.partner__name {
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: clamp(1.28rem, 2.15vw, 1.85rem);
  line-height: 1.2;
  color: var(--text);
  margin: 0; max-width: 13em;
  /* Two lines reserved so the names stay level where subgrid is not
     available. The text sits at the TOP of its row rather than centred,
     so the first line of a one-line name and of a two-line name share a
     baseline - which is what makes the two titles read as aligned. */
  min-height: 2.4em;
  align-self: start;
}
.partner__name a {
  color: inherit; text-decoration: none;
  border-bottom: 1px solid var(--edge-2);
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.partner__name a:hover { color: #fff; border-bottom-color: rgba(255, 255, 255, 0.7); }

.alliance__sub {
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: clamp(1.6rem, 2.8vw, 2.3rem);
  color: var(--text-soft);
  margin: 0 0 24px;
}
.alliance__tbd {
  list-style: none; margin: 0 auto; padding: 0;
  max-width: var(--measure);
  display: flex; flex-direction: column; gap: 12px;
  /* On the token rather than a clamp of its own. The clamp's rem terms
     followed the root, which --content-scale does not touch, so this
     list stayed at 16.2px while everything around it in the section grew
     - the one item in the block the scale could not reach. These are
     organisation names people read. */
  font-size: var(--fs-base);
  font-style: italic; font-weight: 500;
  color: var(--text-soft);
}
.alliance__note {
  margin: 32px auto 0; max-width: 38em;
  font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-dim);
}

/* ---------- 8. FOOTER ---------- */
/* On the home page the alliance note is the last line before the footer:
   one modest step between them, not the section padding and the footer
   margin stacked. */
body.is-home .site-footer { margin-top: clamp(32px, 4vw, 48px); }
.site-footer {
  position: relative; isolation: isolate; overflow: hidden;
  margin-top: clamp(40px, 6vw, 80px);
  /* A full step down from the edge to the lockup, more than the sides. */
  padding: clamp(36px, 5vw, 64px) clamp(20px, 3vw, 46px) 0;
  border-top: 1px solid var(--edge-2);
  color: var(--text-soft);
  font-size: var(--fs-sm); font-weight: 500;
}
/* weighted toward the brand side so the name stands out */
.site-footer__glow {
  position: absolute; left: 30%; top: -40px; z-index: -1;
  width: min(900px, 90%); height: 420px;
  transform: translateX(-50%);
  pointer-events: none;
  background:
    radial-gradient(46% 60% at 50% 20%, rgba(255, 255, 255, 0.13), transparent 72%),
    radial-gradient(70% 90% at 50% 10%, var(--glow-silver), transparent 76%);
}

.site-footer__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 2fr);
  gap: clamp(32px, 5vw, 72px);
  /* The band runs the full width of the window; its contents do not.
     --rail is the width the masthead and the page content use, so the
     columns below line up with the columns above rather than sitting on
     their own measure. */
  max-width: var(--rail); margin: 0 auto;
}

.site-footer__mark {
  /* The masthead's lockup, sized to the footer it sits in: "ONLINE
     LANGUAGE INITIATIVE" at the column headings' size and tracking
     (EXPLORE, PROJECT…), "Iu Mien" 1.6x that as in the banner, and the
     logo exactly as tall as the two lines of the name. */
  --foot-sub: var(--fs-xs);
  --foot-top: calc(var(--fs-xs) * 1.6);
  /* Top-aligned: where the column is too narrow for the subtitle on one
     line (the desktop's four-column footer), it wraps, and a centred logo
     would drop below the name. */
  display: flex; align-items: flex-start; gap: clamp(10px, 1vw, 16px);
  text-decoration: none;
  color: var(--text); line-height: 1.08;
  margin-bottom: 16px;
}
.site-footer__logo {
  flex: none; display: block; height: auto;
  width: calc((var(--foot-top) + var(--foot-sub)) * 1.08);
  border-radius: 10px;
  border: 1px solid var(--edge-2);
}
.site-footer__mark-name { display: block; min-width: 0; }
.site-footer__mark-top {
  display: block;
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: var(--foot-top);
}
.site-footer__mark-sub {
  display: block;
  font-size: var(--foot-sub); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text);
}
.site-footer__mark:hover .site-footer__mark-top { color: #fff; }
.site-footer__brand p {
  margin: 0; max-width: 32em;
  /* A step up on the footer's shared size: this is the one paragraph of
     reading matter down here, among links and labels. */
  /* The column links' size: the footer reads as one set. */
  font-size: var(--fs-sm); font-weight: 500; line-height: 1.65;
  color: var(--text-dim);
}

.site-footer__cols {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 40px);
}
.site-footer__col h2 {
  font-family: var(--font-body);
  font-size: var(--fs-xs); font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text); margin: 0 0 16px;
}
.site-footer__col ul { list-style: none; margin: 0; padding: 0; }
.site-footer__col li + li { margin-top: 11px; }
.site-footer__col a {
  color: var(--text-dim); text-decoration: none;
  font-size: var(--fs-sm); font-weight: 500;
  transition: color var(--dur-fast) var(--ease-out);
}
.site-footer__col a:hover { color: var(--text); }

.site-footer__bar {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 10px 26px;
  max-width: var(--rail);
  margin: clamp(38px, 5vw, 64px) auto 0;
  /* The copyright sits close under its rule: 9px, two-fifths of the 22px
     it had. */
  padding: 9px 0 30px;
  border-top: 1px solid var(--edge-1);
}
.site-footer__bar p { margin: 0; font-size: var(--fs-xs); font-weight: 600; color: var(--text-dim); }

/* ---------- 9. INNER-PAGE COMPONENTS ----------
   Depth comes from the surface ladder - each thing that sits on top
   of another is a step lighter, with a 1px edge. Nothing is black. */

/* Running prose gets the measure. Direct children only: a paragraph
   sitting on its own in the column is a line of reading text and wants
   bounding, while one inside a card or a grid cell has already been
   given its width by the thing containing it, and capping it again
   would strand it in its own box. */
.content > p,
.content > ul,
.content > ol {
  max-width: var(--measure);
}

/* Prose inside a card is NOT given the measure: the card is its box and
   it fills it. Capping the paragraph left 258px of empty card beside
   every line, which read as a mistake. The card's own width is the
   bound, and on a page of cards that is the shape the reader sees. */

/* The reading text on the inner pages, scaled up from the design's own
   size. rem is absolute to the document, so a font-size here would move
   nothing that uses the tokens - the tokens themselves are restated,
   and everything inside inherits the larger scale from them.

   Scoped to the page body on purpose: the masthead, the footer and the
   landing page are composition and keep the design's size. */
.page-head,
.content,
/* The landing page's own reading matter, and the footer on every page.
   These sat at the design's size while the inner pages were scaled, and
   the design's size under --ui-scale is 11.3-12px here - under the floor
   --fs-xs documents, and half what the inner pages read at. They are
   text people are meant to read, not composition. */
.alliance,
.notice--center,
.site-footer {
  --fs-xs:   calc(0.9375rem * var(--content-scale));
  --fs-sm:   calc(1rem      * var(--content-scale));
  --fs-base: calc(1.125rem  * var(--content-scale));
  --fs-lg:   calc(1.375rem  * var(--content-scale));
  --fs-xl:   calc(1.75rem   * var(--content-scale));
  --fs-2xl:  calc(2.25rem   * var(--content-scale));
  --fs-3xl:  calc(3rem      * var(--content-scale));
  font-size: var(--fs-base);
}

.page-head {
  position: relative;
  padding: clamp(30px, 4vw, 52px) 0 clamp(22px, 3vw, 34px);
  border-bottom: 1px solid var(--edge-2);
  overflow: hidden;
}
.page-head::before {
  content: ""; position: absolute; z-index: 0;
  left: 50%; top: -60%;
  width: min(760px, 80%); height: 220%;
  transform: translateX(-50%);
  pointer-events: none;
  background: radial-gradient(50% 46% at 50% 50%, var(--glow-silver), transparent 72%);
}
/* The inner pages' heading sits on the banner's rail with no padding of its own, so "Dictionary"
   starts where the banner, the search row and the cards start (it was 17px outside them on
   desktop and tablets, 6px inside on phones). Its size follows the banner's name: the title
   1.5x "Iu Mien", the subline 0.8x, never under the 15px floor. .wrap is in the selectors
   to outrank the phone rule that sets every h1 to --fs-2xl. */
.page-head .wrap { position: relative; z-index: 1; max-width: var(--rail); padding: 0; }
.page-head .wrap h1 { font-size: calc(var(--banner-name) * 1.5); }
.page-head .wrap p { font-size: max(15px, calc(var(--banner-name) * 0.8)); }
.page-head h1 { color: var(--text); margin: 0; font-style: italic; font-weight: 600; }
/* No reading measure: a subheading is one line under the title, and at
   68ch the longer ones (Translation's) broke onto a second line with
   half the head still empty beside them. It runs the head's width. */
.page-head p { color: var(--text-soft); margin: 8px 0 0; }

main.content { padding: 34px 0 26px; }

/* ---------- THE 404 PAGE ----------
   Black, the number small and high in the dark, and the woven stool
   (img/stool.webp, its own transparent ground) a quarter of the width,
   standing a long step below it. The page ends under the stool, and the
   banner and footer stay. */
body.page-404 { background: #000; }
body.page-404 .site-footer { margin-top: 0; }   /* the page ends under the stool */
.notfound {
  display: flex; flex-direction: column; align-items: center;
  /* the same space above the number as below the stool */
  padding: clamp(44px, 7.5svh, 68px) 20px;
  text-align: center;
}
/* The number alone is set in a heavy grotesque, not the site's serif: a
   solid block of figures, the way an error page traditionally reads. System
   faces only, so nothing extra loads for it - Arial Black where installed
   (macOS, Windows), heavy Helvetica on iOS. */
.notfound__code {
  margin: 0;
  font-family: "Arial Black", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 900; font-style: normal;
  font-size: clamp(3.4rem, 7.5vw, 6.5rem); line-height: 1;
  letter-spacing: -0.02em;
  color: #fff;
}
.notfound__stool {
  display: block;
  width: clamp(220px, 25vw, 360px); height: auto;
  margin-top: clamp(96px, 19svh, 170px);
}

/* A centred notice at the top of an inner page sits between the page
   head above and the cards below, and the space either side of it should
   match. Above it is main.content's own 34px of top padding, so below it
   is the same - .notice--center's own 96px left it floating nearer the
   heading than the content it introduces.

   Scoped to .content deliberately: the landing page's notice is the last
   thing before the footer rather than a lead-in, and keeps the larger
   margin that separates it from the footer. */
.content > .notice--center { margin-bottom: 34px; }

.card {
  position: relative;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.045) 0%, rgba(255, 255, 255, 0) 42%),
    var(--surface-1);
  border: 1px solid var(--edge-2);
  border-radius: var(--radius-card);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset,
              0 18px 40px -30px rgba(0, 0, 0, 0.9);
  padding: 24px 26px;
  margin-bottom: 20px;
}
.card h2:first-child, .card h3:first-child { margin-top: 0; }

.notice {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0) 60%),
    var(--surface-2);
  border: 1px solid var(--edge-2);
  border-radius: 12px;
  padding: 16px 20px;
  font-size: var(--fs-sm); font-weight: 500;
  line-height: 1.6;
  color: var(--text-soft);
  /* The box may be as wide as the column; the sentence inside it may
     not. Padding is added on so the measure is the text's own. */
  max-width: calc(var(--measure) + 40px);
  margin: 0 0 22px;
}
.notice strong { color: var(--text); }
.notice--center {
  max-width: 820px;
  margin: 0 auto clamp(56px, 7vw, 96px);
  text-align: center;
}

.toolbar { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 22px; }
.toolbar input[type="search"], .toolbar textarea, .toolbar select {
  flex: 1; min-width: 220px;
  background: var(--surface-2); border: 1px solid var(--edge-2);
  border-radius: 12px; padding: 13px 16px;
  font-size: var(--fs-sm); font-weight: 500; color: var(--text);
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.toolbar input::placeholder, .toolbar textarea::placeholder { color: var(--text-dim); }
.toolbar input:focus, .toolbar textarea:focus, .toolbar select:focus {
  outline: none;
  border-color: var(--edge-3);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.16);
}

.btn {
  border: 1px solid var(--edge-2); cursor: pointer;
  text-decoration: none; display: inline-block;
  background: var(--surface-2); color: var(--text);
  padding: 12px 20px; border-radius: 12px;
  font-size: var(--fs-sm); font-weight: 600;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.btn:hover {
  background: var(--surface-3); border-color: var(--edge-3);
  box-shadow: 0 0 24px -8px rgba(255, 255, 255, 0.28);
}
.btn.ghost { background: transparent; color: var(--text); border-color: var(--edge-3); }
.btn.ghost:hover { border-color: rgba(255, 255, 255, 0.65); background: transparent; }

.pill-toggle {
  display: inline-flex; background: var(--surface-2);
  border: 1px solid var(--edge-2);
  border-radius: var(--radius-pill); padding: 4px;
}
.pill-toggle button, .pill-toggle a {
  border: 0; background: transparent; cursor: pointer;
  padding: 9px 18px; border-radius: var(--radius-pill);
  white-space: nowrap; text-align: center;
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-soft);
  text-decoration: none; display: block;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.pill-toggle button:hover, .pill-toggle a:hover { color: var(--text); background: var(--surface-3); }
.pill-toggle button.on, .pill-toggle a.on {
  background: rgba(255, 255, 255, 0.92); color: #14171d;
}

.entry { border-top: 1px solid var(--edge-3); }
.entry .headword { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; }
.entry .headword .word {
  font-size: 1.7rem; font-family: var(--font-display);
  font-style: italic; font-weight: 600; color: var(--text);
}
.entry .headword .ipa { color: var(--text-dim); font-size: var(--fs-sm); font-weight: 600; }
.entry .pos {
  font-size: var(--fs-xs); font-weight: 700;
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--edge-2);
  border-radius: 999px; padding: 3px 12px;
}
/* The part of speech is an <abbr> whose title is the full name; no dotted underline in a pill. */
.entry abbr.pos { text-decoration: none; cursor: help; }
.entry .tone-chip {
  font-size: var(--fs-xs); font-weight: 600;
  background: var(--surface-3); border: 1px solid var(--edge-2);
  border-radius: 999px; padding: 3px 12px; color: var(--text-soft);
}
.entry .example {
  background: var(--surface-2); border: 1px solid var(--edge-1);
  border-radius: 10px; padding: 11px 14px; margin: 10px 0 0;
}
.entry .cultural { margin-top: 10px; font-size: var(--fs-sm); font-weight: 500; color: var(--text-soft); }
.entry .cultural strong { color: var(--text); }

.entry .senses { margin: 10px 0 0; padding-left: 1.4em; }
.entry .senses li { margin-bottom: 4px; }
.entry .senses li::marker { color: var(--text-dim); font-weight: 700; }

.entry .phrase-list { margin-top: 14px; border-top: 1px solid var(--edge-2); padding-top: 12px; }
.entry .phrase-list h4 {
  margin: 0 0 8px; font-size: var(--fs-xs); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-dim); font-weight: 700;
}
.entry .phrase { border-left: 2px solid var(--edge-2); padding-left: 12px; margin-bottom: 10px; }
.entry .phrase .mien { font-size: var(--fs-base); color: var(--text); }
.entry .phrase .senses { margin-top: 4px; font-size: var(--fs-sm); }

.entry .entry-foot { margin-top: 14px; margin-bottom: 0; display: flow-root; } /* holds the floated button inside the card */
.entry .entry-foot .btn.small { float: right; padding: 7px 13px; font-size: var(--fs-xs); }

/* Lexicon (lexicon_app): result list and entry page */
/* The dictionary's search field and button as one control. The border and focus ring
   belong to the wrapper; the field inside is bare. */
.typeahead-wrap { position: relative; flex: 1; min-width: 220px; display: flex; }

/* The dictionary's search row: direction picker | search field + button, then Ignore tone
   letters on a line of its own where the direction toggle used to be. */
.lex-search { align-items: stretch; }
.lex-search .lex-search__tl { flex-basis: 100%; }
.dir-select { position: relative; display: flex; }
.dir-select select {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--edge-2); border-radius: 12px;
  flex: none; min-width: 0; /* not the generic .toolbar select rule's flex: 1 / 220px */
  padding: 0 44px 0 16px; min-height: 48px;
  font: inherit; font-size: var(--fs-sm); font-weight: 600;
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.dir-select select:hover { background: var(--surface-3); border-color: var(--edge-3); }
.dir-select select:focus-visible { outline: none; border-color: var(--edge-3); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.16); }
.dir-select select option { background: var(--surface-4); color: var(--text); }
.dir-select__chevron {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  color: var(--text-soft); pointer-events: none;
}
/* ---- the translator: two boxes side by side ----
   Each box has its language picker top left (the dictionary's
   .dir-select) and its button underneath it on the left. The output box
   is the same size as the input, so the pair reads as one instrument. */
/* The two columns share the grid's rows (display: contents), so both
   boxes are one row and equally tall whatever their buttons do: a wrapped
   button label lengthens the row under the boxes, not one box. */
/* The outer box is built like the home page's cards (.door): the page's
   own colour with the spotlight landing along its top edge, and the same
   pointer-tracked rim (.door__rim, lit by js/doors.js through data-door),
   so it brightens as the cursor comes near. Its corner handle sets
   --tr-pane-h, the panes' height; until then they size themselves. */
.tr-shell {
  --near: 0;
  position: relative; isolation: isolate;
  background:
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.075) 0%,
      rgba(255, 255, 255, 0.022) 26%,
      rgba(255, 255, 255, 0.004) 56%,
      rgba(255, 255, 255, 0) 100%),
    var(--surface-0);
  border: 1px solid var(--edge-2); border-radius: var(--radius-card);
  /* A third of the home cards' glow. The box is wide and the cursor is
     nearly always near it while someone types, so at full strength it
     stayed lit the whole time. */
  transition: box-shadow var(--dur-mid) var(--ease-out);
  box-shadow: 0 0 calc(22px + 18px * var(--near)) -18px
              rgba(255, 255, 255, calc(0.04 + 0.10 * var(--near)));
  padding: clamp(12px, 1.6vw, 20px) clamp(12px, 1.6vw, 20px) clamp(28px, 2.6vw, 34px);
  margin-bottom: 22px;
}
/* Both panes are halves of one glass panel, split by a hairline, as in
   Google Translate. The panel is the grid's ::before, laid across the
   first row behind the two halves; the buttons sit in the second row,
   each under its own half. Every item is placed explicitly: the columns
   are display: contents, and auto-placement would step around the
   panel rather than over it. */
.tr-panes {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: var(--tr-pane-h, auto) auto;
  column-gap: 0; row-gap: clamp(10px, 1.2vw, 14px);
}
.tr-col { display: contents; }
.tr-col:first-child > .tr-pane     { grid-area: 1 / 1; }
.tr-col:last-child > .tr-pane      { grid-area: 1 / 2; }
.tr-col:first-child > .tr-actions  { grid-area: 2 / 1; }
.tr-col:last-child > .tr-actions   { grid-area: 2 / 2; }
/* The panel is the Dictionary's search field (.searchbox): the same
   solid --surface-2, hairline edge and 12px corner, so the two "type
   something in" tools look like one product. Clicking in changes
   nothing: the cursor shows where you are. */
.tr-panes::before {
  content: ""; grid-area: 1 / 1 / 2 / 3;
  position: relative; z-index: 0;
  background: var(--surface-2);
  border: 1px solid var(--edge-2);
  border-radius: 12px;
}
/* The rim, toned down to match: a softer, smaller light at the edge. */
.tr-shell > .door__rim {
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, calc(0.16 + 0.28 * var(--near))) 0%,
      rgba(255, 255, 255, calc(0.10 + 0.08 * var(--near))) 55%,
      rgba(255, 255, 255, 0.08) 100%);
}
.tr-pane {
  position: relative; z-index: 1;
  min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
  padding: 12px 14px;
}
/* the separator: a hairline in the field's edge colour, inset from the
   panel's top and bottom so it reads as a division, not a seam */
.tr-col:last-child > .tr-pane::before {
  content: ""; position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 1px; background: var(--edge-2);
}
.tr-pane__head { display: flex; margin-bottom: 4px; }
.tr-panes:not([style*="--tr-pane-h"]) .tr-pane__text { min-height: 11rem; }
/* The pickers and the two buttons are the Dictionary's Search button:
   one step lighter than the field (--surface-3), its hairline edge and
   12px corner, kept slim - a short row, not a block. */
.tr-lang select {
  min-height: 36px; padding-left: 14px; padding-right: 40px;
  background: var(--surface-3); border-color: var(--edge-2);
  border-radius: 12px;
}
.tr-lang select:hover { background: var(--surface-4); border-color: var(--edge-3); }
.tr-actions .btn {
  position: relative; z-index: 1;
  padding: 4px 16px; line-height: 1.35;
  background: var(--surface-3); border-color: var(--edge-2);
  border-radius: 12px;
  font-weight: 600;
}
.tr-actions .btn:hover:not(:disabled) { background: var(--surface-4); border-color: var(--edge-3); }
.tr-pane__text {
  flex: 1; min-height: 3rem; overflow: auto;
  padding: 12px 6px;
  font-size: var(--fs-base); line-height: 1.55; color: var(--text);
}
textarea.tr-pane__text {
  resize: none;  /* the outer box's corner handle sizes both panes instead */
  background: transparent; border: 0; border-radius: 0;
  font-family: inherit; font-weight: 500;
}
textarea.tr-pane__text:focus { outline: none; }
textarea.tr-pane__text::placeholder { color: var(--text-dim); }
.tr-pane__out p { margin: 0 0 10px; }
.tr-pane__note { font-size: var(--fs-xs); color: var(--text-soft); line-height: 1.5; }
/* The handle: a grip in the outer box's bottom-right corner. Dragging it
   (or the arrow keys, when focused) sets both panes' height at once. */
.tr-resize {
  position: absolute; right: 6px; bottom: 6px; z-index: 4;
  width: 24px; height: 24px; padding: 4px;
  display: grid; place-items: center;
  background: transparent; border: 0; border-radius: 6px;
  color: var(--text-dim); cursor: ns-resize;
  touch-action: none;
}
.tr-resize:hover, .tr-resize.is-dragging { color: var(--text); }
.tr-resize:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.16); color: var(--text); }
.tr-actions { display: flex; justify-content: flex-start; align-items: flex-start; }
.btn:disabled { opacity: 0.45; cursor: default; box-shadow: none; }
.btn:disabled:hover { background: var(--surface-2); border-color: var(--edge-2); }
.tr-actions .btn:disabled:hover { background: var(--surface-3); border-color: var(--edge-2); }

/* The gloss: the translation set out as an interlinear gloss, in the
   translator's own build - the outer card (.tr-shell) holding a panel in
   the Dictionary's field colour. Each column: the word in white, its
   part of speech in the wordmark red (the colour the dictionary's links
   use), and its equivalent in the other language under that. Columns
   wrap like a line of text; further rows (the Lao and Thai spellings)
   stack in the same column later. */
.tr-gloss {
  background:
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.075) 0%,
      rgba(255, 255, 255, 0.022) 26%,
      rgba(255, 255, 255, 0.004) 56%,
      rgba(255, 255, 255, 0) 100%),
    var(--surface-0);
  border: 1px solid var(--edge-2); border-radius: var(--radius-card);
  padding: clamp(14px, 1.8vw, 22px) clamp(12px, 1.6vw, 20px);
  margin-bottom: 22px;
}
.tr-gloss__head {
  margin: 0 0 12px;
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: var(--fs-xl); color: var(--text);
}
.tr-gloss__panel {
  background: var(--surface-2);
  border: 1px solid var(--edge-2); border-radius: 12px;
  padding: 16px 18px;
  margin-bottom: 12px;
}
.tr-gloss__lede { margin: 0; font-size: var(--fs-xs); color: var(--text-dim); }
.igt { display: flex; flex-wrap: wrap; gap: 14px 22px; }
.igt__word {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  max-width: 12em;
}
.igt__form {
  font-size: var(--fs-lg); font-weight: 600;
  color: var(--text); text-decoration: none;
  border-bottom: 1px solid var(--edge-3);
  padding-bottom: 1px;
}
a.igt__form:hover { border-bottom-color: var(--text-soft); }
.igt__form--none { color: var(--text-dim); border-bottom-color: transparent; }
.igt__pos {
  font-size: var(--fs-xs); font-weight: 700; font-style: italic;
  color: var(--wordmark-link); line-height: 1.3;
}
.igt__pos--none { color: var(--text-dim); font-style: normal; }
.igt__equiv { font-size: var(--fs-sm); color: var(--text-soft); line-height: 1.35; }
.igt__pos--blank, .igt__equiv--blank { visibility: hidden; }
.igt__pos--blank::before, .igt__equiv--blank::before { content: "x"; }
.igt__free {
  margin: 16px 0 0; padding-top: 12px;
  border-top: 1px solid var(--edge-1);
  font-size: var(--fs-sm); color: var(--text);
}
.igt__free-label {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text-dim); margin-right: 10px;
}

/* Try these: a label and the samples as chips, one compact row under
   the boxes rather than a card of its own. A chip fills the input. */
.tr-try { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-bottom: 22px; }
.tr-try__head {
  margin: 0; font-size: var(--fs-xs); font-weight: 600;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-soft);
}
.tr-try__list { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0; padding: 0; }
.tr-try__chip {
  cursor: pointer;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--edge-2); border-radius: 12px;
  padding: 5px 14px; font-size: var(--fs-sm);
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.tr-try__chip:hover { background: var(--surface-3); border-color: var(--edge-3); }
.tr-try__chip:focus-visible { outline: none; border-color: var(--edge-3); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.16); }

.searchbox {
  flex: 1; min-width: 0; display: flex; align-items: stretch;
  background: var(--surface-2); border: 1px solid var(--edge-2); border-radius: 12px;
  overflow: hidden;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.searchbox:focus-within { border-color: var(--edge-3); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.16); }
.toolbar .searchbox input[type="search"] {
  flex: 1; min-width: 0; border: 0; border-radius: 0; background: transparent; box-shadow: none;
}
.toolbar .searchbox input[type="search"]:focus { box-shadow: none; }
.searchbox button {
  border: 0; border-left: 1px solid var(--edge-2); cursor: pointer;
  background: var(--surface-3); color: var(--text);
  padding: 0 22px; font: inherit; font-size: var(--fs-sm); font-weight: 600;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.searchbox button:hover { background: var(--surface-4); }
.searchbox button:focus-visible { outline: 2px solid var(--edge-hover); outline-offset: -4px; }

/* Typeahead list (main/static/main/js/suggest.js). Not .suggest: that class already marks
   the "Suggest a correction" buttons, which dictionary.js finds by it. */
.typeahead {
  position: absolute; z-index: 30; top: calc(100% + 6px); left: 0; right: 0;
  margin: 0; padding: 0; list-style: none;
  max-height: min(420px, 60vh); overflow-y: auto;
  background: var(--surface-4); border: 1px solid var(--edge-2); border-radius: 12px;
  box-shadow: 0 18px 40px -12px rgba(0, 0, 0, 0.7);
}
.typeahead[hidden] { display: none; }
/* Each suggestion is one line at the search box's own scale: the same text size, the same
   16px side padding, a row as tall as the box. A meaning too long for the row ends in "…". */
.typeahead [role="option"] {
  display: flex; align-items: baseline; gap: 10px;
  min-height: 46px; box-sizing: border-box; padding: 12px 16px;
  cursor: pointer; font-size: var(--fs-sm); line-height: 1.3; overflow: hidden;
}
/* A clear rule between suggestions, edge to edge; the rows fill the list, so the hover
   and keyboard highlight run to its edges too. */
.typeahead [role="option"] + [role="option"] { border-top: 1px solid var(--edge-3); }
.typeahead [role="option"]:hover, .typeahead [role="option"][aria-selected="true"] { background: var(--surface-3); }
.typeahead [role="option"][aria-selected="true"] { box-shadow: inset 3px 0 0 var(--text-soft); }
.typeahead__word {
  flex: none; font-family: var(--font-display); font-style: italic; font-weight: 600; color: var(--text);
}
.typeahead__word b { font-weight: 700; }
/* The Iu Mien word is never cut; the label and meaning give way to it, with "…". */
.typeahead__meta {
  flex: 0 1 auto; max-width: 40%; min-width: 0; margin-left: auto; text-align: right;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font-weight: 600; color: var(--text-dim);
}
.typeahead__gloss {
  flex: 1 1 0; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font-weight: 500; color: var(--text-soft);
}
.toolbar .check {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-soft);
}
/* Dictionary links: no underline, colour unchanged. */
.entry a, .results-meta a { text-decoration: none; }
.entry .phrase a.mien { font-weight: 600; }
.entry .lex-fields { margin-top: 10px; }
.entry .senses .lex-fields { margin-top: 4px; }
/* A phrase's way back to its root word: the typeface's own arrow (as in "more phrases →"),
   at the word's size, then the word. */
.entry .lex-back {
  display: inline-flex; align-items: baseline; gap: 0.3em; margin-bottom: 10px;
  font-size: var(--fs-lg); color: var(--text-soft);
  transition: color var(--dur-fast) var(--ease-out);
}
.entry .lex-back:hover { color: var(--text); }
.entry .lex-back__arrow { font-family: var(--font-body); transition: transform var(--dur-fast) var(--ease-out); }
.entry .lex-back:hover .lex-back__arrow { transform: translateX(-3px); }
.entry .lex-back .mien { font-family: var(--font-display); font-style: italic; }
.entry .headword .lex-variants { font-size: var(--fs-lg); color: var(--text-soft); }
.entry .lex-phrase-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 28px; }
.entry .lex-phrase-grid .phrase { margin-bottom: 6px; }
.entry .lex-phrase-grid .muted { font-size: var(--fs-sm); }
@media (max-width: 640px) { .entry .lex-phrase-grid { grid-template-columns: minmax(0, 1fr); } }
.entry .lex-fields .lex-chips { display: flex; }

.lex-entry .headword .word { margin: 0; }

/* Card footer: the source on the left, the correction button tucked into the bottom-right
   corner, quiet in the card's own colours until pointed at. */
.entry .lex-foot {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 8px 16px;
  margin-top: 14px;
}
.entry .lex-foot p { margin: 0; }
.entry .lex-correct {
  flex: none; margin: 0 -10px -8px 0; /* into the corner, inside the card's padding */
  padding: 4px 11px; border: 1px solid var(--edge-1); border-radius: 999px;
  background: transparent; color: var(--text-dim); cursor: pointer;
  font: inherit; font-size: var(--fs-xs); font-weight: 600;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.entry .lex-correct:hover { color: var(--text); border-color: var(--edge-3); background: var(--surface-2); }
.entry .lex-correct:focus-visible { outline: 2px solid var(--edge-hover); outline-offset: 2px; }

/* Dictionary cards top out at the search controls' size: nothing on a card is larger than the
   "Mien ⇄ English" picker (--fs-sm). Capped, not scaled: the smaller text is already at the
   15px floor tokens.css sets for readers who are elders, and must not go under it. The
   headword keeps its weight and italic, so it still leads at the same size. Written as
   .card.entry to outrank the phone rules that size .entry .headword .word. */
.card.entry,
.card.entry .headword .word,
.card.entry .headword .lex-variants,
.card.entry .lex-back,
.card.entry .senses,
.card.entry .phrase .mien,
.card.entry .phrase a { font-size: var(--fs-sm); }
.card.entry .headword .word { font-weight: 700; }
.lex-entry .lex-form { margin: 8px 0 0; font-size: var(--fs-sm); }
.lex-sense { display: flex; gap: 12px; margin-top: 14px; }
.lex-sense > div { flex: 1; min-width: 0; }
.lex-n { font-weight: 700; color: var(--text-dim); min-width: 1.2em; }
.lex-gl { margin: 0; }
.lex-fields {
  display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px;
  margin: 8px 0 0; font-size: var(--fs-sm);
}
.lex-fields dt { font-weight: 700; color: var(--text-dim); }
.lex-fields dd { margin: 0; overflow-wrap: anywhere; }
.lex-phrases { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.lex-chips { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0; }

.coming-list {
  list-style: none; margin: 12px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 10px;
  font-size: var(--fs-sm); font-weight: 500; color: var(--text-soft);
}
.coming-list li { position: relative; padding-left: 20px; }
.coming-list li::before {
  content: ""; position: absolute; left: 2px; top: 0.62em;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-dim);
}

.contact-list {
  list-style: none; margin: 14px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 10px;
}
.contact-list a {
  font-size: var(--fs-lg); font-weight: 500;
  color: var(--wordmark-link); text-decoration: none;
  /* the rule under the address does the work an underline would */
  border-bottom: 1px solid rgba(191, 26, 31, 0.55);
}
.contact-list a:hover {
  color: var(--wordmark-lift);
  border-bottom-color: var(--wordmark-lift);
}

.results-meta { margin: 0 0 14px; color: var(--text-dim); font-size: var(--fs-sm); font-weight: 600; }

.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }

table.chart { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); font-weight: 500; }
table.chart th, table.chart td { border: 1px solid var(--edge-2); padding: 10px 12px; text-align: left; }
table.chart th { background: var(--surface-3); color: var(--text); font-weight: 700; }
table.chart td { background: var(--surface-2); }
table.chart td .mien { color: var(--text); font-weight: 600; }

/* ---------- 10. LEARN / CULTURE ---------- */
.age-groups { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 24px; }
.age-groups button {
  border: 1px solid var(--edge-3); background: var(--surface-2);
  border-radius: var(--radius-pill); padding: 10px 20px; cursor: pointer;
  font-size: var(--fs-sm); font-weight: 600; color: var(--text);
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.age-groups button:hover { background: var(--surface-3); }
.age-groups button.on { background: rgba(255, 255, 255, 0.92); border-color: #fff; color: #14171d; }

.tone-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 14px; }
.tone-card {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0) 50%),
    var(--surface-2);
  border-radius: 14px; padding: 16px;
  border: 1px solid var(--edge-2); cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.tone-card:hover {
  transform: translateY(-2px); border-color: var(--edge-3);
  box-shadow: 0 0 34px -14px rgba(255, 255, 255, 0.32);
}
.tone-card .letter {
  font-size: 1.8rem; font-family: var(--font-display);
  font-style: italic; font-weight: 600; color: var(--text);
}
.tone-card .contour { color: var(--text-dim); font-size: var(--fs-xs); font-weight: 600; }
.tone-card .demo { margin-top: 8px; font-size: var(--fs-sm); font-weight: 500; }
.tone-card svg.pitch { width: 100%; height: 40px; margin-top: 8px; }
.tone-card svg.pitch path { stroke: var(--silver-shade); stroke-width: 3; fill: none; stroke-linecap: round; }

.quiz .q { font-size: var(--fs-lg); margin-bottom: 14px; }
.quiz .choices { display: grid; gap: 10px; }
.quiz .choices button {
  text-align: left; background: var(--surface-2); border: 1px solid var(--edge-2);
  padding: 13px 16px; border-radius: 12px;
  font-size: var(--fs-sm); font-weight: 500; color: var(--text); cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.quiz .choices button:hover { background: var(--surface-3); border-color: var(--edge-3); }
.quiz .choices button.right { border-color: #63ad70; background: rgba(99, 173, 112, 0.18); }
.quiz .choices button.wrong { border-color: var(--red-tassel); background: rgba(192, 57, 47, 0.20); }
.progress-dots { display: flex; gap: 6px; margin: 14px 0; }
.progress-dots span { width: 10px; height: 10px; border-radius: 50%; background: var(--surface-3); border: 1px solid var(--edge-2); }
.progress-dots span.done { background: #fff; border-color: #fff; }

.dress-stage { display: grid; grid-template-columns: minmax(220px, 340px) 1fr; gap: 26px; align-items: start; }
.dress-stage .hotspot-info { min-height: 180px; }
.dress-svg .region { fill: transparent; cursor: pointer; }
.dress-svg .region:hover, .dress-svg .region.on { fill: rgba(255, 255, 255, 0.18); stroke: #fff; stroke-width: 2; }

/* ---------- RESPONSIVE ---------- */
/* The scale exists to stop the design filling a large screen edge to
   edge. A small desktop has no excess width to give back, and taking a
   quarter off one is what pushed the five nav links out of the masthead
   between 860px and here - the frame went from 767px to 576px at 880 and
   the row stopped fitting. Native size below this width.

   The step at the boundary is deliberate and visible only while
   resizing across it. */
/* Desktop: a little more room than the mobile banner - 14px above and
   below. The logo stays exactly as tall as the name. */
@media (min-width: 1001px) {
  .masthead__frame { padding-top: 14px; padding-bottom: 14px; }
  /* The controls group holds only the menu button, which is hidden here;
     left in the row it still took a gap, and the nav stopped short of the
     frame's edge. Out of the row, the nav sits as far in from the right
     as the logo does from the left. */
  .masthead__controls { display: none; }
  /* A little more air between the subline and the cards than the
     search-to-title gap it otherwise mirrors: 23px to 32px at 1512. */
  .doors { padding-top: clamp(18px, 2.2vw, 32px); }
}

/* 1101-1400px: --ui-scale holds the page to three quarters of the rail,
   so the banner narrows faster than its contents, and the account control
   ran 30-54px past the frame. Tighter link padding and row gaps keep the
   whole row inside. */
@media (min-width: 1101px) and (max-width: 1400px) {
  .masthead__frame { gap: 12px; }
  .masthead__nav a { padding-left: clamp(8px, 0.8vw, 14px); padding-right: clamp(8px, 0.8vw, 14px); }
}

@media (max-width: 1100px) {
  /* --content-scale exists to undo --ui-scale's 0.75 on the reading text
     (1.55 x 0.75 = 1.16). Here --ui-scale goes back to 1, so the content
     scale has to come down by the same 0.75, or the reading text lands
     1.55x too large: the "In conversation" names outgrew their heading
     and the notices and footer read like headlines. 1.1625 keeps every
     piece of text at the same pixel size as on a desktop. */
  :root { --ui-scale: 1; --content-scale: 1.1625; }
}

/* Tablets, 481-1100px: the hero's search text and subline are sized from the root, which
   --ui-scale takes from 12px to 16px at 1100px, while the photograph, wordmark and cards are
   sized from the layout. So at 867px the search text was 22px and the subline 25px under a
   43px wordmark: half again the desktop's proportions. Tied to the wordmark instead, at the
   desktop's ratios (16.5 / 49.8 and 19 / 49.8), with floors: 16px keeps iOS from zooming
   into the field, 15px is the site's text floor. Phones set their own at 480px. */
@media (min-width: 481px) and (max-width: 1100px) {
  .hero__search input { font-size: max(16px, calc(var(--fs-mark) * 0.331)); padding: 12px 0; }
  .hero__sub { font-size: max(15px, calc(var(--fs-mark) * 0.3815)); }
}

@media (max-width: 1000px) {
  .site-footer__inner { grid-template-columns: minmax(0, 1fr); }
  /* Stacked, the pair no longer needs lining up side by side, so the
     row-matching machinery goes: each logo box is its own artwork's
     height, the name takes only the lines it needs, and one even step
     separates logo from name and partner from partner. The two marks
     get the same area: the badge is square, the wordmark 3.41:1, so the
     wordmark is 1.85x the badge's width (1.85² / 3.41 = 1). */
  .alliance__list {
    --badge: clamp(112px, 22vw, 150px);
    grid-template-columns: minmax(0, 1fr); grid-template-rows: none;
    row-gap: clamp(40px, 7vw, 60px);
  }
  /* 8px from the artwork to the name. Both images carry clear space
     under the artwork (9.8% of the badge's height, 13.4% of the
     wordmark's), so each logo gives that much back below it. */
  .partner { display: flex; flex-direction: column; align-items: center; gap: 8px; }
  .partner__logo-box { height: auto; }
  .partner__logo { width: var(--badge); height: var(--badge); margin-bottom: calc(var(--badge) * -0.098); }
  .partner__logo--literacy {
    width: calc(var(--badge) * 1.85); height: calc(var(--badge) * 1.85 / 3.41);
    margin-bottom: calc(var(--badge) * 1.85 / 3.41 * -0.134);
  }
  /* centred: the base rule's align-self: start tops it in a grid row, and in a
     column it would push it to the left */
  .partner__name { min-height: 0; align-self: center; }
}

/* The mobile header - the three-bar menu in place of the nav row - from
   1000px down. Above that the brand, the five links and the account
   control fit on one line; below it they did not, and the account
   control was pushed out past the frame's edge (91px at 861px). */
@media (max-width: 1000px) {
  .masthead__toggle { display: grid; place-items: center; }
  .masthead__frame { flex-wrap: wrap; row-gap: 0; }
  /* A plain show/hide. The animated grid-rows version clipped the
     panel against the frame, which is what was cutting the buttons
     off - this cannot, because nothing is ever height-constrained. */
  .masthead__nav {
    order: 3; flex-basis: 100%; margin-left: 0;
    display: none;
  }
  .masthead.is-open .masthead__nav { display: block; }
  /* The buttons stack; each one is its own pane of glass, so the bar
     behind them goes. */
  .masthead__nav > ul {
    grid-auto-flow: row;
    border: 0; padding: 10px 0 2px; gap: 8px;
    background: none; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  /* Set exactly as the banner's subtitle ("ONLINE LANGUAGE INITIATIVE": its size, weight,
     capitals and tracking), in white rather than its red, so the open menu reads as a quiet
     part of the same header rather than a stack of headlines. Each button stays at least 36px
     tall: above WCAG's 24px target minimum, if under the 44px Apple suggests. */
  .masthead__nav a {
    display: flex; align-items: center;
    min-height: 36px;
    padding: 6px 12px;
    border-radius: var(--radius-button);
    font-size: var(--name-sub);
    line-height: 1.2;
    font-weight: 600;
    letter-spacing: 0.11em; text-transform: uppercase;
    color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--edge-3);
    text-align: left;
  }
  .masthead__nav a:hover,
  .masthead__nav a.is-active { background: var(--surface-3); }
  /* order 1 keeps the group above the nav panel, which is order 3 and
     opens as a full-width row beneath everything. */
  .masthead__controls { order: 1; margin-left: auto; }
  /* The logo is exactly as tall as the name beside it - "Iu Mien" plus
     "Online Language Initiative" - so it is computed from the name's own
     sizes: each line's font size times its line height, plus the 4px
     between them. --sub-lines says how many lines the subtitle takes at
     this width. The brand is centred on the menu button, takes whatever
     the button leaves, and wraps its subtitle inside that rather than
     pushing the button onto a row of its own. */
  /* The desktop header's own pixel sizes (22.8px name, 12.6px subtitle, a ~44px logo and
     menu button). This range was once clamp(1.9rem, 4.6vw, 2.3rem): above 1100px --ui-scale
     draws the chrome at 0.75, below it at 1, so a tablet's header came out 1.6x the
     desktop's and nearly twice the phone's (a 65px logo, a 36px name at 774px). */
  :root {
    --banner-name: clamp(1.3rem, 2.9vw, 1.425rem);
    --banner-sub: clamp(0.8125rem, 1.6vw, 0.875rem);
  }
  .masthead__frame {
    --name-top: var(--banner-name);
    --name-sub: var(--banner-sub);
    --sub-lines: 1;
    --logo: calc(var(--name-top) * 1.08 + 4px
                 + var(--name-sub) * 1.3 * var(--sub-lines));
  }
  .masthead__brand {
    flex: 1 1 0; min-width: 0;
    gap: clamp(12px, 1.8vw, 16px);
  }
  .masthead__controls { flex-shrink: 0; }
  .masthead__name-top { font-size: var(--name-top); line-height: 1.08; }
  .masthead__name-sub {
    font-size: var(--name-sub);
    line-height: 1.3;
    letter-spacing: 0.11em;
    white-space: normal;
    margin-top: 4px;
  }
  .masthead__logo { width: var(--logo); border-radius: 10px; }
  /* The footer lockup follows the header's gap, line heights and logo
     formula here too. */
  .site-footer__mark { gap: clamp(12px, 1.8vw, 16px); }
  .site-footer__mark-sub { line-height: 1.3; margin-top: 4px; }
  .site-footer__logo {
    width: calc(var(--foot-top) * 1.08 + 4px + var(--foot-sub) * 1.3);
  }
  /* The menu button is the logo's size, so the two ends of the row match.
     Its bars scale with it; the X offset is one bar plus one gap, so it
     still closes exactly. Where the button is under a 44px touch target,
     an invisible hit area makes up the difference - reaching into the
     frame's padding and the gap beside it, where nothing else is. */
  /* A compact column of dots, centred on the logo's height (about two thirds of it), set
     against the button's right edge so it sits as far in from the banner's right side as the
     logo does from its left. Stretched to the logo's full height the gaps were twice the
     dots and the column read as three loose points. */
  .masthead__toggle {
    --dot: max(4px, calc(var(--logo) * 0.11));
    --dot-gap: calc(var(--dot) * 1.4);
    place-items: center end;
    width: var(--logo); height: var(--logo);
    border-radius: calc(var(--logo) * 0.25);
  }
  .masthead__toggle::after {
    content: ""; position: absolute;
    inset: min(0px, calc((var(--logo) - 44px) / 2));
  }
}

@media (max-width: 860px) {
  /* The scale is a desktop composition: it exists to stop the design
     filling a large screen edge to edge. A phone has no width to give
     away, and this breakpoint already restates --shell for that reason -
     so the type goes back to full size here too, or the body copy lands
     at 13.5px on the smallest screens. */
  :root { --ui-scale: 1; --shell: min(1440px, 96vw); }
  /* The editorial passage keeps its pinned, overlapping composition for
     exactly as long as the card row above it stays horizontal - the two
     share this breakpoint deliberately. Below it, both stack. */
  .editorial__inner {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(24px, 5vw, 40px);
    min-height: 0;
  }
  /* Out of the shared cell and back into ordinary rows: the copy stops
     crossing the photograph, so it needs no shadow to be read - but it
     stays white. */
  .editorial__text {
    grid-area: auto; width: 100%; min-height: 0;
    position: static; transform: none;
  }
  /* Stacked, the photograph matches the hero's: the same width (the
     rail, reaching a few pixels past this section's own padding) and the
     same rounded corners. */
  .editorial__media {
    grid-area: auto;
    order: -1;
    width: var(--rail);
    margin-inline: calc((100% - var(--rail)) / 2);
    padding-top: 0;
  }
  .editorial__sticky { position: static; }
  .editorial__sticky img { display: block; border-radius: var(--radius-hero); }
  /* The photograph sits 12px under the section's hairline, the same
     spacing as the hero photograph under the banner; the copy ends the
     same 12px above the next rule. */
  .editorial__inner { padding-top: 12px; }
  .editorial { padding-bottom: 12px; }
  .editorial__title { width: auto; max-width: none; text-shadow: none; }
  .editorial__para { text-shadow: none; }
  /* The authored line breaks are for the desktop measure. Stacked, the
     column is a different width, so let the text wrap on its own. */
  .editorial__title br,
  .editorial__para br { display: none; }

  .doors__row { grid-template-columns: 1fr; width: min(420px, 88%); }
  /* Learn's cards stack at the home page's size, not the full column. */
  .doors--inline .doors__row { width: min(420px, 88%); }
  /* The inset is a desktop composition. On a phone the photograph
     already runs nearly edge to edge and stepping it in again just
     narrows an image that has no room to spare. */
  .hero__stage { width: var(--rail); }
  .door { aspect-ratio: 16 / 10; }
  .hero__search { width: 88%; margin-top: -2.4rem; padding-left: 20px; }
  .cols, .dress-stage { grid-template-columns: 1fr; }
  .site-footer__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
  :root { --radius-hero: 18px; }

  .masthead__name-sub { line-height: 1.3; }
  .hero__search input { font-size: var(--fs-base); }
  .site-footer__cols { grid-template-columns: minmax(0, 1fr); }
  .site-footer__bar { flex-direction: column; }
}

/* Phones: the original lockup - a 40px logo, "Iu Mien" at 1.6x the
   subtitle, the subtitle at 13px on one line, the logo as tall as the two
   lines - with the menu button to the right, centred on it.

   The menu button matches the logo. Those sizes need about 400px; below
   that the whole row scales down together, just enough to keep "Online
   Language Initiative" on one line (it renders 17x its font size wide at
   this tracking): 13px from about 400px up, 11.7px at 375px, 9.7px at
   320px. */
@media (max-width: 480px) {
  .typeahead [role="option"] { font-size: var(--fs-base); } /* matches the phone search field */
  /* ...and on phones the picker is --fs-base (iOS zooms on anything smaller), so the cap follows it. */
  .card.entry,
  .card.entry .headword .word,
  .card.entry .headword .lex-variants,
  .card.entry .lex-back,
  .card.entry .senses,
  .card.entry .phrase .mien,
  .card.entry .phrase a { font-size: var(--fs-base); }
  .dir-select { flex-basis: 100%; }
  .dir-select select { width: 100%; }
  /* The translator's boxes stay side by side, as on a desktop, so
     everything inside them steps in: less padding, a tighter picker that
     fills its box (the 100% above is for the dictionary's search row,
     where it is the full width), and the text a step down. The picker
     stays at 16px or iOS zooms on focus. */
  .tr-panes { row-gap: 8px; }
  .tr-shell { padding: 8px 8px 28px; border-radius: 18px; }
  .tr-pane { padding: 8px; }
  .tr-col:last-child > .tr-pane::before { top: 10px; bottom: 10px; }
  .tr-lang { flex: 1 1 auto; min-width: 0; }
  .tr-lang select {
    width: 100%; min-height: 34px;
    padding: 0 24px 0 8px; font-size: 16px;
    text-overflow: ellipsis;
  }
  .tr-lang .dir-select__chevron { right: 6px; width: 16px; height: 16px; }
  /* 16px, not the 15px --fs-sm: under 16px iOS zooms into a field on focus. */
  .tr-pane__text { padding: 10px 4px; font-size: 16px; }
  .tr-actions .btn { padding: 4px 12px; white-space: nowrap; }
  .tr-btn-long { display: none; }  /* "Generate gloss": one line in a half-width column */
  .tr-gloss { padding: 12px 10px; border-radius: 18px; }
  .tr-gloss__panel { padding: 12px; }
  .igt { gap: 12px 16px; }
  /* The banner's type sizes, on the root so the hero below can be sized
     from them too - see the hero rules at the end of this block. */
  :root {
    --banner-sub: clamp(9px, calc(3.99vw - 2.6px), 0.8125rem);
    --banner-top: calc(var(--banner-sub) * 1.6);
    --banner-name: var(--banner-top);
  }
  .masthead__frame {
    --name-sub: var(--banner-sub);
    --name-top: var(--banner-top);
    --logo: calc(var(--name-top) * 1.08 + var(--name-sub) * 1.3);
    column-gap: 12px;
  }
  .masthead__brand { gap: 10px; }
  .masthead__nav a { letter-spacing: 0.06em; } /* the subtitle's phone tracking, below */
  /* 0.06em tracking (0.09 before) lets the subtitle reach 13px - the
     mobile floor - on common phones while staying on one line. */
  .masthead__name-sub {
    letter-spacing: 0.06em;
    white-space: nowrap;
    margin-top: 0;
  }
  .masthead__logo { border-radius: 8px; }

  /* The hero on a phone, in proportion to the banner above it: the
     search field a 44px bar (its text never under 16px, below which iOS
     zooms the page on focus), "Iu Mien Waac" at 1.4x the banner's
     "Iu Mien" and the subline at 0.76x (0.8 of 1.75x and 0.95x). */
  /* At its usual width, straddling the photograph's lower edge, with the
     banner's corner radius. */
  .hero__search {
    min-height: 44px;
    margin-top: -22px;
    padding: 2px 6px 2px 18px;
    gap: 4px;
    border-radius: var(--radius-card);
  }
  .hero__search input {
    padding: 8px 0;
    font-size: max(16px, calc(var(--banner-top) * 0.85));
  }
  .hero__search button { width: 36px; height: 36px; }
  .hero__search button svg { width: 18px; height: 18px; }
  .hero__mark { font-size: calc(var(--banner-top) * 1.75 * 0.8); margin-top: 11px; }
  .hero__sub { font-size: calc(var(--banner-top) * 0.95 * 0.8); margin-top: 4px; }

  /* The three cards keep their 16:10 shape, scaled down with the hero:
     the column is 13x the banner's "Iu Mien" wide (about 260px at 393px,
     against 332px before), labels 1.3x it, corners and gaps a step
     tighter. */
  .doors { --doors-gap: 12px; padding-top: 11px; }
  .doors__row { width: calc(var(--banner-top) * 13); }
  .door { padding: 12px 18px; border-radius: 16px; }
  .doors--inline .doors__row { width: calc(var(--banner-top) * 13); }
  .doors--inline .door { padding: 12px 18px; }
  .door__label { font-size: calc(var(--banner-top) * 1.3); }

  /* ---- THE PHONE TYPE SCALE ----
     One fixed scale for every page on a phone, following the usual mobile
     conventions: body copy 17px, nothing under 13px, form fields at 16px
     or more (below that iOS zooms the page on focus), and headings a
     ~1.2 step apart. It sits with the banner and the front page:
     the banner's "Iu Mien" (~20px) is heading-3 size, the home wordmark
     (~28px) heading-1, the home cards (~26px) between the two.

       caption  13   footnotes, tags        --fs-xs
       small    15   notices, footer, UI    --fs-sm
       body     17   paragraphs, lists      --fs-base
       lead     19   page introductions     --fs-lg
       h3       20   card titles            --m-h3
       h2       23   section heads          --fs-xl
       h1       28   page titles            --fs-2xl

     The tokens are restated here, fixed, for every scope that restates
     them for desktop (see the --content-scale block), so the desktop
     multipliers do not reach a phone. */
  :root,
  .page-head, .content, .alliance, .notice--center, .site-footer {
    --fs-xs:   13px;
    --fs-sm:   15px;
    --fs-base: 17px;
    --fs-lg:   19px;
    --m-h3:    20px;
    --fs-xl:   23px;
    --fs-2xl:  28px;
    --fs-3xl:  32px;
  }
  h1, .page-head h1 { font-size: var(--fs-2xl); }
  /* The inner pages' title (Learn, Dictionary, Translation, About, Connect):
     1.2x the banner's "Iu Mien" here rather than the 1.5x it is above, which
     on a phone made a one-word title the largest thing on the screen.
     .wrap in the selector to outrank that rule. */
  .page-head .wrap h1 { font-size: calc(var(--banner-name) * 1.2); }
  h2 { font-size: var(--fs-xl); }
  h3 { font-size: var(--m-h3); }
  .entry .headword .word { font-size: var(--fs-xl); }
  .tone-card .letter { font-size: var(--fs-2xl); }
  .doors--inline .door__label { font-size: var(--fs-xl); }
  /* Every text field at body size, never under iOS's 16px zoom threshold.
     The hero search sets its own, above. */
  .content :is(input[type="search"], input[type="text"], textarea, select),
  .toolbar :is(input[type="search"], input[type="text"], textarea, select) {
    font-size: var(--fs-base);
  }

  /* The direction toggle spans the column; its options share the width
     and stay on one line each. */
  .pill-toggle { display: flex; width: 100%; margin-bottom: 12px; }
  .pill-toggle button, .pill-toggle a { flex: 1 1 auto; padding: 8px 10px; }

  /* The footer lockup follows the banner's phone sizes on its own (see
     .site-footer__mark); its gap, tracking and corners match the banner's
     here. */
  .site-footer__mark { gap: 10px; }
  .site-footer__logo {
    width: calc(var(--foot-top) * 1.08 + var(--foot-sub) * 1.3);
    border-radius: 8px;
  }
  .site-footer__mark-sub { margin-top: 0; }

  /* The home page's own sections on the same scale: section heads at h2,
     the sub-head at h3, partner names at lead, the copy at body. */
  .editorial__title { font-size: var(--fs-xl); }
  .editorial__para { font-size: var(--fs-base); }
  .alliance__head { font-size: var(--fs-xl); }
  .alliance__sub { font-size: var(--m-h3); }
  .partner__name { font-size: var(--fs-lg); }
}

/* ---------- REDUCED MOTION ----------
   Movement is removed, except for the hero's arrival, which plays at all
   times by design: the bloom, the valley settling, and the woman fading
   in and settling down into place. It runs once, on load, and is slow. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
  /* the copy still pins, it just is not pushed down the photograph */
  .editorial__text { transform: none !important; }
}
