/* ============================================================================
   AKAM — MEDIA QUERIES (single consolidated home for ALL @media breakpoints)
   ----------------------------------------------------------------------------
   Consolidated here 2026-05-30. Every @media rule in the app lives in THIS file
   (previously they were split across styles.css and v2/index.html's inline
   <style>). Edit breakpoints here, one at a time.

   LOAD ORDER IS LOAD-BEARING. This sheet is <link>ed in v2/index.html *after*
   the inline <style> block, so it is the LAST author stylesheet — its rules win
   at equal specificity over the base rules in styles.css, curriculum/styles.css,
   AND the inline <style>. That is required: the compact + chrome-step blocks
   must override base `.ak-card` / `.v2-nav-btn` / `.chrome` / `.stage` rules.
   ⟹ Keep this <link> last in the document, and DON'T re-add @media rules to the
     other files. (The non-@media `body.is-fullscreen …` overrides stay inline —
     they're class-driven, not breakpoints.)

   ORDER WITHIN THIS FILE also matters where two queries can match at once:
     • the chrome steps (768→660) are nested — narrower must come later so it
       overrides the wider one's shared `.chrome` props;
     • the phone-landscape COMPACT block must come BEFORE the chrome steps, so at
       a narrow-AND-short viewport the chrome steps still win `.chrome` padding
       (preserves the prior behaviour). Don't reorder these.

   SMALLEST BREAKPOINT (2026-05-30): the canonical smallest target is the
   BlackBerry Z30 in landscape = 640w × 360h. That is matched by the
   `@(landscape) and (max-height:600)` COMPACT block (§2) — NOT by a max-width
   rule (the viewport is 640px WIDE). The old sub-640 width tiers (max-width
   400/440/520) never fired on a real landscape phone and were DELETED; their
   still-useful rules (bigger picker emoji/text, uniform small chrome buttons)
   were folded into §2. Anything for "smaller than the Z30" goes in §2.

   CONTENTS
     1. WIDTH — picker card relaxation        @max-width:900
     2. SMALLEST — phone-landscape compact    @(landscape) and (max-height:600)
     2b. TALL-LANDSCAPE — q/quiz/letters fill @(landscape)(min-height:440)(max-height:600)
     2c. SHORTEST-LANDSCAPE — q-card+trace fit @(landscape)(max-height:439)
     2d. TABLET/DESKTOP — centre + scale sizes @(landscape)(min-height:601)
     2f. TABLET/DESKTOP — picker fill + scale  @(landscape)(min-height:601)
     2g. TABLET/DESKTOP — chrome bar scale     @(landscape)(min-height:601)
     3. WIDTH — chrome overflow steps         @max-width:768 / 560
     4. AXIS  — landscape lock (touch)        @(portrait) and (pointer:coarse)
     5. AXIS  — TV overscan safe-area         @(min-width:1600) and (min-height:800)
     6. A11Y  — reduced motion                @(prefers-reduced-motion:reduce)

   See the RESPONSIVE MODEL legend at the top of styles.css for the philosophy
   (let layout flow via auto-fit / scale-to-fit; discrete @media only where a
   layout can't flow — chrome — or for device axes — orientation/height).
   ============================================================================ */


/* ── 1. WIDTH — picker CARD relaxation (NOT scroll, NOT columns) ─────────────
   Column counts are auto-fit (no breakpoint); scrolling is the base .intro
   scroll layer. This only relaxes the CARD when it gets wide (few columns):
   let it grow vertically + wrap long module banners to two lines. ~900px is
   roughly where "SIZES AND COLORS" needs the banner to wrap. */
@media (max-width: 900px) {
  .intro .module-card {
    overflow: visible;
    min-height: auto;
  }
  /* Banner wraps to TWO LINES (number on line 1, title on line 2); the em-dash
     separator hides (the line break replaces it). */
  .module-banner {
    flex-wrap: wrap;
    line-height: 1.2;
    row-gap: 1px;
  }
  .module-banner > .module-sep { display: none; }
  .module-banner > .module-title {
    flex-basis: 100%;          /* push title onto its own row */
    flex-shrink: 1;            /* still ellipsise if a title is absurdly long */
  }
  /* Activity header keeps its inline layout — its titles are short. */
}


/* ── 2. SMALLEST — phone-LANDSCAPE compact density (BlackBerry Z30 = 640×360) ─
   THE canonical smallest breakpoint. A phone held sideways is wide but very
   short (~360–430px tall). Reclaim vertical space by shrinking chrome + stage
   insets + the BACK/NEXT bar, AND size the lesson card to the short viewport:
   exercise cards flex-grow to fill, content scales via a HEIGHT-driven --ak-img,
   and the tall composite cards (question/paired/tracing/connect) shrink their
   tokens to fit. Tablets (>600px tall) are unaffected. (Known-deferred: at
   ~400px the tall 2-section cards can still spill onto HEAR — needs a
   height-aware fit; see MEMORY.) MUST stay BEFORE the chrome steps below
   (shared .chrome props). Everything for "smaller than the Z30" lives HERE —
   the old sub-640 max-width tiers were deleted 2026-05-30. */
