  /* Pudding uses Atlas Grotesk (licensed); Hanken Grotesk is the closest open
     grotesque — same big x-height and neutral confidence, free to embed. One
     variable woff2 per range covers every weight we call. Inlined as a data URI
     so the artifact CSP (no external hosts) never has to reach a font CDN. */
  @font-face {
    font-family: "Hanken Grotesk"; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url("/yamal-messi-vozinha/web/hanken_latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }

  /* Caveat — a pen hand for the player names, like an autograph on the shirt.
     Latin subset, inlined as a data URI so the CSP never reaches a font CDN. */
  @font-face {
    font-family: "Caveat"; font-style: normal; font-weight: 700; font-display: swap;
    src: url("/yamal-messi-vozinha/web/handwrite.woff2") format("woff2");
  }

  /* Chinese type. Ma Shan Zheng (马善政毛笔楷书, OFL) — a calligrapher's brush
     hand for display/headlines, the zh sibling of the Caveat autograph; Noto
     Sans SC (思源黑体) — a clean sans for body. Both subset to this page's
     characters and inlined as woff2. Ma Shan Zheng ships one 400 cut declared
     as 100 900 so the hand slots' bold rules select it as-is — its brush
     strokes already carry the weight, and synthetic bold would smear them.
     Its range also takes ASCII + · so "= 11" / "Instagram" inside zh headlines
     stay in the same hand; Noto stays CJK-only, so Latin in body still renders
     in Hanken. */
  @font-face {
    font-family: "Ma Shan Zheng"; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url("/yamal-messi-vozinha/web/mashanzheng_zh_v4.woff2") format("woff2");
    unicode-range: U+0020-007E, U+00B7, U+2014, U+2018-201D, U+2026, U+2E80-2EFF, U+3000-303F, U+3200-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FE30-FE4F, U+FF00-FFEF;
  }
  @font-face {
    font-family: "Noto Sans SC"; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url("/yamal-messi-vozinha/web/noto_zh_v4.woff2") format("woff2");
    unicode-range: U+2E80-2EFF, U+3000-303F, U+3200-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FE30-FE4F, U+FF00-FFEF;
  }


  :root {
    color-scheme: light;

    /* Football's first feeling: blue sky, white cloud, green grass.
       The page falls from sky to pitch as you scroll. Every ground colour
       below keeps --ink above 4.5:1, so the range is deliberately capped —
       it never darkens past #86C46A. */
    --sky-high:  #62B3E4;
    --sky-low:   #96D2EF;
    --haze:      #DCEFF2;

    /* Opening zones: three FLAT sky-blues (not a smooth gradient), one diagonal
       block behind each player so figure and card share a colour and the eye
       can't pair the wrong name to the wrong face. Top→bottom = deep→pale, like
       real sky: deepest overhead, palest at the horizon. */
    --z1: #4E8CC9;   /* top · Vozinha · deepest */
    --z2: #83BBE8;   /* mid · Messi   · mid     */
    --z3: #D8EBF8;   /* low · Yamal   · palest  */
    --grass-lo:  #B2DE9A;
    --grass:     #86C46A;
    --pitch:     #2C6320;   /* the one dark band: 4,000 yellow keepers need it */

    --ink:       #12271E;   /* 6.81 on sky, 7.58 on grass */
    --ink-2:     #3D5148;
    --ink-3:     #5C6F66;
    --paper:     #FFFFFF;

    /* Kit colours are FILLS, never text. Yellow cannot be both dark enough
       to read and still yellow — so identity lives in a badge, like the
       patch on a shirt, with the text sitting on top of it. */
    --voz:       #F7D117;
    --messi:     #75AADB;
    --yamal:     #A50044;

    --font-fig:  "HelveticaNeue-CondensedBlack", "Helvetica Neue Condensed",
                 Impact, Haettenschweiler, "Noto Sans SC", sans-serif;
    --font-body: "Hanken Grotesk", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI",
                 Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-zh:   "Noto Sans SC", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
    --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, "Noto Sans SC", monospace;
    --font-hand: "Caveat", "Ma Shan Zheng", "Bradley Hand", "Segoe Script", cursive;

    /* The heads are worn twice — once by the age ladder, once by the group chat.
       Inlining the base64 at both tags costs the full weight twice: gzip's window
       is 32 KB and the two uses sit far further apart than that, so it never sees
       the repeat. Inlined once here, referenced by both. */
    --head-voz:   url("/yamal-messi-vozinha/web/vozinha_head.webp");
    --head-messi: url("/yamal-messi-vozinha/web/messi_head.webp");
    --head-yamal: url("/yamal-messi-vozinha/web/yamal_head.webp");
    --fig-yamal:  url("/yamal-messi-vozinha/web/yamal.webp");   /* full cut-out, reused by lineup + value scene */
    --img-bath:     url("/yamal-messi-vozinha/web/bath.webp");        /* 2007 · Messi bathing baby Yamal */
    --img-hold:     url("/yamal-messi-vozinha/web/hold.webp");        /* 2007 · Messi holding the toweled baby */
    --img-grown:    url("/yamal-messi-vozinha/web/grown.webp");       /* 2026 · the baby, grown, handing Messi a tub */
    --img-electric: url("/yamal-messi-vozinha/web/electrician.webp"); /* Vozinha's day job */
    --img-save:     url("/yamal-messi-vozinha/web/save.webp");        /* 2026 · Vozinha, 40, keeping goal vs Spain */
    --img-ball:     url("/yamal-messi-vozinha/web/ball.webp");        /* decorative: a few resting on the pitch at the foot of the page */
  }

  * { box-sizing: border-box; }
  /* clip, NOT hidden. `overflow-x: hidden` computes overflow-y to `auto`,
     which silently turns html AND body into scroll containers. The decorative
     pitch balls poke ~5px past body's box, so body became a real 5px scroller
     covering the whole page: scroll gestures latched onto it at the top,
     rubber-banded through its 5px range, and the page refused to move (with a
     near-full-length phantom scrollbar). `clip` clips paint without creating
     a scroller. The `hidden` line stays as the pre-2022-engine fallback. */
  html { overflow-x: hidden; overflow-x: clip; }

  /* Rubber-band reveals the canvas, and a propagated repeating gradient shows
     the WRONG end there (green above the sky, blue under the grass). Safari
     paints ONLY the canvas background into the bounce gaps — element ink
     overflow (a block hung above the document) is ignored there — so the sky
     must live in the canvas itself: a no-repeat sky-colour image positioned
     entirely ABOVE the document owns the top bounce, and the grass
     background-color owns the bottom one. body::before covers the same top
     gap in engines that do paint ink overflow. */
  html {
    background-color: var(--grass);
    background-image: linear-gradient(var(--sky-high), var(--sky-high));
    background-repeat: no-repeat;
    background-size: 100% 300vh;
    background-position: 0 -300vh;
  }
  body::before {
    content: ""; position: absolute; left: 0; right: 0;
    top: -120vh; height: 120vh;
    background: var(--sky-high);
    pointer-events: none;
  }

  /* A background-image can be reused; an <img loading="lazy" decoding="async" src> cannot. role+aria-label keeps
     the name a screen reader would have got from alt. */
  .headshot {
    background-repeat: no-repeat; background-size: contain; background-position: center bottom;
    aspect-ratio: 224 / 259;
  }
  .headshot.voz   { background-image: var(--head-voz); }
  .headshot.messi { background-image: var(--head-messi); }
  .headshot.yamal { background-image: var(--head-yamal); }

  body {
    margin: 0;
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 18px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden; overflow-x: clip;   /* clip, not hidden — see the html rule */
    background:
      linear-gradient(to bottom,
        var(--sky-high)  0%,
        var(--sky-low)   18%,
        var(--haze)      34%,
        var(--haze)      40%,
        var(--grass-lo)  56%,
        var(--grass)     78%,
        var(--grass)    100%);
    background-attachment: scroll;
    position: relative;
  }

  :root[data-lang="zh"] body,
  :root[data-lang="zh"] .lede,
  :root[data-lang="zh"] .note { font-family: var(--font-zh); }

  /* ───────── clouds ───────── */

  /* One layer over the sky stretch only — clouds must not drift down into the
     grass, and must not sit behind running text. They stay in the outer
     thirds; the middle is where the men and the numbers live. */
  .clouds {
    position: absolute; top: 0; left: 0; right: 0; height: 230vh;
    overflow: hidden; pointer-events: none; z-index: 0;
  }
  .cloud {
    position: absolute;
    background: #fff;
    border-radius: 100px;
    filter: blur(1px);
    opacity: .82;
  }
  .cloud::before, .cloud::after {
    content: ""; position: absolute; background: #fff; border-radius: 50%;
  }
  .cloud::before { width: 55%; height: 160%; left: 12%; top: -70%; }
  .cloud::after  { width: 38%; height: 130%; right: 14%; top: -46%; }
  @keyframes drift { from { transform: translateX(-6vw); } to { transform: translateX(6vw); } }

  /* ───────── layout ───────── */

  .card {
    position: relative;
    z-index: 1;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 2.2rem;
    padding: 11vh 22px;
  }

  .col  { width: 100%; max-width: 34rem; display: flex; flex-direction: column; gap: 1rem; }
  .wide { width: 100%; max-width: 62rem; display: flex; flex-direction: column; align-items: center; gap: 2rem; }

  .fig {
    font-family: var(--font-fig);
    font-weight: 900; letter-spacing: -.03em; line-height: .82;
    font-variant-numeric: tabular-nums; margin: 0; padding-bottom: .1em;
  }

  .eyebrow {
    font-family: var(--font-mono); font-size: .72rem;
    letter-spacing: .2em; text-transform: uppercase;
    color: var(--ink-3); margin: 0; text-align: center;
  }

  /* A section's topic as a big handwritten headline, not a small mono label.
     Caveat has no CJK, so 中文 falls back to a brush face (Kaiti) at its own size. */
  .hand-title {
    font-family: var(--font-hand); font-weight: 700;
    font-size: clamp(2.7rem, 12vw, 4.8rem); line-height: .9;
    letter-spacing: .01em; text-align: center; color: var(--ink);
    margin: 0 0 clamp(14px, 3vh, 30px);
  }
  :root[data-lang="zh"] .hand-title {
    font-family: "Ma Shan Zheng", var(--font-zh);
    font-weight: 800; font-size: clamp(2.1rem, 9vw, 3.4rem);
  }

  /* strong chapter opener — a big round number + kicker + title, so each of the
     three comparison dimensions reads as its own numbered round of a contest */
  .chapter { display: flex; align-items: center; justify-content: center; gap: clamp(.8rem, 3vw, 1.5rem);
    width: 100%; max-width: 40rem; margin: 0 auto clamp(1.5rem, 4vh, 2.6rem); }
  .ch-no { font-family: var(--font-fig); font-weight: 900; letter-spacing: -.05em; line-height: .72;
    font-size: clamp(3.6rem, 14vw, 7rem); color: var(--ink); flex: 0 0 auto; }
  .ch-txt { display: flex; flex-direction: column; gap: .4rem; text-align: left;
    border-left: 3px solid var(--ink); padding-left: clamp(.85rem, 2.6vw, 1.35rem); }
  .ch-kick { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-3); }
  .ch-name { font-size: clamp(2rem, 6.5vw, 3.2rem); font-weight: 800; line-height: 1; letter-spacing: -.02em; color: var(--ink); }
  :root[data-lang="zh"] .ch-name { letter-spacing: .02em; font-family: "Ma Shan Zheng", var(--font-zh); }

  /* round result — who won this dimension; a bold trophy banner in the winner's colour */
  .winner { display: flex; align-items: center; gap: clamp(.9rem, 3vw, 1.6rem);
    width: 100%; max-width: 34rem; margin: clamp(1.6rem,4.5vh,2.6rem) auto 0;
    padding: clamp(1rem,3.2vw,1.5rem) clamp(1.2rem,4vw,1.9rem); border-radius: 20px;
    box-shadow: 0 14px 36px rgba(18,39,30,.20); }
  .winner.voz   { background: var(--voz);   color: var(--ink); }
  .winner.messi { background: var(--messi); color: var(--ink); }
  .winner.yamal { background: var(--yamal); color: #fff; }
  .win-cup { font-size: clamp(2.7rem, 9vw, 4.1rem); line-height: 1; flex: 0 0 auto; }
  .win-txt { display: flex; flex-direction: column; gap: .18rem; min-width: 0; }
  .win-kick { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .15em; text-transform: uppercase; opacity: .82; }
  .win-name { font-family: var(--font-hand); font-weight: 700; font-size: clamp(2.3rem, 8vw, 3.3rem); line-height: 1; }
  .win-why { font-size: clamp(.85rem, 2.4vw, 1rem); line-height: 1.35; opacity: .92; margin-top: .2rem; overflow-wrap: anywhere; }
  /* round-result sections are compact, not full-screen like the content cards */
  .card.result { min-height: auto; padding: clamp(2.6rem, 9vh, 5rem) 22px; gap: 0; }

  h2 {
    font-size: clamp(1.6rem, 6vw, 2.1rem);
    font-weight: 800; line-height: 1.24; text-wrap: balance;
    margin: 0; text-align: center; letter-spacing: -.02em;
  }

  p { margin: 0; text-wrap: pretty; }
  .note { font-size: 1.12rem; color: var(--ink); line-height: 1.55; font-weight: 420; }
  .lede { font-size: 1.3rem; font-weight: 430; line-height: 1.45; }
  .src  { font-family: var(--font-mono); font-size: .66rem; color: var(--ink-3); line-height: 1.7; }
  strong { font-weight: 700; color: var(--ink); }
  em { font-style: normal; font-weight: 700; box-shadow: inset 0 -.42em 0 rgba(247,209,23,.55); }

  .r { opacity: 0; transform: translateY(16px); transition: opacity .75s ease, transform .75s ease; }
  .r.in { opacity: 1; transform: none; }

  /* ───────── streaming curtain (mechanism lives inline in the site head) ── */

  #splash .sp-ball { font-size: 46px; line-height: 1;
    animation: sp-bounce .8s cubic-bezier(.35,0,.65,1) infinite alternate; }
  #splash .sp-txt { font-family: var(--font-mono); font-size: .72rem;
    letter-spacing: .18em; text-transform: uppercase; color: var(--ink); }
  :root[data-lang="zh"] #splash .sp-txt { font-family: var(--font-zh); font-size: .9rem; letter-spacing: .14em; }
  @keyframes sp-bounce { from { transform: translateY(-16px) } to { transform: translateY(10px) } }
  @media (prefers-reduced-motion: reduce) { #splash .sp-ball { animation: none } }

  /* ───────── language toggle ───────── */

  #lang {
    position: fixed; top: 14px; right: 14px; z-index: 50;
    display: flex; gap: 0; border: 1.5px solid var(--ink);
    border-radius: 999px; overflow: hidden; background: rgba(255,255,255,.75);
    backdrop-filter: blur(6px);
  }
  #lang a {
    font-family: var(--font-mono); font-size: .64rem; letter-spacing: .1em;
    padding: .42rem .72rem; border: 0; background: transparent;
    color: var(--ink); cursor: pointer; text-decoration: none;
  }
  #lang a[aria-current="page"] { background: var(--ink); color: #fff; }
  #lang a:focus-visible { outline: 2px solid var(--yamal); outline-offset: -4px; }

  /* ───────── the three ───────── */

  .lineup {
    display: flex; align-items: flex-end; justify-content: center;
    gap: clamp(14px, 4vw, 52px); width: 100%;
    border-bottom: 2px solid rgba(18,39,30,.55); padding-bottom: 2px;
    --h: clamp(230px, 47vw, 540px);
  }
  .man img, .man .figman { filter: drop-shadow(0 12px 18px rgba(18,39,30,.28)); }
  .man img { display: block; width: auto; }
  /* Yamal's full cut-out is worn twice — here and again in the value scene. Same
     reuse problem as the heads: inlined once as --fig-yamal (see :root) and
     referenced by both, because a background-image reuses and an <img loading="lazy" decoding="async" src> can't.
     aspect-ratio replaces width:auto; drop-shadow tracks the alpha exactly as it
     did on the <img loading="lazy" decoding="async"> (verified against the original pixel-for-pixel). */
  .figman { display: block; background: var(--fig-yamal) center bottom / contain no-repeat; aspect-ratio: 224 / 695; }
  /* Height carries the metres — never width. The source images have different
     aspect ratios (Messi's arm is out), so sizing by width renders him ~10%
     short and silently breaks the only point this shot makes. */
  .man.voz   img     { height: var(--h); }
  .man.messi img     { height: calc(var(--h) * 0.89947); }  /* 1.70 / 1.89 */
  .man.yamal .figman { height: calc(var(--h) * 0.96825); }  /* 1.83 / 1.89 */

  .plates { display: flex; justify-content: center; gap: clamp(14px, 4vw, 52px); width: 100%; }
  .plates > div { flex: 1; max-width: 205px; display: flex; flex-direction: column; align-items: center; gap: .3rem; }

  .badge {
    font-family: var(--font-mono); font-size: .7rem; font-weight: 600;
    letter-spacing: .1em; text-transform: uppercase;
    padding: .2rem .6rem; border-radius: 3px; white-space: nowrap;
  }
  .badge.voz   { background: var(--voz);   color: var(--ink); }   /* 10.55 */
  .badge.messi { background: var(--messi); color: var(--ink); }   /*  6.39 */
  .badge.yamal { background: var(--yamal); color: #fff; }         /*  7.83 */
  .plates span { font-size: .74rem; color: var(--ink-2); }

  /* ───────── scene 0 · the cast: cut-out figures floating on the sky ─────────
     No frames — the men stand straight on the sky, labels underneath. The three
     source photos are pre-normalised to one canvas (equal heads, shared eye-line,
     face-centred), so one width rule lines them up on their own. */
  .cast {
    position: relative;
    display: flex; justify-content: center; align-items: flex-start;
    gap: clamp(6px, 2vw, 44px); width: 100%; perspective: 1600px;
  }
  /* A framing headline so a first-time visitor immediately grasps the premise.
     Handwritten like the rest of the display type; 中文 borrows Kaiti. */
  .hero-intro { position: relative; z-index: 1; text-align: center; max-width: 40rem;
    margin: 0 auto clamp(16px, 3.5vh, 36px); display: flex; flex-direction: column; gap: .55rem; }
  .hero-head { margin: 0; font-family: var(--font-hand); font-weight: 700; color: var(--ink);
    line-height: 1.02; letter-spacing: .01em; font-size: clamp(2.7rem, 10vw, 4.4rem); text-wrap: balance; }
  :root[data-lang="zh"] .hero-head { font-family: "Ma Shan Zheng", var(--font-zh); font-weight: 800; }
  .hero-sub { margin: 0; font-size: clamp(.98rem, 2.7vw, 1.18rem); color: var(--ink-2); line-height: 1.5; text-wrap: pretty; }
  :root[data-lang="zh"] .hero-sub { font-family: var(--font-zh); }
  .hero0 > .eyebrow, .hero0 > .hero-intro, .hero0 .cast { position: relative; z-index: 1; }
  .hero0 > .eyebrow { color: var(--ink); }
  .person {
    position: relative; z-index: 1;
    margin: 0; flex: 0 0 auto; width: 30vw; max-width: 340px;
    display: flex; flex-direction: column; align-items: center;
  }
  .person .tilt { transform-style: preserve-3d; transition: transform .28s ease-out; will-change: transform; }
  .person .sway { will-change: transform; }
  /* keyframe name is heroSway, not sway: another scene already owns @keyframes sway
     (a ±9deg rocking phone with translateX(-50%)) and a same-name collision would
     hand our figures its -50% shift. */
  .person.voz   .sway { animation: heroSway 5.6s ease-in-out -1.1s infinite; }
  .person.messi .sway { animation: heroSway 6.7s ease-in-out -3.4s infinite; }
  .person.yamal .sway { animation: heroSway 4.9s ease-in-out -0.4s infinite; }
  @keyframes heroSway {
    0%, 100% { transform: translateX(-6px) rotate(-1.1deg); }
    50%      { transform: translateX(6px)  rotate(1.1deg); }
  }
  .cutout {
    display: block; width: 100%; height: auto;
    /* kill the site-wide `figure img` box-shadow + radius — it draws a rectangle
       behind the transparent cut-out and reads as the frame we just removed. */
    box-shadow: none; border-radius: 0;
    filter: drop-shadow(0 16px 20px rgba(18,39,30,.26));
  }
  .plate {
    text-align: center; margin-top: clamp(8px, 1.6vh, 18px);
    display: flex; flex-direction: column; align-items: center; gap: .5rem;
  }
  /* Full numbers, not €50K/€200M — the whole point is the count of zeros, so
     the string must never wrap. Condensed + tabular holds €200,000,000 on one
     line and lets the eye compare lengths. */
  .price {
    font-family: var(--font-hand); font-weight: 700; letter-spacing: 0;
    line-height: .9; font-variant-numeric: tabular-nums; white-space: nowrap;
    font-size: clamp(1.5rem, 4.6vw, 2.8rem); color: var(--ink);
  }
  .pmeta {
    font-family: var(--font-hand); font-weight: 700; color: var(--ink-2);
    font-size: clamp(1.05rem, 2.6vw, 1.6rem); letter-spacing: .045em; line-height: 1.2;
  }
  .pmeta .flag { letter-spacing: normal; }   /* keep the two regional-indicator chars fused into one flag */
  :root[data-lang="zh"] .pmeta {
    font-family: "Ma Shan Zheng", var(--font-zh); font-weight: 800; letter-spacing: .08em;
  }
  /* Names in a signature hand on a bigger colour card — like an autograph on the
     shirt. Scoped to the hero so other scenes' badges keep their mono look. */
  .cast .badge {
    font-family: var(--font-hand); font-weight: 700; text-transform: none;
    letter-spacing: 0; line-height: 1;
    font-size: clamp(1.4rem, 3.4vw, 2rem);
    padding: .16em .62em .3em; border-radius: 9px;
    /* white keyline + soft shadow so the card lifts off whichever blue zone it
       sits on (Messi's blue badge would otherwise sink into its blue block). */
    box-shadow: 0 0 0 2px rgba(255,255,255,.8), 0 4px 12px rgba(18,39,30,.16);
  }
  :root[data-lang="zh"] .cast .badge {
    font-family: "Ma Shan Zheng", var(--font-zh); font-weight: 800; letter-spacing: .08em;
  }
  .pmeta { color: var(--ink); }   /* darker than --ink-2: legible on the deep zone too */
  @media (max-width: 620px) {
    /* The same tidy three-across row as desktop, just scaled to the phone: each
       man keeps his own column so figure → name → price → role read straight
       down, and nobody's label lands under someone else. */
    .cast { gap: 2.5%; align-items: flex-start; }
    .person { width: 31.5%; max-width: none; }
    .cutout { filter: drop-shadow(0 10px 13px rgba(18,39,30,.28)); }
    .plate { margin-top: 10px; gap: .32rem; }
    /* €200,000,000 has to stay on one line inside a ~1/3-width column */
    .price { font-size: clamp(.72rem, 3.3vw, 1.05rem); }
    .cast .badge { font-size: clamp(.95rem, 4.4vw, 1.35rem); }
    .pmeta { font-size: clamp(.72rem, 3.1vw, 1rem); line-height: 1.28; letter-spacing: .03em; }
  }

  /* ───────── stat rows ───────── */

  .row3 {
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: clamp(8px, 2.4vw, 26px); width: 100%; max-width: 52rem; align-items: end;
  }
  .stat { display: flex; flex-direction: column; align-items: center; gap: .7rem; text-align: center; padding: clamp(10px, 2vh, 22px) 0; }
  /* Face + name over each figure — the number means nothing until you know
     whose it is. Same identity system as the follower bars: headshot + bold
     name, with the kit rule as the divider underneath. */
  .stat-who { display: flex; align-items: center; justify-content: center; gap: .45rem; }
  .stat-who .headshot { width: clamp(34px, 7vw, 48px); }
  .stat-who b { font-weight: 800; font-size: clamp(.92rem, 2.7vw, 1.15rem); letter-spacing: -.01em; color: var(--ink); }
  .stat .val  { font-family: var(--font-hand); font-weight: 700; letter-spacing: 0; line-height: .9; font-size: clamp(2.6rem, 10vw, 5.2rem); }
  .stat .unit { font-size: .78rem; color: var(--ink-2); }
  .stat .rule { width: 100%; height: 5px; border-radius: 3px; }
  .rule.voz { background: var(--voz); } .rule.messi { background: var(--messi); } .rule.yamal { background: var(--yamal); }

  /* ───────── the age ladder ───────── */

  /* One line per year lived. All three stand on the same floor — today — so the
     top of each stack lands on the year he was born, and the height IS the age.
     Rows are calendar years: pitch is exactly --row, so the axis, the decade
     rules and all three stacks agree without anyone measuring anything. */
  .ladder {
    --row: clamp(6px, 1.5vw, 8px);
    --tick: 3px;
    --floor: 2025;            /* last full year of life — the bottom row */
    display: grid;
    grid-template-columns: 3.2rem repeat(3, 1fr);
    column-gap: clamp(8px, 2.4vw, 26px);
    width: 100%; max-width: 52rem;
    align-items: end;
  }

  /* The axis and the rules stretch to the row — set by the tallest stack, 40
     lines — so their year offsets are measured off the same floor. The stacks
     themselves must NOT stretch: they hang from the floor, which is what puts
     each man's top line on his birth year. */
  .lad-axis, .lad-rules { grid-row: 1; align-self: stretch; }
  .lad-col { grid-row: 1; align-self: end; }

  .lad-axis { position: relative; }
  .lad-axis span {
    position: absolute; right: 0; line-height: 1;
    bottom: calc((var(--floor) - var(--y)) * var(--row));
    transform: translateY(50%);
    font-family: var(--font-mono); font-size: .6rem;
    letter-spacing: .04em; color: var(--ink-3);
  }
  .lad-axis .now { color: var(--ink); font-weight: 700; }

  .lad-rules { grid-column: 2 / -1; position: relative; }
  .lad-rules b {
    position: absolute; left: 0; right: 0; height: 1px;
    background: rgba(18,39,30,.11);
    bottom: calc((var(--floor) - var(--y)) * var(--row) + 1px);
  }

  .lad-col {
    position: relative;
    display: flex; flex-direction: column;
    gap: calc(var(--row) - var(--tick));
  }
  .lad-col::after {                       /* the floor all three stand on */
    content: ""; position: absolute; left: 0; right: 0; bottom: -5px; height: 1px;
    background: rgba(18,39,30,.28);
  }
  /* Kit colours are fills that cannot hold a 3px line on the sky — yellow is
     1.1:1 against it. So the ink hairline draws the line and the kit colour
     fills it, the same division of labour as the badges. */
  .lad-col i {
    display: block; height: var(--tick); border-radius: 1px;
    box-shadow: 0 0 0 .5px rgba(18,39,30,.5);
    transform: scaleX(0); transform-origin: left center;
    transition: transform .5s cubic-bezier(.2,.8,.2,1);
    transition-delay: calc(140ms + var(--d) * 28ms);
  }
  .ladder.in .lad-col i { transform: scaleX(1); }
  .lad-col.voz   i { background: var(--voz); }
  .lad-col.messi i { background: var(--messi); }
  .lad-col.yamal i { background: var(--yamal); }

  .lad-birth {
    position: absolute; bottom: calc(100% + .55rem); left: 0;
    font-family: var(--font-mono); font-size: .6rem; color: var(--ink-2);
    white-space: nowrap;
    opacity: 0; transition: opacity .5s ease;
    transition-delay: calc(140ms + var(--d) * 28ms);
  }
  .ladder.in .lad-birth { opacity: 1; }

  /* The head crowns each man's own line — big, at the very top of his stack — so
     you meet the face first and read the age underneath. Taller stack (older) =
     higher head, so Vozinha's floats near 1986 and Yamal's sits low near 2007. */
  .lad-col > .headshot {
    order: -1; align-self: center;             /* above the year-lines, centred on the stick */
    width: clamp(72px, 15vw, 118px);
    margin-bottom: clamp(6px, 1.4vw, 12px);
    transform-origin: 50% 90%;                 /* rocks on the neck, not the nose */
    animation: bob 2.8s ease-in-out infinite;
  }
  /* Off the same beat, or three heads nodding in lockstep read as one object. */
  .lad-col.messi > .headshot { animation-duration: 3.3s; animation-delay: -.9s; }
  .lad-col.yamal > .headshot { animation-duration: 2.4s; animation-delay: -1.7s; }
  @keyframes bob {
    0%, 100% { transform: rotate(-7deg); }
    50%      { transform: rotate(7deg); }
  }
  .lad-birth { display: none; }                /* the axis + the head already carry the year */

  /* Age: small, centred, in the same hand as the other numbers, with air above. */
  .lad-num { grid-row: 2; display: flex; justify-content: center; padding-top: clamp(12px, 2.6vh, 24px); }
  .lad-num .val {
    font-family: var(--font-hand); font-weight: 700; letter-spacing: 0; line-height: 1;
    font-size: clamp(2rem, 6.5vw, 3.2rem); color: var(--ink);
  }

  @media (max-width: 560px) {
    .ladder { grid-template-columns: 2.4rem repeat(3, 1fr); }
    .lad-birth { font-size: .54rem; }
  }

  /* ───────── the follower spike ─────────
     56k sat flat for years; one 0-0 draw and it went vertical. The line draws
     itself on scroll (a left→right wipe), a counter races it up, and a solid
     crimson ceiling is Yamal's 46.7M — the curve reaches 62% of the way to it.
     Gold is his: a FILL with an ink line on top, because yellow can't hold a
     stroke on grass (the badge rule). Labels are HTML laid over the SVG so they
     stay crisp and bilingual at any width — every % mirrors the 1000×560 viewBox. */
  .spk {
    position: relative; width: 100%; max-width: 46rem;
    height: clamp(360px, 60vh, 520px); margin: 0 auto;
    font-family: var(--font-hand);
  }
  .spk svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
  .spk-draw { clip-path: inset(0 100% 0 0); transition: clip-path 2.3s linear; }
  .spk.in .spk-draw { clip-path: inset(0 0 0 0); }
  .spk-line { fill: none; stroke: var(--ink); stroke-width: 4.5;
    stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
  .spk-axis { stroke: rgba(18,39,30,.30); stroke-width: 1.4; vector-effect: non-scaling-stroke; }

  .spk-lyr { position: absolute; inset: 0; pointer-events: none; }
  .spk-lyr > * { position: absolute; }
  .spk-o { opacity: 0; transition: opacity .6s ease; transition-delay: var(--dl, 1s); }
  .spk.in .spk-o { opacity: 1; }

  /* Yamal's ceiling — the height he's chasing. Same 4.5px weight as the black
     curve, and solid: the two lines must read as the same coordinate system,
     one man's line against another's, not a stray dashed rule. His face rides
     the line's right end the same way Vozinha's rides the curve start. */
  .spk-ceil { left: 0; right: 0; border-top: 4.5px solid var(--yamal); border-radius: 3px; }
  .spk-ceil b { position: absolute; right: 0; top: auto; bottom: 4.5px; font-weight: 700;
    display: flex; align-items: flex-end; gap: .4rem;
    color: var(--yamal); font-size: clamp(.92rem, 2.5vw, 1.28rem); white-space: nowrap; }
  .spk-ceil b > span:last-child { padding-bottom: .3em; }
  .spk-ceil .headshot { width: clamp(34px, 8vw, 46px); }

  /* Who the black line belongs to — face + name sitting on its flat start,
     so the curve is owned at first glance (the ceiling names its man the
     same way: label riding the line). */
  .spk-owner { display: flex; align-items: flex-end; gap: .4rem; transform: translateY(-100%); }
  .spk-owner .headshot { width: clamp(34px, 8vw, 46px); }
  .spk-owner b { font-weight: 700; color: var(--ink); line-height: 1;
    font-size: clamp(.95rem, 2.6vw, 1.2rem); padding-bottom: 2px; white-space: nowrap; }

  /* dots ride the curve; they pop as the wipe passes */
  .spk-dot { width: var(--r, 14px); height: var(--r, 14px); border-radius: 50%;
    background: var(--voz); box-shadow: 0 0 0 3px var(--ink), 0 0 0 5px rgba(255,255,255,.55);
    transform: translate(-50%, -50%) scale(.15);
    transition: transform .5s cubic-bezier(.2, 1.3, .4, 1); transition-delay: var(--dl, 1s); }
  .spk.in .spk-dot { transform: translate(-50%, -50%) scale(1); }

  /* the running count, landing at the final dot */
  .spk-final { text-align: right; }
  .spk-count { display: block; font-weight: 700; line-height: .82; color: var(--ink);
    font-size: clamp(2.1rem, 7.5vw, 3.9rem); white-space: nowrap; }
  .spk-final .t, .spk-lab .t { display: block; margin-top: .12em; font-weight: 700;
    color: var(--ink-2); font-size: clamp(.82rem, 2.2vw, 1.06rem); line-height: 1.12; }
  .spk-lab { color: var(--ink); font-weight: 700; }
  .spk-lab .n { display: block; font-size: clamp(1.15rem, 3.6vw, 1.85rem); line-height: 1; }

  .spk-ign { border-left: 2px dashed rgba(18,39,30,.42); }
  .spk-tag { color: var(--ink-2); font-weight: 700; text-align: center;
    font-size: clamp(.82rem, 2.2vw, 1.06rem); line-height: 1.1; white-space: nowrap; }
  .spk-baseline { color: var(--ink-2); font-weight: 700; text-align: center;
    font-size: clamp(.82rem, 2.2vw, 1.06rem); line-height: 1.12; }
  /* Messi is off this chart by an order of magnitude — but he is one of the
     three actors, not a footnote: full-strength label, his face peeking in
     from above the frame, same size as the other two heads. */
  .spk-messi { display: flex; align-items: center; gap: .4rem;
    color: var(--ink); font-weight: 700;
    font-size: clamp(.85rem, 2.3vw, 1.1rem); line-height: 1.1; }
  .spk-messi .headshot { width: clamp(34px, 8vw, 46px); }

  @media (max-width: 560px) {
    .spk { height: clamp(330px, 62vh, 440px); }
  }

  /* ───────── three followers, one axis ─────────
     The spike capped at 50M so Vozinha could reach 62% of Yamal. Zoom out to
     fit Messi and both of them shrink to ankle height: 513M is ~11× Yamal and
     ~18× the goalkeeper. True-scale columns, kit-filled with an ink edge (gold
     can't hold on grass alone); the number rides each bar's top so the eye has
     to climb. Ronaldo (650M) sits off the top — even Messi is only second. */
  .fbars {
    position: relative; width: 100%; max-width: 40rem; margin: 0 auto;
    --maxbar: clamp(300px, 48vh, 452px);
    display: flex; align-items: flex-end; justify-content: center;
    gap: clamp(24px, 9vw, 80px); padding-top: 2.8rem;
  }
  .fbar { display: flex; flex-direction: column; align-items: center; }
  .fb-num {
    font-family: var(--font-hand); font-weight: 700; color: var(--ink);
    font-size: clamp(1.4rem, 5.2vw, 2.7rem); line-height: 1;
    margin-bottom: .4rem; white-space: nowrap;
  }
  /* Hand-drawn, not printed: no hard outline, filled with diagonal pencil
     hatching (a faint kit wash so it still reads as gold / crimson / blue, bold
     kit strokes over it). A turbulence filter wobbles the strokes and the edge
     so it looks sketched rather than generated. */
  .fb-col {
    width: clamp(52px, 18vw, 108px); height: var(--h, 40px);
    background-color: rgba(var(--kit-rgb), .17);
    background-image: repeating-linear-gradient(-58deg,
      rgba(var(--kit-rgb), .95) 0 2.6px, rgba(var(--kit-rgb), 0) 2.6px 8.4px);
    filter: url(#fbrough);
    transform: scaleY(0); transform-origin: 50% 100%;
    transition: transform .95s cubic-bezier(.2,.8,.2,1); transition-delay: var(--d, 0ms);
  }
  .fbars.in .fb-col { transform: scaleY(1); }
  .fbar.voz   .fb-col { --kit-rgb: 247,209,23;  --h: calc(var(--maxbar) * 0.0561); }
  .fbar.yamal .fb-col { --kit-rgb: 165,0,68;    --h: calc(var(--maxbar) * 0.0910); }
  .fbar.messi .fb-col { --kit-rgb: 117,170,219; --h: var(--maxbar); }
  .fb-cap { margin-top: .55rem; display: flex; flex-direction: column; align-items: center; gap: .28rem; }
  .fb-cap .headshot { display: block; width: clamp(34px, 9vw, 46px); }
  .fb-name { font-weight: 700; color: var(--ink-2); font-size: clamp(.8rem, 2.4vw, .98rem); }
  .fb-ceil {
    position: absolute; top: 0; left: 0; right: 0; text-align: center;
    font-family: var(--font-hand); font-weight: 700; color: var(--ink-3);
    font-size: clamp(.92rem, 2.6vw, 1.18rem);
    opacity: 0; transition: opacity .6s ease .35s;
  }
  .fbars.in .fb-ceil { opacity: 1; }

  /* ───────── the bath ───────── */

  figure { margin: 0; display: flex; flex-direction: column; gap: .7rem; width: 100%; max-width: 40rem; }
  figure img { width: 100%; height: auto; display: block; border-radius: 2px; box-shadow: 0 10px 30px rgba(18,39,30,.22); }

  /* ───────── annotations on the evidence ─────────
     The line above calls this photograph evidence, so it gets marked up like
     evidence: pins that point AT things. Never speech bubbles — a bubble off
     Messi's mouth reads as a quote, and he has never said a word about this
     photo. Pointing is annotation; speaking would be fabrication. */

  .shot { position: relative; line-height: 0; }
  .mark { position: absolute; left: var(--x); top: var(--y); width: 0; height: 0; }

  .pin {
    position: absolute; left: 0; top: 0;
    transform: translate(-50%, -50%);
    width: 26px; height: 26px; padding: 0;
    border-radius: 50%; cursor: pointer;
    border: 2px solid #fff;
    background: var(--ink);
    color: #fff;
    font-family: var(--font-mono); font-size: .68rem; font-weight: 700; line-height: 1;
    box-shadow: 0 2px 10px rgba(0,0,0,.45);
    transition: transform .18s ease, background .18s ease;
  }
  .pin::after {                       /* a slow halo so they read as touchable */
    content: ""; position: absolute; inset: -6px;
    border-radius: 50%; border: 1.5px solid rgba(255,255,255,.6);
    animation: halo 2.6s ease-out infinite;
  }
  @keyframes halo {
    0%   { transform: scale(.75); opacity: .9; }
    70%  { transform: scale(1.5); opacity: 0; }
    100% { transform: scale(1.5); opacity: 0; }
  }
  .pin:hover, .pin:focus-visible { transform: translate(-50%, -50%) scale(1.18); background: var(--yamal); }
  .pin:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
  .mark.on .pin { background: var(--yamal); }

  .pop {
    position: absolute;
    width: max-content; max-width: min(15rem, 54vw);
    background: var(--paper); color: var(--ink);
    font-family: var(--font-body); font-size: .8rem; line-height: 1.55;
    padding: .6rem .75rem; border-radius: 5px;
    box-shadow: 0 8px 26px rgba(0,0,0,.3);
    opacity: 0; pointer-events: none;
    transition: opacity .18s ease, transform .18s ease;
    z-index: 4;
  }
  .pop.right { left: 18px; top: 0; transform: translate(-6px, -50%); }
  .pop.left  { right: 18px; top: 0; transform: translate(6px, -50%); }
  .pop.up    { left: 0; bottom: 18px; transform: translate(-50%, 6px); }
  .mark:hover .pop, .mark.on .pop, .pin:focus-visible + .pop { opacity: 1; pointer-events: auto; }
  .mark:hover .pop.right, .mark.on .pop.right, .pin:focus-visible + .pop.right { transform: translate(0, -50%); }
  .mark:hover .pop.left,  .mark.on .pop.left,  .pin:focus-visible + .pop.left  { transform: translate(0, -50%); }
  .mark:hover .pop.up,    .mark.on .pop.up,    .pin:focus-visible + .pop.up    { transform: translate(-50%, 0); }

  :root[data-lang="zh"] .pop { font-family: var(--font-zh); }
  figcaption { font-family: var(--font-mono); font-size: .62rem; color: var(--ink-3); line-height: 1.65; }

  blockquote { margin: 0; border-left: 3px solid var(--voz); padding-left: 1.2rem; font-size: 1.2rem; line-height: 1.5; font-weight: 450; }
  blockquote cite { display: block; margin-top: .5rem; font-style: normal; font-family: var(--font-mono); font-size: .64rem; color: var(--ink-3); }

  /* ───────── 2007, three lives in one month ─────────
     Each man wears his own cut-out head, and the kit colour rides a left bar
     (like the blockquote / value rules) instead of the type — yellow and the
     light blue never pass contrast as text. Heads bob off the same beat as the
     age ladder. (A richer "three windows" treatment is on hold until the real
     illustrations arrive — this is the clean text version to drop them into.) */
  .roster { width: 100%; max-width: 34rem; display: flex; flex-direction: column; gap: 1rem; }
  .who { display: flex; align-items: flex-start; gap: .85rem; border-left: 3px solid var(--accent); padding-left: 1rem; }
  .who.messi { --accent: var(--messi); }
  .who.yamal { --accent: var(--yamal); }
  .who.voz   { --accent: var(--voz);   }
  .who .headshot { flex: 0 0 auto; width: 56px; align-self: flex-start; margin-top: .1rem;
    transform-origin: 50% 90%; animation: bob 2.8s ease-in-out infinite; }
  .who.messi .headshot { animation-duration: 3.3s; animation-delay: -.9s; }
  .who.yamal .headshot { animation-duration: 2.4s; animation-delay: -1.7s; }
  .who-txt { display: flex; flex-direction: column; gap: .28rem; }
  .who-name { margin: 0; font-family: var(--font-mono); font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; font-weight: 700; color: var(--ink); }
  .who-name .age { color: var(--ink-3); font-weight: 400; letter-spacing: .04em; }
  .who p:not(.who-name) { margin: 0; font-size: 1.04rem; color: var(--ink); line-height: 1.5; }
  :root[data-lang="zh"] .who-name { font-family: var(--font-zh); letter-spacing: normal; text-transform: none; }
  :root[data-lang="zh"] .who p { font-family: var(--font-zh); }

  /* ── 2007 → 2026 · scrollytelling: five beats over one pinned stage ──
     Pudding-style. The stage pins (position:sticky) while opaque cards scroll
     up through it; an IntersectionObserver at the viewport midline marks who's
     active, and the stage crossfades to their photo: bath → held baby → grown
     Yamal → Vozinha the electrician → Vozinha the World Cup keeper. A trailing
     .s7-spacer lets the final card glide off while its photo is still pinned.
     Everything s7- prefixed so no class collides with the rest of the page. */
  .s7 { position: relative; }
  .s7-stage { position: sticky; top: 0; height: 100svh; overflow: hidden; z-index: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: flex-start; padding: 9svh 20px 0; }
  .s7-year { margin: 0 0 1.6svh; font-family: var(--font-mono); font-size: .72rem; letter-spacing: .34em; color: var(--ink-3); }
  .s7-frame { position: relative; width: clamp(212px, 46vw, 300px); aspect-ratio: 4 / 5;
    border-radius: 12px; overflow: hidden; box-shadow: 0 16px 36px rgba(18,39,30,.3); }
  /* one layer per photo; the active one fades up with a slow Ken-Burns push */
  .s7-photo { position: absolute; inset: 0; opacity: 0; transform: scale(1.06);
    background-size: cover; background-repeat: no-repeat; background-position: center;
    transition: opacity .7s ease, transform 5s ease; }
  .s7-photo.bath  { background-image: var(--img-bath);     background-position: 50% 44%; }
  .s7-photo.hold  { background-image: var(--img-hold);     background-position: 52% 38%; }
  .s7-photo.grown { background-image: var(--img-grown);    background-position: 50% 32%; }
  .s7-photo.voz   { background-image: var(--img-electric); background-position: 52% 28%; }
  .s7-photo.save  { background-image: var(--img-save);     background-position: 50% 42%; }
  .s7-stage[data-active="bath"]  .s7-photo.bath,
  .s7-stage[data-active="hold"]  .s7-photo.hold,
  .s7-stage[data-active="grown"] .s7-photo.grown,
  .s7-stage[data-active="voz"]   .s7-photo.voz,
  .s7-stage[data-active="save"]  .s7-photo.save { opacity: 1; transform: scale(1); }
  .s7-dots { display: flex; gap: 7px; margin-top: 1.6svh; }
  .s7-dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-3); opacity: .3; transition: opacity .3s, background .3s, transform .3s; }
  .s7-stage[data-active="bath"]  .s7-dots i:nth-child(1),
  .s7-stage[data-active="hold"]  .s7-dots i:nth-child(2),
  .s7-stage[data-active="grown"] .s7-dots i:nth-child(3),
  .s7-stage[data-active="voz"]   .s7-dots i:nth-child(4),
  .s7-stage[data-active="save"]  .s7-dots i:nth-child(5) { opacity: 1; transform: scale(1.3); }
  .s7-stage[data-active="bath"]  .s7-dots i:nth-child(1) { background: var(--messi); }
  .s7-stage[data-active="hold"]  .s7-dots i:nth-child(2) { background: var(--yamal); }
  .s7-stage[data-active="grown"] .s7-dots i:nth-child(3) { background: var(--yamal); }
  .s7-stage[data-active="voz"]   .s7-dots i:nth-child(4) { background: var(--voz); }
  .s7-stage[data-active="save"]  .s7-dots i:nth-child(5) { background: var(--voz); }

  .s7-steps { position: relative; z-index: 2; margin-top: -100svh; }
  .s7-step { min-height: 100svh; display: flex; align-items: flex-end; justify-content: center; padding: 0 20px 9svh; }
  /* Solid card (opaque, easy to read over the photo) — no fade; it simply
     scrolls up and off the top when its turn is over. */
  .s7-card { width: 100%; max-width: 30rem; background: var(--paper);
    border-radius: 16px; padding: 1.3rem 1.45rem; box-shadow: 0 16px 38px rgba(18,39,30,.26);
    border-left: 5px solid var(--accent); }
  .s7-step.bath  { --accent: var(--messi); }
  .s7-step.hold  { --accent: var(--yamal); }
  .s7-step.grown { --accent: var(--yamal); }
  .s7-step.voz   { --accent: var(--voz);   }
  .s7-step.save  { --accent: var(--voz);   }
  /* Trailing glide-room so the LAST card slides up over the still-pinned photo
     instead of un-pinning early and drifting off as a block (the "it just stops"
     feel). Not a .s7-step, so the observer never switches the stage to it. */
  .s7-spacer { min-height: 100svh; }
  .s7-card h3 { margin: 0 0 .5rem; font-family: var(--font-mono); font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
  .s7-card p { margin: 0; font-size: 1.08rem; line-height: 1.56; color: var(--ink); }
  :root[data-lang="zh"] .s7-card h3, :root[data-lang="zh"] .s7-card p { font-family: var(--font-zh); }

  @media (prefers-reduced-motion: reduce) {
    .s7-photo { transition: opacity .01ms; transform: none; }
  }

  /* ───────── the group chat (styled as a messaging window) ───────── */

  /* Sits right under the bath photo — the three of them reacting to it. Built to
     be screenshotted, so it reads as a self-contained chat app: a dark header
     with the members, coloured bubbles below. Kit colour + a small name tag say
     who's talking even when the shot is cropped out of the page. */
  .chatwin {
    width: 100%; max-width: 30rem; margin: 2.5rem auto 0;
    background: #EBEEEC; border: 1px solid rgba(18,39,30,.10);
    border-radius: 22px; overflow: hidden;
    box-shadow: 0 22px 50px rgba(18,39,30,.20);
  }
  .chatwin-head { display: flex; align-items: center; gap: .7rem; padding: .72rem .95rem;
    background: var(--ink); color: #fff; }
  .grpav { display: flex; flex: 0 0 auto; }
  .grpav i { width: 30px; height: 30px; border-radius: 50%; margin-left: -9px;
    background-repeat: no-repeat; background-size: 128% auto; background-position: center 88%;
    box-shadow: 0 0 0 2px var(--ink); }
  .grpav i:first-child { margin-left: 0; }
  .grpav .yamal { background-color: var(--yamal); }
  .grpav .messi { background-color: var(--messi); }
  .grpav .voz   { background-color: var(--voz);   }
  .grpmeta { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
  .grpname { font-weight: 700; font-size: .92rem; }
  .grpsub  { font-family: var(--font-mono); font-size: .62rem; letter-spacing: .04em; color: rgba(255,255,255,.6); }
  .chatwin-dots { margin-left: auto; color: rgba(255,255,255,.5); letter-spacing: .1em; }

  .chatwin-body { padding: 1.15rem 1rem 1.25rem; display: flex; flex-direction: column; gap: 1.05rem; }

  .say { display: flex; align-items: flex-start; gap: .6rem; }
  .say.flip { flex-direction: row-reverse; }
  .say .headshot {
    flex: 0 0 auto; width: clamp(40px, 9vw, 48px); display: block;
    transform-origin: 50% 90%;
    animation: bob 2.8s ease-in-out infinite;
  }
  .say.messi .headshot { animation-duration: 3.3s; animation-delay: -.9s; }
  .say.voz   .headshot { animation-duration: 2.4s; animation-delay: -1.7s; }

  .say-col { display: flex; flex-direction: column; gap: .22rem; max-width: 80%; }
  .say.flip .say-col { align-items: flex-end; }
  .say-name { font-family: var(--font-mono); font-size: .6rem; letter-spacing: .06em; color: var(--ink-3); padding: 0 .35rem; }

  .bub {
    position: relative; align-self: flex-start;
    background: var(--bub); color: var(--bub-ink);
    padding: .72rem 1rem; border-radius: 15px;
    font-size: 1.06rem; line-height: 1.5;
    box-shadow: 0 4px 12px rgba(18,39,30,.13);
  }
  .say.flip .bub { align-self: flex-end; }
  /* The global `strong` pins colour to --ink, which would go near-black on the
     crimson bubble. Inside a bubble, bold follows the bubble's own text. */
  .bub strong { color: inherit; }
  .bub em { box-shadow: none; text-decoration: underline; text-underline-offset: .18em; }

  .say .bub::before {
    content: ""; position: absolute; top: 13px; border: 7px solid transparent;
  }
  .say:not(.flip) .bub::before { left:  -13px; border-right-color: var(--bub); }
  .say.flip       .bub::before { right: -13px; border-left-color:  var(--bub); }

  .say.yamal { --bub: var(--yamal); --bub-ink: #fff; }
  .say.messi { --bub: var(--messi); --bub-ink: var(--ink); }
  .say.voz   { --bub: var(--voz);   --bub-ink: var(--ink); }

  .chat-foot {
    font-family: var(--font-mono); font-size: .62rem; color: var(--ink-3);
    line-height: 1.7; margin: .35rem .3rem 0;
  }

  /* You join in: right-aligned like "you" in a messenger, a dark bubble, and a
     hand-drawn "me" token instead of a face. */
  .say.me { --bub: var(--ink); --bub-ink: #fff; }
  .me-av {
    flex: 0 0 auto; width: clamp(40px, 9vw, 48px); height: clamp(40px, 9vw, 48px);
    border-radius: 50%; background: var(--ink); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-hand); font-weight: 700; font-size: 1.25rem; line-height: 1;
    box-shadow: 0 2px 8px rgba(18,39,30,.2);
  }
  /* ── one of them "types", then the reply lands ── */
  .say.typing .bub { display: inline-flex; gap: .3rem; align-items: center; padding: .92rem 1rem; }
  .say.typing .dot {
    width: .5rem; height: .5rem; border-radius: 50%;
    background: currentColor; opacity: .4;
    animation: typing-bounce 1.2s ease-in-out infinite;
  }
  .say.typing .dot:nth-child(2) { animation-delay: .16s; }
  .say.typing .dot:nth-child(3) { animation-delay: .32s; }
  @keyframes typing-bounce {
    0%, 55%, 100% { transform: translateY(0);    opacity: .35; }
    28%           { transform: translateY(-4px); opacity: .9;  }
  }
  /* NB: class is .sayin, not .pop — the page already has a .pop tooltip component
     (position:absolute; white; z-index) and reusing that name floated replies out. */
  .say.sayin { animation: say-in .34s cubic-bezier(.2,.7,.3,1) both; }
  @keyframes say-in { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) { .say.typing .dot, .say.sayin { animation: none; } }

  /* the message box at the foot of the window */
  .chatwin-compose {
    display: flex; gap: .5rem; align-items: center;
    padding: .7rem .8rem; background: #fff; border-top: 1px solid rgba(18,39,30,.10);
  }
  .chatwin-compose input {
    flex: 1 1 auto; min-width: 0; font: inherit; font-size: 1rem;
    border: 1px solid rgba(18,39,30,.16); border-radius: 999px;
    padding: .58rem .95rem; background: #F3F5F4; color: var(--ink);
  }
  .chatwin-compose input::placeholder { color: var(--ink-3); }
  .chatwin-compose input:focus { outline: none; border-color: var(--messi); box-shadow: 0 0 0 3px rgba(117,170,219,.35); }
  :root[data-lang="zh"] .chatwin-compose input { font-family: var(--font-zh); }
  .chatwin-compose button {
    flex: 0 0 auto; border: 0; cursor: pointer; border-radius: 999px;
    background: var(--ink); color: #fff; font: inherit; font-weight: 700; font-size: .95rem;
    padding: .58rem 1.1rem;
  }
  .chatwin-compose button:hover { background: #1c3d2d; }
  .chatwin-compose button:active { transform: translateY(1px); }



  /* ── the balance · drag players on, weight = market value ── */
  .scale-card { gap: 1.25rem; }
  .scale-stage { position: relative; width: 100%; max-width: 40rem; margin: 0 auto; overflow: visible; touch-action: pan-y pinch-zoom; }
  .scaleBox { position: absolute; top: 0; left: 0; width: 1740px; height: 1210px; transform-origin: top left; }
  .s-base, .s-beam, .s-panimg { -webkit-user-drag: none; user-select: none; pointer-events: none; }
  .s-base { position: absolute; left: 584px; top: 33px; width: 582px; filter: drop-shadow(0 14px 16px rgba(18,39,30,.16)); }
  .s-beam { position: absolute; left: 286px; top: 124px; width: 1184px; transform-origin: 584px 86px; will-change: transform; filter: drop-shadow(0 10px 10px rgba(18,39,30,.14)); }
  .s-pan { position: absolute; left: 0; top: 0; width: 632px; will-change: transform; }
  .s-panimg { width: 100%; display: block; filter: drop-shadow(0 8px 8px rgba(18,39,30,.14)); }
  .s-heap { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; }
  .s-load { position: absolute; left: 50%; top: 86%; transform: translate(-50%, -100%); width: 0; height: 0; }
  .s-load img { position: absolute; bottom: 0; transform: translateX(-50%); -webkit-user-drag: none; pointer-events: auto; cursor: grab; touch-action: pan-y pinch-zoom; }
  @keyframes s-drop { from { transform: translateX(-50%) translateY(-70px); opacity: 0; } to { transform: translateX(-50%) translateY(0); opacity: 1; } }
  .s-load img.drop { animation: s-drop .36s cubic-bezier(.25, 1.25, .5, 1) backwards; }
  @media (prefers-reduced-motion: reduce) { .s-load img.drop { animation: none; } }
  .s-tag { position: absolute; left: 0; top: 0; transform: translate(-50%, -100%); font-family: var(--font-hand); font-weight: 700;
    font-size: clamp(.8rem, 3.4vw, 1.05rem); color: var(--ink); background: var(--paper); border: 2.5px solid var(--ink);
    border-radius: 999px; padding: .18rem .6rem; white-space: nowrap; font-variant-numeric: tabular-nums;
    box-shadow: 0 3px 0 rgba(18,39,30,.14); pointer-events: none; opacity: 0; transition: opacity .18s; }
  .s-tag.on { opacity: 1; }

  .s-verdict { font-family: var(--font-hand); font-weight: 700; font-size: clamp(1.4rem, 5.4vw, 2.2rem); color: var(--ink);
    text-align: center; max-width: 30rem; margin: 0 auto; line-height: 1.3; min-height: 2.3em; text-wrap: balance; }
  :root[data-lang="zh"] .s-verdict { font-family: var(--font-zh); }
  .s-verdict em { font-style: normal; color: var(--yamal); font-weight: 900; }

  .s-bench { display: flex; gap: clamp(8px, 3vw, 20px); justify-content: center; align-items: flex-end; flex-wrap: wrap; }
  .s-chip { display: flex; flex-direction: column; align-items: center; gap: .28rem; cursor: grab; touch-action: pan-y pinch-zoom;
    appearance: none; border: 0; background: transparent; color: inherit; font: inherit;
    user-select: none; padding: 6px 8px; border-radius: 12px; transition: transform .12s ease, background .12s; }
  .s-chip:hover { background: rgba(18,39,30,.05); transform: translateY(-2px); }
  .s-chip:focus-visible { outline: 3px solid var(--yamal); outline-offset: 2px; background: rgba(18,39,30,.05); }
  .s-chip:active { cursor: grabbing; }
  .s-chip img { height: clamp(62px, 15vw, 96px); width: auto; display: block; -webkit-user-drag: none; pointer-events: none;
    filter: drop-shadow(0 6px 8px rgba(18,39,30,.22)); }
  .s-chip .s-val { font-family: var(--font-hand); font-weight: 700; font-size: 1rem; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1; }
  .s-ghost { position: fixed; left: 0; top: 0; z-index: 999; pointer-events: none; will-change: transform; opacity: .95;
    filter: drop-shadow(0 12px 12px rgba(18,39,30,.32)); }
  .s-ghost img { height: clamp(62px, 15vw, 96px); display: block; }

  .s-tools { display: flex; gap: 10px; justify-content: center; }
  .s-btn { font-family: var(--font-fig); font-weight: 800; font-size: .95rem; padding: .5rem 1.1rem; border-radius: 10px;
    border: 2px solid var(--ink); background: var(--paper); color: var(--ink); cursor: pointer; }
  .s-btn:active { transform: translateY(1px); }
  .s-btn.primary { background: var(--yamal); color: #fff; border-color: var(--yamal); }
  .s-btn.ghost { background: transparent; color: var(--ink-2); border-color: rgba(18,39,30,.2); }
  .s-hint { font-family: var(--font-mono); font-size: .68rem; letter-spacing: .04em; color: var(--ink-3); text-align: center; }

  /* ───────── the finale · three cards, and the rankings that disagree ───────── */

  .cards3 {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(10px, 3vw, 34px);
    width: 100%; max-width: 60rem; align-items: start;
  }
  .pcard { display: flex; flex-direction: column; align-items: center; text-align: center; min-width: 0; }
  /* Same order as everywhere else: Yamal · Messi · Vozinha (keeper on the right).
     Reordered visually so the huge honour lists don't have to be moved in markup. */
  .pcard.yamal { order: 1; } .pcard.messi { order: 2; } .pcard.voz { order: 3; }

  /* tidy circular head avatars — each face is cropped from the real cut-out and
     normalised to the same size + centred, so the three read as one clean row.
     The cut-out sits inside a round window; per-person offsets frame each face. */
  .pcard .figwrap { position: relative; width: clamp(98px, 13.5vw, 150px); height: clamp(98px, 13.5vw, 150px);
    max-width: none; border-radius: 50%; overflow: hidden; margin: 0 auto .15rem; }
  .pcard .cutout { position: absolute; width: 200%; height: auto; box-shadow: none; border-radius: 0; filter: none; }
  .pcard.voz   .figwrap { background: #FBF3C9; box-shadow: 0 0 0 4px var(--voz),   0 10px 22px rgba(18,39,30,.18); }
  .pcard.messi .figwrap { background: #E4EFF8; box-shadow: 0 0 0 4px var(--messi), 0 10px 22px rgba(18,39,30,.18); }
  .pcard.yamal .figwrap { background: #F7DEE8; box-shadow: 0 0 0 4px var(--yamal), 0 10px 22px rgba(18,39,30,.18); }
  .pcard.voz   .cutout { left: -85%; top: -67%; }
  .pcard.messi .cutout { left: -49%; top: -36%; }
  .pcard.yamal .cutout { left: -43%; top: -43%; }

  /* big signature-style name card, same look as the hero lineup */
  .pname { display: inline-block; font-family: var(--font-hand); font-weight: 700; line-height: 1;
    font-size: clamp(1.55rem, 3.6vw, 2.25rem); padding: .14em .56em .28em; border-radius: 9px; margin-top: -.3rem;
    box-shadow: 0 0 0 2px rgba(255,255,255,.8), 0 4px 12px rgba(18,39,30,.16); }
  .pname.voz   { background: var(--voz);   color: var(--ink); }
  .pname.messi { background: var(--messi); color: var(--ink); }
  .pname.yamal { background: var(--yamal); color: #fff; }
  .pname.you   { background: var(--ink);   color: #fff; }

  .page { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .1em; color: var(--ink-2); margin-top: .55rem; }
  /* full numbers, never €50k/€200M — the zeros are the point; condensed +
     tabular + nowrap keeps €200,000,000 on one line so lengths compare */
  .pval { font-family: var(--font-hand); font-weight: 700; letter-spacing: 0; line-height: .82;
    font-variant-numeric: tabular-nums; white-space: nowrap; font-size: clamp(1.3rem, 4.4vw, 2.5rem); margin: .12rem 0 .05rem; }
  .pcap { font-family: var(--font-mono); font-size: .5rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }

  /* the roster: one ruled line per honour — the length itself is the argument */
  .honors { list-style: none; margin: 0; padding: 0; width: 100%; text-align: left; }
  .honors li { padding: .42rem 0; border-top: 1px solid rgba(18,39,30,.12); }
  .honors li.grp { border-top: none; padding: .75rem 0 .15rem; font-family: var(--font-mono); font-size: .56rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
  .ics { display: flex; flex-wrap: wrap; gap: 2px; margin-bottom: 4px; }
  .ic { width: 20px; height: 20px; flex: 0 0 auto; filter: drop-shadow(0 1px 1px rgba(18,39,30,.30)); }
  .ics.sub .ic { opacity: .32; filter: grayscale(.8); }
  .honors .h { font-size: .86rem; font-weight: 700; line-height: 1.24; color: var(--ink); overflow-wrap: anywhere; }
  .honors .h b { font-weight: 800; }
  .honors .res { font-weight: 800; }
  .honors .res.win { color: #1a7d34; }
  .honors .res.lose { color: var(--ink-3); }
  .honors .yr { font-family: var(--font-mono); font-size: .56rem; letter-spacing: .01em; color: var(--ink-3); line-height: 1.4; overflow-wrap: anywhere; margin-top: 2px; }
  .ptot { width: 100%; text-align: left; margin-top: .55rem; padding-top: .5rem; border-top: 2px solid var(--ink);
    font-weight: 800; font-size: .95rem; line-height: 1.3; }
  .ptot .n { font-family: var(--font-hand); font-weight: 700; font-size: 1.4em; }
  .ptot .sub { display: block; font-family: var(--font-mono); font-weight: 400; font-size: .58rem; color: var(--ink-3); letter-spacing: .04em; margin-top: .18rem; line-height: 1.4; }

  /* honours live in a soft card tinted with each man's colour — a trophy cabinet
     you can screenshot; the cut-out person stays unframed, floating above it */
  .cabinet { width: 100%; border-radius: 16px; margin-top: .75rem;
    padding: clamp(.7rem, 2.2vw, 1rem) clamp(.6rem, 2vw, .9rem) clamp(.8rem, 2.4vw, 1.05rem);
    box-shadow: 0 0 0 2px var(--cab-line), 0 10px 26px rgba(18,39,30,.10); }
  .pcard.voz   .cabinet { background: #FBF3C9; --cab-line: var(--voz); }
  .pcard.messi .cabinet { background: #E4EFF8; --cab-line: var(--messi); }
  .pcard.yamal .cabinet { background: #F7DEE8; --cab-line: var(--yamal); }
  .cabtitle { font-family: var(--font-hand); font-weight: 700; font-size: 1.35rem; line-height: 1;
    text-align: center; color: var(--ink); margin-bottom: .5rem; }
  .cabinet .honors li:first-child { border-top: none; padding-top: 0; }
  /* Vozinha's card is deliberately tiny — one confident line does the talking */
  /* Left-aligned like every list in the three cabinets, and in the plain body
     face — the citation is dense information, not a headline flourish (the zh
     brush hand in particular was hard to parse here). */
  .onefer { font-weight: 800; font-size: clamp(1rem, 2.2vw, 1.15rem); line-height: 1.4;
    text-align: left; color: var(--ink); margin: .1rem 0 .7rem; text-wrap: pretty; }

  /* the finale as a mock awards ceremony — a witty handwritten host (hand / Kaiti)
     narrating between three deadpan-formal ranking cards you can screenshot */
  .awards { width: 100%; max-width: 38rem; margin: 0 auto; }
  .host { font-family: var(--font-hand); font-weight: 700; color: var(--ink);
    font-size: clamp(1.3rem, 3vw, 1.75rem); line-height: 1.32; text-align: center; margin: 0 auto .35rem; text-wrap: balance; }
  :root[data-lang="zh"] .host { font-family: "Ma Shan Zheng", var(--font-zh); font-weight: 800; }
  .host.lead2 { margin-bottom: 1.5rem; }
  .host.punch { font-size: clamp(1.45rem, 3.6vw, 2rem); margin-top: 1.6rem; }
  .award { background: #fff; border-radius: 16px; margin-bottom: 1rem;
    padding: clamp(.85rem,2.4vw,1.25rem) clamp(1rem,2.8vw,1.5rem) clamp(.7rem,2vw,1rem);
    box-shadow: 0 8px 30px rgba(18,39,30,.12); }
  .ahead { display: flex; align-items: baseline; gap: .55rem; border-bottom: 2px solid var(--ink); padding-bottom: .45rem; }
  .anum { font-family: var(--font-fig); font-weight: 900; font-size: 1.35rem; color: var(--ink); line-height: 1; }
  .acat { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-2); font-weight: 600; }
  .host.aside { font-family: var(--font-hand); font-size: clamp(1.1rem, 2.4vw, 1.4rem); color: var(--ink-2);
    text-align: left; margin: .55rem 0 .35rem; }
  :root[data-lang="zh"] .host.aside { font-family: "Ma Shan Zheng", var(--font-zh); }
  .podium { list-style: none; margin: 0; padding: 0; }
  .podium li { display: grid; grid-template-columns: 1.6rem 42px 1fr auto; align-items: center; gap: .7rem;
    padding: .5rem .35rem; border-top: 1px solid rgba(18,39,30,.10); }
  .podium li:first-child { border-top: none; }
  .podium li.p1 { background: #FBF6E6; border-radius: 10px; }
  .podium .rk { font-family: var(--font-fig); font-weight: 900; font-size: 1.15rem; text-align: center; }
  .podium .p1 .rk { color: #C8912A; } .podium .p2 .rk { color: #98A2AA; } .podium .p3 .rk { color: #B67A43; }
  .mini-av { width: 42px; height: 42px; border-radius: 50%; background-repeat: no-repeat; background-size: 200% auto; }
  .mini-av.voz   { background-image: url("/yamal-messi-vozinha/web/vozinha_hero.webp"); background-position: 85% 77%; background-color: #FBF3C9; box-shadow: 0 0 0 2px var(--voz); }
  .mini-av.messi { background-image: url("/yamal-messi-vozinha/web/messi_hero.webp");   background-position: 49% 41%; background-color: #E4EFF8; box-shadow: 0 0 0 2px var(--messi); }
  .mini-av.yamal { background-image: url("/yamal-messi-vozinha/web/yamal_hero.webp");   background-position: 43% 49%; background-color: #F7DEE8; box-shadow: 0 0 0 2px var(--yamal); }
  .podium .nm { font-weight: 800; font-size: 1rem; color: var(--ink); }
  .podium .p1 .nm { font-size: 1.12rem; }
  .podium .vv { font-family: var(--font-fig); font-weight: 900; font-variant-numeric: tabular-nums; letter-spacing: -.02em;
    font-size: clamp(1rem, 2.6vw, 1.35rem); color: var(--ink); white-space: nowrap; }
  .podium .vv .u { font-family: var(--font-mono); font-weight: 400; font-size: .56rem; color: var(--ink-3); letter-spacing: .03em; }
  @media (max-width: 460px) {
    .cards3 { gap: 8px; }
    .pcard .figwrap { width: 88px; height: 88px; max-width: none; box-shadow: 0 0 0 3px var(--cab-line, #ccc), 0 6px 14px rgba(18,39,30,.18); }
    .pcard.voz   .figwrap { --cab-line: var(--voz); }
    .pcard.messi .figwrap { --cab-line: var(--messi); }
    .pcard.yamal .figwrap { --cab-line: var(--yamal); }
    .pname { font-size: clamp(1.05rem, 4.8vw, 1.45rem); margin-top: -.2rem; }
    .page { font-size: .52rem; letter-spacing: .04em; margin-top: .4rem; }
    .pval { font-size: clamp(.85rem, 4.4vw, 1.2rem); }
    .pcap { font-size: .4rem; letter-spacing: .02em; }
    .cabinet { border-radius: 11px; margin-top: .5rem; padding: .5rem .42rem .58rem; box-shadow: 0 0 0 1.5px var(--cab-line), 0 6px 16px rgba(18,39,30,.10); }
    .cabtitle { font-size: .92rem; margin-bottom: .32rem; }
    .onefer { font-size: .72rem; line-height: 1.3; margin: .05rem 0 .45rem; }
    .honors li { padding: .22rem 0; }
    .honors .h { font-size: .62rem; line-height: 1.2; }
    .honors .yr { font-size: .46rem; line-height: 1.32; }
    .honors li.grp { font-size: .5rem; letter-spacing: .1em; }
    .ic { width: 14px; height: 14px; }
    .ics { gap: 1.5px; }
    .ptot { font-size: .66rem; }
    .ptot .sub { font-size: .46rem; }
    .host { font-size: 1.18rem; }
    .host.punch { font-size: 1.3rem; }
    .award { padding: .8rem .8rem .65rem; border-radius: 13px; margin-bottom: .8rem; }
    .anum { font-size: 1.1rem; }
    .acat { font-size: .6rem; letter-spacing: .08em; }
    .host.aside { font-size: 1rem; margin: .45rem 0 .3rem; }
    .podium li { grid-template-columns: 1.2rem 32px 1fr auto; gap: .5rem; padding: .42rem .25rem; }
    .mini-av { width: 32px; height: 32px; }
    .podium .rk { font-size: .95rem; }
    .podium .nm, .podium .p1 .nm { font-size: .82rem; }
    .podium .vv { font-size: .92rem; }
  }

  footer { position: relative; z-index: 1; padding: 3rem 22px 5rem; display: flex; justify-content: center; }
  footer .col { gap: .7rem; }

  /* A few balls resting on the pitch at the very foot of the page. Decorative:
     they sit in the side margins and along the bottom edge, clear of the
     centred sources text, and bob gently on their own beats. */
  /* Overflow stays visible so a kicked ball can leap ABOVE the strip; the
     container itself ignores the pointer, but each ball takes it back so only
     the balls are clickable. */
  .pitch-balls {
    position: relative; z-index: 0; width: 100%;
    height: clamp(140px, 22vw, 210px); margin-top: clamp(-116px, -11vw, -58px);
    pointer-events: none;
  }
  .pitch-balls .ball {
    position: absolute; bottom: var(--b, 3%); left: var(--x);
    width: var(--s); height: var(--s);
    background: var(--img-ball) center / contain no-repeat;
    rotate: var(--r, 0deg);
    filter: drop-shadow(0 5px 5px rgba(18,39,30,.16));
    animation: ballbob var(--t, 6s) ease-in-out var(--delay, 0s) infinite;
    pointer-events: auto; cursor: pointer; -webkit-tap-highlight-color: transparent;
  }
  /* Click = a proper kick: leaps up, spins a full turn, squashes on landing,
     a small second hop, then settles back on its own tilt. translate/scale/
     rotate are independent properties, so this rides over the idle bob cleanly. */
  .pitch-balls .ball.pop { animation: ballpop .72s cubic-bezier(.28,.7,.32,1) both; }
  @keyframes ballbob { 0%, 100% { translate: 0 0; } 50% { translate: 0 -9px; } }
  @keyframes ballpop {
    /* the shadow offset grows as the ball rises so it reads as staying on the
       ground, and tightens hard on each landing for the impact. */
    0%   { translate: 0 0;     scale: 1 1;       rotate: var(--r, 0deg);               filter: drop-shadow(0 5px 5px rgba(18,39,30,.16)); }
    28%  { translate: 0 -54px; scale: .90 1.12;  rotate: calc(var(--r, 0deg) + 128deg); filter: drop-shadow(0 54px 13px rgba(18,39,30,.11)); }
    52%  { translate: 0 1px;   scale: 1.17 .83;  rotate: calc(var(--r, 0deg) + 300deg); filter: drop-shadow(0 4px 4px rgba(18,39,30,.22)); }
    70%  { translate: 0 -17px; scale: .98 1.04;  rotate: calc(var(--r, 0deg) + 336deg); filter: drop-shadow(0 20px 8px rgba(18,39,30,.14)); }
    86%  { translate: 0 1px;   scale: 1.06 .94;  rotate: calc(var(--r, 0deg) + 354deg); filter: drop-shadow(0 4px 4px rgba(18,39,30,.2)); }
    100% { translate: 0 0;     scale: 1 1;       rotate: calc(var(--r, 0deg) + 360deg); filter: drop-shadow(0 5px 5px rgba(18,39,30,.16)); }
  }
  @media (prefers-reduced-motion: reduce) { .pitch-balls .ball { animation: none; } }

  @media (prefers-reduced-motion: reduce) {
    * { transition-duration: .01ms !important; animation: none !important; }
    .r { opacity: 1; transform: none; }
  }

  /* ── share poster · take the verdict with you ──────────────────────────── */
  .share-card { text-align: center; }
  .share-stage { display: flex; justify-content: center; margin: 1.5rem 0 1.3rem; }
  #posterCanvas {
    width: min(342px, 84vw); height: auto; display: block; border-radius: 16px;
    box-shadow: 0 20px 55px rgba(18,39,30,.30); background: #F1F8FB;
  }
  .share-tools { display: flex; gap: .7rem; justify-content: center; flex-wrap: wrap; }
  .share-btn {
    font: inherit; font-weight: 700; letter-spacing: .01em; border: 0; cursor: pointer;
    border-radius: 999px; padding: .82rem 1.6rem; line-height: 1;
    transition: transform .08s ease, box-shadow .15s ease;
  }
  .share-btn.primary { background: var(--ink); color: #fff; box-shadow: 0 8px 20px rgba(18,39,30,.24); }
  /* Equal weight, not equal colour: the two buttons are parallel share
     channels (image vs link), so the link button is a solid white twin of
     the ink primary — same mass, complementary fill — not a thin outline
     that reads as an afterthought. */
  .share-btn.ghost { background: var(--paper); color: var(--ink); box-shadow: 0 8px 20px rgba(18,39,30,.18); }
  .share-btn:hover { transform: translateY(-1px); }
  .share-btn:active { transform: translateY(1px); }
  .share-btn:focus-visible { outline: 2px solid var(--yamal); outline-offset: 3px; }
  .share-btn[hidden] { display: none; }
  .share-btn .cp-done { display: none; }
  .share-btn.copied .cp-idle { display: none; }
  .share-btn.copied .cp-done { display: inline; }
  .share-hint { text-align: center; color: var(--ink3); font-size: .88rem; margin: .9rem auto 0; max-width: 30rem; }
  :root[data-lang="zh"] .share-btn,
  :root[data-lang="zh"] .share-hint { font-family: var(--font-zh); }

  /* ───────── zh · a legibility floor for the small labels ─────────
     The mono-label sizes are tuned for spaced UPPERCASE Latin; 汉字 carry far
     more strokes per em and turn to grey mud at .5–.78rem. Every small label
     gets a zh floor here: family swapped to the zh sans, size raised ~25–40%,
     tracking eased (CJK never letterspaces well). Latin pages are untouched. */
  :root[data-lang="zh"] .eyebrow {
    font-family: var(--font-zh); font-size: clamp(.9rem, 2.2vw, 1.05rem);
    font-weight: 600; letter-spacing: .14em;
  }
  :root[data-lang="zh"] .ch-kick   { font-family: var(--font-zh); font-size: .95rem; letter-spacing: .12em; }
  :root[data-lang="zh"] .win-kick  { font-family: var(--font-zh); font-size: .88rem; letter-spacing: .08em; }
  :root[data-lang="zh"] .stat .unit { font-size: 1.02rem; }
  :root[data-lang="zh"] .src        { font-family: var(--font-zh); font-size: .82rem; }
  :root[data-lang="zh"] figcaption  { font-family: var(--font-zh); font-size: .82rem; }
  :root[data-lang="zh"] .grpsub     { font-family: var(--font-zh); font-size: .78rem; }
  :root[data-lang="zh"] .say-name   { font-family: var(--font-zh); font-size: .78rem; }
  :root[data-lang="zh"] .chat-foot  { font-family: var(--font-zh); font-size: .8rem; }
  :root[data-lang="zh"] .s7-card h3 { font-family: var(--font-zh); font-size: .95rem; letter-spacing: .06em; }
  :root[data-lang="zh"] .acat       { font-family: var(--font-zh); font-size: .92rem; letter-spacing: .08em; }
  :root[data-lang="zh"] .page       { font-family: var(--font-zh); font-size: .85rem; letter-spacing: .05em; }
  :root[data-lang="zh"] .pcap       { font-family: var(--font-zh); font-size: .72rem; letter-spacing: .05em; }
  :root[data-lang="zh"] .honors li.grp { font-family: var(--font-zh); font-size: .75rem; letter-spacing: .08em; }
  :root[data-lang="zh"] .honors .yr { font-family: var(--font-zh); font-size: .74rem; }
  :root[data-lang="zh"] .ptot .sub  { font-family: var(--font-zh); font-size: .74rem; }
  :root[data-lang="zh"] .s-hint     { font-family: var(--font-zh); font-size: .82rem; }
  :root[data-lang="zh"] .podium .vv .u { font-family: var(--font-zh); font-size: .74rem; }
  :root[data-lang="zh"] .fb-name    { font-size: clamp(.88rem, 2.6vw, 1.05rem); }
  :root[data-lang="zh"] .spk-messi  { font-size: clamp(.8rem, 2.1vw, 1rem); }
  @media (max-width: 460px) {
    /* The three-across finale cards shrink everything hard; give 汉字 the same
       relative bump there so the honour lists stay readable on a phone. */
    :root[data-lang="zh"] .page       { font-size: .62rem; }
    :root[data-lang="zh"] .pcap       { font-size: .5rem; }
    :root[data-lang="zh"] .cabtitle   { font-size: 1rem; }
    :root[data-lang="zh"] .onefer     { font-size: .78rem; }
    :root[data-lang="zh"] .honors .h  { font-size: .7rem; }
    :root[data-lang="zh"] .honors .yr { font-size: .56rem; }
    :root[data-lang="zh"] .honors li.grp { font-size: .58rem; }
    :root[data-lang="zh"] .ptot       { font-size: .72rem; }
    :root[data-lang="zh"] .ptot .sub  { font-size: .56rem; }
    :root[data-lang="zh"] .acat       { font-size: .72rem; }
    :root[data-lang="zh"] .podium .nm, :root[data-lang="zh"] .podium .p1 .nm { font-size: .88rem; }
  }
