/* ═══════════════════════════════════════════════════════════════════
   高性价比人生指南 — Swiss International / index-card poster
   ═══════════════════════════════════════════════════════════════════ */

@layer reset, tokens, base, layout, components, home, motion;

/* ── reset ─────────────────────────────────────────────────────────── */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; }
  html {
    -webkit-text-size-adjust: 100%;
    /* Backstop only. The drifting section wash is intentionally wider than the
       viewport so its translate cannot expose an edge, which leaves <html>'s
       scrollWidth overshooting even though nothing is reachable. `clip` contains
       that without making <html> a scroll container -- `overflow-x: hidden`
       here would, and it silently breaks the sticky topbar plus every scroll-spy
       offset measured against it. `clip` is the one value that does not force
       the other axis away from `visible`. */
    overflow-x: clip;
  }
  body { min-height: 100svh; line-height: 1.5; -webkit-font-smoothing: antialiased; }
  main { view-transition-name: content; }
  img, svg, video { display: block; max-width: 100%; }
  button, input, select, textarea { font: inherit; color: inherit; }
  button { background: none; border: 0; cursor: pointer; }
  a { color: inherit; text-decoration: none; }
  ul, ol { list-style: none; padding: 0; }
  table { border-collapse: collapse; width: 100%; }
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 1px; }
}

/* Route changes keep the fixed shell steady while the reading surface moves.
   This is progressive enhancement: browsers without View Transitions use the
   existing instant swap, and reduced-motion users get no travel animation. */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(content) { animation: routeOut .18s ease both; }
  ::view-transition-new(content) { animation: routeIn .46s var(--ease-out) both; }
  @keyframes routeOut { to { opacity: 0; transform: translateY(-7px) scale(.997); } }
  @keyframes routeIn { from { opacity: 0; transform: translateY(13px) scale(.995); filter: blur(3px); } }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(content), ::view-transition-new(content) { animation: none; }
  .pdfviewer__stage canvas { transition: none; }
}

/* ── tokens ────────────────────────────────────────────────────────── */
@layer tokens {
  :root {
    --ink:        #0B0B0C;
    --ink-2:      #3A3A3D;
    --muted:      #63636A;
    --paper:      #F7F6F2;
    --paper-2:    #EFEEE8;
    --paper-3:    #E6E4DC;
    /* ── the palette: neutrals + TWO hues, and nothing else ────────────────
       Measured across the rendered home page, the site was painting five
       chromatic colours plus a warm grey: vermilion (127 uses), blue (52), amber
       (24), violet (22) and --wedge #D6D0C0. Amber and violet were almost
       entirely in the lower bands -- 23 of 24 amber and 21 of 22 violet sat in
       the situations section, from the per-chapter evidence bars. That is what
       read as "a new colour appearing halfway down the page".

       So the evidence scale stops being a hue scale. A is the only grade that
       gets a hue; B and C are steps of the neutral ink instead. One hue means
       "measured", and the walk from blue through dark grey to light grey still
       reads as strong / medium / weak without asking anyone to learn two more
       colours. It is also how published data graphics normally do it.

       1  neutrals   paper / paper-2 / paper-3 and ink / ink-2 / muted, plus
                     --wedge. Every one of these is a true grey or a near-grey.
       2  accent     vermilion. One job: this needs attention NOW. Active state,
                     focus ring, the 寿命 in the title, the group dot, and the
                     search-term highlight (as a tint of itself, so highlighting
                     costs no new hue either).
       3  evidence   blue for grade A, then ink-2 for B and a mid grey for C.
                     Never red for C -- red reads as danger, and C is meant to
                     be unremarkable.

       Two hues against a limit of four. The two spare slots are deliberately
       left empty rather than filled: the last two rounds both spent their
       budget on colour that had to be explained. */
    --accent:     #E4320B;
    --accent-deep: #BE2A06;
    --accent-ink: #FFFFFF;

    --ev-a:       #1B3FD8;
    --ev-a-ink:   #FFFFFF;
    --ev-a-soft:  rgba(27, 63, 216, .12);
    /* B and C are the ink ladder, not hues. B is dark enough to outrank C
       against the paper in both themes, which is the whole point of the step. */
    --ev-b:       #3A3A3D;
    --ev-b-ink:   #FFFFFF;
    --ev-b-soft:  rgba(58, 58, 61, .12);
    --ev-c:       #8E8D87;
    --ev-c-ink:   #0B0B0C;
    --ev-c-soft:  rgba(142, 141, 135, .20);

    /* Links are the same blue as grade A on purpose: both mean "go and look at
       the evidence", and a link hue on top of an evidence hue would be a third
       thing to learn. */
    --link:       #1B3FD8;
    --line:       rgba(11, 11, 12, .16);
    --line-2:     rgba(11, 11, 12, .085);
    --line-3:     rgba(11, 11, 12, .05);
    /* The search-term highlight used to be its own yellow, which was a fifth
       chromatic colour by the same accounting. It is now the accent mixed into
       the paper, so it still reads as a highlighter -- ink on it is ~14:1 -- and
       the ink it sits on does not have to change to keep passing. */
    --mark:       color-mix(in srgb, var(--accent) 20%, var(--paper));

    /* ── the alternating surface washes are gone ──────────────────────────
       --paper-warm #F1EEE7 and --paper-cool #EEF1F5 are deleted. They were not
       hues in the token sense, but they were a second and third background tone
       that appeared only in the lower half of the page, which is precisely the
       "a new colour shows up down there" effect. Every section is now the same
       --paper; the bands are separated by whitespace and a hairline rule
       instead, and the drifting ::after carries the rhythm. */
    /* The rose's B/C remainder. A true neutral, not the old warm #D6D0C0 which
       put a fifth cast into the hero. Not --paper-3, which sits at 1.05:1 on the
       paper and made the outer half of every wedge invisible. */
    --wedge:      #CBCBCB;

    --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
            "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Source Han Sans SC",
            sans-serif;
    --mono: "SF Mono", "JetBrains Mono", ui-monospace, "Cascadia Mono", Menlo, Consolas,
            "Noto Sans Mono CJK SC", monospace;

    --gut: clamp(18px, 4.2vw, 56px);
    --maxw: 1560px;
    --r: 2px;
    /* The sticky header's box: .topbar__in is 60px plus the 1px border, and it is
       in flow rather than overlaying, so it eats the top of the first screen. The
       hero is sized as 100svh minus this so that header + hero is exactly one
       screen and the fold lands on the hero's own rule. 54+1 below 760px. */
    --topbar-h: 61px;

    --step--1: clamp(.78rem, .76rem + .1vw, .84rem);
    --step-0:  clamp(.94rem, .91rem + .16vw, 1rem);
    --step-1:  clamp(1.06rem, 1rem + .3vw, 1.2rem);
    --step-2:  clamp(1.28rem, 1.15rem + .65vw, 1.7rem);
    --step-3:  clamp(1.7rem, 1.4rem + 1.5vw, 2.6rem);
    --step-4:  clamp(2.3rem, 1.6rem + 3.4vw, 4.4rem);
    --step-5:  clamp(3.2rem, 1.4rem + 8vw, 9rem);

    --ease: cubic-bezier(.22, .61, .36, 1);
    --ease-out: cubic-bezier(.16, 1, .3, 1);
    --dur: .34s;
  }

  html[data-theme="dark"] {
    --ink:        #F4F3EE;
    --ink-2:      #C9C8C1;
    --muted:      #8E8D87;
    --paper:      #0A0A0B;
    --paper-2:    #131316;
    --paper-3:    #1C1C20;
    --accent:     #FF5A2B;
    --accent-deep: #FF6A3D;
    --accent-ink: #0A0A0B;
    /* Same two hues as the light theme, stepped for a near-black ground: blue lifts
       to #7FA4F2, and the B/C ladder becomes light-on-dark rather than
       dark-on-light -- B #C9C8C1 outranks C #8E8D87 against the paper here, which
       keeps the strong/medium/weak reading identical in both themes. */
    --ev-a:       #7FA4F2;
    --ev-a-ink:   #0A0A0B;
    --ev-a-soft:  rgba(127, 164, 242, .18);
    --ev-b:       #C9C8C1;
    --ev-b-ink:   #0A0A0B;
    --ev-b-soft:  rgba(201, 200, 193, .16);
    --ev-c:       #8E8D87;
    --ev-c-ink:   #0A0A0B;
    --ev-c-soft:  rgba(142, 141, 135, .18);
    --wedge:      #2A2A2F;
    --link:       #7FA4F2;
    --line:       rgba(244, 243, 238, .2);
    --line-2:     rgba(244, 243, 238, .11);
    --line-3:     rgba(244, 243, 238, .06);
    --mark:       color-mix(in srgb, var(--accent) 26%, var(--paper));
  }

  @media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) {
      --ink: #F4F3EE; --ink-2: #C9C8C1; --muted: #8E8D87;
      --paper: #0A0A0B; --paper-2: #131316; --paper-3: #1C1C20;
      --accent: #FF5A2B; --accent-deep: #FF6A3D; --accent-ink: #0A0A0B; --link: #7FA4F2;
      --ev-a: #7FA4F2; --ev-a-ink: #0A0A0B; --ev-a-soft: rgba(127,164,242,.18);
      --ev-b: #C9C8C1; --ev-b-ink: #0A0A0B; --ev-b-soft: rgba(201,200,193,.16);
      --ev-c: #8E8D87; --ev-c-ink: #0A0A0B; --ev-c-soft: rgba(142,141,135,.18);
      --wedge: #2A2A2F;
      --line: rgba(244,243,238,.2); --line-2: rgba(244,243,238,.11); --line-3: rgba(244,243,238,.06);
      --mark: color-mix(in srgb, var(--accent) 26%, var(--paper));
    }
  }
}

/* ═══════════════════════════════════════════════════════════════════
   ABOUT — 李哲. Identity, history, friend links. Quiet, editorial.
   ═══════════════════════════════════════════════════════════════════ */
@layer components {

  .about { padding-block: clamp(20px, 3vw, 40px) clamp(50px, 7vw, 110px); }
  .about__hero { padding-block: clamp(18px, 3vw, 44px) clamp(34px, 5vw, 70px); max-width: 780px; }
  .about__id { display: flex; align-items: center; gap: 18px; margin-bottom: 20px; }
  .about__mark {
    display: grid; place-items: center; width: 58px; height: 58px; flex: none;
    background: var(--ink); color: var(--paper);
    font-family: var(--mono); font-size: 17px; font-weight: 500; letter-spacing: .04em;
  }
  .about__id h1 {
    font-size: var(--step-4); letter-spacing: -.04em; line-height: 1;
    display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  }
  .about__id em {
    font-style: normal; font-family: var(--mono); font-weight: 400;
    font-size: var(--step-0); letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
  }
  .about__role {
    font-family: var(--mono); font-size: var(--step--1); letter-spacing: .14em;
    text-transform: uppercase; color: var(--accent-deep); margin-bottom: 12px;
  }
  .about__intro { max-width: 62ch; color: var(--ink-2); line-height: 1.72; }

  .about__sec { margin-bottom: clamp(30px, 4.4vw, 56px); }
  .about__hero--tight { padding-block: clamp(14px, 2.4vw, 30px) clamp(26px, 3.6vw, 46px); }
  .about__hero--tight .about__id { margin-bottom: 12px; }
  .about__hero--tight .about__role { margin-bottom: 0; }
  .about__icp { margin-top: 20px; font-family: var(--mono); font-size: 11.5px; color: var(--muted); }

  .about__cards { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); }
  .about__card {
    background: var(--paper); padding: clamp(16px, 2vw, 24px);
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 4px 13px; align-items: start; position: relative;
    transition: background-color .24s var(--ease), color .24s var(--ease);
  }
  .about__cardi {
    color: var(--ev-a); display: grid; place-items: center;
    grid-row: 1 / span 2; padding-top: 2px;
  }
  .about__cardb { display: grid; gap: 3px; min-width: 0; }
  .about__cardt { font-size: var(--step-0); font-weight: 600; letter-spacing: -.012em; }
  .about__cardd { font-size: var(--step--1); color: var(--muted); line-height: 1.55; }
  .about__cardgo { color: var(--muted); padding-top: 2px; transition: transform .3s var(--ease-out), color .2s var(--ease); }
  .about__card:hover { background: var(--ink); color: var(--paper); }
  .about__card:hover .about__cardd,
  .about__card:hover .about__cardgo { color: var(--paper-2); }
  .about__card:hover .about__cardi { color: var(--paper-2); }
  .about__card:hover .about__cardgo { transform: translateX(3px); }

  .author-roles {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1px; background: var(--line); border: 1px solid var(--line);
  }
  .author-role { background: var(--paper); padding: clamp(20px, 3vw, 34px); }
  .author-role h2 { margin: 8px 0 12px; font-size: var(--step-2); letter-spacing: -.025em; }
  .author-role p { color: var(--ink-2); line-height: 1.72; margin-bottom: 14px; }
  .author-profile {
    display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(220px, .7fr);
    gap: clamp(22px, 4vw, 60px); align-items: start;
  }
  .author-profile p { color: var(--ink-2); line-height: 1.75; margin-bottom: 14px; }
  .author-profile__lead { font-size: var(--step-1); color: var(--ink) !important; }
  .author-profile__focus { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); }
  .author-profile__focus li { background: var(--paper); padding: 12px 15px; font-size: var(--step--1); }

  @media (max-width: 760px) {
    .about__hero { max-width: none; }
    .about__hero--tight { display: grid; gap: 18px; }
    .author-roles, .author-profile { grid-template-columns: 1fr; }
  }

  /* friend links in the footer */
  .foot__links { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 16px; }
  .foot__links a {
    font-size: var(--step--1); color: var(--muted);
    padding: 3px 0; border-bottom: 1px solid transparent;
    transition: color .2s var(--ease), border-color .2s var(--ease);
  }
  .foot__links a:hover { color: var(--ink); border-bottom-color: var(--accent); }
}

/* ═══════════════════════════════════════════════════════════════════
   REFINEMENT — what makes it read as "expensive" rather than "grey text
   on cream". Mostly restraint: fewer borders, more air, one real shadow
   reserved for things that float, and motion that only ever touches
   transform/opacity so it stays on the compositor.
   ═══════════════════════════════════════════════════════════════════ */