@media (orientation: landscape) and (max-height: 600px) {
  :root { --ak-stage-x: 14px; }   /* smaller side inset; chrome + stage both follow */
  .chrome { padding: 6px var(--ak-shell-pad); gap: 10px; }
  /* Chrome buttons — uniform compact size for ALL controls (named icons AND the
     language/filter pills), so the row reads evenly on the short phone. Folded in
     from the deleted max-width:440 tier. The pills are scoped under `.chrome`
     (0,2,0) so they BEAT the §3 max-width:768 step's `.v2-lang-btn` (0,1,0),
     which also matches at 640w and would otherwise force the pills back to 44px
     (uneven row). padding:0 centres the icon now that the label is hidden. */
  .v2-nav-btn { width: 34px; height: 34px; }
  .chrome .v2-lang-btn, .chrome .v2-filter-pill { width: 34px; height: 34px; padding: 0; }
  .stage { top: 50px; left: var(--ak-stage-x); right: var(--ak-stage-x); }
  /* `top:50px` already clears the compact chrome (~46px), so trim the inherited
     padding-top (was double-clearing) but keep a comfortable card↔chrome gap.
     padding-bottom stays — it keeps cards above the fixed NEXT bar. */
  .stage { padding-top: clamp(24px, 5vh, 34px); }
  /* Smaller NEXT/BACK on the short landscape phone so they clear the lesson
     card's bottom-right corner instead of overlapping it (the base 78px button
     overlapped the splash card by ~9px at 640×360). Card is untouched.
     MUST use `.stage >` (0,2,0) — a plain `.next-btn` (0,1,0) loses to the
     base `.stage > .next-btn` compact rule in styles.css. */
  .stage > .next-btn, .stage > .back-btn {
    height: 34px; padding: 0 12px; font-size: 12px; gap: 7px; bottom: 8px;   /* −15% (2026-05-30) */
  }
  /* min-height:0 lets cards YIELD (base min-height:clamp(360px,…) overflows a
     short viewport). Selector MUST be `.stage > .ak-card` (0,2,0) to beat the
     base rule — a plain `.ak-card` (0,1,0) silently loses.
     padding 12 (was 2.4vw≈15.36) + gap 12 (was --ak-row-gap≈16) tighten the
     space around the card edge AND between rows (locator→title→tiles, and the
     bottom padding under the tiles) on small screens. (2026-05-30 user req.) */
  .stage > .ak-card { padding: 12px; min-height: 0; gap: 12px; }
  /* Bigger lesson CONTENT + let the title-less EXERCISE card grow into the free
     space (pushing HEAR down). Base --ak-img is WIDTH-driven (12vw) → tiny
     emojis on a short-but-wide phone; attachFit only scales DOWN by width. Drive
     the tokens off viewport HEIGHT here so content grows as the screen gets
     taller. (Splash/vocab keep hugging — only `.ak-card-notitle` cards grow.) */
  /* --ak-img reduced 10% (96→86 / 28vh→25vh / 200→180) on small screens
     (≤640 landscape) 2026-05-30 — tall emoji (snail/cucumber/pear) were
     underflowing the splash card's bottom edge. Applies windowed + fullscreen. */
  .stage { --ak-img: clamp(86px, 25vh, 180px); --ak-pill-font: clamp(18px, 4.2vh, 32px); --ak-gap: clamp(16px, 4vh, 44px); }
  .stage > .ak-card.ak-card-notitle { flex: 1 1 auto; }
  /* Splash/vocab cards (NO HEAR button, so they don't flex-grow like the
     exercise cards above) were floating high with a big ~43px empty gap above
     the fixed NEXT button on the short phone. Let them grow into that band too
     — same proven `flex:1 1 auto` mechanism, scoped to the non-notitle cards.
     Doesn't touch the shared stage bottom padding (which HEAR needs on
     present/quiz/tracing). (2026-05-30 user req — shrink card→NEXT gap.) */
  .stage > .ak-card:not(.ak-card-notitle) { flex: 1 1 auto; }
  /* CONNECT card: 4 fixed pill+emoji rows, no attachFit scaling, its own
     min-height rule (0,3,0). Needs matching-specificity min-height:0 + shrunk
     row tokens so all 4 rows fit a ~400px-tall phone. */
  .stage > .ak-card.ak-connect-card { min-height: 0; }
  /* Bigger connect emojis (2026-05-30 user req): the prior clamp(34,6vw,50)
     left ~34px of empty space above+below the 4 rows on the short phone, so the
     emojis looked small. Height-driven bump (like the other compact fixes) — the
     4 rows still fit (verified). The connector SVG layer is positioned relative
     to the grid, so it tracks the new cell size automatically. */
  .ak-connect-card { --ak-img: clamp(44px, 13vh, 80px); gap: clamp(6px, 1.2vw, 14px); }   /* 2026-05-31: 11vh/68→13vh/80 — slightly bigger cells at 481 (~62px); floor 44 (was 48) gives the flex-filled 4 rows 2px headroom so they don't clip the 360 end */
  .ak-connect-card .ak-card-title { font-size: clamp(16px, 2vw, 24px); padding-bottom: clamp(3px, 0.6vw, 6px); }
  /* row-gap bumped (2026-05-30 user req): the freed vertical headroom lets the 4
     connect rows spread to fill the card top/bottom instead of bunching. */
  /* Fill + SPREAD (2026-05-31 user req): make the grid take the card height and
     distribute the 4 rows evenly so they absorb the top/bottom clearance instead
     of floating centred with dead space. (Pairs with the bigger --ak-img above.) */
  .ak-connect-grid {
    row-gap: clamp(10px, 2.4vh, 22px); column-gap: clamp(40px, 10vw, 120px);
    flex: 1 1 auto; align-content: space-evenly;
  }
  /* Tall COMPOSITE cards (question/paired = 2 grids + reveal; tracing = letters
     + picture) are ~360px tall and spilled their lower section onto HEAR on a
     ≤~500px landscape. Shrink their own --ak-img so both sections fit. */
  .stage > .ak-card.ak-question-card { --ak-img: clamp(40px, 5.4vw, 72px); --ak-gap: clamp(10px, 2vw, 24px); }
  .stage > .ak-card.ak-question-card .ak-paired-divider,
  .stage > .ak-card.ak-question-card .ak-answer-reveal { margin-top: clamp(2px, 0.6vh, 6px); margin-bottom: clamp(2px, 0.6vh, 6px); }
  .stage > .ak-card.ak-trace-card    { --ak-img: clamp(40px, 5.6vw, 76px); }
  /* FILL-QUIZ card (drag-a-word into blanks) on the short phone (2026-05-30 user
     req): pictogram row + tall blank-slot row (each blank cell reserves
     min-height 0.75×--ak-img) + the drag pill stacked too tall for the ~212px
     card, so the pictograms ate the top half and the blanks + pill crammed the
     bottom edge. Same fix as the question/trace cards: give the quiz card its own
     smaller --ak-img — shrinks BOTH the pictograms AND the 0.75× blank-row
     min-height together — so all three rows breathe. Scoped via :has(.ak-pill-drag),
     unique to the drag-to-fill quiz (present/exercise cards have no drag pill;
     match uses .ak-match-pill). Also top-align so the rows pack from the top
     instead of floating centred. */
  .stage > .ak-card.ak-card-notitle:has(.ak-pill-drag) { --ak-img: clamp(50px, 15vh, 80px); gap: 10px; justify-content: flex-start; }
  /* LETTERS card (drag-a-letter into the word blank) on the short phone
     (2026-05-30 user req). It also matches :has(.ak-pill-drag) above, so it
     inherited that flex-start (content bunched at the top, big empty void below,
     with the tiny tray letter floating mid-card). This card has its OWN layout
     needs, so override AFTER (later source order wins at equal specificity):
     - HEIGHT-driven pictograms (base clamp(28px,3.6vw,52px) is vw-floored to 28px
       on a narrow phone — same vw-floor trap as the other compact emoji fixes).
     - bigger draggable tray letter (clear touch target; was vw-floored too).
     - space-between: the word+pictogram puzzle pins to the TOP, the letter "bank"
       drops to the BOTTOM, dead space spreads evenly between — reads as
       puzzle-above / drag-bank-below instead of everything top-bunched. */
  /* NOTE specificity: the :has(.ak-pill-drag) rule above is (0,4,0) — it must be
     matched, not just followed, or its justify-content:flex-start wins. So this
     card rule carries all three of its classes (.ak-card.ak-card-notitle
     .ak-letter-card = (0,4,0)) and sits LATER → wins. */
  /* CENTER the whole cluster (word+pictogram lineup ABOVE the draggable letter
     bank) as one centred block (2026-05-30 user req): space-between pushed the
     letter to the very bottom, leaving too big a vertical gap from the lineup.
     Centring keeps them close (just the card gap + tray margin between them),
     splitting the leftover space top/bottom instead of dumping it all in the
     middle. Tray margin trimmed so the letter sits nearer the lineup. */
  /* justify-content:flex-start (not center) + a real top padding so the lineup is
     pushed DOWN from the card's top edge (2026-05-30 user req) and the cluster
     flows top→down (grid, then tray) without centring shoving it back over the
     bottom edge. Padding sized to push down meaningfully yet keep the big tray
     letter inside the ~212px card (no overflow onto HEAR). */
  .stage > .ak-card.ak-card-notitle.ak-letter-card { justify-content: flex-start; gap: 2px; padding-top: clamp(30px, 8vh, 44px); }
  /* Zero BOTH the tray margin AND its inherited padding-top (the base .ak-tray
     carries ~8px top padding) — that padding was the remaining visual gap between
     the pictogram row and the draggable letter. (2026-05-30 user req.) */
  .stage > .ak-card.ak-letter-card .ak-letter-tray { margin-top: 0; padding-top: 0; }
  /* Tighten the word↔pictogram row-gap on the short phone (base clamp(22,3vw,44)
     = 22px here) so the taller pictograms still read as one tight lineup. */
  .stage > .ak-card.ak-letter-card .ak-letter-grid { row-gap: clamp(8px, 2vh, 16px); }
  .stage > .ak-card.ak-letter-card .ak-letter-emoji { width: clamp(40px, 11vh, 60px); height: clamp(40px, 11vh, 60px); }
  .stage > .ak-card.ak-letter-card .ak-letter-pill {
    min-width: clamp(60px, 16vh, 84px); min-height: clamp(60px, 16vh, 84px);
    font-size: clamp(54px, 13vh, 76px);
  }

  /* Bottom band tightening on the short phone (2026-05-30 user req): the
     present/quiz card was clipping its word-pill row at the card's bottom edge
     because the stage reserved a tall 112px bottom band for HEAR + the segment
     bar. Shrink that band so the flex-grow card gets ~28px more height (pills
     clear), and make HEAR + the segment bar smaller and pushed lower.
     - stage padding-bottom 112 → 84 gives the card the height back
     - HEAR: smaller icon/padding, sits just under the taller card
     - segment bar: thinner (8px), narrower, lower (bottom 10) */
  .stage { padding-bottom: 84px; }
  /* HEAR centred in the band between the card's lower edge and the segment bar
     (2026-05-30 user req): band ≈60px, HEAR ≈36px → ~12px margin each side. */
  .stage .hear-btn { margin-top: 10px; padding: 2px 12px; gap: 8px; }   /* HEAR −15% (2026-05-30) */
  .stage .hear-btn .hear-btn-pill { font-size: 14px; }
  .stage .hear-btn .hear-btn-icon { width: 20px; height: 20px; }
  #ak-segment-bar { height: 7px; bottom: 10px; width: min(46vw, 320px); gap: 3px; }   /* bar −15% */
  #ak-segment-bar .seg-cell { border-radius: 4px; border-width: 1.5px; }
  /* FULLSCREEN on a short phone must match the compact stage, not the global
     fullscreen scale-up: that global rule (body.is-fullscreen .stage, 0,2,0)
     enlarges --ak-img to 30vh (~108px) + sets padding-top:40, making content too
     tall so the pill row clipped the card bottom. media.css loads last, so this
     (0,2,0) override wins. There's no canvas to "reclaim" on a 360px-tall screen
     anyway — keep it identical to windowed compact. (2026-05-30.) */
  body.is-fullscreen .stage {
    /* padding-bottom 64 (vs windowed 84) compensates for the fullscreen stage
       sitting 20px higher (top:30 vs 50): without it the in-flow card+HEAR end
       ~20px above the fixed segment bar, leaving HEAR off-centre (big gap below).
       64 drops them so HEAR sits halfway between card edge and the bar. */
    padding-top: 24px; padding-bottom: 64px;
    --ak-img: clamp(86px, 25vh, 180px);
    --ak-gap: clamp(16px, 4vh, 44px);
    --ak-pill-font: clamp(18px, 4.2vh, 32px);
  }
  /* Fullscreen X icon + crumb shrink back to the COMPACT size on the short phone
     (2026-05-30 user req). The inline styles.css `body.is-fullscreen` rules size
     them for a tablet (30px button / 26px icon / 15px crumb); on the Z30 that's
     too big for the cramped top edge, so step them down to the prior small size.
     media.css loads last, so these EQUAL-specificity rules win by source order. */
  body.is-fullscreen .v2-nav-btn { width: 22px; height: 22px; }
  body.is-fullscreen .v2-nav-btn .v2-nav-icon-fs { width: 14px; height: 14px; }
  body.is-fullscreen #ak-act-crumb { font-size: 12px; }

  /* PICKER cards on the short phone — folded in from the deleted max-width:400
     tier. The picker scrolls (it's the .intro scroll layer), so bigger content
     just makes the module list taller; emoji/text were sitting at their tiny
     clamp floors otherwise. */
  .module-preview img { width: 42px; height: 42px; }
  .module-desc  { font-size: 12px; white-space: normal; }
  .module-count { font-size: 13px; }
  .module-banner { font-size: 14px; flex-wrap: wrap; white-space: normal; row-gap: 0; }
  .module-banner .module-title { overflow: visible; text-overflow: clip; white-space: normal; }

  /* VOCAB (NEW WORDS) on the short landscape card — FLEXBOX so each row (incl. a
     partial LAST row on odd counts) CENTRES. Grid left-aligned the last row,
     stranding an empty cell (7→6+1, 9→6+3 before). 2026-05-31 user req. The card
     grows, so flex:1 + align-content:space-evenly makes the grid fill the height
     and spread its 2 rows. A per-count cell width sets the per-row count =
     ceil(n/2) → balanced 2 rows: 5-6→3/row · 7-8→4/row · 9-10→5/row · 11-12→6/row.
     Tiles are HEIGHT-driven (always 2 rows → height is the binding axis) and fit
     the very short 640×360 end. Sparse (1-4 words) keeps its own layout. */
  .stage .ak-card .ak-vocab-grid:not(.is-sparse) {
    display: flex; flex-wrap: wrap;
    justify-content: center; align-content: space-evenly;
    flex: 1 1 auto;
    gap: clamp(4px, 1.4vh, 16px) clamp(8px, 1.6vw, 18px);
  }
  .stage .ak-card .ak-vocab-grid:not(.is-sparse) .ak-image-slot,
  .stage .ak-card .ak-vocab-grid:not(.is-sparse) .ak-splatter {
    width:  clamp(48px, 14vh, 88px);
    height: clamp(48px, 14vh, 88px);
  }
  /* Per-row count via cell flex-basis. The % alone decides the wrap (N×basis ≤
     ~92% fits, (N+1)×basis ≥ ~104% wraps), so it's robust to the small column gap
     and to viewport width. min-width:0 stops a long label (AEROPLANE…) from
     expanding the cell and breaking the count. Cascade: a 9-item grid matches
     :has(7) AND :has(9) — the later (narrower) basis wins → 5/row. */
  .stage .ak-card .ak-vocab-grid:not(.is-sparse) > .ak-vocab-cell { flex: 0 0 30%; min-width: 0; }   /* 5-6 → 3/row */
  .stage .ak-card .ak-vocab-grid:not(.is-sparse):has(.ak-vocab-cell:nth-child(7)) > .ak-vocab-cell { flex-basis: 23%; }   /* 7-8 → 4/row */
  .stage .ak-card .ak-vocab-grid:not(.is-sparse):has(.ak-vocab-cell:nth-child(9)) > .ak-vocab-cell { flex-basis: 18%; }   /* 9-10 → 5/row */
  .stage .ak-card .ak-vocab-grid:not(.is-sparse):has(.ak-vocab-cell:nth-child(11)) > .ak-vocab-cell { flex-basis: 15%; }  /* 11-12 → 6/row */
  /* Label font shrinks as the per-row count climbs (cells get narrower). */
  .stage .ak-card .ak-vocab-grid:not(.is-sparse) .ak-word-slot { font-size: clamp(13px, 2vh, 17px); padding: 5px 12px; }
  .stage .ak-card .ak-vocab-grid:not(.is-sparse):has(.ak-vocab-cell:nth-child(9)) .ak-word-slot { font-size: 12px; padding: 4px 9px; }
  .stage .ak-card .ak-vocab-grid:not(.is-sparse):has(.ak-vocab-cell:nth-child(11)) .ak-word-slot { font-size: 11px; padding: 3px 7px; }

  /* MATCH quiz on the short phone (2026-05-30 user req): the card stacks 3 rows
     — word pills, dashed drop-slots, then the drag-from tray (`margin-top:auto`,
     bottom-anchored). On the ~252px short card the lineup (pills+slots) is taller
     than its flex box and visually spilled DOWN into the tray, so the tray
     overlapped the dashed slots (measured: slots end 248, tray starts 212). Fix
     = shrink the match tiles (slots + tray pills) so all 3 rows are shorter and
     separate cleanly, + trim the tray's top padding. Small-screen-only (inside
     this @media). Targets the unique .ak-match-* classes so only MATCH cards are
     affected, not other quiz/letter trays. */
  .stage .ak-card .ak-match-slot,
  .stage .ak-card .ak-match-pill {
    /* Bumped 56/16vh/78 → 64/20vh/86 (2026-05-30 user req): the match card had
       unused vertical space (slot row ended ~193, pills started ~212, card
       bottom 326), so grow the emoji tiles + dashed placeholders to fill it. */
    width: clamp(64px, 20vh, 86px); height: clamp(64px, 20vh, 86px);
  }
  .stage .ak-card .ak-match-pill { padding: 6px; }
  /* Spread the tray emojis apart so they don't look cramped (wider gap, not
     column-aligned under the slots). */
  .stage .ak-card .ak-tray { padding-top: 8px; gap: clamp(18px, 6vw, 48px); }
  /* Smaller word-pill text on the match top row only. Scoped via :has() to the
     content-grid that contains match slots, so other cards' pills are untouched. */
  .stage .ak-card .ak-content-grid:has(.ak-match-slot) .ak-word-slot {
    font-size: clamp(18px, 3.2vw, 26px); padding: 5px 13px;   /* bigger word text (2026-05-31 user req) — width-driven: ~24px @767, ~20px @640 so it still fits 5 cols */
  }
  /* Tighten the match card's internal gaps + CENTRE its content vertically
     (2026-05-31 user req — was top-aligned, which left a big empty band below the
     emoji row in the taller landscape card; the 3 rows still fit the short 360
     end, so centring is safe there). Scoped via :has() to MATCH cards only. */
  /* NOTE: must carry .ak-card-notitle (specificity 0,4,0) to BEAT the generic
     drag-card rule above (`.ak-card-notitle:has(.ak-pill-drag)`, 0,4,0, earlier
     in source) — the match tray emojis are also `.ak-pill-drag`, so that rule
     otherwise forces this card to flex-start and the centring silently no-ops. */
  .stage > .ak-card.ak-card-notitle:has(.ak-match-slot) { padding: 10px 8px 8px; gap: 10px; justify-content: center; }
  .stage .ak-card .ak-content-grid:has(.ak-match-slot) { row-gap: 20px; }   /* vertical spread (2026-05-30) */
  /* Column gaps — TIGHTENED 2026-05-31 (user req): the bigger word pills wanted
     less air between columns. Reduced both the pills/slots grid gap and the tray
     gap (tray kept a touch wider so the emojis still track under the wider pills). */
  .stage .ak-card .ak-content-grid:has(.ak-match-slot) { column-gap: clamp(10px, 2.8vw, 28px); }
  .stage .ak-card .ak-tray { gap: clamp(14px, 3.6vw, 38px); }

  /* CHEER / triumph screen on the short phone (2026-05-31 user req). `.cheer` is
     position:absolute inset:0 with vertically-centred content and NO compact
     override, so the tall stack (stars 70px + title 56px + sub + sticker +
     72px button) overflowed: stars hid behind the chrome at top, the continue
     button clipped at the bottom, and its long "NEXT MODULE · …" label overran
     the pill. Shrink every layer + clear the chrome with padding-top so the whole
     celebration fits one short screen. Covers activity AND module (.is-module). */
  /* ROOT CAUSE of the overflow: `.cheer` is `position:absolute; inset:0` but its
     parent `#stage` is offset (`top:50px`) AND `height:100vh`, so the cheer box
     runs y=50→410 — its bottom 50px is BELOW the viewport and its centred content
     sits ~25px too low, pushing the lower button(s) off-screen (same trap the
     picker hit — fixed there too). Re-anchor to the VIEWPORT with position:fixed
     so centring is viewport-relative; the shrinks below then guarantee fit.
     Compact-scoped (tall screens never overflowed). */
  .cheer { position: fixed; gap: clamp(4px, 1.2vh, 10px); padding: 44px 14px 10px; }
  .cheer-stars { gap: 12px; }
  .cheer-stars span { font-size: clamp(30px, 9vh, 48px); }
  .cheer-title { font-size: clamp(26px, 8vh, 44px); }
  .cheer-sub   { font-size: clamp(12px, 3vh, 17px); letter-spacing: 0.06em; }
  .cheer-sticker { margin-top: 2px; gap: 1px; }
  .cheer-sticker img { width: clamp(34px, 9vh, 52px); height: clamp(34px, 9vh, 52px); }
  .cheer-sticker span { font-size: clamp(11px, 2.4vh, 15px); }
  /* Continue button (.splash-go-btn.cheer-continue-btn): shrink height/font/
     padding + tighter spacing so the full "NEXT MODULE · TITLE" fits the pill and
     the button stays on-screen. */
  .cheer .splash-go-btn {
    height: clamp(44px, 13vh, 60px); font-size: clamp(15px, 4vh, 22px);
    padding: 0 clamp(16px, 4vw, 30px); letter-spacing: 0.04em;
  }
  .cheer .cheer-continue-btn { margin-top: clamp(4px, 1.5vh, 12px); box-shadow: 0 6px 0 var(--pink-deep); }
}


