/* ============================================================
   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: clamp(11px, 1.35vw, 19px) 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; }
.masthead__name { display: block; line-height: 1.08; }
.masthead__name-top {
  display: block;
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: clamp(1.5rem, 1.95vw, 1.9rem); letter-spacing: 0.01em;
  white-space: nowrap;
}
.masthead__name-sub {
  display: block;
  font-size: clamp(0.9375rem, 1.08vw, 1.05rem); 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. */
  color: var(--wordmark);
  transition: color var(--dur-fast) var(--ease-out);
}
.masthead__brand:hover .masthead__name-sub { color: var(--wordmark-lift); }

.masthead__nav { margin-left: auto; }
.masthead__nav > ul {
  display: flex; align-items: center; gap: 4px;
  list-style: none; margin: 0; padding: 5px;
  border: 1px solid var(--edge-1);
  border-radius: var(--radius-pill);
}
.masthead__nav a {
  display: block; position: relative;
  color: var(--text-soft); text-decoration: none;
  padding: 8px clamp(12px, 1.4vw, 18px);
  border-radius: var(--radius-pill);
  /* 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);
}
.masthead__nav a:hover { color: var(--text); background: rgba(255, 255, 255, 0.07); }
.masthead__nav a.is-active {
  color: var(--text);
  background: rgba(255, 255, 255, 0.10);
  box-shadow: inset 0 0 0 1px var(--edge-2);
}
.masthead__nav a.is-active::after {
  content: ""; position: absolute; left: 50%; bottom: 3px;
  width: 16px; height: 2px; border-radius: 2px;
  transform: translateX(-50%);
  background: #fff;
}

/* 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;
}
.masthead__account {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; padding: 0;
  background: transparent; color: var(--text-soft);
  border: 1px solid var(--edge-2);
  border-radius: 50%;
  cursor: not-allowed;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.masthead__account svg { width: 21px; height: 21px; }
.masthead__account:hover { color: var(--text); border-color: var(--edge-3); }

.masthead__toggle {
  display: none;
  width: 46px; height: 46px; padding: 0;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--edge-3); border-radius: 12px;
  cursor: pointer;
}
.masthead__toggle-bars { display: block; width: 18px; margin: 0 auto; }
.masthead__toggle-bars i {
  display: block; height: 1.6px; background: currentColor; border-radius: 2px;
  transition: transform var(--dur-fast) var(--ease-out);
}
.masthead__toggle-bars i + i { margin-top: 5px; }
.masthead__toggle[aria-expanded="true"] .masthead__toggle-bars i:first-child {
  transform: translateY(3.3px) rotate(45deg);
}
.masthead__toggle[aria-expanded="true"] .masthead__toggle-bars i:last-child {
  transform: translateY(-3.3px) rotate(-45deg);
}

/* ---------- 4. HERO ----------
   --hero-p is the 0->1 scroll progress past the hero, written by
   js/home.js. Everything that leaves on scroll reads from it. */
.hero {
  --hero-p: 0;
  position: relative;
  padding: clamp(22px, 2.6vw, 40px) 0 0;
  text-align: center;
}

.hero__stage {
  position: relative; z-index: 2;
  width: var(--shell-hero);
  margin: 0 auto;
  opacity: calc(1 - var(--hero-p));
  transform: translate3d(0, calc(var(--hero-p) * -46px), 0);
  will-change: opacity, transform;
}

.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, her opacity is driven by the scroll instead —
   she thins out a little ahead of the rest of the hero. The shorter
   transition keeps that responsive rather than laggy. */
.hero.is-bloomed .hero__woman {
  transform: translate3d(0, 0, 0);
  opacity: calc(1 - var(--hero-p) * 0.55);
  transition: transform 3400ms var(--ease-out), opacity 600ms var(--ease-out);
}

.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%);
  opacity: calc(1 - var(--hero-p) * 0.5);
}

.hero__mark {
  position: relative; z-index: 2;
  font-family: var(--font-mark); font-style: italic; font-weight: 400;
  font-size: var(--fs-mark);
  line-height: 0.98; letter-spacing: 0.005em;
  color: var(--wordmark);
  margin: clamp(18px, 2.2vw, 30px) 0 0;
  /* the one piece of coloured light on the site, and only just */
  text-shadow: 0 0 26px rgba(211, 26, 30, 0.34),
               0 0 60px rgba(211, 26, 30, 0.16);
  opacity: calc(1 - var(--hero-p) * 0.85);
}
.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: 500; font-style: italic;
  color: var(--text-soft);
  margin: 8px auto 0; max-width: 34em; padding: 0 20px;
  opacity: calc(1 - var(--hero-p) * 0.85);
}

/* ---------- 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. */
  padding: clamp(26px, 3.2vw, 46px) 0
           calc((clamp(56px, 6vw, 96px) + 46px) * 0.6);
}
/* 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;
  display: grid; grid-template-columns: repeat(3, 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;
  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 scrambles 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 random
   glyphs are passing through — js/doors.js measures it. */
.door__label--scramble {
  display: inline-block;
  min-width: var(--scramble-w, auto);
}
/* 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 is real. While a glyph is still churning it is
   random noise standing in for a letter that has not arrived, so it
   stays 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--churning,
.door__label--mien .door__char--churning {
  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 {
  width: 100%;
}
/* 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. It says so instead of pretending: no
   link, no pointer cursor, and the label sits back a step. */