@layer components {

  /* a single elevation, used only where something genuinely floats */
  :root {
    --lift: 0 1px 2px rgba(11, 11, 12, .04), 0 12px 34px -14px rgba(11, 11, 12, .18);
    --lift-lg: 0 2px 4px rgba(11, 11, 12, .05), 0 34px 70px -28px rgba(11, 11, 12, .3);
    --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  }
  html[data-theme="dark"] {
    --lift: 0 1px 2px rgba(0, 0, 0, .5), 0 14px 40px -16px rgba(0, 0, 0, .8);
    --lift-lg: 0 2px 6px rgba(0, 0, 0, .55), 0 40px 80px -30px rgba(0, 0, 0, .9);
  }

  /* Section rhythm: the single biggest lever on "expensive" vs "crowded". */
  .wrap > section + section,
  .home > section + section { margin-top: clamp(52px, 7.5vw, 118px); }

  /* Sections announce themselves with a hairline that draws in, once. */
  .band { border-top: 1px solid var(--line); padding-top: clamp(18px, 2.4vw, 30px); }
  .band h2 { max-width: 20ch; }

  /* Cards lift. The shadow is the payoff for the hover, not decoration. */
  .card {
    position: relative;
    transition:
      border-color .28s var(--ease),
      transform .28s var(--ease-out),
      box-shadow .28s var(--ease),
      background-color .28s var(--ease);
  }
  .card:hover {
    transform: translateY(-2px);
    border-color: var(--ink);
    box-shadow: var(--lift);
  }
  /* the accent bar wipes in from the left instead of blinking on */
  .card::before {
    content: ""; position: absolute; left: -1px; top: 14px; bottom: 14px; width: 2px;
    background: var(--accent); transform: scaleY(0); transform-origin: 50% 0;
    transition: transform .34s var(--ease-out);
  }
  .card:hover::before { transform: scaleY(1); }.sit, .idx33, .lg, .about__card{
    transition-property: background-color, color;
    transition-duration: .24s;
    transition-timing-function: var(--ease);
  }

  /* Nav: the underline grows from the centre rather than the left edge. */
  .nav a::after { transform: scaleX(0); transform-origin: 50% 50%; }
  .nav a:hover::after { transform: scaleX(1); }
  .nav a[aria-current="page"]::after { transform: scaleX(1); }

  /* Section headings get a rule that draws itself as they scroll in. Driven by a
     scroll-timeline where supported; the reveal fallback covers the rest. */
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .band { animation: drawRule linear both; animation-timeline: view(); animation-range: entry 0% entry 60%; }
      @keyframes drawRule { from { border-top-color: transparent; } to { border-top-color: var(--line); } }
    }
  }

  /* This stagger used transition-delay on the cards, which delays the *hover*
     transition too -- so moving across a row felt laggy and broken rather than
     staggered. Delays belong on the reveal animation, not on hover. */
  @media (prefers-reduced-motion: no-preference) {
    .cards > .card:nth-child(6n+2) { animation-delay: .03s; }
    .cards > .card:nth-child(6n+3) { animation-delay: .06s; }
    .cards > .card:nth-child(6n+4) { animation-delay: .09s; }
    .cards > .card:nth-child(6n+5) { animation-delay: .12s; }
    .cards > .card:nth-child(6n+6) { animation-delay: .15s; }
  }.sit:active, .idx33:active, .lg:active, .about__card:active, .seven__i a:active{ transform: scale(.988); }.sit, .idx33, .lg, .about__card{ transition-property: background-color, color, transform; }.sit, .idx33, .lg, .about__card{ transition-duration: .24s; }

  /* Focus is visible on the keyboard path and never on mouse clicks. */
  :where(a, button, input, select, [tabindex]):focus-visible {
    outline: 2px solid var(--accent-deep); outline-offset: 2px; border-radius: 1px;
  }

  /* Buttons get a fill that wipes up from the baseline. */
  .btn { isolation: isolate; }
  .btn::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    background: var(--ink); transform: scaleY(0); transform-origin: 50% 100%;
    transition: transform .34s var(--ease-out);
  }
  .btn:hover::before { transform: scaleY(1); }
  .btn--primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
  .btn--primary::before { background: var(--accent-deep); }
  .btn--ghost:hover { color: var(--paper); }

  /* Icon buttons acknowledge the press. */
  .iconbtn { transition: background-color .2s var(--ease), transform .2s var(--ease-spring), color .2s; }
  .iconbtn:hover { transform: translateY(-1px); }
  .iconbtn:active { transform: scale(.9); }

  /* Detail panels open by measuring, so text reflows instead of popping. */
  /* Animate by measuring, but keep [hidden] doing its accessibility job:
     JS removes it, forces a reflow, then adds .is-open. Never override
     [hidden] to display:grid — that puts collapsed content back in the
     accessibility tree. */
  .card__detail {
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows .38s var(--ease-out), opacity .28s var(--ease);
    opacity: 0;
  }
  .card__detail > * { overflow: hidden; }
  .card.is-open .card__detail { grid-template-rows: 1fr; opacity: 1; }
}

/* ═══════════════════════════════════════════════════════════════════
   COMMAND PALETTE — the fastest path to anything on the site.
   ═══════════════════════════════════════════════════════════════════ */
@layer components {

  .pal { position: fixed; inset: 0; z-index: 300; }
  .pal[hidden] { display: none; }
  .pal__scrim {
    position: absolute; inset: 0; background: color-mix(in srgb, var(--ink) 42%, transparent);
    opacity: 0; transition: opacity .24s var(--ease);
    backdrop-filter: blur(3px);
  }
  .pal.is-on .pal__scrim { opacity: 1; }

  .pal__panel {
    position: relative; margin: clamp(60px, 12vh, 130px) auto 0; width: min(660px, calc(100vw - 2 * var(--gut)));
    max-height: min(70vh, 620px); display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
    background: var(--paper); border: 1px solid var(--ink);
    box-shadow: var(--lift-lg);
    opacity: 0; transform: translateY(-10px) scale(.985);
    transition: opacity .26s var(--ease), transform .34s var(--ease-out);
  }
  .pal.is-on .pal__panel { opacity: 1; transform: none; }

  .pal__head { display: flex; align-items: center; gap: 12px; padding: 4px 4px 4px 16px; border-bottom: 1px solid var(--line); }
  .pal__ico { font-family: var(--mono); font-size: 14px; color: var(--muted); }
  .pal__input {
    flex: 1; min-width: 0; border: 0; background: none; padding: 18px 0;
    font: 400 var(--step-1)/1.3 var(--sans); color: var(--ink);
  }
  .pal__input:focus { outline: none; }
  .pal__input::placeholder { color: var(--muted); }
  .pal__input::-webkit-search-cancel-button { display: none; }
  .pal__esc {
    font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
    color: var(--muted); border: 1px solid var(--line); padding: 5px 9px; flex: none;
    transition: color .2s, border-color .2s;
  }
  .pal__esc:hover { color: var(--ink); border-color: var(--ink); }

  .pal__list { overflow-y: auto; overscroll-behavior: contain; padding: 6px 0; scrollbar-width: thin; }
  .pal__g {
    font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase;
    color: var(--muted); padding: 12px 16px 6px;
  }
  .pal__row {
    display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: baseline;
    padding: 9px 16px; border-left: 2px solid transparent;
    transition: background-color .12s var(--ease), border-color .12s var(--ease);
  }
  .pal__no { font-size: 11.5px; color: var(--muted); min-width: 2.2em; }
  .pal__b { display: grid; gap: 2px; min-width: 0; }
  .pal__t { font-size: var(--step-0); line-height: 1.35; }
  .pal__t mark { background: var(--mark); color: inherit; padding: 0 1px; }
  .pal__d {
    font-size: 12.5px; color: var(--muted); line-height: 1.45;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .pal__row.is-on { background: var(--paper-2); border-left-color: var(--accent); }
  .pal__row.is-on .pal__t { font-weight: 600; }
  .pal__none { padding: 26px 16px; color: var(--muted); font-size: var(--step--1); }

  .pal__foot {
    display: flex; gap: 16px; flex-wrap: wrap; padding: 10px 16px;
    border-top: 1px solid var(--line); background: var(--paper-2);
    font-family: var(--mono); font-size: 10.5px; letter-spacing: .04em; color: var(--muted);
  }
  .pal__foot kbd {
    font: 500 10px/1 var(--mono); border: 1px solid var(--line); padding: 2px 4px;
    margin-right: 4px; color: var(--ink-2);
  }
}

  /* These duplicate the filter rail on the explore page, which a phone gets
     anyway — dropping the row keeps the hero inside one screen. Must come after
     the base rule above, or the later declaration wins on equal specificity. */
  @media (max-width: 640px) { }
  .lg__ico { color: var(--muted); }

  /* content-author credit table */
  .credit { border-top: 1px solid var(--line); }
  .credit__row {
    display: grid; gap: 2px clamp(16px, 3vw, 40px);
    grid-template-columns: 84px minmax(0, 1fr); align-items: baseline;
    padding: 13px 2px; border-bottom: 1px solid var(--line-2);
  }
  @media (min-width: 720px) { .credit__row { grid-template-columns: 110px minmax(0, 1fr); } }
  .credit__k { font-size: var(--step--1); color: var(--muted); }
  .credit__v { font-size: var(--step-0); overflow-wrap: anywhere; }
  .credit__v em {
    display: block; font-style: normal; color: var(--muted);
    font-size: var(--step--1); margin-top: 3px;
  }
  .credit__v--link { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }

/* boot skeleton — mirrors the result grid so the first paint already shows
   the shape of what is coming, instead of a spinner and a line of text */
  .boot { padding-block: clamp(30px, 5vw, 64px); }
  .boot__bar { height: 2px; background: var(--paper-3); margin-bottom: clamp(24px, 4vw, 44px); }
  .boot__bar i {
    display: block; height: 100%; width: 38%; background: var(--accent-deep);
    animation: bootslide 1.15s var(--ease) infinite alternate;
  }
  @keyframes bootslide { from { transform: translateX(-10%); } to { transform: translateX(180%); } }
  .boot__grid { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line);
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }
  .boot__card { background: var(--paper); padding: 18px; display: grid; gap: 9px; }
  .boot__l, .boot__t, .boot__p { display: block; background: var(--paper-2); border-radius: 2px; }
  .boot__l { width: 34%; height: 9px; }
  .boot__t { height: 15px; }
  .boot__p { height: 10px; }
  .boot__card > * { animation: bootpulse 1.5s var(--ease) infinite alternate; }
  .boot__card > *:nth-child(2) { animation-delay: .06s; }
  .boot__card > *:nth-child(3) { animation-delay: .12s; }
  .boot__card > *:nth-child(4) { animation-delay: .18s; }
  @keyframes bootpulse { from { opacity: .55; } to { opacity: 1; } }
  @media (prefers-reduced-motion: reduce) {
    .boot__bar i, .boot__card > * { animation: none; }
  }

  /* load more: state where you are, not just a bare verb */
  .loadmore {
    grid-column: 1 / -1; margin-top: 10px;
    display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center; gap: 10px;
    padding: 18px; border: 1px solid var(--line); background: var(--paper);
    font-size: var(--step-0); color: var(--ink);
    transition: border-color .2s var(--ease), background-color .2s var(--ease);
  }
  .loadmore i { font-style: normal; font-size: var(--step--1); color: var(--muted); }
  .loadmore:hover { border-color: var(--ink); background: var(--paper-2); }

  /* touch feedback — :hover does nothing on a phone, :active is the real signal */
  @media (hover: none) {
    .card:active { transform: none; box-shadow: none; }
    .card:active::before { transform: scaleY(1); }.find__chip:active{ background: var(--paper-2); border-color: var(--ink); }
  }

/* rotating placeholder — a real placeholder can't animate, so the field goes
     transparent and this sibling does the typing. It must be positioned against
     .find__field (which wraps the input), not against .find: anchoring it to the
     form put the text on top of the magnifier icon. */
  .find__field { position: relative; flex: 1; min-width: 0; display: flex; align-items: center; }
  .find__ghost {
    position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    pointer-events: none; color: var(--muted);
    font: 400 var(--step-1)/1.35 var(--sans); max-width: 100%;
    white-space: nowrap; overflow: hidden; opacity: 0;
  }
  .find__ghost::after {
    content: ""; display: inline-block; width: 1.5px; height: 1.05em;
    margin-left: 3px; background: var(--accent-deep); vertical-align: -.16em;
    animation: caret 1.05s steps(2, start) infinite;
  }
  .find__ghost.is-on { opacity: 1; }
  @keyframes caret { 0%, 100% { opacity: 0; } 50% { opacity: 1; } }
  /* The placeholder is the fallback for no-JS and for the empty idle state. While
     the ghost is painting it must step aside completely -- hiding it only on
     :focus stacked it on top of the typing, because the ghost runs unfocused. */
  .find__field.has-ghost .find__input::placeholder { color: transparent; }
  @media (prefers-reduced-motion: reduce) {
    .find__ghost { display: none; }
    .find__input::placeholder { color: var(--muted); }
  }
  @media (max-width: 640px) {
    .find__ghost { font-size: var(--step-0); }
  }

  /* chapter index: search, filter, sort */
  .chindex__bar {
    display: grid; gap: 10px; align-items: center; margin: 6px 0 12px;
    grid-template-columns: minmax(0, 1fr) auto auto auto;
  }
  @media (max-width: 860px) {
    .chindex__bar { grid-template-columns: minmax(0, 1fr) auto; }
    .chindex__bar .ghostbtn { grid-column: 2; }
  }
  .chindex__count {
    font-family: var(--mono); font-size: 12px; color: var(--muted);
    white-space: nowrap; font-variant-numeric: tabular-nums;
  }
  .chfilter {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    padding-bottom: 16px; margin-bottom: 4px; border-bottom: 1px solid var(--line);
  }
  .chfilter__l { font-size: var(--step--1); color: var(--muted); margin-right: 2px; }
  .chfilter__c {
    font-size: var(--step--1); padding: 5px 11px; border: 1px solid var(--line);
    color: var(--muted);
    transition: border-color .18s var(--ease), color .18s var(--ease), background-color .18s var(--ease);
  }
  .chfilter__c:hover { color: var(--ink); border-color: var(--ink); }
  .chfilter__c[aria-pressed="true"] {
    background: var(--ink); color: var(--paper); border-color: var(--ink);
  }
  .chcard__marked { font-size: 11px; color: var(--accent-deep); }

  /* chapter reader: in-section index and mark counter */
  .chjump {
    border: 1px solid var(--line); margin: 4px 0 22px; background: var(--paper);
  }
  .chjump__sum {
    display: flex; align-items: center; gap: 10px; cursor: pointer;
    padding: 11px 14px; font-size: var(--step--1); color: var(--muted);
    list-style: none;
  }
  .chjump__sum::-webkit-details-marker { display: none; }
  .chjump__sum::after {
    content: "▾"; margin-left: auto; font-size: 11px; transition: transform .2s var(--ease);
  }
  .chjump[open] .chjump__sum::after { transform: rotate(180deg); }
  .chjump__sum:hover { color: var(--ink); }
  .chjump__n { font-size: 11px; }
  .chjump__l {
    list-style: none; margin: 0; padding: 0 0 6px;
    max-height: 320px; overflow-y: auto; scrollbar-width: thin;
    border-top: 1px solid var(--line);
  }
  .chjump__l a {
    display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px;
    align-items: baseline; padding: 7px 14px;
    transition: background-color .15s var(--ease);
  }
  .chjump__l a:hover { background: var(--paper-2); }
  .chjump__no { font-size: 11px; color: var(--muted); }
  .chjump__t { font-size: var(--step--1); line-height: 1.4; }
  @media (max-width: 640px) { .chjump__l { max-height: 240px; } }

  .chmarked {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
    margin-top: 26px; padding: 13px 15px;
    background: var(--paper-2); border-left: 2px solid var(--accent-deep);
    font-size: var(--step--1); color: var(--muted);
  }
  .chmarked b { font-size: var(--step-1); color: var(--ink); }
  .chmarked__go { margin-left: auto; color: var(--ink); border-bottom: 1px solid var(--line); }
  .chmarked__go:hover { border-bottom-color: var(--accent); }