/* ── 2b. TALL-LANDSCAPE — question / quiz / letters fill + HEAR (440–600 tall) ─
   On the taller landscape phone (≥440 tall, e.g. 767×481) the question, QUIZ and
   LETTERS cards are top-pinned with a big empty band below; this block CENTRES +
   enlarges them to fill, and bumps the HEAR button. The ≤439 end keeps §2's tight
   top-aligned layout (no room there). Question-card detail:
   §2 shrinks the M3/M4/M8/M9/M12 question+answer cards' tokens and top-pins them
   so they survive the very short 360 end (where the 2 sections already overflow).
   On the TALLER landscape phone (≥440 tall, e.g. 767×481) that left a big empty
   band BELOW the content (measured: 82px). Here there IS room: CENTRE the
   question+answer block and enlarge the tokens to fill. Scoped ≥440 so the 360
   end keeps §2's tight top-aligned layout (must NOT grow there). 2026-05-31. */
@media (orientation: landscape) and (min-height: 440px) and (max-height: 600px) {
  .stage > .ak-card.ak-question-card {
    justify-content: center;
    --ak-img:       clamp(48px, 12vh, 78px);
    --ak-gap:       clamp(14px, 3vh, 30px);
    --ak-pill-font: clamp(20px, 3.2vh, 30px);
  }
  /* CONTENT-SIZE BUMP (2026-05-31 user req): these cards had ~35-44px slack each
     side at 481 — grow the emojis/font a notch so they feel fuller. Connect is
     already full (untouched). The SHOW/HIDE question/paired cards are NOT touched:
     they set their OWN --ak-img/-pill-font above (overrides the .stage value), and
     none of the bumped selectors below match them (no .ak-pill-drag / .ak-match-*
     / .ak-letter-card). PRESENT (+ splash) ride the .stage tokens — the special
     cards all override --ak-img, so this reaches only the plain content cards. */
  .stage { --ak-img: clamp(96px, 28vh, 200px); --ak-pill-font: clamp(20px, 4.8vh, 36px); }
  /* MATCH tiles — the card with the most slack (44px). */
  .stage .ak-card .ak-match-slot,
  .stage .ak-card .ak-match-pill { width: clamp(72px, 22vh, 100px); height: clamp(72px, 22vh, 100px); }
  /* QUIZ (drag-a-word into the blank) — was flex-start with ~93px empty below at
     481. CENTRE + enlarge the tokens to fill. :not(.ak-letter-card) so the
     letters card keeps its own rule below. Specificity (0,5,0 via :has+:not)
     beats §2's :has(.ak-pill-drag) flex-start. (2026-05-31 user req.) */
  .stage > .ak-card.ak-card-notitle:has(.ak-pill-drag):not(.ak-letter-card) {
    justify-content: center;
    --ak-img: clamp(64px, 19vh, 110px);
  }
  /* LETTERS — was flex-start + a top push with ~88px empty below. CENTRE the
     puzzle+letter cluster and enlarge the pictograms + the draggable letter. */
  .stage > .ak-card.ak-card-notitle.ak-letter-card {
    justify-content: center;
    padding-top: 0;
    gap: clamp(10px, 3vh, 28px);
  }
  .stage > .ak-card.ak-letter-card .ak-letter-emoji {
    width:  clamp(56px, 16vh, 88px);
    height: clamp(56px, 16vh, 88px);
  }
  .stage > .ak-card.ak-letter-card .ak-letter-pill {
    min-width:  clamp(80px, 21vh, 110px);
    min-height: clamp(80px, 21vh, 110px);
    font-size:  clamp(72px, 18vh, 104px);
  }
  /* HEAR button — a bit bigger on this taller landscape (was 30px/14px/20px).
     Stays small on the cramped ≤439 end (that's §2's value; this block excludes
     it). Applies to present/quiz/letters/tracing — all the HEAR cards. */
  .stage .hear-btn { margin-top: 14px; padding: 4px 18px; gap: 10px; }
  .stage .hear-btn .hear-btn-pill { font-size: 17px; }
  .stage .hear-btn .hear-btn-icon { width: 25px; height: 25px; }
}