.door--soon {
  cursor: default;
}
.door--soon .door__label { color: var(--text-soft); }
.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. This is the ~1080px scroll distance — change it here. */
  --editorial-scroll: 1000px;
  /* 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 is what separates it from
     the card row above; the run-up to it is deliberately short. */
  padding: clamp(30px, 4vw, 52px) 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. */
  transform: translate3d(
      0,
      calc(var(--editorial-p, 0) * var(--editorial-travel, 0px) - var(--editorial-lead, 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 {
  padding: clamp(56px, 8vw, 104px) 20px clamp(56px, 7vw, 96px);
  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;
  /* three explicit rows — logo, name, date — so subgrid below can pin
     each partner's three parts to the same lines as the other's */
  grid-template-rows: auto auto auto;
  gap: 13px clamp(28px, 4vw, 60px);
  max-width: 820px;
}
/* A fixed-height logo box and a two-line name reservation, so the two
   partners' names and dates sit on the same lines as each other however
   differently their marks are proportioned. */
/* Subgrid pins the logo, the name and the date of both partners to the
   same three 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 3;
  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 dates 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); }
.partner__date {
  margin: 0;
  font-size: var(--fs-sm); font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-dim);
}

.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 ---------- */
.site-footer {
  position: relative; isolation: isolate; overflow: hidden;
  margin-top: clamp(40px, 6vw, 80px);
  padding: clamp(46px, 6vw, 84px) 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 {
  display: block; text-decoration: none;
  color: var(--text); line-height: 1.1;
  margin-bottom: 16px;
}
.site-footer__mark-top {
  display: block;
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  /* 1.2x the 2rem it was. Left in rem rather than moved onto --fs-sm
     like the two lines around it: the name is the fixed point the others
     are proportioned against, so it should not shift when they do. */
  font-size: 2.4rem;
}
.site-footer__mark-sub {
  display: block;
  /* A step down: it is the descriptor under the name, not a line of
     equal standing with it. */
  font-size: calc(var(--fs-sm) * 0.85); font-weight: 600;
  letter-spacing: 0.15em; 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. */
  font-size: calc(var(--fs-sm) * 1.15); font-weight: 500; line-height: 1.65;
  color: var(--text-dim);
}

.site-footer__social {
  display: flex; gap: 10px;
  list-style: none; margin: 22px 0 0; padding: 0;
}
.site-footer__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px;
  color: var(--text-soft);
  border: 1px solid var(--edge-2); border-radius: 50%;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.site-footer__social svg { width: 18px; height: 18px; }
.site-footer__social a:hover {
  color: var(--text); border-color: var(--edge-3);
  background: var(--surface-2); transform: translateY(-2px);
}

.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;
  padding: 22px 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); }
.site-footer__note { max-width: 46em; }

/* ---------- 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%);
}
.page-head .wrap { position: relative; z-index: 1; max-width: var(--max-width); }
.page-head h1 { color: var(--text); margin: 0; font-style: italic; font-weight: 600; }
.page-head p { color: var(--text-soft); margin: 8px 0 0; max-width: var(--measure); }

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

/* 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);
  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;
  letter-spacing: 0.08em; text-transform: uppercase;
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--edge-2);
  border-radius: 999px; padding: 3px 12px;
}
.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; }
.entry .entry-foot .btn.small { float: right; padding: 7px 13px; font-size: var(--fs-xs); }

.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. */
@media (max-width: 1100px) {
  :root { --ui-scale: 1; }
}

@media (max-width: 1000px) {
  .site-footer__inner { grid-template-columns: minmax(0, 1fr); }
  .alliance__list { grid-template-columns: minmax(0, 1fr); }
}

@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); }
  .masthead__toggle { display: block; }
  .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; }
  .masthead__nav > ul {
    flex-direction: column; align-items: stretch;
    border: 0; padding: 14px 0 4px; gap: 10px;
  }
  /* big, obviously-tappable targets — most people arrive on a phone */
  .masthead__nav a {
    padding: 16px 18px;
    border-radius: 14px;
    font-size: var(--fs-lg);
    font-weight: 600;
    color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--edge-3);
    text-align: left;
  }
  .masthead__nav a:hover { background: var(--surface-3); }
  .masthead__nav a.is-active {
    background: rgba(255, 255, 255, 0.92);
    color: #14171d;
    border-color: #fff;
  }
  .masthead__nav a.is-active::after { display: none; }
  /* 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 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;
  }
  .editorial__media {
    grid-area: auto;
    order: -1;
    width: 100%;
    padding-top: 0;
  }
  .editorial__sticky { position: static; }
  .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%); }
  /* 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; }
  .hero { padding-top: 12px; }
  /* "ONLINE LANGUAGE INITIATIVE" is wider than a phone at this
     tracking, so let it wrap rather than run off the edge. */
  /* flex:0 0 auto sizes the brand to max-content, which is what let the
     subtitle run past the frame — let it shrink so the text can wrap. */
  .masthead__brand { flex: 1 1 auto; min-width: 0; }

  /* Side by side there is no room left for the wordmark to wrap into, so
     the group turns into a column: account on top, menu button directly
     under it, both flush to the right edge. */
  .masthead__controls { flex-direction: column; align-items: flex-end; }

  .masthead__name-top { font-size: 1.3rem; }
  .masthead__name-sub {
    white-space: normal;
    font-size: 0.8125rem;
    letter-spacing: 0.09em;
    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; }
}

/* ---------- REDUCED MOTION ----------
   Movement is removed. The black-and-white -> color bloom is a COLOR
   change, not motion, so it is kept: it carries the meaning of the
   hero and does not trigger vestibular symptoms. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  .hero__stage { opacity: 1 !important; transform: none !important; }
  .hero__mark, .hero__sub, .hero__arch { opacity: 1 !important; }
  .hero__woman { opacity: 1 !important; transform: none !important; transition: none !important; }
  .hero__valley { transform: none !important; transition: filter var(--dur-cine) var(--ease-out) !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; }
}