/* original text index: full PDF plus classified feature articles */
  .longidx { padding-bottom: clamp(50px, 7vw, 100px); }
  .pdfdoc { margin-bottom: clamp(42px, 6vw, 78px); border: 1px solid var(--line); background: var(--paper); }
  .pdfdoc__head {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 22px;
    align-items: end; padding: clamp(20px, 3vw, 34px); border-bottom: 1px solid var(--line);
  }
  .pdfdoc__head h2 { margin: 7px 0 8px; font-size: var(--step-2); letter-spacing: -.025em; }
  .pdfdoc__head p { color: var(--ink-2); line-height: 1.62; }
  .pdfdoc__head .pdfdoc__note { margin-top: 8px; max-width: 72ch; color: var(--muted); font-size: var(--step--1); }
  .pdfdoc__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
  .pdfviewer { background: var(--paper-2); }
  .pdfviewer__bar {
    position: sticky; top: var(--topbar-h); z-index: 2;
    display: flex; align-items: center; justify-content: center; gap: 14px;
    min-height: 50px; padding: 8px 14px; border-bottom: 1px solid var(--line);
    background: color-mix(in srgb, var(--paper) 92%, transparent);
    backdrop-filter: blur(12px);
  }
  .pdfviewer__bar button {
    min-height: 34px; padding: 6px 12px; border: 1px solid var(--line);
    color: var(--ink); background: var(--paper); font-size: var(--step--1);
  }
  .pdfviewer__bar button:hover:not(:disabled) { border-color: var(--ink); }
  .pdfviewer__bar button:disabled { opacity: .38; cursor: not-allowed; }
  .pdfviewer__pages { min-width: 110px; text-align: center; font-size: 12px; color: var(--muted); }
  .pdfviewer__pages b { color: var(--ink); font-weight: 600; }
  .pdfviewer__stage {
    display: grid; justify-items: center; min-height: 560px; max-height: min(76svh, 900px);
    overflow: auto; padding: 16px; background: #2a2a2c;
  }
  .pdfviewer__stage canvas {
    display: block; max-width: 100%; height: auto; background: white;
    box-shadow: 0 10px 34px rgba(0, 0, 0, .24);
    transition: opacity .24s var(--ease), transform .42s var(--ease-out), box-shadow .42s var(--ease);
  }
  .pdfviewer__stage canvas.is-turning { opacity: .42; transform: translateY(5px) scale(.992); box-shadow: 0 5px 18px rgba(0, 0, 0, .16); }
  .pdfviewer__status { padding: 9px 14px; color: var(--muted); font-size: 12px; text-align: center; border-top: 1px solid var(--line); }

  .originals__intro { margin-bottom: 24px; }
  .originals__intro > p { max-width: 64ch; color: var(--muted); line-height: 1.66; }
  .originals__group { margin-top: clamp(28px, 4vw, 48px); }
  .originals__group-title {
    font-family: var(--mono); font-size: 12px; letter-spacing: .12em;
    color: var(--muted); padding-bottom: 10px; border-bottom: 1px solid var(--line);
  }
  .originals__grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    gap: 1px; background: var(--line); border: 1px solid var(--line); border-top: 0;
  }
  .original-card { background: var(--paper); padding: clamp(20px, 3vw, 30px); display: grid; align-content: start; gap: 13px; }
  .original-card__meta { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
  .original-card__meta .num { color: var(--muted); font-size: 11.5px; }
  .original-card h3 { font-size: var(--step-2); line-height: 1.24; letter-spacing: -.022em; }
  .original-card h3 a:hover { color: var(--accent-deep); }
  .original-card > p { color: var(--ink-2); line-height: 1.7; }
  .original-card__tags, .prose__meta { display: flex; flex-wrap: wrap; gap: 6px; }
  .original-card__tags span, .prose__meta > span:not(.u-label) {
    padding: 4px 8px; border: 1px solid var(--line); color: var(--muted);
    font-size: 11px; line-height: 1.2;
  }
  .original-card .piece__read { margin-top: auto; }

  @media (max-width: 760px) {
    .pdfdoc__head { grid-template-columns: 1fr; align-items: start; }
    .pdfdoc__actions { justify-content: flex-start; }
    .pdfviewer__stage { min-height: 460px; max-height: 70svh; }
  }
  @media (max-width: 480px) {
    .pdfviewer__stage { min-height: 400px; padding: 8px; }
    .pdfviewer__bar { justify-content: space-between; gap: 6px; }
    .pdfviewer__pages { min-width: 92px; }
  }

  .piece {
    border-top: 1px solid var(--line);
    padding-block: clamp(22px, 3vw, 36px);
    display: grid; gap: 12px;
  }
  .piece:last-child { border-bottom: 1px solid var(--line); }
  .piece__top { display: grid; gap: 6px; }
  .piece__top h2 {
    font-size: var(--step-2); letter-spacing: -.024em; line-height: 1.18;
    max-width: 26ch;
  }
  .piece__top h2 a:hover { color: var(--accent-deep); }
  .piece__meta { font-size: 11.5px; color: var(--muted); letter-spacing: .04em; }
  .piece__lead { color: var(--ink); line-height: 1.66; max-width: 62ch; }
  .piece__first { color: var(--muted); line-height: 1.7; max-width: 62ch; }
  .piece__body {
    display: grid; gap: 12px; max-width: 62ch; padding-top: 4px;
    animation: unfurl .34s var(--ease-out) both;
  }
  .piece__body[hidden] { display: none; }
  .piece__body p { color: var(--ink-2); line-height: 1.72; }
  @keyframes unfurl { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
  .piece__act { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; padding-top: 4px; }
  .piece__more {
    padding: 9px 16px; border: 1px solid var(--ink); background: var(--paper);
    font-size: var(--step--1); color: var(--ink);
    transition: background-color .2s var(--ease), color .2s var(--ease);
  }
  .piece__more:hover { background: var(--ink); color: var(--paper); }
  .piece__read { font-size: var(--step--1); color: var(--muted); padding: 9px 0; }
  .piece__read:hover { color: var(--ink); }
  @media (prefers-reduced-motion: reduce) { .piece__body { animation: none; } }
  .pillar__k {
    font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em;
    color: var(--accent-deep); display: block; margin-bottom: 9px;
  }
  @media (max-width: 640px) { }
  @media (max-width: 640px) { }
  @media (max-width: 640px) { }

  .about__icp { margin-top: 22px; font-size: 11.5px; color: var(--muted); font-family: var(--mono); }

/* ── base ──────────────────────────────────────────────────────────── */
@layer components {

  /* topbar search affordance — looks like an input, opens a palette */
  .palbtn {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 8px 6px 11px; border: 1px solid var(--line); color: var(--muted);
    font-size: var(--step--1); transition: color .2s var(--ease), border-color .2s var(--ease);
  }
  .palbtn span { min-width: 2.4em; text-align: left; }
  .palbtn kbd {
    font: 500 10px/1 var(--mono); border: 1px solid var(--line); padding: 3px 5px;
    color: var(--muted);
  }
  .palbtn:hover { color: var(--ink); border-color: var(--ink); }
  @media (max-width: 640px) {
    .palbtn span, .palbtn kbd { display: none; }
    .palbtn { padding: 8px; }
  }

  /* route transition: a short, quiet settle rather than a slide-in */
  main > * { animation: settle .42s var(--ease-out) both; }
  @keyframes settle {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: none; }
  }
  html.has-pal { overflow: hidden; }
}
@layer base {
  html { scroll-behavior: smooth; scroll-padding-top: 88px; }
  body {
    font-family: var(--sans);
    font-size: var(--step-0);
    color: var(--ink);
    background: var(--paper);
    font-feature-settings: "tnum" 0, "kern" 1;
    overflow-wrap: break-word;
    transition: background-color .5s var(--ease), color .5s var(--ease);
    /* `clip`, not `hidden`, and only as a backstop. The drifting section wash is
       deliberately wider than the viewport (116vw) so its translate can never
       expose an edge, which makes documentElement.scrollWidth overshoot even
       though nothing is actually reachable. `overflow-x: clip` contains that
       without turning <html> into a scroll container -- `hidden` here would make
       it one, and that silently breaks the sticky topbar and every scroll-spy
       offset measured against it. Supported everywhere that supports
       aspect-ratio, which this stylesheet already relies on. */
    overflow-x: clip;
  }
  h1, h2, h3, h4 { font-weight: 600; line-height: 1.08; letter-spacing: -.018em; text-wrap: balance; }
  p { text-wrap: pretty; }
  strong { font-weight: 600; }
  .u-mono { font-family: var(--mono); font-variant-ligatures: none; }
  .sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  .u-label {
    font-family: var(--mono); font-size: var(--step--1); letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted); font-weight: 500;
  }
  .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
  .skip {
    position: fixed; top: 8px; left: 8px; z-index: 200; padding: 10px 16px;
    background: var(--ink); color: var(--paper); transform: translateY(-160%);
    transition: transform .2s var(--ease); font-size: var(--step--1);
  }
  .skip:focus { transform: none; }
  .noscript { padding: 40px var(--gut); font-size: var(--step-1); }
  ::selection { background: var(--mark); color: #0B0B0C; }
}

/* ── layout ────────────────────────────────────────────────────────── */
@layer layout {
  .wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gut); }
  .rule { height: 1px; background: var(--line); border: 0; }

  .progress { position: fixed; inset: 0 0 auto; height: 2px; z-index: 90; pointer-events: none; }
  .progress i {
    display: block; height: 100%; width: 0; background: var(--accent);
    transform-origin: 0 50%; transition: width .1s linear;
  }

  .topbar {
    position: sticky; top: 0; z-index: 80;
    background: color-mix(in srgb, var(--paper) 86%, transparent);
    backdrop-filter: saturate(1.6) blur(14px);
    border-bottom: 1px solid var(--line-2);
    transition: box-shadow .3s var(--ease), background-color .3s var(--ease), border-color .3s var(--ease);
  }
  .topbar--lifted {
    background: color-mix(in srgb, var(--paper) 94%, transparent);
    border-bottom-color: var(--line);
    box-shadow: 0 12px 34px -26px color-mix(in srgb, var(--ink) 46%, transparent);
  }
  .topbar__in {
    max-width: var(--maxw); margin-inline: auto; padding: 0 var(--gut);
    height: 60px; display: flex; align-items: center; gap: clamp(14px, 3vw, 40px);
  }
  .brand { display: flex; align-items: center; gap: 10px; font-weight: 600; letter-spacing: -.01em; white-space: nowrap; }
  .brand__mark { display: grid; gap: 2.5px; width: 18px; }
  .brand__mark i { display: block; height: 2.5px; background: var(--ink); }
  .brand__mark i:nth-child(1) { background: var(--accent); width: 100%; }
  .brand__mark i:nth-child(2) { width: 66%; }
  .brand__mark i:nth-child(3) { width: 84%; }
  .brand:hover .brand__mark i:nth-child(2) { width: 100%; }
  .brand__mark i { transition: width .4s var(--ease-out); }

  .nav { display: flex; gap: clamp(10px, 1.6vw, 26px); margin-inline-end: auto; }
  .nav a {
    position: relative; padding: 4px 0; font-size: var(--step--1); color: var(--muted);
    transition: color .22s var(--ease); white-space: nowrap;
  }
  .nav a::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 1.5px;
    background: var(--accent); transform: scaleX(0); transform-origin: 0 50%;
    transition: transform .3s var(--ease-out);
  }
  .nav a:hover { color: var(--ink); }
  .nav a[aria-current="page"] { color: var(--ink); }
  .nav a[aria-current="page"]::after { transform: scaleX(1); }

  .topbar__end { display: flex; gap: 6px; align-items: center; }
  .iconbtn {
    width: 34px; height: 34px; display: grid; place-items: center; position: relative;
    border: 1px solid var(--line); border-radius: var(--r); color: var(--ink-2);
    transition: border-color .2s, color .2s, background-color .2s;
  }
  .iconbtn:hover { border-color: var(--ink); color: var(--ink); background: var(--paper-2); }
  .iconbtn svg { transition: transform .38s var(--ease-spring); }
  .iconbtn:hover svg { transform: scale(1.08); }
  #themeBtn:hover svg { transform: rotate(16deg) scale(1.08); }
  .iconbtn b {
    position: absolute; top: -6px; right: -6px; min-width: 17px; height: 17px; padding: 0 4px;
    display: grid; place-items: center; border-radius: 9px; background: var(--accent-deep);
    color: var(--accent-ink); font: 600 10px/1 var(--mono);
  }
  .iconbtn b[data-empty="1"] { display: none; }

  .foot { border-top: 1px solid var(--line); margin-top: clamp(60px, 9vw, 130px); }
  .foot__grid {
    max-width: var(--maxw); margin-inline: auto; padding: clamp(38px, 5vw, 66px) var(--gut);
    display: grid; gap: clamp(24px, 3vw, 50px);
    grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
  }
  .foot__col--wide { grid-column: span 1; }
  .foot__title { font-size: var(--step-2); font-weight: 600; letter-spacing: -.02em; margin-bottom: 8px; }
  .foot__note { color: var(--muted); font-size: var(--step--1); max-width: 42ch; }
  .foot__label {
    font-family: var(--mono); font-size: var(--step--1); letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted); margin-bottom: 8px;
  }
  .foot__col p { font-size: var(--step--1); color: var(--muted); }
  .foot__col a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }
  .foot__bar {
    max-width: var(--maxw); margin-inline: auto; padding: 14px var(--gut) 22px;
    display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
    border-top: 1px solid var(--line-2);
    font-family: var(--mono); font-size: 11.5px; letter-spacing: .04em; color: var(--muted);
  }
  .foot__bar a {
    color: var(--ink); border-bottom: 1px solid var(--line);
    transition: color .2s var(--ease), border-color .2s var(--ease);
  }
  .foot__bar a:hover { color: var(--accent-deep); border-color: var(--accent); }

  .seo-fallback {
    width: min(860px, calc(100% - 2 * var(--gut))); margin-inline: auto;
    padding-block: clamp(56px, 9vw, 120px);
  }
  .seo-fallback header { margin-bottom: clamp(46px, 7vw, 84px); }
  .seo-fallback h1 { font-size: var(--step-5); line-height: .98; letter-spacing: -.055em; margin-block: 14px 22px; }
  .seo-fallback header > p:last-child { max-width: 58ch; color: var(--ink-2); font-size: var(--step-1); line-height: 1.75; }
  .seo-fallback section { border-top: 1px solid var(--line); }
  .seo-fallback h2 { font-size: var(--step-2); margin-bottom: 12px; }
  .seo-fallback section p { color: var(--ink-2); line-height: 1.75; margin-bottom: 10px; }
  .seo-fallback a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }

  /* Section rhythm. Excludes .poster on purpose: the hero carries its own
     padding-block on .poster__in, and inheriting this on top of it added 180px
     at 1280 -- which is most of why the hero kept coming in over budget. */
  section:not(.poster) { padding-block: clamp(46px, 7vw, 104px); }
  .sec-head { display: flex; align-items: baseline; gap: 18px; margin-bottom: clamp(22px, 3vw, 40px); }
  .sec-head h2 { font-size: var(--step-3); }
  .sec-head .u-label { flex: none; }
  .sec-head::after { content: ""; flex: 1; height: 1px; background: var(--line); align-self: center; }
}