/* ── 2c. SHORTEST-LANDSCAPE — question/paired + tracing card FIT (≤439 tall) ─
   The flip side of §2b: on the very short landscape phone (≤439 tall, e.g.
   640×360) the question+answer 2-section card is TALLER than the ~188px it has,
   so the answer row spilled past the card bottom onto HEAR. §2's tokens (img 40)
   aren't tight enough. Shrink the tokens + the SHOW/HIDE button so both sections
   fit, then centre. (§2b handles ≥440 by GROWING instead.) 2026-05-31 user req. */
@media (orientation: landscape) and (max-height: 439px) {
  .stage > .ak-card.ak-question-card {
    justify-content: center;
    --ak-img:       clamp(28px, 8.5vh, 44px);
    --ak-gap:       clamp(5px, 1.3vh, 12px);
    --ak-pill-font: clamp(13px, 3.6vh, 18px);
  }
  .stage > .ak-card.ak-question-card .ak-show-answer-btn { font-size: 12px; padding: 4px 14px; }
  .stage > .ak-card.ak-question-card .ak-paired-divider,
  .stage > .ak-card.ak-question-card .ak-answer-reveal { margin-top: 2px; margin-bottom: 2px; }
  /* TRACING (2026-05-31 item 2b): the +20% trace-pic + the big 25vw letters
     overflowed this short card (~32px). Shrink the LETTERS here so the whole
     card fits with the bigger pic kept. ≥440 (§2b range) keeps full-size letters
     — it has the height. */
  .stage > .ak-card.ak-trace-card .ak-trace-letter { height: clamp(80px, 30vh, 132px); }
}

