    /* ===== DOTS mode: numbered dots on a hidden picture outline ===== */
    /* v93 (addendum 2 + night order): the dots panel joins the
       bottom-zone standard (the clock's v92 treatment) — a flex
       column that can never push the page: the board is sized by
       HEIGHT (capped by the same stage-width budget as before)
       and flex-shrinks when the win banner joins the flow, so
       playing / win / shrunk-window states all fit one screen
       with the banner and the pet pill fully visible. The old
       always-on 88px reserve padding goes: in a flex column the
       board hands the space back itself. */
    #screen-pet .mode-panel#mode-dots.active { display: flex; flex-direction: column; overflow: hidden; }
    #mode-dots .dots-board {
      position: relative; flex: 0 1 auto; min-height: 0;
      height: min(calc(var(--stage-w) * 0.77), 280px, 100%); width: auto; aspect-ratio: 1 / 1;
      max-width: min(calc(var(--stage-w) * 0.77), 280px); margin: 4px auto 0;
    }
    #mode-dots .msg, #mode-dots .win-banner { flex: 0 0 auto; }
    
    #mode-dots .dots-board svg { position: absolute; inset: 0; width: 100%; height: 100%; }
    #mode-dots .dots-fill { transition: opacity .8s ease; }
    #mode-dots .dots-detail { transition: opacity .8s ease .15s; }
    .dot-btn {
      position: absolute; width: var(--dot-d, 38px); height: var(--dot-d, 38px);
      margin: calc(var(--dot-d, 38px) / -2) 0 0 calc(var(--dot-d, 38px) / -2); padding: 0;
      border-radius: 50%; border: 3px solid #1971c2; background: #fff; color: #1971c2;
      font-size: calc(var(--dot-d, 38px) * 0.44); font-weight: 900; line-height: 1; z-index: 2;
      display: grid; place-items: center; box-shadow: 0 2px 6px rgba(25, 80, 140, .25);
      -webkit-tap-highlight-color: transparent;
    }
    .dot-btn.done { background: #37b24d; border-color: #2b8a3e; color: #fff; z-index: 1; }
    .dot-btn.next { z-index: 4; animation: dotNextPulse 1s ease-in-out infinite; }
    /* v71: the standing next-dot pulse is the LEVEL-1 hint only.
       Level 2 plays clean (finding the next number is the skill);
       level 3 drags, and its .next stays quiet too — the only L3
       hint is the .hint flash after a wrong tap or a long idle. */
    #dotsBoard[data-level="2"] .dot-btn.next, #dotsBoard[data-level="3"] .dot-btn.next { animation: none; }
    #dotsBoard.dots-drag { touch-action: none; }
    .dot-btn.hint { z-index: 5; border-color: #f08c00; background: #fff4e0; animation: dotHintPulse .45s ease-in-out 3; }
    @keyframes dotNextPulse { 0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(25, 113, 194, .45); } 50% { transform: scale(1.14); box-shadow: 0 0 0 7px rgba(25, 113, 194, 0); } }
    @keyframes dotHintPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.3); } }
    /* Shapes + clock are big-panel modes: slim stage like dots/word,
       the duplicated task line goes (the bubble instructs), and in
       clock mode the bubble clamps to one line so the pet stays
       clearly visible under it. */
    #screen-pet .pet-panel:has(#mode-clock.active) .bubble .b-text { }
    #screen-pet .pet-panel:has(#mode-clock.active) .pet-stage > .pet-face { height: min(132px, calc(100% - 114px)); bottom: 8px; }
    #mode-shapes .task-sub, #mode-clock .task-sub { display: none; }
    
    
    
    
    /* --- Shapes match --- */
    .shapes-sample { min-height: 84px; display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 2px; }
    .shapes-sample .ss-label { font-size: 16px; font-weight: 800; color: var(--ink-soft); }
    .shapes-sample .ss-card {
      width: 76px; height: 76px; border-radius: 20px; border: 3px dashed #f08c00; background: #fff8e1;
      display: grid; place-items: center; font-size: 42px; line-height: 1;
    }
    .shapes-sample .ss-card svg { width: 62%; height: 62%; }
    /* v71: the sample's name label (shape + colour words) */
    .shapes-sample .ss-name { font-size: 16px; font-weight: 900; color: var(--ink); background: #fff3bf; border: 2px solid #ffd43b; border-radius: 999px; padding: 3px 12px; white-space: nowrap; }
    .shapes-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; width: min(calc(var(--stage-w) * 0.82), 300px); margin: 6px auto 0; }
    /* v69: while the win bar is up the dots panel reserves the
       bar's strip below the finished picture. The panel's height
       follows its content here, so the docked bar rides up with the
       board — only reserved padding puts real space between the
       picture and the bar (shrinking the board cannot). The board
       keeps its full size: the child sees the whole picture. */
    /* (v93: the dots reserve padding is retired — see the flex
       treatment at the top of this file. The selector also matched
       while the banner was hidden, taxing every round 88px.) */
    /* v69: while the win bar is up the panel reserves the bar's
       strip and the sample + grid compact, so the finished round's
       cards stay fully above the bar (the grid otherwise reaches
       into the bar's strip on most phones). */
    #screen-pet #mode-shapes.mode-panel:has(> .win-banner) { padding-bottom: 88px; }
    #screen-pet #mode-shapes.mode-panel:has(> .win-banner) .shapes-grid { width: min(calc(var(--stage-w) * 0.67), 246px); gap: 8px; }
    #screen-pet #mode-shapes.mode-panel:has(> .win-banner) .shapes-sample { min-height: 54px; }
    #screen-pet #mode-shapes.mode-panel:has(> .win-banner) .shapes-sample .ss-card { width: 50px; height: 50px; font-size: 30px; }
    #screen-pet #mode-shapes.mode-panel:has(> .win-banner) .shape-card { font-size: 36px; }
    .shape-card {
      aspect-ratio: 1 / 1; padding: 6px; border-radius: 20px; border: 3px solid var(--line);
      background: #fff; display: grid; place-items: center; cursor: pointer;
      font-size: 46px; line-height: 1; box-shadow: 0 3px 0 rgba(35, 50, 70, .12);
      -webkit-tap-highlight-color: transparent;
    }
    .shape-card svg { width: 72%; height: 72%; pointer-events: none; }
    .shape-card:active { transform: scale(.94); }
    .shape-card.right { border-color: var(--teal); background: #e6fffa; box-shadow: 0 0 0 3px rgba(12, 194, 171, .35); }
    .shape-card.locked { pointer-events: none; }
    @media (max-height: 880px) {
      .shapes-sample { min-height: 58px; }
      .shapes-sample .ss-card { width: 54px; height: 54px; font-size: 34px; }
      #mode-shapes .game-score { margin-top: 0; min-height: 22px; }
      #mode-shapes .msg { min-height: 30px; margin-top: 4px; }
      #mode-clock .msg { min-height: 30px; margin-top: 4px; }
      .clock-scene { min-height: 36px; }
      .clock-scene .cs-pic { font-size: 34px; }
      .clock-actions { margin-top: 4px; }
      .clock-actions .ghost-btn { padding: 6px 14px; min-height: 40px; }
    }
    
    /* --- Pet clock --- */
    /* v71: the digital readout (SET rounds) and the digital cards
       (READ rounds). The readout is a chunky two-digit-minutes time
       the child matches the hands to; the cards are big tap targets.
       In READ rounds the face shrinks a touch so face + cards + the
       win bar still fit one screen. */
    .clock-digital {
      min-height: 52px; display: flex; align-items: center; justify-content: center;
      font-size: 44px; font-weight: 900; letter-spacing: 2px; color: var(--ink);
      font-variant-numeric: tabular-nums; line-height: 1.05;
    }
    .clock-digital[hidden] { display: none; }
    .clock-choices { display: flex; gap: 10px; justify-content: center; margin-top: 8px; }
    .clock-choices[hidden] { display: none; }
    .clock-card {
      min-width: 96px; min-height: 60px; padding: 8px 14px; border-radius: 18px; cursor: pointer;
      border: 3px solid #74c0fc; background: #eef7ff; color: var(--ink);
      font-size: 27px; font-weight: 900; font-variant-numeric: tabular-nums; line-height: 1.1;
      box-shadow: 0 4px 0 rgba(35, 50, 70, .14); -webkit-tap-highlight-color: transparent;
    }
    .clock-card:active { transform: scale(.94); }
    .clock-card.right { border-color: var(--teal-dark); background: #e6fcf5; box-shadow: 0 0 0 5px var(--teal); }
    .clock-card.wrong { opacity: .45; border-color: var(--line); box-shadow: none; }
    #screen-pet #mode-clock.read .clock-wrap { width: min(calc(var(--stage-w) * 0.74), 272px); }
    
    .clock-scene { min-height: 40px; display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 2px; font-size: 21px; font-weight: 800; color: var(--ink); }
    .clock-scene .cs-pic { font-size: 38px; line-height: 1; }
    /* v87 (bi QA): the face was capped at min(72vw, 248px, 26dvh) —
       186px on a 390x844 phone, 163px at 320px wide — small for a
       4-year-old's finger to steer by angle. Caps raised (300px /
       32dvh); the stage keeps its slim clock height so the pet
       stays fully visible and the panel still fits one screen. */
    .clock-wrap { width: min(calc(var(--stage-w) * 0.82), 300px); margin: 2px auto 0; }
    /* v69: while the win bar is up the clock shrinks a touch so its
       rim stays clear of the bar on short screens. */
    #screen-pet #mode-clock.mode-panel:has(> .win-banner) .clock-wrap { width: min(calc(var(--stage-w) * 0.74), 272px); }
    .clock-svg { width: 100%; height: auto; display: block; touch-action: none; }
    .clock-svg .clock-num { font-size: 21px; font-weight: 900; fill: #233246; text-anchor: middle; dominant-baseline: central; }
    .clock-svg .clock-num.hint { fill: #e8590c; animation: clockNumHint .45s ease-in-out 3; }
    @keyframes clockNumHint { 0%, 100% { font-size: 21px; } 50% { font-size: 27px; } }
    .clock-svg .hand { pointer-events: none; }
    /* v87: invisible fat grab strokes ride on top of the hands —
       pointer-events: stroke makes the fat line itself draggable,
       far wider than the slim drawn hand (esp. the minute tip). */
    .clock-svg .hand-hit { pointer-events: stroke; }
    .clock-svg .ghost { opacity: 0; pointer-events: none; transition: opacity .25s ease; }
    .clock-svg .ghost.show { opacity: .45; }
    .clock-actions { display: flex; justify-content: center; margin-top: 8px; }
    
    /* --- Number chart 1-100 overlay (opened from count mode) --- */
    /* The chart button shares the message row so opening the chart
       costs no extra panel height in any count round. */
    .count-msgrow { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 8px; }
    .count-msgrow .msg { flex: 1 1 auto; margin-top: 0; min-height: 0; }
    .chart-open-btn { flex: 0 0 auto; min-height: 44px; padding: 8px 12px; font-size: 15px; white-space: nowrap; border-color: #57b6f7; color: var(--sky-dark); }
    .chart-overlay {
      position: fixed; inset: 0; z-index: 75; overflow: hidden;
      /* v93: the sheet stands in the yard like everything else —
         the stage's own sky/grass stops (layout.css scene-bg):
         header on sky, the grid on grass. The old gradient washed
         to cream and the whole chart floated on a pale sheet. */
      background:
        radial-gradient(circle at 85% 6%, rgba(255, 214, 90, .5), rgba(255, 214, 90, 0) 180px),
        linear-gradient(180deg, #6ec9ff 0%, #aee9ff 30%, #8fe06a 30.5%, #4cc24a 100%);
      display: flex; flex-direction: column; padding: 10px 10px calc(10px + env(safe-area-inset-bottom));
    }
    .chart-overlay[hidden] { display: none; }
    .chart-head { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
    .chart-title { flex: 1 1 auto; font-size: 21px; font-weight: 900; color: var(--ink); text-align: left; }
    .chart-close {
      width: 48px; height: 48px; flex: 0 0 auto; border-radius: 50%;
      border: 3px solid var(--coral); background: #fff; color: var(--coral-dark);
      font-size: 24px; font-weight: 900; cursor: pointer; line-height: 1;
    }
    .chart-big {
      flex: 0 0 auto; min-height: 78px; display: flex; align-items: center; justify-content: center; gap: 12px;
      font-weight: 900; color: var(--ink); width: 100%; box-sizing: border-box;
    }
    .chart-big .cb-pic { font-size: 46px; line-height: 1; flex: 0 0 auto; }
    .chart-big .cb-num { font-size: 58px; line-height: 1; color: var(--cat-dark); flex: 0 0 auto; }
    /* v113: the intro hint must never be clipped. care.css's global
       .cb-word{white-space:nowrap} belongs to the speech-bubble
       typewriter and leaked into this row: the centered flex row
       overflowed BOTH edges on phone widths (the JA hint measures
       ~430px on a 375px screen), cutting the pet pic on the left and
       the last kana on the right (bi's photo 2026-10-31). The word
       now stays inside the row: it may shrink and wrap instead. */
    .chart-big .cb-word { font-size: 23px; color: var(--ink-soft); white-space: normal; flex: 0 1 auto; min-width: 0; text-align: center; line-height: 1.2; word-break: keep-all; overflow-wrap: break-word; }
    .chart-big.pop .cb-num { animation: chartPop .45s ease; }
    @keyframes chartPop { 0% { transform: scale(.6); } 55% { transform: scale(1.25); } 100% { transform: scale(1); } }
    .chart-grid {
      flex: 1 1 auto; min-height: 0; width: 100%; max-width: 560px; margin: 2px auto 0;
      display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 4px; align-content: center;
    }
    .chart-cell {
      aspect-ratio: 1 / 1; min-height: 0; padding: 0; border-radius: 9px;
      border: 2px solid #9fd8ff; background: #fff; color: var(--sky-dark);
      font-size: clamp(11px, 3.4vw, 18px); font-weight: 800; cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      -webkit-tap-highlight-color: transparent;
    }
    .chart-cell:nth-child(10n) { background: #fff3bf; border-color: #ffd43b; color: #a86e00; }
    .chart-cell:active { transform: scale(.9); }
    .chart-cell.pop { background: var(--sun); border-color: var(--sun-dark); color: var(--ink); animation: chartPop .45s ease; }

    /* --- Alphabet chart (v55): free-lookup sibling of the number
       chart. Flexible 5-column grid whose rows stretch to the space
       left under the big display, so the whole board always fits one
       screen without scrolling (fit-first: glyphs shrink via clamp
       on short screens instead of pushing rows off-screen). --- */
    .abc-grid {
      flex: 1 1 auto; min-height: 0; width: 100%; max-width: 560px; margin: 2px auto 0;
      display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); grid-auto-rows: 1fr; gap: 4px;
    }
    .abc-cell { display: flex; flex-direction: column; gap: 2px; min-height: 0; }
    .abc-half, .abc-single {
      border-radius: 9px; border: 2px solid #9fd8ff; background: #fff; color: var(--sky-dark);
      font-weight: 800; cursor: pointer; padding: 0; line-height: 1.05;
      display: flex; align-items: center; justify-content: center;
      -webkit-tap-highlight-color: transparent;
    }
    .abc-half { flex: 1 1 0; min-height: 0; }
    .abc-half.hira { font-size: clamp(13px, 3.3vh, 24px); }
    .abc-half.kata { font-size: clamp(10px, 2.6vh, 18px); background: #f2faff; color: #8a6d3b; border-color: #d8ecff; }
    .abc-single { min-height: 0; font-size: clamp(15px, 3.8vh, 27px); }
    .abc-single .abc-lower { font-size: .6em; color: var(--ink-soft); margin-left: 3px; font-weight: 700; }
    .abc-gap { visibility: hidden; }
    .abc-half:active, .abc-single:active { transform: scale(.92); }
    .abc-half.pop, .abc-single.pop { background: var(--sun); border-color: var(--sun-dark); color: var(--ink); animation: chartPop .45s ease; }
    /* v71: Vietnamese tone strip — one row of 6 tone-form buttons
       under the big display; the letter grid below (flex 1fr rows)
       absorbs the height, so the board stays one screen. */
    .abc-tone-row {
      flex: 0 0 auto; display: flex; gap: 5px; width: 100%; max-width: 560px;
      margin: 6px auto 0; padding: 5px; border-radius: 14px;
      background: rgba(255, 255, 255, .65); border: 2px dashed #ffd43b;
    }
    .abc-tone-row[hidden] { display: none; }
    .abc-tone-btn {
      flex: 1 1 0; min-width: 0; min-height: 50px; padding: 2px 0; border-radius: 11px;
      border: 2.5px solid #ffb84d; background: #fff8e1; color: var(--ink);
      font-size: clamp(19px, 5.4vw, 28px); font-weight: 900; line-height: 1.05; cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .abc-tone-btn:active { transform: scale(.92); }
    .abc-tone-btn.pop { background: var(--sun); border-color: var(--sun-dark); animation: chartPop .45s ease; }
    @media (max-height: 600px) { .abc-tone-btn { min-height: 42px; } }
    .abc-big .cb-mate { font-size: 30px; line-height: 1; color: var(--ink-soft); font-weight: 800; flex: 0 0 auto; }
    .abc-big .cb-word rt { font-size: .48em; }
    @media (max-height: 600px) {
      /* short screens: pair halves go side-by-side so each glyph
         keeps a readable size in the squashed rows */
      .abc-cell { flex-direction: row; }
      .chart-big.abc-big { min-height: 52px; }
      .abc-big .cb-num { font-size: 38px; }
      .abc-big .cb-pic { font-size: 30px; }
      .abc-big .cb-mate { font-size: 22px; }
      .abc-big .cb-word { font-size: 17px; }
    }

    /* --- Trace letter picker (v62) + the abc -> trace handoff ---
       The picker is a chart-style overlay: one screen, no scroll —
       the grid rows stretch (1fr) so every glyph stays on-screen
       and FIT-FIRST shrinks the glyph font on short screens. JA
       uses an 8-column grid for the 46-glyph tabs (6 rows) and 5
       columns for the 25 dakuten glyphs; EN/VI use 5 columns. */
    .trace-pick-row { display: flex; justify-content: center; margin-top: 8px; }
    .abc-trace-row { flex: 0 0 auto; display: flex; justify-content: center; margin-top: 4px; }
    .abc-trace-row[hidden] { display: none; }
    .abc-trace-row .next-btn { min-height: 48px; padding: 8px 22px; font-size: 18px; }
    .tracepick-tabs { flex: 0 0 auto; display: flex; justify-content: center; gap: 8px; margin: 8px 0 2px; }
    .tracepick-tabs[hidden] { display: none; }
    .tp-tab {
      min-height: 44px; padding: 7px 18px; border-radius: 999px; cursor: pointer;
      border: 3px solid #9fd8ff; background: #fff; color: var(--sky-dark);
      font-size: 17px; font-weight: 900; -webkit-tap-highlight-color: transparent;
    }
    .tp-tab.active { background: var(--sun); border-color: var(--sun-dark); color: var(--ink); }
    .tp-tab:active { transform: scale(.94); }
    .tracepick-grid {
      flex: 1 1 auto; min-height: 0; width: 100%; margin: 4px auto 0;
      display: grid; gap: 5px; grid-auto-rows: 1fr;
    }
    .tracepick-grid.cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); max-width: 560px; }
    .tracepick-grid.cols-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); max-width: 680px; }
    .tp-cell {
      min-height: 0; padding: 0; border-radius: 10px; border: 2px solid #9fd8ff; background: #fff;
      color: var(--sky-dark); font-weight: 800; cursor: pointer; line-height: 1.05;
      display: flex; align-items: center; justify-content: center;
      -webkit-tap-highlight-color: transparent;
    }
    .tracepick-grid.cols-5 .tp-cell { font-size: clamp(17px, 4.6vh, 34px); }
    .tracepick-grid.cols-8 .tp-cell { font-size: clamp(13px, 3.8vh, 30px); }
    .tp-cell:active { transform: scale(.92); background: var(--sun); border-color: var(--sun-dark); color: var(--ink); }
    @media (max-height: 600px) {
      .trace-pick-row { margin-top: 4px; }
      .trace-pick-row .ghost-btn { min-height: 38px; padding: 5px 14px; font-size: 14px; }
      .tp-tab { min-height: 38px; font-size: 15px; padding: 5px 13px; }
      .abc-trace-row .next-btn { min-height: 42px; font-size: 16px; }
    }

    /* --- Sticker shop (v17): kid overlay, one screen, never scrolls --- */
    /* v72 (Addendum 9, as amended): the shop button is a compact
       round button fixed at the farm scene's bottom-right corner —
       one shop icon + a small star pill; no text, no double bags,
       so the top-right sky stays empty for the sun. */
    .shop-btn {
      position: absolute; right: 10px; bottom: 10px; z-index: 320;
      width: 54px; height: 54px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      background: rgba(255, 255, 255, .94); border: 2.5px solid #ffb84d;
      font-size: 26px; line-height: 1; cursor: pointer;
      box-shadow: 0 3px 0 rgba(35, 50, 70, .16);
    }
    .shop-btn:active { transform: translateY(2px); box-shadow: none; }
    .shop-btn .shop-bal {
      position: absolute; top: -7px; right: -9px;
      background: #fff3bf; border: 1.5px solid #ffb84d; border-radius: 999px;
      padding: 1px 6px; font-size: 11px; font-weight: 800; white-space: nowrap;
    }
    .shop-overlay {
      position: fixed; inset: 0; z-index: 76; overflow: hidden;
      background: linear-gradient(180deg, #ffd9ec 0%, #fff0f7 34%, #fffbe8 100%);
      display: flex; flex-direction: column; padding: 10px 10px calc(10px + env(safe-area-inset-bottom));
    }
    .shop-overlay[hidden] { display: none; }
    .shop-head { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
    .shop-title { flex: 0 1 auto; font-size: 21px; font-weight: 900; color: var(--ink); }
    .shop-bal-big {
      margin-left: auto; background: #fff3bf; border: 3px solid #ffd43b; border-radius: 999px;
      padding: 5px 14px; font-size: 19px; font-weight: 900; color: var(--ink);
    }
    .shop-msg { flex: 0 0 auto; min-height: 24px; margin: 6px 2px 2px; font-size: 15.5px; font-weight: 700; color: var(--ink-soft); text-align: center; }
    .shop-grid {
      /* v91: shop column capped at the 620px kid-area column (chart/abc
         overlays are the E3 exception and keep their own widths). */
      flex: 1 1 auto; min-height: 0; width: 100%; max-width: 620px; margin: 4px auto 0;
      display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; align-content: center;
    }
    .shop-card {
      min-height: 118px; border-radius: 22px; border: 3px solid #ffb3d1; background: #fff;
      box-shadow: 0 4px 0 rgba(35, 50, 70, .12); padding: 10px 8px 8px; cursor: pointer;
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
      color: var(--ink); -webkit-tap-highlight-color: transparent;
    }
    .shop-card:active { transform: translateY(2px); box-shadow: none; }
    .shop-card.pet-card { border-color: #ff9f45; background: linear-gradient(180deg, #fff6e8, #ffeeda); }
    .shop-card .sc-pic { font-size: 40px; line-height: 1.05; }
    .shop-card.pet-card .sc-pic { width: 84px; height: 84px; margin: 0 auto; }
    .shop-card.pet-card .sc-pic svg { width: 100%; height: 100%; }
    .shop-card .sc-name { font-size: 16.5px; font-weight: 900; line-height: 1.15; text-align: center; }
    .shop-card.pet-card .sc-name { font-size: 17px; }
    .shop-card .sc-price { font-size: 15px; font-weight: 800; color: #a86e00; background: #fff3bf; border-radius: 999px; padding: 2px 10px; }
    .shop-card .sc-owned { font-size: 14.5px; font-weight: 800; color: #2b8a3e; background: #d3f9d8; border-radius: 999px; padding: 3px 10px; }
    .shop-confirm {
      position: fixed; inset: 0; z-index: 77; display: flex; align-items: center; justify-content: center;
      background: rgba(40, 30, 60, .45); padding: 14px;
    }
    .shop-confirm[hidden] { display: none; }
    .shop-confirm-card {
      width: min(430px, 100%); max-height: 100%; overflow: hidden;
      background: #fff; border-radius: 28px; border: 4px solid #ffb3d1; padding: 14px 14px 16px;
      display: flex; flex-direction: column; align-items: center; gap: 8px; box-shadow: 0 12px 32px rgba(30, 20, 60, .3);
    }
    .shop-preview {
      position: relative; width: min(240px, 62vw); aspect-ratio: 1; border-radius: 24px;
      background: radial-gradient(circle at 50% 88%, #8fe06a 0 34%, transparent 35%), linear-gradient(180deg, #aee9ff, #e8fbff);
      overflow: hidden; flex: 0 0 auto;
    }
    .shop-preview-face { position: absolute; inset: 0; }
    .shop-preview-face > svg { width: 100%; height: 100%; }
    .shop-confirm-name { font-size: 21px; font-weight: 900; color: var(--ink); text-align: center; line-height: 1.2; }
    .shop-confirm-price { font-size: 17px; font-weight: 800; color: #a86e00; text-align: center; }
    .shop-confirm-price .shop-poor-note { color: var(--coral-dark); font-weight: 900; }
    .shop-pet-pick { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
    .shop-pet-pick[hidden] { display: none; }
    .shop-pet-chip {
      min-height: 48px; padding: 6px 13px; border-radius: 999px; border: 3px solid var(--line);
      background: #eef9ff; font-size: 15.5px; font-weight: 800; color: var(--ink); cursor: pointer;
    }
    .shop-pet-chip.active { border-color: #e8598a; background: #ffe3ef; }
    .shop-confirm-btns { display: flex; gap: 10px; width: 100%; }
    .shop-cancel, .shop-remove, .shop-buy {
      flex: 1 1 0; min-height: 56px; border-radius: 18px; font-size: 18px; font-weight: 900; cursor: pointer;
      border: 3px solid var(--line); background: #fff; color: var(--ink);
    }
    .shop-buy { background: linear-gradient(180deg, #ffd43b, #fab005); border-color: #e8a100; color: #5c3d00; }
    .shop-buy:disabled { opacity: .45; cursor: default; }
    .shop-remove { background: #ffe3e3; border-color: #ffa8a8; color: #c2255c; }
    .shop-remove[hidden], .shop-buy[hidden] { display: none; }

    /* v50: a locked pet is a full farm pet (same size, same roaming,
       styled by the .farm-pet rules) — the v46 fixed corner slot is
       gone; only the greyed look and the lock badge distinguish it. */
    .locked-pet .p-face { position: relative; }
    /* v72 (Addendum 7): locked pets wear their REAL colours — the
       v50 greyscale silhouette is gone; the lock is a badge, not a
       colour drain. */
    /* v21 (feedback #13.5): the locked fox is the real Pixi fox rig,
       greyed into a silhouette (the static SVG stays as fallback). */
    .locked-pet .pet-mini-host canvas { filter: none; }
    /* v86: plates are always fully opaque now (overlay layer above
       every card) — the old dim-on-collision rule is gone, and with
       it the .45 translucency that let fence posts show through. */
    .fp-plate { transition: opacity .3s ease; }
    /* v112 (F-02): the yard lock badge is a price pill ON the card —
       lock + star price together, like the shop card pill. */
    .locked-pet .lock-badge {
      position: absolute; right: 2%; top: 38%; font-size: 13px; font-weight: 800; line-height: 1.25;
      color: #a86e00; background: rgba(255, 243, 191, .96); border-radius: 999px; padding: 2px 8px;
      white-space: nowrap; filter: drop-shadow(0 2px 2px rgba(255,255,255,.7));
    }
    /* v112 (F-02/#345): a shop pet the balance cannot yet cover reads
       muted next to an affordable one; the pet stays fully visible. */
    .shop-card.cant-afford .sc-price { background: #e9ecef; color: #868e96; }
    .shop-card.cant-afford .sc-pic { opacity: .62; filter: grayscale(.45); }
    .locked-pet:active .fp-plate { transform: translateY(2px); }
    /* v50 starter choice: every yard pet gently invites the tap. */
    .starter-pick .p-face { animation: starterPulse 1.7s ease-in-out infinite; }
    @keyframes starterPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.055); } }
    @media (prefers-reduced-motion: reduce) { .starter-pick .p-face { animation: none; } }
    .farm-pet[data-pet="fox"] { left: 35%; top: 32%; width: 31%; z-index: 1; }

    /* SVG-fallback accessory overlay (shown only when no live Pixi pet
       is mounted in the same face box; hidden via .has-live) */
    .acc-overlay { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
    .has-live > .acc-overlay, .has-live .acc-overlay { display: none; }
    .acc-slot { position: absolute; line-height: 1; display: none; }
    .acc-slot.on { display: block; }
    .acc-slot.acc-head { font-size: clamp(26px, 9cqw, 44px); top: 1%; left: 60%; transform: translateX(-50%) rotate(10deg); }
    .acc-slot.acc-neck { font-size: clamp(20px, 7cqw, 34px); top: 54%; left: 50%; transform: translateX(-50%); }
    #stageAccOverlay .acc-slot.acc-head { font-size: 46px; }
    #stageAccOverlay .acc-slot.acc-neck { font-size: 34px; }


    /* ===== v92 — BẢNG NẰM TRONG CẢNH (bi QA 19:59 + chuẩn G) =====
       Board modes (dots / clock / shapes): in v91 the board sat in
       the shared pale "clearing" panel under a slim stage strip —
       on tall/wide frames the clearing was a huge dead pale area
       and the screen read as TWO boxes, not one yard (the exact
       split the v81 scene law forbids; the clearing works for the
       dense panel games, not for a lone board). The board panel is
       now a GRASS CONTINUATION of the stage's ground — same move
       as the care panel at >=600px (v88 addendum 3): the margin
       closes so grass meets grass, ONE fence, ONE horizon, and the
       whole screen below the sky is meadow. A soft sunlit patch
       stays behind the board itself for contrast. Paint-only: no
       box moves, board geometry is byte-identical to v91. */
    #screen-pet .mode-panel#mode-dots.active,
    #screen-pet .mode-panel#mode-clock.active,
    #screen-pet .mode-panel#mode-shapes.active {
      margin-top: 0;
      /* v99: same continuation recipe as the shared panel rule
         (responsive.css) — the .52 sunlit patch started exactly
         at this panel's top edge and read as a colour step
         against the stage's ground; the board's own white card
         carries the contrast now. */
      background: linear-gradient(180deg, #5cca52 0%, #4cc24a 100%);
    }

    /* ===== v92 — ĐỒNG HỒ CŨNG THEO CHUẨN VÙNG ĐÁY (spec §5) =====
       Same disease as trace: at 414×736 the clock panel overflowed
       by 90px. Same cure: panel becomes a column, the face zone
       flexes, the face SVG is sized by the height actually left
       (square via aspect-ratio, capped at the size the cascade
       effectively gave it in v91 — 272px stage cap), and the
       actions/choices stay pinned below it. At frames with room
       the face is byte-identical to v91. */
    #screen-pet .mode-panel#mode-clock.active { display: flex; flex-direction: column; overflow: hidden; }
    #screen-pet #mode-clock .clock-wrap {
      flex: 1 1 auto; min-height: 0; width: 100%; margin: 2px auto 0;
      max-height: min(calc(var(--stage-w) * 0.74), 272px);
      display: flex; align-items: flex-start; justify-content: center;
    }
    @media (max-height: 780px) {
      #screen-pet #mode-clock .clock-scene { min-height: 34px; }
      #screen-pet #mode-clock .clock-wrap { margin-top: 0; }
      #screen-pet #mode-clock .clock-actions { margin-top: 4px; }
      /* the msg line's 60px reserve is the last overflow at short
         frames — the line still fits one wrapped line at 26px */
      #screen-pet #mode-clock .msg { min-height: 26px; margin-top: 2px; }
    }
    #screen-pet #mode-clock .clock-svg {
      height: 100%; width: auto; aspect-ratio: 1 / 1;
      max-width: min(calc(var(--stage-w) * 0.74), 272px);
      max-height: min(calc(var(--stage-w) * 0.74), 272px);
    }
    #screen-pet #mode-clock .clock-scene,
    #screen-pet #mode-clock .clock-actions,
    #screen-pet #mode-clock .clock-choices,
    #screen-pet #mode-clock .clock-digital { flex: 0 0 auto; }

    /* ===== v92 — SHOP ĐEO SÂN CHUNG + TỜ BAY VÀO (chuẩn G + H) =====
       The shop used to wear its own pink backdrop — a different
       background system from the shared yard (commitment item 1:
       the yard is the background of EVERY screen). It now wears
       the stage's own sky→grass gradient at the stage's horizon
       stops, and anim.js injects the same .scene-bg yard layer
       (sun, clouds, fence, meadow) behind the cards. The lookup
       sheets (number chart / ABC board) and the shop content land
       like props: a short rise-in when the sheet opens. Paint and
       motion only — no shop/chart geometry changes. */
    .shop-overlay {
      background: linear-gradient(180deg, #8fd8ff 0%, #c8f2ff 54%, #93e77e 54.5%, #63d64f 100%);
    }
    .shop-overlay > :not(.scene-bg) { position: relative; }
    .shop-overlay:not([hidden]) .shop-grid,
    .chart-overlay:not([hidden]) .chart-grid,
    .abc-overlay:not([hidden]) .abc-grid {
      animation: sheetRiseIn .38s cubic-bezier(.2,.7,.3,1) both;
    }
    .chart-overlay:not([hidden]) .chart-big,
    .abc-overlay:not([hidden]) .abc-big { animation: sheetRiseIn .34s cubic-bezier(.2,.7,.3,1) both; }
    @keyframes sheetRiseIn { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
    @media (prefers-reduced-motion: reduce) {
      .shop-overlay:not([hidden]) .shop-grid,
      .chart-overlay:not([hidden]) .chart-grid,
      .abc-overlay:not([hidden]) .abc-grid,
      .chart-overlay:not([hidden]) .chart-big,
      .abc-overlay:not([hidden]) .abc-big { animation: none; }
    }

    /* ===== v109 (I — S107E): shop grid on short sheets =====
       The grid is vertically CENTRED (align-content: center); when
       the card stack is taller than the sheet's leftover space
       (414x628: content 575px vs 519px box), centring pushes the
       first row UP over the header — the dog card covered the
       "Cửa hàng" title and the star pill, with the yard scene
       showing through the gap. On short sheets the grid now starts
       under the header and scrolls internally (its designed
       behaviour); tall sheets keep the centred layout untouched. */
    @media (max-height: 760px) {
      .shop-grid { align-content: start; overflow-y: auto; }
    }