/* ── components ────────────────────────────────────────────────────── */
@layer components {

  .btn {
    display: inline-flex; align-items: center; gap: 9px; padding: 12px 20px;
    border: 1px solid var(--ink); border-radius: var(--r); font-size: var(--step--1);
    font-weight: 500; letter-spacing: .01em; position: relative; overflow: hidden;
    transition: color .3s var(--ease), border-color .3s var(--ease);
  }
  .btn::before {
    content: ""; position: absolute; inset: 0; background: var(--ink);
    transform: scaleY(0); transform-origin: 50% 100%; transition: transform .34s var(--ease-out);
    z-index: -1;
  }
  .btn > * { position: relative; }
  .btn:hover { color: var(--paper); }
  .btn:hover::before { transform: scaleY(1); }
  .btn--primary { background: var(--accent-deep); border-color: var(--accent-deep); color: var(--accent-ink); }
  .btn--primary::before { background: var(--ink); }
  .btn--primary:hover { color: var(--paper); border-color: var(--ink); }
  .btn--ghost { border-color: var(--line); }
  .btn--ghost:hover { border-color: var(--ink); }
  .btn kbd {
    font: 500 11px/1 var(--mono); padding: 3px 5px; border: 1px solid currentColor;
    opacity: .6; border-radius: 2px;
  }

  /* resources strip */
  .res {
    display: grid; gap: 0; background: transparent;
    border-top: 1px solid var(--line); border-left: 1px solid var(--line);
  }
  .res__cell {
    background: var(--paper); padding: clamp(16px, 2vw, 26px);
    border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
    display: grid; gap: 8px; align-content: start; text-align: left;
    transition: background-color .25s var(--ease);
  }
  .res__cell:hover, .res__cell[aria-pressed="true"] { background: var(--paper-2); }
  .res__k { font-size: var(--step-1); font-weight: 600; }
  .res__d { color: var(--muted); font-size: var(--step--1); line-height: 1.6; }
  .res--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* stat band */
  .stats {
    display: grid; gap: 0; background: transparent;
    border: 1px solid var(--line); grid-template-columns: repeat(7, minmax(0, 1fr));
  }
  .stats__c {
    background: var(--paper); padding: clamp(16px, 2vw, 26px) clamp(12px, 1.4vw, 18px);
    border-right: 1px solid var(--line);
  }
  .stats__c:last-child { border-right: 0; }
  .stats__v { font-family: var(--mono); font-size: var(--step-2); line-height: 1; letter-spacing: -.03em; }
  .stats__k { margin-top: 10px; color: var(--muted); font-size: var(--step--1); line-height: 1.4; }

  /* generic list of links */
  .qlist { border-top: 1px solid var(--line); }
  .qlist a {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 16px; align-items: baseline;
    padding: 14px 4px; border-bottom: 1px solid var(--line-2);
    transition: background-color .2s var(--ease), padding-inline .25s var(--ease);
  }
  .qlist a:hover { background: var(--paper-2); padding-inline: 12px; }
  .qlist a:hover .qlist__go { color: var(--accent-deep); transform: translateX(3px); }
  .qlist__q { font-size: var(--step-1); letter-spacing: -.01em; max-width: 46ch; }
  @media (min-width: 1100px) {
    .qlist {
      display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(28px, 4vw, 64px);
    }
    .qlist a:nth-last-child(2):nth-child(odd) { border-bottom: 1px solid var(--line-2); }
  }
  .qlist__go {
    font-family: var(--mono); font-size: var(--step--1); color: var(--muted);
    display: flex; align-items: center; gap: 10px; white-space: nowrap;
    transition: color .2s, transform .25s var(--ease);
  }

  /* chapter index */
  .chgrid {
    display: grid; gap: 0; background: transparent;
    border-top: 1px solid var(--line); border-left: 1px solid var(--line);
    grid-template-columns: repeat(auto-fill, minmax(min(248px, 100%), 1fr));
  }
  .chcard {
    background: var(--paper); padding: clamp(14px, 1.6vw, 22px); text-align: left;
    display: grid; gap: 10px; align-content: start; position: relative;
    border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
    transition: background-color .25s var(--ease);
  }
  .chcard::after {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
    background: var(--accent); transform: scaleY(0); transform-origin: 50% 0;
    transition: transform .35s var(--ease-out);
  }
  .chcard:hover { background: var(--paper-2); }
  .chcard:hover::after { transform: scaleY(1); }
  .chcard__top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
  .chcard__no { font-family: var(--mono); font-size: var(--step-1); letter-spacing: .04em; }
  .chcard__n { font-family: var(--mono); font-size: 11.5px; color: var(--muted); }
  .chcard__t { font-weight: 600; letter-spacing: -.015em; line-height: 1.2; }
  /* Multi-line truncation.
   NOT display:-webkit-box + -webkit-line-clamp: Chrome now computes that
   display as `flow-root` and ignores the clamp entirely, so the text renders at
   full length and runs over whatever sits below it. Clamping by max-height
   against a known line-height works everywhere and cuts at the line boundary. */
.chcard__b {
    font-size: var(--step--1); color: var(--muted); line-height: 1.55;
    overflow: hidden; min-width: 0; max-width: 100%;
    max-height: 3.1em;              /* 2 lines x 1.55 */
  }
  .chcard__bar { display: flex; height: 3px; gap: 1px; margin-top: 2px; }
  .chcard__bar i { display: block; height: 100%; background: var(--line); }
  .chcard__bar i[data-e="A"] { background: var(--ev-a); }
  .chcard__bar i[data-e="B"] { background: var(--ev-b); }
  .chcard__bar i[data-e="C"] { background: var(--ev-c); }
}

/* ── motion ────────────────────────────────────────────────────────── */
@layer motion {
  @keyframes riseIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
  @keyframes drawIn { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
  @keyframes sheetIn { from { transform: translateY(18px); opacity: 0; } to { transform: none; opacity: 1; } }

  /* Progressive enhancement: visible by default, only hidden once JS is live. */
  html.js .reveal { opacity: 0; }
  html.js .reveal.in { animation: riseIn .62s var(--ease-out) both; }
  .reveal.in { animation: riseIn .62s var(--ease-out) both; }

  /* ── the hero, on its own ─────────────────────────────────────────────
     The hero had no entrance at all: its four elements sat at opacity 1 with
     animation-name none, so the first screen simply appeared. It is the one
     screen every visitor sees, so it is also the one that has to move.

     It runs on .reveal rather than a private CSS-only entrance, so the same
     machinery that reveals the rest of the page reveals the hero:
     IntersectionObserver, the data-d stagger, the immediate hand-back under
     prefers-reduced-motion, selfHeal() for a missed element, and revealAll()
     for print. A bare `animation: ... both` here would have left the title and
     the search box invisible in any environment where animations do not
     advance, with no way back.

     Two treatments, because they are different objects. The title is two chosen
     lines of type, so it gets a clip wipe that respects the <br> and reads as
     the lines arriving; the controls rise instead, because a search field that
     wipes in looks like it is loading.

     The wipe overshoots to inset(-9%) at the end so descenders are never shaved,
     and the translate is in em so the offset scales with the title's own
     font-size rather than being one visual weight at 22px and another at 83px. */
  @media (prefers-reduced-motion: no-preference) {
    /* The `html.js` prefix is load-bearing, not decoration. Two rules above set
       `html.js .reveal.in { animation: riseIn ... }`, and that selector scores
       (0,3,1) against a bare `.poster__title.reveal.in` at (0,3,0) -- so without
       the prefix the title silently got the generic rise and the wipe never ran,
       in the one place it was written for. */
    html.js .poster__title.reveal.in, .poster__title.reveal.in {
      animation-name: heroWipe; animation-duration: .78s;
    }
  }
  @keyframes heroWipe {
    from { opacity: 0; transform: translateY(.10em); clip-path: inset(0 0 104% 0); }
    to   { opacity: 1; transform: none;            clip-path: inset(-9% -6% -9% -6%); }
  }

  /* Ambient: the hero breathes instead of sitting dead. A soft halo behind the
     ring, well outside the wedges, so it cannot touch the data. It sits on
     .rosebox::before rather than .rose because an <svg> takes no pseudo-element,
     and .rosebox needs position:relative (added below) to contain it.

     8.5s is slow enough to be felt rather than watched. Scale stays under 6% and
     the peak alpha is .5 of a 13% tint, so nothing crosses the threshold where it
     would be doing anything to legibility. */
  @media (prefers-reduced-motion: no-preference) {
    .rosebox { position: relative; }
    .rosebox > * { position: relative; z-index: 1; }
    .rosebox::before {
      content: ""; position: absolute; z-index: 0; pointer-events: none;
      inset: 4% -6% 24%;
      border-radius: 50%;
      background: radial-gradient(circle at 50% 50%,
        color-mix(in srgb, var(--ev-a) 13%, transparent) 0%, transparent 66%);
      animation: halo 4.2s var(--ease) infinite;
    }
  }
  @keyframes halo {
    0%, 100% { opacity: .34; transform: scale(.95); }
    50%      { opacity: .62; transform: scale(1.045); }
  }

  /* Ambient: a slow wash drifting across the whole hero. Two large soft radials
     on a pseudo-element of .poster, behind everything (the content is a grid
     item; the wash is absolutely positioned and z-index 0).

     Peak tint is 5% and 4% of accent / evidence blue. That is far below anything
     that moves a contrast ratio -- the tokens audit and the per-route contrast
     sweep both re-run after this and the hero's ink-on-paper numbers do not
     move. It exists to stop the largest surface on the page reading as a flat
     rectangle of #F7F6F2.

     26s and 34s are deliberately not multiples of each other, so the two blobs
     never line up again into a repeating pattern the eye can lock onto. */
  @media (prefers-reduced-motion: no-preference) {
    .poster::before {
      content: ""; position: absolute; inset: 0; z-index: 0;
      pointer-events: none; overflow: hidden;
      background:
        radial-gradient(58% 44% at 14% 8%,
          color-mix(in srgb, var(--accent) 5%, transparent) 0%, transparent 70%),
        radial-gradient(52% 46% at 86% 92%,
          color-mix(in srgb, var(--ev-a) 4%, transparent) 0%, transparent 72%);
      animation: wash 15s var(--ease) infinite alternate;
    }
    .poster__in { position: relative; z-index: 1; }
  }
  /* Every ambient period below was originally 2-6x longer and read as a still
     image. 180s per revolution is 2 degrees a second: in a five-second visit you
     see 10 degrees and conclude nothing is moving, which is exactly what
     happened. A thing that is supposed to look alive has to be visibly alive
     within the two or three seconds somebody actually looks at it, so the rose
     turns once every 32s (11 deg/s -- fast enough to catch in peripheral vision,
     slow enough not to be a spinner) and its bezel once every 14s.

     Ambient, per band. With the tinted section backgrounds gone, this is what
     gives the lower half of the page any motion at all -- one slow drift per
     band, so scrolling never crosses a dead rectangle.

     The box is oversized on BOTH axes so a translate cannot expose an unpainted
     edge: block by -25% of the section, inline to 116vw via calc(50% - 58vw).
     The translate peaks at 4% of the element's own width, which for a 116vw box
     is 4.6vw against 8vw of slack each side. The 116vw width is also why <html>
     needs overflow-x: clip.

     Alternate bands drift in opposite directions and at periods that are not
     multiples of each other (12s / 16s), so nothing on the page ever lines up
     into a single repeating rhythm. */
  @media (prefers-reduced-motion: no-preference) {
    .home > section[class*="tint-"]::after {
      content: ""; position: absolute; z-index: -1;
      inset-block: -25%; inset-inline: calc(50% - 58vw);
      pointer-events: none;
      background: radial-gradient(42% 36% at 32% 28%,
        color-mix(in srgb, var(--accent) 4%, transparent) 0%, transparent 72%);
      animation: drift 12s var(--ease) infinite alternate;
    }
    .home > section.tint-cool::after {
      background: radial-gradient(42% 36% at 68% 72%,
        color-mix(in srgb, var(--ev-a) 3%, transparent) 0%, transparent 72%);
      animation-duration: 16s; animation-direction: alternate-reverse;
    }
  }
  @keyframes drift {
    from { transform: translate3d(-3%, 2.5%, 0); }
    to   { transform: translate3d(4%, -3%, 0); }
  }

  /* ── the rose moves on its own ───────────────────────────────────────
     The hero's right-hand graphic had an entrance and a breathing halo, but the
     graphic itself never moved, which is the thing actually being looked at.

     Two layers, because they carry different weights:

       .rose__g           32s per revolution, clockwise. The 33 wedges. PAUSED on
                          hover and on focus-within, so the one thing the reader
                          may want to compare stops moving the moment they reach
                          for it. A rotating chart you cannot stop is a chart you
                          cannot read.

       outermost ring      14s counter-clockwise, and dashed. The guide rings are
                          plain circles, and a plain circle looks identical
                          rotated, so spinning one of those would cost a compositing
                          layer and change nothing on screen. Dashing it is what
                          makes the rotation visible; it also makes the ring read
                          as an instrument bezel rather than as a data series.

     transform-box: view-box with a 50%/50% origin is what rotates both about
     (150,150) -- the ring's centre in user units -- without hardcoding it. */
  @media (prefers-reduced-motion: no-preference) {
    .rose__g, .rose__ring:last-of-type { transform-box: view-box; transform-origin: 50% 50%; }
    .rose__g { animation: roseSpin 32s linear infinite; }
    .rose__ring:last-of-type {
      stroke-dasharray: 2 7;
      animation: roseSpinBack 14s linear infinite;
    }
    .rosebox:hover .rose__g, .rosebox:focus-within .rose__g,
    .rosebox:hover .rose__ring:last-of-type, .rosebox:focus-within .rose__ring:last-of-type {
      animation-play-state: paused;
    }
  }
  @keyframes roseSpin    { to { transform: rotate(360deg); } }
  @keyframes roseSpinBack{ to { transform: rotate(-360deg); } }
  /* An earlier edit overwrote the @keyframes wash block while adding the rose
     rules and left the name declared but undefined, which computes as
     animation-name: wash with transform: none -- the hero wash silently did
     nothing. Declared here with the rest so that cannot recur. */
  @keyframes wash {
    from { transform: translate3d(-2.5%, -1.5%, 0) scale(1); }
    to   { transform: translate3d(3%, 2%, 0) scale(1.07); }
  }

  /* The chapter evidence bars grow when their row scrolls into view. This is the
     one animation on the page that carries information rather than decoration:
     the bar is the claim "this chapter's entries are mostly grade A", and
     watching it extend is the claim being made.

     transform, not width. The widths are inline percentages of the row, so
     animating `width` would relayout 33 rows on every frame; scaleX stays on the
     compositor and costs nothing. transform-origin left so it grows rightwards
     from the same edge the reader is looking from. Short (560ms) because there
     are eleven of them in a grid and a slow one reads as lag.

     Starts at scaleX(0) only under no-preference, and only once .in is on the
     row -- so with reduced motion the bar is simply its final width from the
     start, which is the correct resting state rather than a fallback. */
  @media (prefers-reduced-motion: no-preference) {
    /* `html.js` for the same progressive-enhancement reason as .reveal: the base
       state is the collapsed bar, so without this guard a browser that never runs
       the app would show 33 empty tracks. With JS, .in arrives and the bar grows. */
    html.js .idx33__seg { transform: scaleX(0); transform-origin: left center; }
    .idx33.in .idx33__seg { animation: barGrow .56s var(--ease-out) both; }
    .idx33.in:nth-child(3n+2) .idx33__seg { animation-delay: .06s; }
    .idx33.in:nth-child(3n+3) .idx33__seg { animation-delay: .12s; }
  }
  @keyframes barGrow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
      animation-duration: .01ms !important; animation-iteration-count: 1 !important;
      transition-duration: .01ms !important; scroll-behavior: auto !important;
    }
    .reveal, html.js .reveal { opacity: 1; }
    /* The two ambient washes would otherwise hold a compositing layer open for
       the life of the page. .01ms above already parks them on their first frame,
       which is the neutral end state for both keyframes, but stop them outright
       so nothing keeps ticking behind the reader's back. The rose halo is dropped
       entirely rather than frozen, because a still radial gradient is just a
       smudge behind the ring with nothing to look at. */
    .poster::before, .rosebox::before { animation: none !important; }
    .rosebox::before { content: none !important; }
    .home > section[class*="tint-"]::after { animation: none !important; }
    .rose__g, .rose__ring:last-of-type { animation: none !important; }
    /* scaleX(0) must not survive the kill switch, or every evidence bar would be
       an empty track. The animation is already neutralised above; this clears the
       base state it starts from. */
    .idx33__seg { transform: none !important; }
  }
}