/* ── 2c-tiny. VERY-SHORT LANDSCAPE — match / quiz tray cards (≤360 tall) ──────
   On the shortest landscape phones (e.g. Galaxy S9+, ~320–360px usable height
   once the browser bars are subtracted) the §2 `:has(.ak-pill-drag)` rule pins
   --ak-img at a 50px floor tuned for ~360–480px phones. Stacked with the chrome,
   the 2 picture/word rows, the tray, and HEAR, that floor is too tall — the
   splat/picture cells poke out of their tiles and the rows overlap. Shrink
   --ak-img (which also shrinks the 0.75×--ak-img blank-slot rows) so all rows
   fit with margin. `:not(.ak-letter-card)` leaves the letters card's own tuning
   (explicit emoji/pill sizes) untouched. Scoped ≤360 — no effect on taller
   phones / tablets / desktop. 2026-06-02 user req. */
@media (orientation: landscape) and (max-height: 360px) {
  .stage > .ak-card.ak-card-notitle:has(.ak-pill-drag):not(.ak-letter-card) {
    --ak-img: clamp(34px, 10.5vh, 50px);
    gap: clamp(5px, 1.4vh, 9px);
  }
  /* SHOW-ANSWER reveal cards. The reveal block reserves its space even while
     hidden (visibility:hidden, not display:none), so the quiz-with-reveal card
     (question grid + words + tray + reveal) overflowed onto the HEAR/NEXT
     controls when the answer was shown on a ~320px screen. Shrink that crowded
     card's pictograms below the tray floor above + tighten gaps, and let ANY
     question card scroll its overflow as a safety net so the answer is reachable
     instead of spilling. 2026-06-02 user req. */
  .stage > .ak-card.ak-question-card { overflow-y: auto; overscroll-behavior: contain; }
  .stage > .ak-card.ak-card-notitle.ak-question-card:has(.ak-pill-drag) {
    --ak-img: clamp(24px, 7vh, 40px);
    gap: clamp(4px, 1vh, 8px);
  }
  .stage > .ak-card.ak-question-card .ak-show-answer-btn { font-size: 11px; padding: 3px 12px; }
}


/* ── 2d. TABLET / DESKTOP — exercise-card CENTRING (≥601 tall) ───────────────
   The BASE top-pins each lesson card with a ~200px empty band below. GROWING the
   card to fill that band FLOATS the short horizontal content in a giant card
   (tried → reverted). Instead CENTRE the [card + HEAR] block — margin-top:auto on
   the card + margin-bottom:auto on HEAR splits the band into a balanced margin so
   it reads as a focused card — and enlarge the content a touch. Targets present /
   quiz / letters / tracing / question ONLY; vocab + splash (title cards), match
   (:has match-slot) and connect (.ak-connect-card) keep top-pinned (user: fine);
   pickers untouched. Phones keep §2. Landscape-scoped (app is landscape-locked).
   2026-05-31 user req — tablet + desktop + larger. */
@media (orientation: landscape) and (min-height: 601px) {
  .stage > .ak-card.ak-card-notitle:not(.ak-connect-card):not(:has(.ak-match-slot)),
  .stage > .ak-card.ak-question-card {
    margin-top: auto;
  }
  .stage .hear-btn { margin-bottom: auto; }
  /* Pin the NON-scaled cards to the old ~1100 width (user: leave vocab / match /
     connect). --ak-content-w was raised to 1400 for the scaled exercise cards +
     BACK/NEXT, which otherwise spreads these thin. Connect already has its own
     width var. (Splash = .ak-card-titleonly.) */
  .stage > .ak-card:has(.ak-vocab-grid),
  .stage > .ak-card.ak-card-notitle:has(.ak-match-slot),
  .stage > .ak-card.ak-card-titleonly {
    max-width: clamp(720px, 90vw, 1100px);
  }
  /* SCALE content + chrome with the viewport — ONE clamp each, no device tiers.
     Grows whenever there's space; the raised --ak-content-w (styles.css) widens
     the CARD so the bigger content actually fits instead of being scaled back to
     1100px. Targets the exercise cards; vocab/match/connect/pickers untouched;
     phones keep §2 (≤600). (2026-05-31 user req.) */
  .stage > .ak-card.ak-card-notitle:not(.ak-connect-card):not(.ak-question-card):not(:has(.ak-match-slot)) {
    --ak-img: clamp(120px, 16vw, 340px);
  }
  .stage > .ak-card.ak-question-card { --ak-img: clamp(96px, 12vw, 220px); }
  .stage > .ak-card.ak-letter-card .ak-letter-emoji { width: clamp(48px, 6vw, 120px); height: clamp(48px, 6vw, 120px); }
  .stage > .ak-card.ak-letter-card .ak-letter-pill { min-width: clamp(72px, 9vw, 180px); min-height: clamp(72px, 9vw, 180px); font-size: clamp(64px, 8vw, 150px); }
  .stage .hear-btn { padding: 8px clamp(20px, 2vw, 44px); gap: clamp(13px, 1vw, 20px); }
  .stage .hear-btn .hear-btn-pill { font-size: clamp(22px, 1.9vw, 46px); }
  .stage .hear-btn .hear-btn-icon { width: clamp(40px, 3.4vw, 78px); height: clamp(40px, 3.4vw, 78px); }
  .stage > .next-btn, .stage > .back-btn { height: clamp(62px, 5.6vw, 120px); padding: 0 clamp(30px, 3vw, 60px); font-size: clamp(21px, 1.9vw, 42px); }
  #ak-segment-bar { height: clamp(18px, 1.6vw, 38px); }
  #ak-segment-bar .seg-cell { border-radius: 6px; }
}