/* ── components: part 2 ─────────────────────────────────────────────── */
@layer home {

  .band { display: grid; gap: 6px; margin-bottom: clamp(20px, 2.6vw, 34px); position: relative; }
  .band h2 { font-size: var(--step-3); letter-spacing: -.028em; }
  .band .u-label { position: absolute; top: .35em; right: 0; }
  .band__d { color: var(--muted); font-size: var(--step-0); max-width: 52ch; margin-top: 4px; }
  .band__more {
    position: absolute; right: 0; bottom: .3em; font-family: var(--mono); font-size: 12px;
    color: var(--muted); letter-spacing: .04em;
    /* keep the visual position, grow the hit area to a comfortable target */
    padding: 7px 2px 7px 12px; margin: -7px -2px -7px -12px;
  }
  .band__more:hover { color: var(--accent-deep); }
  /* on narrow screens the description runs full width, so let the link sit in flow */
  @media (max-width: 719px) {
    .band { padding-bottom: 26px; }
    .band__more { position: static; display: inline-block; margin-top: 4px; }
  }

  /* ── the hero ────────────────────────────────────────────────────────────
     The hero IS the first screen. It used to be sized purely by its content, which
     left it at 47-65% of the viewport on desktop and 31-46% on a phone, so the
     fold always cut through the middle of the band below and the first scroll
     started from an arbitrary place.

     min-height, not height: a short window (or a long translation) must be able
     to push the hero past one screen rather than clip the search box.

     The two min-heights are an ordered pair, not a mistake. vh first as the
     fallback for browsers without svh (Safari < 15.4, Chrome < 108); svh second
     so it wins where supported. svh, not vh: 100vh on a phone is the height with
     the URL bar HIDDEN, so the bottom of the hero would sit under the bar, while
     svh is the height with the bar shown -- the one the reader is looking at when
     they arrive. dvh is deliberately NOT used: it would resize the hero every
     time the bar animates, shifting the content under the reader's thumb.

     align-content: centre puts the single row (the .wrap) in the middle of the
     leftover space, so the title sits on the optical centre of the screen
     instead of hugging the header with a void beneath it.

     THE COST, measured, because it is not free. Four text elements plus the ring
     are 295-505px tall, so filling a 645-1051px screen leaves this much centred
     space: 7% at 1440x600 and 0-9% at 1280x560 / 834x400 / 900x520 (short
     windows are tight, as intended), 36-52% on landscape desktops, and 58-68% on
     tall narrow screens -- 68% at 834x1112, 63% at 390x844. That is the price
     of a fold that lands on a rule instead of mid-section.

     The tall-narrow end is the weak one, because that is exactly where the ring
     is dropped (see .rosebox below): a screen 1051px tall holding 339px of
     content. Filling it would mean either letting the ring grow on those
     viewports or capping the hero below one screen there, and both change the
     hero's composition rather than its height, so neither was done unilaterally. */
  .poster {
    position: relative; border-bottom: 1px solid var(--line);
    min-height: calc(100vh - var(--topbar-h));
    min-height: calc(100svh - var(--topbar-h));
    display: grid; align-content: center;
  }
  .poster__in {
    padding-block: clamp(26px, 3.4vw, 52px) clamp(22px, 2.8vw, 38px);
    display: grid; gap: clamp(18px, 2.2vw, 26px);
  }
  /* One column until there is room for the second. The panel is not decoration,
     so it earns its width rather than filling leftover space. */
  @media (min-width: 900px) {
    .poster__in {
      grid-template-columns: minmax(0, 1fr) clamp(276px, 24vw, 330px);
      column-gap: clamp(28px, 3.4vw, 52px);
      align-items: start;
    }
  }
  .poster__col { display: grid; gap: clamp(14px, 1.8vw, 22px); min-width: 0; }
  .poster__col .find__count { margin-top: -10px; }
  /* In two-column mode the title has to be sized against its COLUMN, not the
     viewport: at 1280 the column is 806px and line 1 needs 11 glyphs. The
     matching rule sits after the base .poster__title, further down. */

  /* Line 1 is 12 CJK glyphs and each advances exactly 1em in the resolved font,
     so the font-size IS the line length: it has to satisfy
     11.496 x font-size <= column width or the line breaks.

     The column is viewport minus gutters, and the gutters grow with width
     (measured: 269 at 320, 424 at 480, 688 at 768, 808 at 899). Solving
     7.4vw against the narrow end gave 272px of glyphs in a 269px column at
     320, orphaning 力， on a 17%-full line -- and lowering the clamp floor did
     NOT fix it, because 7.4vw at 320 is 23.7px, well above any floor you'd
     pick, so clamp keeps choosing the vw term. It is the coefficient that has
     to move. 7vw holds the line at 91-99% fill from 320 to 899, the tightest
     point being 320: 22.4px needs 258px against 269px, 11px of slack.

     7vw, not 6vw: at 6vw the cap kicked in at 1280 and the title stopped
     filling the measure. 1.4rem floor = 22.4px, which is exactly 7vw at 320, so
     it binds only below the narrowest phone width tested. */
  /* text-wrap: wrap, explicitly -- NOT by omission. The base heading rule sets
     text-wrap: balance on every h1-h4, so merely not repeating it here leaves
     balance in force. It optimises for even line lengths and will re-break a
     line you broke on purpose, ignoring the <br>: at 360px it split the title
     into 3 lines and cut 寿命 in half across two of them, which also destroyed
     the emphasis; at 900-910px it broke line 1 into 6+6 glyphs at 50% fill on
     each line while 465px of a 503px box sat empty. The two lines below are
     chosen, so the browser is better off left alone. */
  .poster__title {
    font-size: clamp(1.4rem, 7vw, 5.2rem);
    line-height: 1.04; letter-spacing: -.042em;
    font-weight: 700; margin: 0;
    text-wrap: wrap;
  }
  .poster__title em { font-style: normal; color: var(--accent-deep); }
  /* Two-column range only -- this has to come AFTER the base rule above, which
     is why it is not next to the grid declaration it belongs with. */
  @media (min-width: 900px) {
    .poster__title { font-size: clamp(2rem, 5.4vw, 5.2rem); }
  }
  /* The tight end of that range. Line 1 is 用最少的钱、时间和精力，= 12 glyphs, and
     every CJK glyph advances exactly 1em in the resolved font, so line 1 needs
     12 x font-size x (1 - .042 tracking) of column. The column here is about
     0.875w - 285 (measured: 503 at 900, 538 at 940, 764 at 1199).

     4.9vw was the old figure and it is 4px OVER at exactly 900 -- the title
     broke into 用最少的钱、时间和精 / 力， / 换回寿命、金钱和自由, orphaning
     力， on a 17%-full line. Solving 4.9vw against the column gives a
     coefficient that only just fits, so this uses 4.6vw: 41.4px at 900 against
     a 503px column, 27px of slack (5.4%), and slack only grows with width
     because the column grows at 0.875 per px while the glyphs grow at 0.529.

     Note the layout switch at 900 is itself a 62.9 -> 41.4px jump in this
     heading, and the band edge at 1200 is a 55.1 -> 64.8px step. Both are
     inherent -- the first because the measure halves, the second because two
     adjacent vw coefficients cannot be made continuous in CSS. Both sides
     render two lines at 84-99% fill, so they are left alone rather than faked
     by chasing a ramp that a single clamp() cannot express. */
  @media (min-width: 900px) and (max-width: 1199px) {
    .poster__title { font-size: clamp(2rem, 4.6vw, 5.2rem); }
  }

  /* The ways-in row. Quiet on purpose: a rule, a label, three numbers and an
     escape. It gives the hero a bottom edge without adding a second block of
     visual weight competing with the title. */
  /* ── the ways-in row ─────────────────────────────────────────────────────
     Three links, each coloured by its own grade and each labelled in words
     rather than letters. The letters alone meant nothing until you had read
     the methodology page, which is exactly what this row asks you to skip. */
  .ways {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
    border-top: 1px solid var(--line); padding-top: 14px;
  }
  .ways__c {
    display: inline-flex; align-items: baseline; gap: 7px;
    padding: 5px 11px 5px 9px;
    border: 1px solid var(--line); border-radius: 2px;
    background: var(--paper);
    transition: border-color .2s var(--ease), background-color .2s var(--ease);
  }
  .ways__c b {
    font-family: var(--mono); font-size: 12px; font-weight: 600;
    color: var(--paper); background: var(--ink);
    width: 17px; height: 17px; display: grid; place-items: center;
    border-radius: 2px; align-self: center;
    transition: background-color .2s var(--ease);
  }
  /* the grade does the colouring, so the row needs no separate legend */
  .ways__c[data-ev="A"] b { background: var(--ev-a); color: var(--ev-a-ink); }
  .ways__c[data-ev="B"] b { background: var(--ev-b); color: var(--ev-b-ink); }
  .ways__c[data-ev="C"] b { background: var(--ev-c); color: var(--ev-c-ink); }
  .ways__t { font-size: var(--step--1); color: var(--ink-2); white-space: nowrap; }
  .ways__n { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
  .ways__c:hover { border-color: var(--ink); background: var(--paper-2); }
  .ways__c:hover .ways__t { color: var(--ink); }
  .ways__all {
    margin-inline-start: auto; font-size: var(--step--1); color: var(--muted);
    border-bottom: 1px solid var(--line); padding: 3px 0;
    transition: color .2s var(--ease), border-color .2s var(--ease);
  }
  .ways__all:hover { color: var(--ink); border-bottom-color: var(--accent); }
  @media (max-width: 520px) {
    .ways__all { margin-inline-start: 0; }
  }

  /* ── search field ────────────────────────────────────────────────────────
     Full measure of its column rather than capped at 640px. Under a title this
     size, a half-width field read as a leftover, and the strongest action on the
     page was the smallest object on it. */
  .find {
    position: relative;
    display: flex; align-items: center; gap: 12px; width: 100%;
    padding: 5px 5px 5px 16px;
    background: var(--paper); border: 1px solid var(--ink);
    box-shadow: var(--lift);
    transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
  }
  .find:focus-within { box-shadow: var(--lift-lg); }
  .find__ico { color: var(--muted); display: grid; place-items: center; flex: none; }
  /* the rotating ghost has to share the input's origin -- see .find__ghost */
  .find__field { position: relative; flex: 1; min-width: 0; display: flex; align-items: center; }
  .find__input {
    position: relative; z-index: 1; /* typed text sits above the ghost */
    flex: 1; min-width: 0; border: 0; background: none; padding: 15px 0;
    font: 400 var(--step-1)/1.35 var(--sans); color: var(--ink);
  }
  .find__input:focus { outline: none; }
  .find__input::placeholder { color: var(--muted); }
  .find__input::-webkit-search-cancel-button { display: none; }
  .find__go {
    flex: none; align-self: stretch; padding: 0 26px;
    border: 0; background: var(--ink); color: var(--paper);
    font-size: var(--step-0); font-weight: 500;
    transition: background-color .2s var(--ease);
  }
  .find__go:hover { background: var(--accent-deep); }
  .find__kbd {
    flex: none; font: 500 11px/1 var(--mono); color: var(--muted);
    border: 1px solid var(--line); padding: 5px 7px; margin-right: 8px;
  }

  /* rotating placeholder -- a real placeholder cannot animate, so the field goes
     transparent and this sibling does the typing. It must be positioned against
     .find__field (which wraps the input), not against .find: anchoring it to the
     form put the text on top of the magnifier icon.
     The placeholder is the fallback for no-JS and for the empty idle state. While
     the ghost is painting it must step aside completely -- hiding it only on
     :focus stacked it on top of the typing, because the ghost runs unfocused. */
  .find__ghost {
    position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    pointer-events: none; color: var(--muted);
    font: 400 var(--step-1)/1.35 var(--sans); max-width: 100%;
    white-space: nowrap; overflow: hidden; opacity: 0;
  }
  .find__ghost::after {
    content: ""; display: inline-block; width: 1.5px; height: 1.05em;
    margin-left: 3px; background: var(--accent-deep); vertical-align: -.16em;
    animation: caret 1.05s steps(2, start) infinite;
  }
  .find__ghost.is-on { opacity: 1; }
  @keyframes caret { 0%, 100% { opacity: 0; } 50% { opacity: 1; } }
  .find__field.has-ghost .find__input::placeholder { color: transparent; }
  @media (prefers-reduced-motion: reduce) {
    .find__ghost { display: none; }
    .find__input::placeholder { color: var(--muted); }
  }
  @media (max-width: 640px) {
    .find__ghost { font-size: var(--step-0); }
  }

  .find__count {
    font-size: var(--step--1); color: var(--muted); min-height: 1.4em;
    margin-top: -14px; font-variant-numeric: tabular-nums;
  }
  .find__count.is-empty { color: var(--accent-deep); }
  .find__eg { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
  .find__egl { font-size: var(--step--1); color: var(--muted); margin-right: 2px; }
  .find__chip {
    font-size: var(--step--1); padding: 5px 11px;
    border: 1px solid var(--line); color: var(--ink-2);
    transition: border-color .18s var(--ease), color .18s var(--ease), background-color .18s var(--ease);
  }
  .find__chip:hover { border-color: var(--ink); color: var(--ink); background: var(--paper-2); }

  /* ── the live preview panel ───────────────────────────────────────────────
     The hero's right column: the same query the field is running, rendered as
     entries instead of a count. Rows are keyed by id and only NEW rows animate,
     so refining a query does not strobe the list -- the motion reports a change
     in the result set rather than decorating it. */
  .prev {
    align-self: start;
    display: grid; border: 1px solid var(--line);
    background: var(--paper);
  }
  .prev__h {
    font-family: var(--mono); font-size: 11px; letter-spacing: .04em;
    color: var(--muted); padding: 10px 14px;
    border-bottom: 1px solid var(--line); background: var(--paper-2);
  }
  .prev__h b { color: var(--ink); font-weight: 600; }
  .prev__l { list-style: none; }
  .prev__i { border-bottom: 1px solid var(--line-2); }
  .prev__i:last-child { border-bottom: 0; }
  .prev__i a {
    display: grid; gap: 5px; padding: 11px 14px;
    transition: background-color .18s var(--ease), padding-inline .22s var(--ease);
  }
  .prev__i a:hover { background: var(--paper-2); padding-inline: 18px; }
  .prev__t {
    font-size: var(--step--1); font-weight: 600; letter-spacing: -.005em;
    line-height: 1.36;
  }
  .prev__m {
    display: flex; align-items: center; gap: 8px;
    font-size: 10.5px; color: var(--muted);
    font-family: var(--mono);
  }
  .prev__m .num { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .prev__i .badge { width: 17px; height: 17px; font-size: 10px; flex: none; }
  .prev__e { padding: 14px; font-size: var(--step--1); color: var(--muted); }

  .prev__i.is-in { animation: prevIn .34s var(--ease-out) backwards; animation-delay: calc(var(--k, 0) * 34ms); }
  @keyframes prevIn { from { opacity: 0; transform: translateY(6px); } }
  @media (prefers-reduced-motion: reduce) {
    .prev__i.is-in { animation: none; }
  }
  /* Below the two-column breakpoint the panel stacks under the field, where four
     rows push the hero past a screen. Two still show that the query is going the
     right way, which is the job. */
  @media (max-width: 899px) {
    .prev__i:nth-child(n+3) { display: none; }
  }

  /* ── band separation ────────────────────────────────────────────────────
     There are no tinted section backgrounds any more. --paper-warm and
     --paper-cool are deleted, because two extra background tones that only
     appeared in the lower half of the page are exactly the "a new colour shows
     up down there" effect. Every band is --paper.

     The rhythm they used to carry is now carried by a hairline rule at the top
     of each band plus the drifting ::after below. The rule is drawn in on
     scroll by animation-timeline: view(), which degrades to a plain always-there
     border in browsers without scroll-driven animations. */
  .home > section { position: relative; }
  .home > section[class*="tint-"] { border-top: 1px solid var(--line-2); }
  .home > section[class*="tint-"]:first-of-type { border-top: 0; }
  /* the sections stack with margins, so extend the wash into the gap */
  .home > section[class*="tint-"] { margin-top: 0; }
  .home > section[class*="tint-"]::before { inset-block: -1px; }

  /* situation groups: one hue each, so the five kinds read apart */
  .sitgroup__dot { background: var(--gh, var(--accent-deep)); }

  /* ── the rose ────────────────────────────────────────────────────────────
     33 wedges in one ring. Radial length is the entry count; the inner fill is
     the grade-A share. Colour now does real work here -- blue inner, warm grey
     outer -- so you read density and evidence quality without a legend. */
  .rosebox { margin: 0; display: grid; gap: 10px; }
  /* Below the two-column breakpoint the ring has no column to sit in. Stacked at
     width:100% a square SVG became a 900x900 block and the hero hit 181% of the
     viewport; capped to 210px it still pushed three widths past 80%, because a
     single-column hero there is already ~67%. So it is dropped rather than
     shrunk into something unreadable -- the 33-section index directly below
     carries the same information, and a ring 150px across says nothing. */
  @media (max-width: 899px) {
    .rosebox { display: none; }
  }
  /* aspect-ratio has to be stated. `overflow: visible` on an <svg> makes Chrome
     treat it as non-replaced, which drops the intrinsic ratio the viewBox would
     otherwise give it -- with only `height: auto` the whole ring collapsed to
     about 30px tall. */
  .rose { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; overflow: visible; }

  .rose__cap {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted);
    padding-bottom: 7px; border-bottom: 1px solid var(--line);
  }
  .rose__key { margin-inline-start: auto; display: flex; align-items: center; gap: 5px; letter-spacing: .04em; }
  .rose__ka, .rose__kr { width: 11px; height: 3px; flex: none; }
  .rose__ka { background: var(--ev-a); }
  .rose__kr { background: var(--wedge); border: 1px solid var(--line); }

  .rose__ring { fill: none; stroke: var(--line-2); stroke-width: .6; }
  /* The B/C remainder has to be VISIBLE while still receding. --paper-3 was
     1.05:1 against the paper, i.e. the outer half of every wedge was invisible
     and the legend swatch matched nothing. These land at 1.42 / 1.39: dark
     enough to read as a fill, light enough that the blue still leads. */
  .rose__rest { fill: var(--wedge); }
  .rose__a { fill: var(--ev-a); }

  /* Wedges grow outward from the centre, which is the same direction as the
     data: a long wedge is a dense chapter. Staggered so the ring assembles
     clockwise rather than appearing all at once. transform-origin is the
     chart's centre, so this stays on the compositor. */
  .rose__w {
    transform-box: view-box; transform-origin: 150px 150px;
    cursor: pointer;
    animation: roseGrow .5s var(--ease-out) backwards;
    animation-delay: calc(var(--i, 0) * 13ms);
  }
  @keyframes roseGrow { from { transform: scale(.18); opacity: 0; } }
  .rose__w:hover .rose__rest, .rose__w.is-on .rose__rest { fill: var(--ev-b); }
  .rose__w:hover .rose__a, .rose__w.is-on .rose__a { fill: var(--ev-b); }
  .rose__w:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 1px; }

  .rose__n {
    font-family: var(--mono); font-size: 21px; font-weight: 600;
    fill: var(--ink); letter-spacing: -.02em;
  }
  .rose__u { font-family: var(--mono); font-size: 9.5px; fill: var(--muted); letter-spacing: .08em; }

  .rose__out {
    display: grid; gap: 1px; min-height: 2.6em; align-content: start;
    border-top: 1px solid var(--line); padding-top: 8px;
  }
  .rose__out b { font-size: 12.5px; font-weight: 600; line-height: 1.3; }
  .rose__out span { font-size: 10.5px; color: var(--muted); }

  @media (prefers-reduced-motion: reduce) {
    .rose__w { animation: none; }
  }

  /* ── scroll-spy indicator ───────────────────────────────────────────────
     The TOC is a list of links; this is the one bar that slides to whichever
     heading you are actually reading. Position comes from offsetTop rather
     than a hardcoded row height, so it survives line wrapping.

     .toc is position: sticky, which is already a containing block, so the bar
     needs no position of its own -- and must not be given one, or it would
     override the sticky and break the narrow-screen scroller below. */
  .toc__ind {
    position: absolute; left: 0; top: 0; width: 2px; height: 0;
    background: var(--accent); opacity: 0;
    transition: transform .38s var(--ease-out), height .38s var(--ease-out), opacity .3s var(--ease);
    pointer-events: none;
  }
  .toc a[aria-current="true"] { color: var(--ink); border-left-color: var(--accent); padding-left: 16px; }

  @media (prefers-reduced-motion: reduce) {
    .toc__ind { transition: none; }
  }

  /* ── pointer spotlight on tiles ─────────────────────────────────────────
     One delegated listener writes --sx/--sy; the pseudo-element does the rest.
     It sits under the text (z-index 0 with children lifted to 1) so it can
     never fight the accent wipe for the same stacking context. */
  .spot { position: relative; }
  .spot::after {
    content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
    background: radial-gradient(130px circle at var(--sx, 50%) var(--sy, 50%),
      color-mix(in srgb, var(--accent) 14%, transparent), transparent 72%);
    opacity: 0; transition: opacity .3s var(--ease);
  }
  .spot:hover::after { opacity: 1; }
  .spot > * { position: relative; z-index: 1; }.sit__ico, .about__cardi, .lg__ico, .idx33__no{
    transition: transform .3s var(--ease-spring), color .22s var(--ease);
  }
  .about__card:hover .about__cardi { transform: rotate(-8deg) scale(1.06); }
  .idx33:hover .idx33__no { transform: translateX(3px); }
  .sit:active .sit__ico { transform: translateY(-1px); }

  /* On a phone the hero must not eat more than a screen — otherwise the first
     actionable thing (the situation tiles) is below the fold.
     No font-size override on the title here: a fixed 1.72rem beat the clamp, and
     at 360px that made line 1 wrap even though 7.4vw fits it. The clamp already
     scales it, and line 1 needs about 10.5em of content box to stay on one line. */
  @media (max-width: 640px) {
    .poster__in { padding-block: 22px 26px; gap: 16px; }
    .poster__title { line-height: 1.1; }
    .find { padding-left: 11px; }
    .find__input { padding: 11px 0; font-size: var(--step-0); }
    .find__kbd { display: none; }
    .find__go { padding: 0 14px; }
    .sitgroup { margin-top: 20px; }
    .sitgroup__t { padding-bottom: 7px; margin-bottom: 10px; font-size: var(--step-0); }
  }

  /* situation groups — five kinds of problem, each with its own grid */
  .sitgroup { margin-top: clamp(22px, 2.8vw, 38px); }
  .sitgroup:first-of-type { margin-top: 0; }
  .sitgroup__t {
    display: flex; align-items: center; gap: 10px;
    font-size: var(--step-1); font-weight: 600; letter-spacing: -.01em;
    padding-bottom: 9px; margin-bottom: 12px; border-bottom: 1px solid var(--ink);
  }
  .sitgroup__dot { width: 9px; height: 9px; background: var(--gh, var(--accent-deep)); flex: none; }
  .sitgroup__n { margin-inline-start: auto; font-size: 11.5px; color: var(--muted); font-weight: 400; }

  .situations {
    display: grid; gap: 1px; background: var(--line);
    border: 1px solid var(--line);
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  @media (max-width: 900px) { .situations { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (max-width: 400px) { .situations { grid-template-columns: minmax(0, 1fr); } }

  .sit {
    background: var(--paper); padding: clamp(14px, 1.6vw, 20px) clamp(13px, 1.5vw, 18px);
    display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px;
    align-items: start; position: relative; overflow: hidden; min-height: 96px;
    transition: background-color .24s var(--ease), color .24s var(--ease);
  }
  .sit__ico { color: var(--accent-deep); display: grid; place-items: center; padding-top: 1px; }
  .sit__b { display: grid; gap: 3px; min-width: 0; }
  .sit:hover .sit__ico { color: var(--accent-ink); }
  .sit:hover .sit__no { color: color-mix(in srgb, var(--accent-ink) 88%, var(--accent-deep)); }

  @media (max-width: 640px) {
    .sit { min-height: 0; padding: 12px 14px; align-items: baseline; }
  }
  .sit__no { font-size: 11px; letter-spacing: .12em; color: var(--muted); transition: color .24s var(--ease); }
  .sit__t { font-size: var(--step-1); font-weight: 600; letter-spacing: -.015em; line-height: 1.25; }
  .sit__h { font-size: var(--step--1); color: var(--muted); line-height: 1.5; }
  .sit:hover { background: var(--accent-deep); color: var(--accent-ink); }
  /* ≥86% is the floor: below it the light theme drops under 4.5:1 */
  .sit:hover .sit__no { color: color-mix(in srgb, var(--accent-ink) 88%, var(--accent-deep)); }
  .sit:hover .sit__h { color: color-mix(in srgb, var(--accent-ink) 92%, var(--accent-deep)); }

  /* the seven: a numbered poster list */
  .seven { list-style: none; border-top: 1px solid var(--ink); }
  .seven__i { border-bottom: 1px solid var(--line); }
  .seven__i a {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: clamp(14px, 2.4vw, 34px);
    align-items: start; padding: clamp(16px, 2vw, 26px) 4px;
    transition: background-color .2s var(--ease), padding-inline .25s var(--ease);
  }
  .seven__i a:hover { background: var(--paper-2); padding-inline: 14px; }
  .seven__no {
    font-size: clamp(1.7rem, 3.2vw, 2.9rem); line-height: .84; letter-spacing: -.04em;
    color: var(--accent-deep); font-weight: 500;
  }
  .seven__b { display: grid; gap: 6px; min-width: 0; }
  .seven__t { font-size: var(--step-2); font-weight: 600; letter-spacing: -.022em; line-height: 1.2; }
  .seven__p { color: var(--muted); font-size: var(--step--1); line-height: 1.6; max-width: 64ch; }
  .seven__i a:hover .seven__no { color: var(--ink); }

  @media (max-width: 640px) {
    .seven__i a { gap: 10px 12px; padding: 13px 2px; }
    .seven__no { font-size: 1.5rem; }
    .seven__t { font-size: var(--step-1); }
    .seven__p {
      /* three lines is enough to tell you what the entry is about */
      overflow: hidden; max-height: 4.8em;   /* 3 lines x 1.6 */
    }
  }

  /* 33-section index. Two columns from tablet down, and the blurb is dropped on a
     phone — at one column it was 2,159px, two and a half screens of scrolling
     for a table of contents. */
  .index33 {
    display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line);
    grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr));
  }
  .idx33 {
    background: var(--paper); padding: 11px 14px;
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 3px 12px;
    align-items: baseline; transition: background-color .2s var(--ease);
  }
  .idx33:hover { background: var(--paper-2); }
  .idx33__no { font-size: 12px; color: var(--accent-deep); letter-spacing: .04em; }
  .idx33__t { font-size: var(--step-0); font-weight: 600; letter-spacing: -.01em; }
  .idx33__b {
    grid-column: 2; font-size: 11.5px; color: var(--muted); line-height: 1.45;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    /* grid items default to min-width:auto, which defeats the ellipsis and lets
       the blurb run under the 条数 in column 3 */
    min-width: 0;
  }
  .idx33__n { font-size: 11px; color: var(--muted); }
  .idx33:hover .idx33__n { color: var(--ink); }

  /* Each chapter's evidence mix. 1px between segments so A/B/C stay legible as
     separate facts at 4px tall rather than blending into one muddy bar. Widths
     are set inline from stats.A/B/C over stats.n. */
  .idx33__bar {
    grid-column: 1 / -1;
    display: flex; gap: 1px; height: 4px; margin-top: 6px;
  }
  .idx33__seg { display: block; width: var(--w); min-width: 2px; }
  .idx33__seg[data-g="A"] { background: var(--ev-a); }
  .idx33__seg[data-g="B"] { background: var(--ev-b); }
  .idx33__seg[data-g="C"] { background: var(--ev-c); }

  /* 33 rows: 3 columns divides evenly at 11, 4 columns leaves one orphan on the
     last row. Pin the wide case to 3 so the grid ends square. */
  @media (min-width: 1100px) {
    .index33 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }
  @media (max-width: 900px) {
    .index33 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (max-width: 640px) {
    .index33 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .idx33 { padding: 9px 10px; gap: 2px 8px; }
    .idx33__b { display: none; }
    .idx33__no { font-size: 11px; min-width: 1.6em; }
    .idx33__t { font-size: 13px; }
  }

  /* long-form strip */
  .longgrid {
    display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line);
    grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  }
  .lg {
    background: var(--paper); padding: clamp(16px, 1.8vw, 24px);
    display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px;
    align-items: baseline; transition: background-color .2s var(--ease);
  }
  .lg:hover { background: var(--paper-2); }
  .lg__no { font-size: 11px; color: var(--muted); letter-spacing: .1em; }
  .lg__t { font-size: var(--step-0); font-weight: 600; letter-spacing: -.012em; line-height: 1.3; }
}

@layer components {

  /* boot */
  .boot { min-height: 52svh; display: grid; align-content: center; gap: 18px; justify-items: start; }
  .boot__bar { width: min(340px, 70vw); height: 2px; background: var(--line); overflow: hidden; }
  .boot__bar i { display: block; width: 40%; height: 100%; background: var(--accent); animation: slide 1.15s var(--ease) infinite; }
  @keyframes slide { 0% { transform: translateX(-100%); } 100% { transform: translateX(350%); } }
  .boot--err { gap: 12px; }
  .boot--err pre {
    font-family: var(--mono); font-size: var(--step--1); background: var(--paper-2);
    border: 1px solid var(--line); padding: 12px 16px; overflow-x: auto;
  }
  .boot--err p { color: var(--muted); max-width: 60ch; }

  /* page head (chapter / long / method) */
  .pagehead { padding-block: clamp(24px, 4vw, 52px) clamp(20px, 3vw, 36px); position: relative; }
  .pagehead h1 { font-size: var(--step-4); margin-block: 6px 0; }
  .pagehead > p { max-width: 62ch; color: var(--ink-2); margin-top: 14px; line-height: 1.6; }
  .pagehead__no {
    font-size: var(--step-4); line-height: .8; color: var(--accent); letter-spacing: -.04em;
  }
  .pagehead__meta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 18px; }
  .pagehead__intro { max-width: 68ch; color: var(--muted); font-size: var(--step--1); line-height: 1.7; margin-top: 16px; }
  .pagehead__tools { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--line); }

  /* split page head (chapter reader) */
  @media (min-width: 900px) {
    .pagehead--split {
      display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
      gap: clamp(28px, 4vw, 72px); align-items: start;
    }
    .pagehead--split .pagehead__full { grid-column: 1 / -1; }
    .pagehead--split .pagehead__meta { margin-top: 20px; }
    .pagehead--split .pagehead__tools { margin-top: 20px; }
  }

  .crumbs {
    display: flex; gap: 8px; align-items: center; padding-top: clamp(18px, 3vw, 34px);
    font-family: var(--mono); font-size: 11.5px; letter-spacing: .05em; color: var(--muted);
    text-transform: uppercase;
  }
  .crumbs a {
    /* breadcrumbs are tappable targets, not inline prose — pad the hit area
       without changing the visual position */
    padding: 7px 4px; margin: -7px -4px;
  }
  .crumbs a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
  .crumbs i { opacity: .5; font-style: normal; }

  .pager { display: flex; gap: 18px; margin-inline-start: auto; }
  .pager a { color: var(--muted); font-size: var(--step--1); max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pager a:hover { color: var(--accent); }

  /* chips / tags / badges */
  .chip {
    display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px;
    font-family: var(--mono); font-size: 11px; letter-spacing: .03em;
    border: 1px solid var(--line); border-radius: 100px; color: var(--muted); white-space: nowrap;
  }
  .chip--free { border-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--accent-deep); }
  .chip--cj { background: var(--paper-2); }
  .chip--est { border-style: dashed; }
  .chip--a { border-color: var(--ev-a); color: var(--ev-a); background: var(--ev-a-soft); }
  .chip--b { color: var(--ink-2); }
  .chip--c { opacity: .8; }
  .tag {
    font-family: var(--mono); font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
    padding: 3px 6px; border-radius: 2px;
  }
  .tag--warn { background: var(--accent-deep); color: var(--accent-ink); }
  .tag--mute { border: 1px solid var(--line); color: var(--muted); }

  .badge {
    display: inline-grid; place-items: center; width: 22px; height: 22px;
    font: 600 12px/1 var(--mono); border: 1px solid var(--ink); color: var(--ink);
    background: transparent;
  }
  .badge[data-ev="A"] { background: var(--ev-a); color: var(--ev-a-ink); border-color: var(--ev-a); }
  .badge[data-ev="B"] { background: var(--ev-b-soft); color: var(--ev-b); border-color: var(--ev-b); }
  .badge[data-ev="C"] { background: var(--ev-c-soft); color: var(--ev-c); border-color: var(--ev-c); }
  .badge--big { width: 40px; height: 40px; font-size: 20px; margin-bottom: 12px; }

  /* term (glossary inline) */
  .term {
    border-bottom: 1px dashed color-mix(in srgb, var(--accent) 60%, transparent);
    color: inherit; padding: 0 1px; cursor: help; font: inherit;
    transition: background-color .15s, color .15s;
  }
  .term:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

  mark.hl { background: color-mix(in srgb, var(--mark) 78%, transparent); color: inherit; padding: 0 1px; border-radius: 1px; }

  /* card */
  .cards { display: grid; gap: 0; }
  .cards--2, .cards--grid {
    grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr));
    border-top: 1px solid var(--line); border-left: 1px solid var(--line);
  }
  .cards--2 .card, .cards--grid .card {
    border-right: 1px solid var(--line); border-bottom: 0;
  }
  @media (min-width: 1180px) {
    .cards--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cards--grid .card:nth-last-child(2):nth-child(odd) { border-right: 1px solid var(--line); }
  }
  .card {
    position: relative; padding: clamp(16px, 2vw, 24px) clamp(14px, 1.6vw, 22px);
    border-bottom: 1px solid var(--line-2); transition: background-color .25s var(--ease);
    scroll-margin-top: 96px;
  }
  .card::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
    background: var(--accent); transform: scaleY(0); transform-origin: 50% 0;
    transition: transform .4s var(--ease-out);
  }
  .card:hover { background: var(--paper-2); }
  .card:hover::before, .card.is-target::before { transform: scaleY(1); }
  .card.is-marked { background: color-mix(in srgb, var(--accent) 4%, transparent); }
  .card.is-active { background: var(--paper-2); }
  .card.is-active::before { transform: scaleY(1); }
  .card:focus-within { background: var(--paper-2); }
  .card.flash { animation: flash 1.5s var(--ease); }
  @keyframes flash { 0%, 100% { background: transparent; } 20% { background: color-mix(in srgb, var(--accent) 14%, transparent); } }
  .card__head { display: flex; align-items: center; gap: 10px; }
  .card__no {
    font-size: 12px; letter-spacing: .06em; color: var(--accent-deep); flex: none;
    min-width: 1.6em;
  }
  .card__where {
    font-size: 11px; letter-spacing: .02em; color: var(--muted); min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .card__where i { opacity: .45; font-style: normal; margin: 0 5px; }
  .card__non { font-family: var(--mono); white-space: nowrap; }
  .card__tags { display: flex; gap: 6px; align-items: center; margin-inline-start: auto; flex: none; }
  .card__title { font-size: var(--step-1); margin-top: 9px; letter-spacing: -.012em; line-height: 1.32; }
  .card__plain {
    margin-top: 7px; color: var(--ink-2); line-height: 1.66; max-width: 62ch;
    font-size: var(--step-0);
  }
  .card__meta {
    display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: baseline;
    margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line-3);
    font-family: var(--mono); font-size: 11px; letter-spacing: .03em; color: var(--muted);
  }
  .card__cost { color: var(--accent-deep); }
  .card__cj { color: var(--ink-2); }
  .card__val { margin-inline-start: auto; cursor: help; border-bottom: 1px dotted var(--line); }
  .card__tail { display: flex; justify-content: flex-end; margin-top: 8px; }

  /* On a phone the result list ran to ~345px per card — 24 screens for one page of
     results. Three lines of plain-language summary is enough to scan; the rest is
     one tap away behind 原文与来源. */
  @media (max-width: 640px) {
    .card { padding: 13px 14px 11px; }
    .card__title { font-size: var(--step-0); margin-top: 7px; }
    .card__plain {
      margin-top: 5px; line-height: 1.6;
      overflow: hidden; max-height: 4.8em;   /* 3 lines x 1.6 */
    }
    .card__meta { margin-top: 9px; padding-top: 8px; gap: 4px 12px; font-size: 10.5px; }
    .card__tail { margin-top: 4px; }
    .card:hover { transform: none; box-shadow: none; }
  }
  .btn-ghost {
    display: inline-flex; align-items: center; gap: 7px; padding: 6px 10px;
    font-size: var(--step--1); color: var(--muted); border: 1px solid transparent;
    border-radius: var(--r); transition: color .2s, border-color .2s, background-color .2s;
  }
  .card__actions { display: flex; gap: 6px; align-items: center; }
  .btn-ghost:hover { color: var(--ink); border-color: var(--ink); background: var(--paper); }
  .btn-ghost__i { font-family: var(--mono); }
  .iconbtn--mark { width: 32px; height: 32px; border-color: transparent; }
  .iconbtn--mark:hover { border-color: var(--line); }
  .iconbtn--mark[aria-pressed="true"] { color: var(--accent); }
  .iconbtn--mark .markicon { display: grid; place-items: center; }

  /* Touch devices get larger hit areas without changing the visual weight. */
  @media (pointer: coarse) {
    .iconbtn, .iconbtn--mark { width: 40px; height: 40px; }
    .btn-ghost { padding: 10px 13px; }
    .rail__reset, .rail__toggle { padding: 8px 12px; }
    .select, .ghostbtn { padding: 10px 12px; }
    .facet__i { padding-block: 10px; }
    .facet__box { width: 16px; height: 16px; }
    .nav a { padding: 8px 0; }
    .ref, .fchip, .chip { padding-block: 6px; }
    .qlist a { padding-block: 16px; }
    .loadmore { padding-block: 26px; }
  }

  .card__detail { margin-top: 16px; border-top: 1px solid var(--line-2); padding-top: 6px; }
  .muted-note { color: var(--muted); font-size: var(--step--1); padding: 10px 0; }
  .drow { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 14px; padding: 12px 0; border-bottom: 1px solid var(--line-3); }
  .drow:last-child { border-bottom: 0; }
  .drow__k { padding-top: 2px; }
  .drow__v { color: var(--ink-2); line-height: 1.66; font-size: var(--step--1); min-width: 0; overflow-wrap: anywhere; }
  .drow__v a { color: var(--link); text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }
  .srcs li { margin-bottom: 6px; }
  .refs { display: flex; flex-wrap: wrap; gap: 6px; }
  .ref {
    display: inline-flex; align-items: center; gap: 6px; padding: 5px 9px; font-size: 11.5px;
    border: 1px dashed color-mix(in srgb, var(--link) 45%, transparent); color: var(--link);
    border-radius: 100px; transition: background-color .2s;
  }
  .ref:hover { background: color-mix(in srgb, var(--link) 10%, transparent); }
  .ref i { font-style: normal; }
  .ref__t { color: var(--ink-2); max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* explore: search */
  .explore { padding-block: clamp(20px, 3vw, 40px) clamp(50px, 8vw, 100px); }
  .explore__top { display: grid; gap: 12px; margin-bottom: clamp(18px, 2.4vw, 28px); }
  .search { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; }
  .search__box {
    position: relative; display: flex; align-items: center; gap: 12px; flex: 1 1 380px;
    border-bottom: 2px solid var(--ink); padding-bottom: 6px; transition: border-color .2s;
  }
  .search__box:focus-within { border-color: var(--accent); }
  .search__i { color: var(--muted); display: grid; place-items: center; flex: none; }
  .search__input {
    flex: 1; min-width: 0; border: 0; background: none; padding: 6px 0;
    font-size: var(--step-2); letter-spacing: -.015em; font-weight: 500;
  }
  .search__input::placeholder { color: var(--muted); font-weight: 400; letter-spacing: -.01em; }
  .search__input::-webkit-search-cancel-button { display: none; }
  .search__clear { font-size: 22px; color: var(--muted); line-height: 1; padding: 0 6px; }
  .search__clear:hover { color: var(--accent); }
  .search__hint { font-family: var(--mono); font-size: 11px; color: var(--muted); letter-spacing: .03em; }
  .search__hint kbd, .search__hint code, .btn kbd {
    border: 1px solid var(--line); border-radius: 3px; padding: 1px 5px; font-family: var(--mono);
  }

  .fchips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
  .fchip {
    display: inline-flex; align-items: center; gap: 7px; padding: 5px 11px;
    border: 1px solid var(--ink); border-radius: 100px; font-size: 12px; background: var(--paper);
    animation: pop .28s var(--ease-out) both;
  }
  @keyframes pop { from { opacity: 0; transform: scale(.9); } to { opacity: 1; transform: none; } }
  .fchip i { font-style: normal; opacity: .55; font-size: 14px; line-height: 1; }
  .fchip:hover { background: var(--ink); color: var(--paper); }
  .fchip--mark { border-color: var(--accent); color: var(--accent); }
  .fchip--reset { border-color: transparent; color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
  .fchip--reset:hover { background: none; color: var(--accent); }

  .explore__body { display: grid; grid-template-columns: 224px minmax(0, 1fr); gap: clamp(20px, 3vw, 48px); align-items: start; }

  /* rail */
  .rail {
    position: sticky; top: 84px; max-height: calc(100svh - 108px); overflow-y: auto;
    padding-right: 6px; scrollbar-width: thin; display: grid; gap: 22px; align-content: start;
  }
  .rail__head { display: flex; justify-content: space-between; align-items: baseline; border-bottom: 1px solid var(--line); padding-bottom: 8px; }
  .rail__reset { font-size: var(--step--1); color: var(--muted); padding: 5px 8px; margin: -5px -8px; }
  .rail__reset:hover { color: var(--accent); }
  .rail__toggle { display: none; font-size: var(--step--1); color: var(--ink); border: 1px solid var(--line); padding: 3px 10px; border-radius: 100px; }
  .rail__note { font-size: 11px; color: var(--muted); line-height: 1.6; border-top: 1px solid var(--line-2); padding-top: 12px; }
  .rail__note a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }

  .facet { border: 0; padding: 0; }
  .facet__t { padding: 0 0 8px; }
  .facet__list { display: grid; gap: 1px; }
  .facet__i {
    position: relative; display: grid; grid-template-columns: 16px minmax(0, 1fr) auto;
    gap: 9px; align-items: center; padding: 5px 6px; cursor: pointer; border-radius: var(--r);
    transition: background-color .16s;
  }
  .facet__i:hover { background: var(--paper-2); }
  .facet__i[data-zero="1"] { opacity: .4; }
  .facet__i input { position: absolute; opacity: 0; width: 0; }
  .facet__box {
    width: 13px; height: 13px; border: 1.5px solid var(--line); border-radius: 2px;
    display: grid; place-items: center; transition: background-color .15s, border-color .15s;
  }
  .facet__i[data-on="1"] .facet__box { background: var(--accent); border-color: var(--accent); }
  .facet__i[data-on="1"] .facet__box::after {
    content: ""; width: 6px; height: 6px; background: var(--accent-ink);
    clip-path: polygon(14% 44%, 0 60%, 40% 100%, 100% 22%, 84% 8%, 38% 68%);
  }
  .facet__i input:focus-visible + .facet__box { outline: 2px solid var(--accent); outline-offset: 2px; }
  .facet__l { font-size: var(--step--1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .facet__i[data-on="1"] .facet__l { font-weight: 600; }
  .facet__n { font-size: 11px; color: var(--muted); }

  /* results */
  .res { min-width: 0; }
  .res__head {
    display: flex; justify-content: space-between; align-items: center; gap: 14px; flex-wrap: wrap;
    border-bottom: 1px solid var(--line); padding-bottom: 12px; margin-bottom: 4px;
  }
  .res__count { font-size: var(--step--1); color: var(--muted); }
  .res__count b { font-size: var(--step-2); color: var(--ink); margin-right: 2px; }
  .res__of { color: var(--muted); opacity: .7; }
  .res__relaxed {
    font-size: var(--step--1); color: var(--accent-deep);
    border-left: 2px solid var(--accent-deep); padding-left: 10px;
  }
  .res__tools { display: flex; gap: 8px; align-items: center; }
  .select {
    font-size: var(--step--1); padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--r);
    background: var(--paper); cursor: pointer;
  }
  .ghostbtn {
    font-size: var(--step--1); padding: 6px 11px; border: 1px solid var(--line); border-radius: var(--r);
    color: var(--muted);
  }
  .ghostbtn:hover { color: var(--ink); border-color: var(--ink); }
  .ghostbtn[aria-pressed="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }

  .loadmore {
    display: block; width: 100%; padding: 20px; margin-top: 4px; border-top: 1px solid var(--line);
    color: var(--muted); font-size: var(--step--1); letter-spacing: .04em;
  }
  .loadmore:hover { color: var(--accent); background: var(--paper-2); }

  .empty { padding: clamp(30px, 6vw, 70px) 0; max-width: 58ch; }
  .empty__t { font-size: var(--step-2); letter-spacing: -.02em; }
  .empty__d { color: var(--muted); margin-top: 12px; line-height: 1.7; }
  .empty__d code { font-family: var(--mono); font-size: .92em; border: 1px solid var(--line); padding: 1px 5px; border-radius: 3px; }
  .empty .btn { margin-top: 20px; }

  /* long-form reader */
  .long { padding-bottom: 40px; }
  .long__grid { display: grid; grid-template-columns: 208px minmax(0, 1fr); gap: clamp(24px, 4vw, 64px); align-items: start; }
  .toc { position: sticky; top: 84px; display: grid; gap: 10px; max-height: calc(100svh - 120px); overflow-y: auto; }
  .toc a {
    font-size: var(--step--1); color: var(--muted); padding: 5px 0 5px 12px;
    border-left: 2px solid var(--line-2); transition: color .2s, border-color .2s, padding-left .2s;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .toc a:hover { color: var(--ink); border-color: var(--accent); padding-left: 16px; }
  .toc__sel { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line-2); display: grid; gap: 8px; }
  .toc__sel .select { width: 100%; min-width: 0; }

  .prose { min-width: 0; max-width: 74ch; }
  .prose__meta { margin-bottom: 14px; align-items: center; }
  .prose h1 { font-size: var(--step-3); line-height: 1.12; margin-bottom: 20px; letter-spacing: -.025em; }
  .prose .lead { font-size: var(--step-1); color: var(--ink-2); line-height: 1.62; margin-bottom: 26px; }
  .prose .prose__source-lead { color: var(--muted); padding: 14px 16px; border-left: 2px solid var(--accent); background: var(--paper-2); }
  .prose h2 {
    font-size: var(--step-2); margin-top: clamp(34px, 4vw, 56px); margin-bottom: 14px;
    padding-top: 18px; border-top: 1px solid var(--line); scroll-margin-top: 96px;
  }
  .prose h3 { font-size: var(--step-1); margin-top: 26px; margin-bottom: 10px; }
  .prose p { margin-bottom: 16px; line-height: 1.78; color: var(--ink-2); overflow-wrap: anywhere; }
  .prose p.li { padding-left: 20px; position: relative; }
  .prose p.li::before { content: ""; position: absolute; left: 0; top: .72em; width: 6px; height: 1.5px; background: var(--accent); }

  /* data tables */
  .tablewrap {
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    border: 1px solid var(--line); margin-block: 22px 26px; background: var(--paper);
    max-width: 100%;
  }
  .dtable { font-size: var(--step--1); width: max-content; min-width: 100%; }
  .dcaption { caption-side: top; text-align: left; font-family: var(--mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); padding: 12px 14px 0; }
  .dtable th, .dtable td { text-align: left; padding: 11px 14px; border-bottom: 1px solid var(--line-2); vertical-align: top; line-height: 1.6; }
  .dtable thead th { font-family: var(--mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); font-weight: 500; background: var(--paper-2); border-bottom: 1px solid var(--line); white-space: nowrap; }
  .dtable tbody tr:last-child td { border-bottom: 0; }
  .dtable tbody tr:hover { background: var(--paper-2); }
  .dtable .rowh { font-weight: 600; white-space: nowrap; }
  .dtable thead .rowh { font-weight: 500; }

  /* method */
  .method section { border-top: 1px solid var(--line); }
  .tiers { counter-reset: t; display: grid; gap: 12px; }
  .tiers li { display: grid; grid-template-columns: minmax(0, 220px) minmax(0, 1fr); gap: 16px; padding-bottom: 12px; border-bottom: 1px solid var(--line-2); }
  .tiers b { font-size: var(--step-1); }
  .tiers span { color: var(--muted); line-height: 1.66; }
  .evgrid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
  .evcard p { color: var(--ink-2); line-height: 1.68; }
  .note { color: var(--muted); font-size: var(--step--1); line-height: 1.7; max-width: 74ch; margin-top: 14px; }
  .note--warn { border-left: 2px solid var(--accent); padding-left: 14px; }
  .bullets { display: grid; gap: 12px; max-width: 74ch; }
  .bullets li { position: relative; padding-left: 22px; color: var(--ink-2); line-height: 1.7; }
  .bullets li::before { content: ""; position: absolute; left: 0; top: .78em; width: 8px; height: 1.5px; background: var(--accent); }
  .caveat { color: var(--muted); font-size: var(--step--1); line-height: 1.7; max-width: 74ch; margin-top: 20px; }

  .glossary-groups { display: grid; gap: clamp(28px, 5vw, 48px); }
  .glossary-group { display: grid; gap: 12px; }
  .glossary-group__title { margin: 0; font-size: var(--step-1); letter-spacing: -.02em; }
  .glossary { display: grid; gap: 1px; margin: 0; background: var(--line); border: 1px solid var(--line); grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }
  .gl { background: var(--paper); padding: 16px 18px; }
  .gl dt { font-family: var(--mono); font-weight: 600; font-size: var(--step--1); margin-bottom: 6px; }
  .gl dd { color: var(--muted); font-size: var(--step--1); line-height: 1.62; margin: 0; }

  .chcard__cj { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
  .chgrid--long { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }

  /* sheet */
  .sheet { position: fixed; inset: 0; z-index: 120; }
  .sheet__scrim { position: absolute; inset: 0; background: color-mix(in srgb, var(--ink) 34%, transparent); opacity: 0; transition: opacity .28s var(--ease); }
  .sheet.on .sheet__scrim { opacity: 1; }
  .sheet__panel {
    position: absolute; left: 0; right: 0; bottom: 0; max-height: 82svh; overflow-y: auto;
    background: var(--paper); border-top: 1px solid var(--line);
    padding: clamp(20px, 4vw, 40px) var(--gut) clamp(26px, 5vw, 44px);
    transform: translateY(100%); transition: transform .4s var(--ease-out);
  }
  .sheet.on .sheet__panel { transform: none; }
  .sheet__panel::before {
    content: ""; display: block; width: 42px; height: 3px; background: var(--line);
    margin: 0 auto 18px;
  }
  .sheet__x { position: absolute; top: 16px; right: var(--gut); color: var(--muted); }
  .sheet__x:hover { color: var(--accent); }
  .sheet__body { max-width: 74ch; margin-inline: auto; }
  .sheethead__t { font-size: var(--step-2); margin-block: 8px 12px; letter-spacing: -.02em; }
  .sheetfoot { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line); }

  /* toast */
  .toast {
    position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 20px);
    background: var(--ink); color: var(--paper); padding: 11px 20px; border-radius: 100px;
    font-size: var(--step--1); opacity: 0; pointer-events: none; z-index: 200;
    transition: opacity .25s, transform .25s var(--ease-out); max-width: 90vw; text-align: center;
  }
  .toast.on { opacity: 1; transform: translate(-50%, 0); }

  /* tooltip */
  .tip {
    position: fixed; top: 0; left: 0; z-index: 210; max-width: min(300px, 88vw);
    background: var(--ink); color: var(--paper); padding: 10px 13px; border-radius: var(--r);
    font-size: 12.5px; line-height: 1.6; pointer-events: none; box-shadow: 0 8px 28px rgba(0, 0, 0, .2);
  }
  .tip b { display: block; font-family: var(--mono); font-size: 11px; letter-spacing: .06em; opacity: .7; margin-bottom: 3px; }

  /* responsive */
  @media (max-width: 1240px) {
    .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  }
  @media (max-width: 900px) {
    .res--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (max-width: 1080px) {
    .explore__body { grid-template-columns: 1fr; }
    .rail { position: static; max-height: none; display: block; }
    .rail__head { border-bottom: 1px solid var(--line); padding-bottom: 10px; }
    .rail__body {
      display: grid; grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr));
      gap: 18px 24px; margin-top: 16px;
    }
    .rail[data-open="0"] .rail__body { display: none; }
    .rail__toggle { display: inline-block; }
    .rail[data-open="1"] { padding-bottom: 4px; }
    .rail__note { grid-column: 1 / -1; }
    .long__grid { grid-template-columns: minmax(0, 1fr); }
    .toc { position: static; max-height: none; grid-auto-flow: column; grid-auto-columns: minmax(min(150px, 100%), 1fr); overflow-x: auto; padding-bottom: 8px; }
    .toc__t, .toc__sel { display: none; }
  }
  @media (max-width: 760px) {
    .topbar__in { height: 54px; gap: 12px; }
    :root { --topbar-h: 55px; }
    .nav { overflow-x: auto; scrollbar-width: none; }
    .nav::-webkit-scrollbar { display: none; }
    .brand__txt { display: none; }
    .cards--2, .cards--grid { grid-template-columns: 1fr; }
    .drow { grid-template-columns: 1fr; gap: 4px; }
    .drow__k { padding-top: 0; }
    .tiers li { grid-template-columns: 1fr; gap: 4px; }
    .search__input { font-size: var(--step-1); }
    .hero h1 { max-width: none; }
    .card__foot { gap: 10px; }
    .pagehead__tools { flex-direction: column; align-items: stretch; }
    .pager { margin-inline-start: 0; }
    .rail__reset, .rail__toggle, .select, .ghostbtn { padding: 9px 13px; }
    .facet__i { padding-block: 9px; }
    .qlist a { padding-block: 14px; }
  }
  @media (max-width: 480px) {
    .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .res--4 { grid-template-columns: 1fr; }
    .res__tools { width: 100%; justify-content: space-between; }
  }
  @media print {.topbar, .rail, .foot, .card__actions, .search, .toc, .sheet, .toast, .progress{ display: none !important; }
    body { background: #fff; }
    .card { break-inside: avoid; }
  }
}