/* ── 2f. TABLET / DESKTOP — picker fill + scale (≥601 tall) ──────────────────
   The module/activity picker sat at the TOP of a tall viewport with a big empty
   band below, and its cards stayed small on big screens. CENTRE it vertically
   (safe-center → still scrolls top-down if the cards overflow a short viewport)
   and SCALE the cards + internals + the prompt with the viewport. The vw floors ≈
   the 1024×768 base values + the --ak-shell-w clamp floor (1180) so 1024×768 is
   ~unchanged; phones keep §2. Grid widens via the raised --ak-shell-w. 2026-05-31
   user req — scale the whole app on big screens. */
@media (orientation: landscape) and (min-height: 601px) {
  .intro { display: flex; flex-direction: column; justify-content: safe center; align-items: center; }
  .intro .module-grid { grid-template-columns: repeat(auto-fit, minmax(clamp(220px, 19vw, 380px), 1fr)); }
  .intro .intro-sub { font-size: clamp(22px, 2.4vw, 46px); }
  .intro .module-card { min-height: clamp(110px, 10.5vw, 220px); }
  .intro .module-banner { font-size: clamp(13px, 1.25vw, 24px); }
  .intro .module-preview img { width: clamp(34px, 2.6vw, 58px); height: clamp(34px, 2.6vw, 58px); }
  .intro .module-desc { font-size: clamp(8.5px, 0.85vw, 16px); }
  .intro .module-count { font-size: clamp(12px, 1vw, 20px); }
}


/* ── 2g. TABLET / DESKTOP — CHROME bar scale (≥601 tall) ─────────────────────
   The chrome elements already used vw clamps but with LOW caps (icons 27-34,
   brand 32, crumb 13), so they stopped growing on big screens. Raise the caps
   ONLY — the vw stays the same, so 1024×768 is unchanged (vw < cap there) and
   bigger screens grow. The bar itself widens via the raised --ak-shell-w. Phones
   keep §2/§3. 2026-05-31 user req — scale the whole app. */
@media (orientation: landscape) and (min-height: 601px) {
  .brand { font-size: clamp(22px, 2.8vw, 52px); }
  .v2-nav-btn { width: clamp(44px, 4.3vw, 72px); height: clamp(44px, 4.3vw, 72px); }
  .v2-nav-icon { width: clamp(22px, 2.55vw, 44px); height: clamp(22px, 2.55vw, 44px); }
  .v2-nav-icon-mute { width: clamp(18px, 2.08vw, 36.9px); height: clamp(18px, 2.08vw, 36.9px); }  /* −10% 2026-06-03 */
  #settings-btn .v2-nav-icon { width: clamp(22px, 2.6vw, 44px); height: clamp(22px, 2.6vw, 44px); }
  #fullscreen-btn .v2-nav-icon-fs { width: clamp(16.2px, 1.76vw, 30.6px); height: clamp(16.2px, 1.76vw, 30.6px); }  /* −15% 2026-06-03 */
  #ak-act-crumb { font-size: clamp(9px, 1.1vw, 22px); padding: clamp(5px, 0.5vw, 11px) clamp(14px, 1.2vw, 26px); }
}


/* ── 3. WIDTH — chrome OVERFLOW STEPS (the only discrete width ladder) ───────
   The chrome is a single flex row (brand + controls + separators) that CANNOT
   reflow, so it tightens in discrete steps where the row actually overflows —
   NOT device tiers; don't align them to the picker (container-driven). Narrower
   comes LAST so it overrides the wider step's shared props. The 768 step fires
   on the Z30 (640w landscape) AND narrow desktop windows; the 560 step only on
   sub-560 desktop windows. The old 520/440/400 tiers below 640 were deleted
   2026-05-30 — anything smaller is handled by the §2 landscape-compact block
   (the Z30 is the smallest real target). */
@media (max-width: 768px) {
  .chrome { gap: 8px; padding: 12px var(--ak-shell-pad); }
  .nav-buttons { gap: 6px; }
  .v2-nav-sep { display: none; }                      /* drop separators as the row tightens */
  /* (The language + activities pills already collapse to icon-only at ALL sizes
     now — labels + carets were removed from the chrome 2026-06-01, see
     styles.css — so the per-step pill collapse that used to live here is gone.) */
}
@media (max-width: 560px) {
  /* 3) BREATHE — at ≤560 the wordmark + 7 controls (44px @ 6px gaps) overflowed
     the row by ~10px and read as a cramped cluster (2026-05-31 user req: keep
     AKAM, give the controls a bigger gap). Shrink each control 44→40 and nearly
     double the gap (6→10): brand 74 + 7×40 + 6×10 fits the ~425px content box
     with no overflow, so the icons spread out evenly instead of bunching. The
     lang/filter pills match the 40px so the row stays uniform. Width-only —
     doesn't touch the Z30 landscape-compact block (640w, above this step). */
  .nav-buttons { gap: 10px; }
  .v2-nav-btn { width: 40px; height: 40px; }
  .chrome .v2-lang-btn, .chrome .v2-filter-pill { width: 40px; }
}


/* ── 4. AXIS — lock to LANDSCAPE on touch devices ───────────────────────────
   `pointer: coarse` scopes this to phones + tablets (incl. iPad Pro at 1024px
   portrait, which a max-width guard would miss) and excludes desktop/mouse — a
   narrow desktop window just reflows the picker rather than being blocked. */
@media (orientation: portrait) and (pointer: coarse) {
  .rotate-hint { display: flex; }
}


/* ── 5. AXIS — TV overscan safe-area (≥1600×800) ────────────────────────────
   Old/mid-range TVs clip ~30–50px off each edge. Best fix is on the TV; this is
   a defensive fallback that pulls content inward. top/left/right only — `.stage`
   is top+height:100vh (no bottom inset); the fixed NEXT button + stage
   padding-bottom provide the bottom safe-area. */
@media (min-width: 1600px) and (min-height: 800px) {
  .chrome { padding-top: 36px; padding-bottom: 18px; }
  .stage { top: 110px; left: 56px; right: 56px; }
  .next-btn { bottom: 52px; }
}


/* ── 6. A11Y — reduced motion ───────────────────────────────────────────────
   The ONE documented `!important` exception (keyframe-using rules defined later
   would otherwise win the cascade and silently ignore the user's preference).
   Standard a11y pattern: https://web.dev/prefers-reduced-motion/. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
