      /* ---------- Mul/Div visual stage ---------- */
      .muldiv-stage { margin-top: 14px; }
      .md-groups, .md-bowls { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
      .md-group, .md-bowl { border-radius: 18px; padding: 8px 10px; min-width: 86px; }
      .md-group { background: #fff8e1; border: 3px dashed #ffb84d; }
      .md-group.done { border-style: solid; border-color: var(--teal); background: #e6fff8; }
      .md-bowl { background: #eaf8ff; border: 3px solid #57b6f7; cursor: pointer; }
      .md-group-items, .md-bowl-items { display: flex; flex-wrap: wrap; gap: 3px; justify-content: center; align-items: center; min-height: 30px; max-width: 124px; margin: 0 auto; }
      .md-mini { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; }
      .md-mini svg { width: 24px; height: auto; }
      .md-mini.empty { border: 2px dashed #cfdff0; border-radius: 50%; width: 22px; height: 22px; }
      .md-bowl .md-mini { cursor: pointer; }
      .md-group-label, .md-bowl-label { font-weight: 800; font-size: 16px; color: var(--ink-soft); margin-top: 4px; text-align: center; }
      .food-btn.sel { outline: 4px solid var(--sun); outline-offset: 2px; }
      .md-reset { margin-top: 10px; min-height: 48px; padding: 8px 20px; border-radius: 999px; border: 3px solid #57b6f7; background: var(--card); font-weight: 800; font-size: 17px; color: var(--sky-dark); cursor: pointer; }

      /* ---------- Number / answer choices ---------- */
      .choices { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-top: 16px; }
      .choices.hide { display: none; }
      .choice-btn {
        min-height: 110px; border-radius: 24px; font-size: 56px; font-weight: 800;
        background: var(--card); border: 4px solid var(--sky); color: var(--sky-dark);
        box-shadow: 0 6px 0 rgba(35,50,70,.14); display: flex; align-items: center; justify-content: center;
      }
      .choice-btn:active { transform: translateY(3px); box-shadow: none; }
      .choice-btn.right { background: var(--teal); border-color: var(--teal-dark); color: #fff; }
      .choice-btn.wrong { background: #ffe1e1; border-color: var(--coral); color: var(--coral-dark); }

      /* ---------- Letter cookies ---------- */
      .cookie-choices { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-top: 18px; }
      .cookie-choices.cols4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
      /* v72: the cookie is a drawn SVG (cookieFaceHTML in helpers.js):
         golden disc, dotted baked edge, choc chips, glyph on top.
         The button itself is transparent — the art carries the look,
         the drop-shadow replaces the old offset box shadow. */
      .cookie-btn {
        position: relative;
        aspect-ratio: 1 / 1; max-width: 150px; width: 100%; margin: 0 auto; padding: 0;
        border-radius: 50%;
        background: none; border: none; color: #5b3a12;
        font-size: 62px; font-weight: 800; line-height: 1;
        display: flex; align-items: center; justify-content: center;
        transition: transform .18s ease, opacity .3s ease;
      }
      .cookie-btn .cookie-art { width: 100%; height: 100%; display: block; filter: drop-shadow(0 6px 0 rgba(35,50,70,.16)); }
      .cookie-btn .cookie-ch {
        position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
        font-size: 62px; font-weight: 800; color: #5b3a12; line-height: 1;
      }
      .cookie-choices.cols4 .cookie-btn .cookie-ch { font-size: 50px; }
      .cookie-btn:active { transform: scale(.93); }
      .cookie-btn.eaten { opacity: 0; transform: scale(.25) rotate(20deg); pointer-events: none; }
      .cookie-btn.right { box-shadow: 0 0 0 5px var(--teal), 0 0 0 9px var(--teal-dark); }
      /* F1 (Issue #61) letters fixes: dimmed losers after a win, the
         session cookie counter, the daily-limit warning chip, and a
         compact win banner that no longer covers the cookie row. */
      .cookie-btn.dim { opacity: .3; }
      .letter-round-note { position: absolute; top: 8px; right: 14px; font-size: 15px; font-weight: 800; color: var(--ink-soft); }
      .limit-warn { width: fit-content; max-width: 92%; margin: 10px auto 0; padding: 6px 14px; border-radius: 999px; background: #fdf0d5; border: 2px solid var(--sun-dark); font-size: 15px; font-weight: 800; color: var(--ink); text-align: center; }
      .limit-warn.urgent { background: #ffd9c9; border-color: var(--coral-dark); }
      .limit-warn[hidden] { display: none; }
      /* Reserved space + compaction for the win bar's strip: the panel
         grows by the bar's height (the stage flexes, as it does per
         mode elsewhere) and the cookie row compacts, so the bar
         overlays only the reserved strip — never the cookies.
         (The bar itself is the shared v69 slim bar in base.css.)
         v79 (frame stability): this geometry is now PERMANENT — the
         :has(> .win-banner) form (the banner element always exists in
         the panel; only its .show class toggles) applies the win-state
         reserve + compaction at ALL times, in every mode below and in
         dots.css/layout.css, so the win moment itself moves nothing:
         bi's rule is that a frame's box may only change its contents,
         never its geometry. The :has() argument keeps the original
         specificity, so the cascade is unchanged. */
      #screen-pet #mode-letter.mode-panel:has(> .win-banner) { padding-bottom: 84px; }
      #screen-pet #mode-letter.mode-panel:has(> .win-banner) .letter-word { font-size: 19px; line-height: 1.4; margin-top: 2px; }
      #screen-pet #mode-letter.mode-panel:has(> .win-banner) .cookie-choices { margin-top: 4px; gap: 10px; }
      #screen-pet #mode-letter.mode-panel:has(> .win-banner) .cookie-btn { max-width: 54px; font-size: 36px; }
      #screen-pet #mode-letter.mode-panel:has(> .win-banner) .msg { min-height: 24px; margin-top: 4px; }
      /* F1 (Issue #61) listen fix: while the win bar is up the panel
         reserves its strip and the choices compact, so no card is
         covered. (The bar itself is the shared v69 slim bar in
         base.css — it replaced the stock 206px banner that painted
         over the answer cards.) */
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) { padding-bottom: 84px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-choices { min-height: 0; margin-top: 2px; gap: 8px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-card { min-width: 72px; min-height: 76px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-card .lc-pic { font-size: 46px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-card .lc-ch { font-size: 40px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-card .lc-word { font-size: 14px; min-height: 16px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-show { min-height: 40px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-show .ls-pic { font-size: 40px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .listen-show .ls-big { font-size: 42px; }
      #screen-pet #mode-listen.mode-panel:has(> .win-banner) .msg { min-height: 24px; margin-top: 4px; }

      /* F1 (Issue #61) word fixes: slim one-row win banner + reserved
         space like letters, so the banner never covers the slot row
         (Nhe 4); a gentler banner pop whose scale stays inside the
         panel's 12px insets plus clipped horizontal overflow in the
         word panel, so no transient streak can grow a horizontal
         scrollbar at win time (Nhe 6); the slot-row word-boundary
         spacer is a fixed narrow gap (Nhe 5). */
      #screen-pet #mode-word { overflow-x: clip; }
      /* the shared v69 slim bar, with word's gentler pop (Nhe 6) */
      #screen-pet #mode-word .win-banner.show { animation: wordWinPop .45s ease; }
      #screen-pet #mode-word.mode-panel:has(> .win-banner) { padding-bottom: 84px; }
      #screen-pet #mode-word.mode-panel:has(> .win-banner) .word-pic { font-size: 60px; }
      #screen-pet #mode-word.mode-panel:has(> .win-banner) .msg { min-height: 24px; margin-top: 4px; }
      #screen-pet #mode-word .word-slot-gap { flex: 0 0 16px; width: 16px; }
      @keyframes wordWinPop { 0% { transform: scale(1); } 40% { transform: scale(1.03); } 100% { transform: scale(1); } }

      /* ---------- Number foods ---------- */
      .num-choices { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-top: 18px; }
      .num-food {
        position: relative; min-height: 118px; border-radius: 24px;
        background: #bdeaff; border: 3px solid #57b6f7;
        display: flex; align-items: center; justify-content: center; padding: 10px;
        box-shadow: 0 6px 0 rgba(35,50,70,.12);
        transition: transform .18s ease, opacity .3s ease;
      }
      .num-food svg { width: 72px; height: auto; }
      .num-food:active { transform: scale(.94); }
      .num-food .num-badge {
        position: absolute; top: 8px; right: 8px; width: 48px; height: 48px; border-radius: 50%;
        background: var(--sky); border: 3px solid #fff; color: #fff;
        font-size: 28px; font-weight: 800; display: flex; align-items: center; justify-content: center;
        box-shadow: 0 3px 0 rgba(35,50,70,.2);
      }
      .num-food.eaten { opacity: 0; transform: scale(.25); pointer-events: none; }
      .num-food.right { border-color: var(--teal-dark); box-shadow: 0 0 0 5px var(--teal); }
      /* v69: while the win bar is up the panel reserves the bar's
         strip and the food cards compact, so the bar never paints
         over them (on short screens the tall cards otherwise reach
         into the bar). */
      #screen-pet #mode-feed.mode-panel:has(> .win-banner) { padding-bottom: 88px; }
      #screen-pet #mode-number.mode-panel:has(> .win-banner) { padding-bottom: 88px; }
      #screen-pet #mode-number.mode-panel:has(> .win-banner) .num-choices { margin-top: 8px; }
      #screen-pet #mode-number.mode-panel:has(> .win-banner) .num-food { min-height: 84px; }
      #screen-pet #mode-number.mode-panel:has(> .win-banner) .num-food svg { width: 52px; }

      /* ---------- Sentence builder ---------- */
      /* v65 (Workstream C): in-flow back pill in the header row
         (see .sent-head in pets.css) — same row as the task and
         the pet corner, so nothing floats over the game. */
      .sent-back {
        flex: 0 0 auto;
        display: inline-flex; align-items: center; gap: 6px;
        font: inherit; font-size: 17px; font-weight: 800; color: #26436b;
        background: #eef6ff; border: 2px solid var(--line); border-radius: 999px;
        padding: 8px 14px; cursor: pointer; box-shadow: 0 3px 0 rgba(35, 50, 70, .15);
      }
      .sent-back:active { transform: translateY(2px); box-shadow: none; }
      /* v65: the unified feeding game's ONE fullness meter — fills
         with every eaten item and every finished request; at 100%
         the pet is full and the session's single win fires. */
      .feed-meter { display: flex; align-items: center; gap: 8px; margin: 2px 4px 8px; }
      .feed-meter-track { flex: 1 1 auto; height: 16px; border-radius: 999px; background: #e7eef5; border: 2px solid var(--line); overflow: hidden; }
      .feed-meter-fill { height: 100%; width: 0%; border-radius: 999px; background: linear-gradient(90deg, #ffd166, #ff8fa3); transition: width .45s ease; }
      .feed-meter .fm-ico { font-size: 19px; line-height: 1; flex: 0 0 auto; }
      .sentence-slots { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; margin: 14px 0 6px; }
      .slot {
        background: var(--bg); border: 3px solid var(--line); border-radius: 22px;
        padding: 12px 10px; min-width: 104px; text-align: center;
      }
      .slot .s-pic { font-size: 52px; line-height: 1.15; }
      .slot .s-word { font-size: 24px; font-weight: 800; line-height: 1.2; }
      .slot .s-role { font-size: 13px; font-weight: 700; color: var(--ink-soft); }
      .pick-row { margin-top: 10px; text-align: left; flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; }
      .pick-row:first-child { margin-top: 4px; }
      .pick-row .p-label { font-size: 19px; font-weight: 800; margin: 0 0 8px; }
      .pick-opts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: 1fr; gap: 10px; flex: 1 1 auto; min-height: 0; }
      .pick-opt {
        background: var(--card); border: 3px solid var(--line); border-radius: 20px;
        padding: 10px 4px; min-height: 0; font-size: 19px; font-weight: 800; line-height: 1.2;
      }
      .pick-opt .p-pic { font-size: 38px; display: block; margin-bottom: 2px; }
      .pick-opt.sel { border-color: var(--cat); background: #fff1e2; box-shadow: 0 0 0 3px var(--cat) inset; }
      .sentence-read { margin-top: 20px; text-align: center; }
      .sentence-text { font-size: 30px; font-weight: 800; margin: 0 0 14px; line-height: 1.35; }

      /* ---------- Sentence patterns D4 (v39, ja only) ---------- */
      .sent-d4-bar { display: flex; gap: 8px; justify-content: center; margin-top: 8px; flex: 0 0 auto; }
      .sent-d4-bar[hidden] { display: none; }
      .sent-d4-btn {
        min-height: 38px; padding: 5px 14px; border-radius: 999px; cursor: pointer;
        background: var(--card); border: 3px solid var(--line); color: var(--ink);
        font-size: 15px; font-weight: 800; line-height: 1.15;
      }
      .sent-d4-btn.sel { border-color: var(--cat); background: #fff1e2; box-shadow: 0 0 0 3px var(--cat) inset; }
      .sent-d4-btn:active { transform: scale(.95); }
      /* D4 pick row: 10 slot words in a 5x2 grid, icon over word so the
         longest slot (おかあさん) still shows in full; fitSentence()
         steps the font down further when a card is tight. */
      #mode-sentence #sentPicks.d4-picks .pick-opts {
        display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; flex: 1 1 auto; min-height: 0;
      }
      #mode-sentence #sentPicks.d4-picks .pick-opt {
        flex-direction: column; gap: 1px; padding: 6px 2px; font-size: 15px;
      }
      #mode-sentence #sentPicks.d4-picks .pick-opt .p-pic { font-size: 25px; margin-bottom: 0; }

      /* v47: the note is decorative parent-facing text — it must
         NEVER eat a tap (on short screens it used to overlap the
         farm pets' buttons and swallow their taps whole). */
      .foot-note { text-align: center; color: var(--ink-soft); font-size: 15px; font-weight: 600; margin-top: 22px; line-height: 1.5; pointer-events: none; }
      .top-actions{display:flex;gap:8px;align-items:center}
      .icon-btn{min-height:52px;min-width:52px;border-radius:999px;background:var(--card);border:3px solid var(--line);box-shadow:var(--shadow);font-size:22px;display:inline-flex;align-items:center;justify-content:center;padding:6px 12px;font-weight:800}
      .icon-btn:active{transform:translateY(2px);box-shadow:none}
      .parent-entry{margin-top:18px;width:100%;background:var(--card);border:2px dashed var(--line);border-radius:18px;padding:14px;text-align:center}
      .hold-btn{position:relative;overflow:hidden;background:#e6f6ff;border:2px solid var(--line);border-radius:999px;padding:12px 18px;font-weight:700;font-size:15px;color:var(--ink-soft);min-height:52px;width:100%}
      .hold-btn .hold-fill{position:absolute;inset:0;background:#bfe8df;width:0%}
      .hold-btn span{position:relative;z-index:1}
      .hold-btn.holding .hold-fill{transition:width 3s linear;width:100%}

      /* ---------- Garden (v30): plant -> water daily -> harvest ---------- */
      .garden-board {
        display: flex; gap: 10px; align-items: stretch;
        background: linear-gradient(180deg, #aee7ff 0%, #d9f7c9 52%, #9ede7d 100%);
        border: 3px solid #4cb944; border-radius: 22px;
        padding: 10px; margin-top: 2px; box-shadow: var(--shadow);
      }
      .garden-buddy { flex: 0 0 72px; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; }
      .garden-buddy-art { width: 72px; height: 72px; }
      .garden-buddy-art svg { width: 100%; height: 100%; }
      .garden-buddy-name { font-weight: 800; font-size: 13px; color: #2d5a1e; margin-top: 2px; }
      .garden-buddy.cheer .garden-buddy-art { animation: gBounce .45s ease 2; }
      .garden-buddy.eat .garden-buddy-art { animation: gEat .28s ease 4; }
      @keyframes gBounce { 50% { transform: translateY(-13px); } }
      @keyframes gEat { 50% { transform: scale(1.13) rotate(-3deg); } }
      /* v44: card labels marked .fit-label are shrunk by fitTextEl
         (js/core/helpers.js) until the whole label fits — the
         ellipsis/clamp pattern is banned in the kid area. */
      .fit-label { white-space: nowrap; }
      .garden-plots { flex: 1 1 auto; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
      .g-plot {
        position: relative; min-height: 76px; border-radius: 16px; cursor: pointer;
        border: 3px solid #8a5a2b; color: #fff;
        background: linear-gradient(180deg, #b57e46 0%, #96662f 70%, #7d5222 100%);
        box-shadow: inset 0 -8px 0 rgba(0,0,0,.13), 0 4px 0 rgba(35,50,70,.14);
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
        padding: 4px 2px 5px; transition: transform .15s ease, box-shadow .2s ease;
      }
      .g-plot:active { transform: scale(.95); }
      .g-plot.empty { border-style: dashed; border-color: #a9743a; background: linear-gradient(180deg, #c08b4e, #a06a30); }
      .g-plot.asking { box-shadow: 0 0 0 4px #ffd93c, inset 0 -8px 0 rgba(0,0,0,.13); }
      .g-plot.ripe { border-color: #e8a100; animation: gGlow 1.1s ease-in-out infinite alternate; }
      @keyframes gGlow { from { box-shadow: 0 0 0 2px rgba(255,201,60,.55), inset 0 -8px 0 rgba(0,0,0,.13); } to { box-shadow: 0 0 0 6px rgba(255,201,60,.95), inset 0 -8px 0 rgba(0,0,0,.13); } }
      .g-pic { font-size: 36px; line-height: 1.05; }
      .g-ghost { opacity: .38; filter: grayscale(.4); }
      .g-plus {
        position: absolute; top: 4px; right: 8px; font-size: 20px; font-weight: 800; color: #ffe9b0;
        text-shadow: 0 1px 0 rgba(0,0,0,.25);
      }
      .g-tag {
        background: rgba(255,255,255,.92); color: #5b3a12; border-radius: 999px;
        font-size: 14px; font-weight: 800; line-height: 1; padding: 3px 8px; max-width: 96%;
        white-space: nowrap;
      }
      .g-badge { position: absolute; top: 3px; right: 5px; font-size: 17px; line-height: 1; }
      .g-badge.thirst { animation: gBounce .8s ease-in-out infinite alternate; }
      .g-badge.done { opacity: .75; font-size: 13px; }
      .g-rain {
        position: absolute; inset: 2px auto auto 50%; transform: translateX(-50%);
        font-size: 15px; letter-spacing: 2px; pointer-events: none;
        animation: gRain .9s ease-in forwards;
      }
      @keyframes gRain { from { opacity: 0; margin-top: -6px; } 25% { opacity: 1; } to { opacity: 0; margin-top: 16px; } }
      /* v86 (PLAY1 garden notice): the panel stack ran ~25px long, so
         the two-line watered-today notice slid under the pet pill
         (its second line hidden behind the pill). Trim the stack:
         plots 88→76px min-height (two rows: −24px) and tighten the
         show/choices margins — the notice then ends above the pill
         with room to spare, at every height band. */
      .garden-show { min-height: 36px; margin-top: 4px; font-size: 24px; font-weight: 800; color: var(--ink); display: flex; align-items: center; justify-content: center; }
      .garden-show .g-drops { font-size: 26px; letter-spacing: 4px; }
      .garden-show ruby rt { font-size: 12px; }
      .garden-choices { display: flex; gap: 10px; justify-content: center; margin-top: 2px; min-height: 68px; }
      .garden-choice {
        min-width: 82px; min-height: 64px; padding: 6px 12px; border-radius: 18px; cursor: pointer;
        background: #fff8e6; border: 3px solid #f2b544; color: var(--ink);
        font-size: 30px; font-weight: 800; line-height: 1.1;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
        box-shadow: 0 5px 0 rgba(35,50,70,.14); transition: transform .15s ease;
      }
      .garden-choice:active { transform: scale(.94); }
      .garden-choice.right { border-color: var(--teal-dark); box-shadow: 0 0 0 5px var(--teal); }
      .garden-choice .gc-pic { font-size: 34px; line-height: 1; }
      .garden-choice .gc-cap { font-size: 15px; font-weight: 700; }
      .garden-choice .gc-cap ruby rt { font-size: 9px; }
      .garden-count { margin: 6px 0 0; font-size: 16px; font-weight: 800; color: #2d5a1e; min-height: 20px; }
      

      /* ---------- Listen & guess (v31): hear it, tap the card ---------- */
      .listen-replay-row { display: flex; justify-content: center; margin-top: 8px; }
      .listen-replay {
        min-height: 60px; padding: 10px 30px; border-radius: 999px; cursor: pointer;
        background: linear-gradient(180deg, #ffd93c, #ffb703); color: #5b3a12;
        border: 3px solid #e8a100; font-size: 24px; font-weight: 800;
        box-shadow: 0 5px 0 rgba(35,50,70,.16); transition: transform .15s ease;
      }
      .listen-replay:active { transform: scale(.94); }
      .listen-show {
        min-height: 64px; margin-top: 8px; display: flex; align-items: center; justify-content: center; gap: 10px;
        font-size: 26px; font-weight: 800; color: var(--ink);
      }
      .listen-show .ls-ear { font-size: 44px; animation: lsPulse 1.2s ease-in-out infinite alternate; }
      @keyframes lsPulse { from { transform: scale(1); } to { transform: scale(1.14); } }
      .listen-show .ls-pic { font-size: 52px; line-height: 1; }
      .listen-show .ls-big { font-size: 54px; line-height: 1.1; }
      .listen-show .ls-cap { font-size: 28px; }
      .listen-show .ls-cap ruby rt { font-size: 13px; }
      .listen-choices { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 6px; min-height: 118px; }
      .listen-card {
        min-width: 92px; min-height: 110px; padding: 8px 12px; border-radius: 20px; cursor: pointer;
        background: #eef7ff; border: 3px solid #74c0fc; color: var(--ink);
        font-weight: 800; line-height: 1.1;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
        box-shadow: 0 5px 0 rgba(35,50,70,.14); transition: transform .15s ease;
      }
      .listen-card:active { transform: scale(.94); }
      .listen-card.right { border-color: var(--teal-dark); box-shadow: 0 0 0 5px var(--teal); background: #e6fcf5; }
      /* v71: choice pictures go BIG and clear; the word-label slot
         under the picture is part of every word card — empty while
         the question is live (listening first), filled on the
         answered card so the round ends as picture + word. */
      .listen-card .lc-pic { font-size: 68px; line-height: 1.05; }
      .listen-card .lc-ch { font-size: 52px; line-height: 1.1; }
      .listen-card .lc-word { font-size: 17px; font-weight: 900; color: var(--ink); min-height: 20px; line-height: 1.15; }
      .listen-card .lc-word rt { font-size: .48em; }
      

      /* ---------- Pet race (v87): a real race — the pet and three
         friends on a dirt track (grass lip, checkered start post,
         dashed white lane lines, a flag at every finish). Runners
         idle-bounce while waiting so the track never looks frozen;
         .hop / .dash / .sad are the frolic, correct and wrong
         beats driven from race.js. ---------- */
      .race-track {
        position: relative; overflow: hidden;
        margin-top: 10px; padding: 8px 10px; border-radius: 18px;
        background:
          linear-gradient(180deg, #95d5b2 0 12px, rgba(255,255,255,0) 12px),
          repeating-linear-gradient(90deg, rgba(255,255,255,.13) 0 3px, rgba(255,255,255,0) 3px 54px),
          linear-gradient(180deg, #f2a65a, #dd7f45);
        border: 3px solid #b5651d;
      }
      .race-track::before {
        content: ""; position: absolute; left: 3px; top: 6px; bottom: 6px; width: 7px;
        border-radius: 3px; opacity: .85;
        background: repeating-linear-gradient(45deg, #fff 0 6px, #233246 6px 12px);
      }
      .race-lane {
        position: relative; height: 44px; margin: 2px 0;
        border-bottom: 3px dashed rgba(255,255,255,.55);
      }
      .race-lane:last-child { border-bottom: none; }
      .race-lane.pet-lane { background: rgba(255,255,255,.20); border-radius: 10px; }
      .race-runner {
        position: absolute; left: 0; top: 50%; transform: translateY(-50%); z-index: 1;
        font-size: 36px; line-height: 1; transition: left .45s ease;
      }
      .race-runner.pet-runner { animation: raceIdle 1.5s ease-in-out infinite; }
      .race-runner.friend-runner { animation: raceIdle 1.9s ease-in-out infinite; }
      .race-lane:nth-child(3) .friend-runner { animation-delay: .35s; }
      .race-lane:nth-child(4) .friend-runner { animation-delay: .7s; }
      @keyframes raceIdle { 0%, 100% { transform: translateY(-50%); } 50% { transform: translateY(-66%); } }
      .race-runner.hop { animation: raceHop .6s ease; }
      @keyframes raceHop { 0% { transform: translateY(-50%); } 35% { transform: translateY(-118%) scale(1.08); } 100% { transform: translateY(-50%); } }
      .race-runner.dash { animation: raceDash .5s ease; }
      @keyframes raceDash { 0% { transform: translateY(-50%); } 40% { transform: translateY(-78%) scale(1.16) rotate(-4deg); } 100% { transform: translateY(-50%); } }
      .race-runner.sad { animation: raceSad .7s ease; filter: grayscale(.5); }
      @keyframes raceSad { 0%, 100% { transform: translateY(-50%); } 30% { transform: translateY(-32%) rotate(-7deg); } 60% { transform: translateY(-38%) rotate(5deg); } }
      .race-flag { position: absolute; right: 0; top: 50%; transform: translateY(-50%); font-size: 30px; line-height: 1; }
      .race-show {
        min-height: 60px; margin-top: 6px; display: flex; align-items: center; justify-content: center; gap: 10px;
        font-size: 26px; font-weight: 800; color: var(--ink);
        /* v109 (H): the show shares a row with the choices (flex
           0 1 auto beside flex 1 1 0) but rendered as a BARE glyph
           — no box at all — so it read as a broken 4th card cut at
           the row's left edge (S107E/#340 "bare 15", pinned by
           geometry: #raceShow [12,384 50x60] beside #raceChoices
           [62,380] at 414x628, same at 412x915). Give the target a
           chip identity of its own: dashed "find this" box, never
           mistakable for an answer card. */
        background: #ffffff; border: 3px dashed #ffa94d; border-radius: 18px;
        padding: 4px 12px; min-width: 64px; align-self: center;
        box-shadow: 0 3px 0 rgba(35,50,70,.10);
      }
      .race-show .rc-pic { font-size: 50px; line-height: 1; }
      .race-show .rc-big { font-size: 52px; line-height: 1.1; }
      .race-choices { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 4px; min-height: 84px; }
      .race-card {
        min-width: 82px; min-height: 80px; padding: 8px 12px; border-radius: 20px; cursor: pointer;
        background: #fff4e0; border: 3px solid #ffa94d; color: var(--ink);
        font-weight: 800; line-height: 1.1;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
        box-shadow: 0 5px 0 rgba(35,50,70,.14); transition: transform .15s ease;
      }
      .race-card:active { transform: scale(.94); }
      .race-card.right { border-color: var(--teal-dark); box-shadow: 0 0 0 5px var(--teal); background: #e6fcf5; }
      .race-card.wrong { border-color: #e8590c; background: #ffe8cc; }
      /* v109 (D): a dimmed (disabled) answer card swallows taps
         completely — no visual life, no events. */
      .race-card:disabled { opacity: .55; pointer-events: none; }
      .race-card .rc-ch { font-size: 42px; line-height: 1.1; }
      .race-card .rc-cap { font-size: 24px; }
      .race-card .rc-cap ruby rt { font-size: 12px; }
      
      /* v86 (PLAY1 banner-over-score): race and house had NO win-state
         reserve at all — the absolute banner dropped straight onto
         the score + message lines (race: 62% of the score line
         covered; house: 100% of score AND message). Reserve the
         strip like every other mode, collapse the choices row the
         finish empties (an 84/78px empty box was pushing the score
         down into the strip), and compact the finish stack so it
         ends above the strip with margin. */
      #screen-pet #mode-race.mode-panel:has(> .win-banner.show) { padding-bottom: 88px; }
      #screen-pet #mode-race.mode-panel:has(> .win-banner) .race-show { min-height: 50px; }
      #screen-pet #mode-race.mode-panel:has(> .win-banner) .race-lane { height: 40px; }
      #screen-pet #mode-race.mode-panel:has(> .win-banner) .race-track { padding: 6px 10px; }
      #screen-pet #mode-race.mode-panel:has(> .win-banner.show) .task-sub { display: none; }
      /* v87: the 4-lane track is ~84px taller than the old 2-lane
         one, so at the win moment the flow (track + show + score +
         msg) overflowed the panel and the message line slid into
         the 88px reserve, under the banner (measured overlap
         40px x full width). At win the show strip only repeats the
         banner's own message (pet + trophy), so it yields its 50px:
         the stack ends above the reserve. Geometry of the tracked
         frame (stage/panel/track) is untouched — the show is not
         part of it and lane heights stay constant start-to-win. */
      #screen-pet #mode-race.mode-panel:has(> .win-banner.show) .race-show { display: none; }
      .race-choices:empty { min-height: 0; margin-top: 0; }

      /* LAYOUT-FIX-D 5 (race entry fit, spec 5.1/5.2): panel thanh hang flex
         quan — track 100%, show + choices chung 1 hang (cau hoi ben canh
         dap an), score/msg 100%. Lane co 44->32 de hang dap an len tren
         fold o 414x736; quy tac thang (lane 40, show an, choices rong)
         van ap dung khi banner .show. */
      #screen-pet #mode-race.mode-panel.active { display: flex; flex-wrap: wrap; align-content: flex-start; }
      #screen-pet #mode-race.mode-panel.active .race-track { flex: 1 1 100%; margin-top: 6px; padding: 6px 10px; }
      /* v99: lanes 32 -> 30 — with the stage trim (responsive.css)
         the panel fits without internal scroll at 1024x768; the
         win-state lane rule below (40px, earlier in file) is
         overridden by this later :has rule as before. */
      #screen-pet #mode-race.mode-panel.active:has(> .win-banner) .race-lane { height: 30px; }
      #screen-pet #mode-race.mode-panel.active .race-runner { font-size: 30px; }
      #screen-pet #mode-race.mode-panel.active .race-flag { font-size: 26px; }
      #screen-pet #mode-race.mode-panel.active:has(> .win-banner) .race-show { flex: 0 1 auto; min-height: 48px; margin-top: 4px; }
      #screen-pet #mode-race.mode-panel.active .race-show .rc-pic { font-size: 42px; }
      #screen-pet #mode-race.mode-panel.active .race-show .rc-big { font-size: 44px; }
      #screen-pet #mode-race.mode-panel.active .race-choices { flex: 1 1 0; margin-top: 4px; min-height: 64px; }
      #screen-pet #mode-race.mode-panel.active .race-card { min-height: 64px; padding: 6px 10px; }
      #screen-pet #mode-race.mode-panel.active .race-card .rc-ch { font-size: 36px; }
      #screen-pet #mode-race.mode-panel.active .race-card .rc-cap { font-size: 20px; }
      #screen-pet #mode-race.mode-panel.active .game-score { flex: 1 1 100%; }
      #screen-pet #mode-race.mode-panel.active .msg { flex: 1 1 100%; }

      /* LAYOUT-FIX2-S #267: short frames (414x736) - the race status
         line sat under the pet pill (panel visible window ends exactly
         at the pill top). NOTE: this block MUST stay after the FIX-D
         flex block above: same-specificity trims lose to it if placed
         earlier, and the FIX-D lane rule (with :has) matches even
         outside win state, so lanes need the guard below (win keeps
         32px). Word cards share one row instead of wrapping
         to two, so the score clears the pill in ALL kinds
         (number/letter/word). Pill untouched; taller frames unaffected. */
      @media (max-height: 740px) {
        #screen-pet #mode-race.mode-panel.active .race-track { margin-top: 0; padding: 2px 10px; }
        /* Lanes need one more specificity level: the FIX-D lane rule
           carries :has(> .win-banner) so it matches even outside win
           state; :not(:has(> .win-banner.show)) both outranks it and
           confines the trim to non-win rounds (win keeps 32px). */
        #screen-pet #mode-race.mode-panel.active:not(:has(> .win-banner.show)) .race-lane { height: 28px; }
        #screen-pet #mode-race.mode-panel.active .race-runner { font-size: 26px; }
        #screen-pet #mode-race.mode-panel.active .race-flag { font-size: 22px; }
        #screen-pet #mode-race.mode-panel.active .race-choices { margin-top: 0; }
        #screen-pet #mode-race.mode-panel.active .race-show { margin-top: 0; }
        #screen-pet #mode-race.mode-panel.active .race-card { flex: 1 1 0; min-width: 0; min-height: 60px; }
        #screen-pet #mode-race.mode-panel.active .game-score { margin-top: 0; }
      }

      /* ---------- Cooking by menu (v36): menu -> pot, in order ---------- */
      .cook-menu {
        display: flex; gap: 8px; justify-content: center; flex-wrap: nowrap;
        margin-top: 8px; min-height: 64px;
      }
      .cook-menu-item {
        position: relative; flex: 1 1 0; max-width: 76px; min-width: 0;
        padding: 5px 2px 4px; border-radius: 16px;
        background: #fff8e6; border: 3px solid #f2b544; color: var(--ink);
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
      }
      .cook-menu-item.current { border-color: #e8590c; box-shadow: 0 0 0 4px rgba(255,201,60,.85); animation: cookBlink 1s ease-in-out infinite alternate; }
      @keyframes cookBlink { from { box-shadow: 0 0 0 2px rgba(255,201,60,.55); } to { box-shadow: 0 0 0 6px rgba(255,201,60,.95); } }
      .cook-menu-item.done { opacity: .5; border-color: var(--teal); background: #e6fcf5; }
      .cook-menu-item .cm-pic { font-size: 32px; line-height: 1.05; }
      .cook-menu-item .cm-cap { font-size: 12.5px; font-weight: 800; line-height: 1.15; max-width: 100%; white-space: nowrap; }
      .cook-menu-item .cm-cap ruby rt { font-size: 8px; }
      .cook-menu-item .cm-check { position: absolute; top: -7px; right: -5px; font-size: 15px; font-weight: 900; color: var(--teal-dark); background: #fff; border-radius: 50%; width: 20px; height: 20px; line-height: 20px; }
      .cook-pot {
        margin-top: 4px; min-height: 58px;
        display: flex; align-items: center; justify-content: center; gap: 8px;
      }
      .cook-pot .ck-smoke { font-size: 17px; letter-spacing: 1px; min-width: 30px; text-align: right; }
      .cook-pot .ck-pot { font-size: 50px; line-height: 1; }
      .cook-pot.done .ck-pot { animation: cookBounce .45s ease 2; }
      @keyframes cookBounce { 50% { transform: translateY(-8px) scale(1.08); } }
      .cook-pot .ck-inside { font-size: 21px; line-height: 1.1; max-width: 46%; white-space: nowrap; }
      .cook-tray { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 2px; min-height: 132px; }
      .cook-card {
        width: calc(25% - 7px); max-width: 84px; min-height: 62px; padding: 5px 2px; border-radius: 18px; cursor: pointer;
        background: #eef7ff; border: 3px solid #74c0fc; color: var(--ink);
        font-weight: 800; line-height: 1.1;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
        box-shadow: 0 4px 0 rgba(35,50,70,.14); transition: transform .3s ease, opacity .3s ease;
      }
      .cook-card:active { transform: scale(.94); }
      .cook-card.used { opacity: 0; transform: scale(.25) translateY(14px); pointer-events: none; }
      .cook-card .cc-pic { font-size: 34px; line-height: 1.05; }
      .cook-card .cc-cap { font-size: 12.5px; font-weight: 800; max-width: 100%; white-space: nowrap; }
      .cook-card .cc-cap ruby rt { font-size: 8px; }
      
      /* v69: while the win bar is up the panel reserves the bar's
         strip and the pot + tray compact, so the finished dish and
         the ingredient tray stay fully visible above the bar (the
         tall stacked banner used to cover the tray's lower row). */
      #screen-pet #mode-cook.mode-panel:has(> .win-banner) { padding-bottom: 88px; }
      #screen-pet #mode-cook.mode-panel:has(> .win-banner) .cook-menu-item .cm-pic { font-size: 27px; }
      #screen-pet #mode-cook.mode-panel:has(> .win-banner) .cook-pot { min-height: 46px; }
      #screen-pet #mode-cook.mode-panel:has(> .win-banner) .cook-pot .ck-pot { font-size: 40px; }
      #screen-pet #mode-cook.mode-panel:has(> .win-banner) .cook-tray { min-height: 114px; }
      #screen-pet #mode-cook.mode-panel:has(> .win-banner) .cook-card { min-height: 55px; }
      #screen-pet #mode-cook.mode-panel:has(> .win-banner) .cook-card .cc-pic { font-size: 29px; }

      /* ---------- Fishing for letters (v37): pond of labelled fish ---------- */
      .fish-top {
        margin-top: 8px; display: flex; align-items: center; justify-content: center; gap: 10px;
      }
      .fish-show {
        /* v78 frame stability: reserve the word-caption line at ALL
           times — a word target's caption (fs-cap + ruby) used to
           grow this card 54 -> 80px mid-game, and in the flex column
           every pixel the panel gained came straight out of the pet
           stage (measured: stage 375 -> 304px across one game).
           v88: the reserve was still 6px short — a JA word target
           (fs-pic + ruby caption) measures 90px, so the card grew
           84 -> 90 mid-game and pushed the pond/msg down 6px
           (v78-frame solo catch; letter/number targets are 84px).
           Reserve the true worst case. */
        min-height: 90px; min-width: 96px; padding: 2px 16px;
        display: flex; align-items: center; justify-content: center; gap: 8px;
        background: #fff8e6; border: 3px solid #f2b544; border-radius: 16px;
        font-size: 24px; font-weight: 800; color: var(--ink);
      }
      .fish-show .fs-pic { font-size: 38px; line-height: 1; }
      .fish-show .fs-big { font-size: 40px; line-height: 1.1; }
      .fish-show .fs-cap { font-size: 22px; }
      .fish-show .fs-cap ruby rt { font-size: 11px; }
      .fish-replay {
        min-height: 48px; padding: 6px 18px; border-radius: 999px; cursor: pointer;
        background: linear-gradient(180deg, #ffd93c, #ffb703); color: #5b3a12;
        border: 3px solid #e8a100; font-size: 19px; font-weight: 800;
        box-shadow: 0 4px 0 rgba(35,50,70,.16); transition: transform .15s ease;
      }
      .fish-replay:active { transform: scale(.94); }
      .fish-pond {
        position: relative; margin-top: 6px; height: 196px; overflow: hidden;
        border-radius: 20px; border: 3px solid #1971c2;
        background: linear-gradient(180deg, #a5d8ff 0%, #74c0fc 45%, #4dabf7 100%);
      }
      .fish {
        /* v109 (F): height comes from fishLayoutLanes via --fh on the
           pond (min(36, pondH/5)); 36px remains the fallback. */
        position: absolute; left: 1%; height: var(--fh, 36px); min-width: 104px; max-width: 46%;
        padding: 2px 10px 2px 5px; border-radius: 999px; cursor: pointer;
        background: rgba(255,255,255,.94); border: 3px solid #ffa94d; color: var(--ink);
        font-weight: 800; line-height: 1.1; white-space: nowrap;
        display: flex; align-items: center; gap: 4px;
        box-shadow: 0 4px 0 rgba(35,50,70,.18);
        animation-name: fishSwim; animation-timing-function: ease-in-out;
        animation-iteration-count: infinite; animation-direction: alternate;
        transition: transform .18s ease, opacity .45s ease;
      }
      .fish.wide { max-width: 62%; }
      .fish.rev { animation-direction: alternate-reverse; }
      @keyframes fishSwim { from { left: 1%; } to { left: 52%; } }
      .fish.wide { animation-name: fishSwimWide; }
      @keyframes fishSwimWide { from { left: 1%; } to { left: 36%; } }
      .fish:active { transform: scale(.94); }
      .fish.dart { transform: translateX(34px) rotate(5deg); border-color: #e8590c; }
      .fish.caught { opacity: 0; transform: translateY(-16px) scale(.35); pointer-events: none; }
      /* v109 (F): label sizes scale with the derived tile height
         (--fh = 36px at a full pond, so these match the old fixed
         sizes there: 27 / 25 / 24 / ~15px). */
      .fish .ff-body { font-size: calc(var(--fh, 36px) * 0.75); line-height: 1; }
      .fish .ff-label { display: flex; align-items: center; gap: 3px; min-width: 0; }
      .fish .ff-ch { font-size: calc(var(--fh, 36px) * 0.7); line-height: 1.1; }
      .fish .ff-pic { font-size: calc(var(--fh, 36px) * 0.67); line-height: 1; }
      .fish .ff-cap { font-size: calc(var(--fh, 36px) * 0.42); white-space: nowrap; }
      .fish .ff-cap ruby rt { font-size: 8px; }
      .fish .ff-splash { position: absolute; top: -16px; right: -6px; font-size: 20px; }
      .fish-bottom {
        margin-top: 4px; min-height: 30px;
        display: flex; align-items: center; justify-content: center; gap: 12px;
        flex-wrap: nowrap;
      }
      /* v78 frame stability: the caught-fish string used to wrap to
         a second line from the 3rd catch on (+29px panel, stage
         shrank with every wrap) — one row, always. */
      .fish-bucket { display: flex; align-items: center; gap: 4px; min-height: 30px; flex: 0 0 auto; flex-wrap: nowrap; }
      .fish-bucket .fb-caught { white-space: nowrap; }
      #mode-fish .game-score { white-space: nowrap; }
      .fish-bucket .fb-bucket { font-size: 27px; line-height: 1; }
      .fish-bucket .fb-caught { font-size: 19px; line-height: 1; letter-spacing: 1px; }
      /* LAYOUT-FIX2-D #266 §2 — short viewports: the bucket must clear the pet pill.
         At 414x736 the fish column (show 90 + scene 62 + pond 196 + bucket row 34
         + panel padding) put the bucket bottom 79px past the pill top; the pond
         alone cannot absorb it (196 -> 178 is the sanctioned short pond, -18).
         On heights <= 760px only: hide the decorative rod diorama (fish.js keeps
         building it; jerk/line code runs invisibly), pond to its short 178px,
         and trim the fish column margins/padding. Measured after: bucket bottom
         645 at 414x736, 11px clear of the pill top; 360x800 / 390x844 keep the
         full scene + 196px pond (heights > 760px, already clear). */
      @media (max-height: 760px) {
        #screen-pet #mode-fish.mode-panel.active { padding-top: 0; }
        #mode-fish .fish-top { margin-top: 0; }
        #mode-fish #fishScene { display: none; }
        #mode-fish .fish-pond { height: 178px; }
        #mode-fish .fish-bottom { margin-top: 2px; }
      }
      
      /* v69: while the win bar is up the panel reserves the bar's
         strip so the pond AND the caught-fish bucket row stay clear
         of the bar. v86: the old pond compaction to 164px is GONE —
         it was made permanent by v79, and at 164px five 36px fish on
         19.5% lanes overlap by 4px in EVERY lane (the answer fish's
         edges hidden under its neighbours). The pond stays at its
         natural 196px (178px at short heights), where the lanes
         clear with ~2px gaps; the 88px reserve alone keeps the
         win bar off the content. */
      #screen-pet #mode-fish.mode-panel:has(> .win-banner) { padding-bottom: 88px; }
      #screen-pet #mode-fish.mode-panel:has(> .win-banner) .fish-top { margin-top: 2px; }

      /* v78 frame stability: an empty .msg sits at the compact
         34px min-height (layout.css) but a one-line message stands
         40px — every encouragement line used to grow the panel and
         shrink the yard/stage by 6px mid-game (measured on bath).
         Pin the natural one-line height so text appearing or
         clearing never moves the frame. */
      #mode-fish .msg, #mode-bath .msg, #mode-chase .msg { min-height: 40px; }
      /* v82: pin fishMsg to its fixed one-line height. A loaded
         production probe run in v81 caught the win-moment box one
         line taller (+40px) while every other frame box held 0px;
         re-measurement on a quiet host (continuous sampling, both
         phone viewports, plus a full v78-frame baseline) shows the
         box at exactly 40px in every state — empty, praise and
         try-again lines all fit one line at phone widths. The fixed
         height therefore changes no layout; it removes the one
         remaining variable (font-metric line growth) for good. */
      #mode-fish .msg { height: 40px; }

      
      /* ---------- v70: model garden card (house model/variants removed in v110) ---------- */
      /* Model garden: pinned plan strip above the board — one cell
         per plot, mirroring the board's order (data-gm-idx). */
      .garden-model {
        display: flex; align-items: center; gap: 8px; margin-top: 6px;
        background: #fff8e6; border: 3px solid #f2b544; border-radius: 16px; padding: 5px 8px;
      }
      .gm-label { flex: 0 0 auto; font-weight: 800; font-size: 13.5px; color: var(--ink); white-space: nowrap; }
      .gm-grid { flex: 1 1 auto; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 4px; }
      .gm-cell {
        position: relative; min-height: 38px; border-radius: 10px;
        border: 2px dashed #b08954; background: #e9dcc3;
        display: flex; align-items: center; justify-content: center;
      }
      .gm-cell.target { border-style: solid; border-color: #e8a100; background: #fff3bf; }
      .gm-cell.next { box-shadow: 0 0 0 3px rgba(255, 165, 0, .75); }
      .gm-cell.planted { border-style: solid; border-color: #4cb944; background: #d9f7c9; }
      .gm-cell.future { opacity: .45; }
      .gm-num { position: absolute; top: 0; left: 3px; font-size: 9px; font-weight: 800; color: #8a5a2b; }
      .gm-pic { font-size: 21px; line-height: 1; }
      /* Plan badge on a real empty plot + level-1 target pulse */
      .g-plan-badge {
        position: absolute; top: 3px; left: 5px; width: 25px; height: 25px;
        font-size: 15px; line-height: 25px; text-align: center;
        background: rgba(255, 255, 255, .94); border-radius: 50%;
        box-shadow: 0 1px 3px rgba(35, 50, 70, .3);
      }
      .g-plot.g-target { animation: gGlow 1.1s ease-in-out infinite alternate; }
      /* Short viewports (phones): the garden model card adds height,
         so its cells compact to keep panel + board on one screen.
         (The house scene compaction that shared this band was
         removed with the v110 house rebuild.) */
      @media (max-height: 880px) {
        .gm-cell { min-height: 32px; }
        .gm-pic { font-size: 18px; }
      }

      /* LAYOUT-FIX-D 4 (spec verbatim colours): the shared yard
         window — since v110 only cook still uses it (the house
         scene's own window was removed with the rebuild). */
      /* ===== LAYOUT-FIX-S #261 §3 — panel liền cảnh (mẫu v92 dots/clock/shapes:
         margin khép về 0 để cỏ chạm cỏ + mảng nắng mềm sau nội dung; paint-only,
         không đổi kích thước/vị trí nội dung trong panel) ===== */
      #screen-pet .mode-panel#mode-chase.active,
      #screen-pet .mode-panel#mode-cook.active,
      #screen-pet .mode-panel#mode-garden.active,
      #screen-pet .mode-panel#mode-listen.active,
      #screen-pet .mode-panel#mode-word.active {
        margin-top: 0;
        /* v99: same continuation recipe as the shared panel rule
           (responsive.css) — no sunlit patch at the seam. */
        background: linear-gradient(180deg, #5cca52 0%, #4cc24a 100%);
      }
      /* ===== LAYOUT-FIX-S #261 §4 — cửa sổ nhìn ra sân cho cook (mẫu đặc tả).
         Panel cook không có vùng tường trống ở phía trên (hàng thực đơn kín
         chiều ngang) nên cửa sổ đặt ở vùng trống dưới panel, không đè đạo cụ,
         không đổi hình học sẵn có; vị trí này để lead/bi phán trên dev preview. */
      #screen-pet #mode-cook.mode-panel { position: relative; }
      .yard-window { position: absolute; width: 92px; height: 74px; border: 5px solid #b07a45;
        border-radius: 12px; overflow: hidden; pointer-events: none;
        background: linear-gradient(180deg, #8fd8ff 0%, #c8f2ff 54%, #93e77e 54.5%, #63d64f 100%); }
      .yard-window .yw-sun { position: absolute; top: 6px; right: 8px; width: 16px; height: 16px;
        border-radius: 50%; background: #ffd93c; box-shadow: 0 0 10px #ffe97a; }
      .yard-window .yw-cloud { position: absolute; top: 10px; left: 10px; width: 26px; height: 10px;
        border-radius: 999px; background: rgba(255,255,255,.92); }
      .yard-window .yw-fence { position: absolute; left: 0; right: 0; top: 46%; height: 7px;
        background: repeating-linear-gradient(90deg, #e8a869 0 7px, transparent 7px, transparent 12px); }
      .yard-window .yw-grass { position: absolute; left: 0; right: 0; bottom: 0; height: 30%;
        background: linear-gradient(180deg, #7ddb63, #4fbb3f); }

/* LAYOUT-FIX-D #260 §3 + Lead 6469387951: trace panel = grass continuation
   like dots (no separate pale panel): board sits on grass, pet + board share
   one scene. Width-independent: dots panel is grass at 360 and 1280. */
#screen-pet #mode-trace.mode-panel.active {
  /* v99: same continuation recipe as the shared panel rule
     (responsive.css) — no sunlit patch at the seam. */
  background: linear-gradient(180deg, #5cca52 0%, #4cc24a 100%);
  border: none; box-shadow: none; margin-top: 0; border-radius: 0;
}
      #screen-pet #mode-cook .yard-window { bottom: 10px; right: 10px; }

      /* ---------- v110: BUILD A HOUSE, REBUILT — one big SVG house ----------
         The scene is ONE inline SVG (index.html #houseScene); each part
         is a <g data-h-part> that sits as a dashed ghost outline until
         built, then pops in. In the find phase the groups themselves
         are the touch targets. Choice cards carry a mini SVG of the
         part + its name. All pre-v110 house styles (v38 scene parts,
         v70 model/variants, chips strip, door pet) are deleted. */
      #screen-pet #mode-house.mode-panel.active { display: flex; flex-direction: column; }
      /* v110 round 2 (lead eye-check on the rebuild): at 414x628
         the stage cap left the pet face 22px tall — the pet drew
         as a ~17px smear under the bubble. The panel yields its
         slack to the stage instead: tighter panel padding here,
         score+msg merged into one row below, cards at 96px, and
         the <=700px stage cap raised in responsive.css. Scene
         floor at 414x628 becomes 180px (was 190) by design. */
      #screen-pet #mode-house.mode-panel.active { padding-top: 2px; padding-bottom: 4px; }
      /* Win reserve ONLY while the banner shows: the permanent
         :has(> .win-banner) reserve the other modes carry would
         eat 88px of this panel at every moment and crush the flex
         scene to its floor at 414x628. At the win moment the
         choices row is already empty and the elastic scene yields
         the strip instead (the race v87 precedent: at win, the
         redundant strip yields its height) — score + msg end above
         the banner, never under it. */
      #screen-pet #mode-house.mode-panel:has(> .win-banner.show) { padding-bottom: 88px; }
      .house-scene { flex: 1 1 auto; min-height: 120px; margin: 0 auto; width: min(430px, 96%); display: flex; }
      /* Stack trims so the elastic scene keeps its height floors
         (probe-measured: the generic task/score/msg margins cost
         the scene ~50px at tablet size). House-only. */
      /* v112 (#356/X356-D): the score row is a FIXED-height band. The
         msg used to grow with its text (empty 48px -> wrapped praise/
         try-again up to +91px), and because the scene is flex:1 every
         pixel the msg gained came straight off the house — parts and
         cards jumped on every right/wrong tap. The band now reserves
         room for two lines at 17px up front; feedback text wraps
         inside it and the scene never moves mid-round. */
      .house-score-row { display: flex; align-items: center; gap: 10px; margin-top: 2px; height: 52px; flex: 0 0 auto; }
      #mode-house .game-score { margin-top: 0; flex: 0 0 auto; }
      #mode-house .msg { margin-top: 0; padding-top: 4px; padding-bottom: 4px; flex: 1 1 auto; min-width: 0; min-height: 0; font-size: 17px; line-height: 1.25; max-height: 44px; overflow: hidden; }
      .house-scene svg { display: block; width: 100%; height: 100%; }
      #houseScene svg g[data-h-part] { pointer-events: none; transition: opacity .3s ease; }
      #houseScene svg g.ghost { opacity: .28; }
      #houseScene svg g.ghost .hp-main { stroke-dasharray: 7 5; }
      #houseScene svg g.current { animation: houseGhostPulse 1.1s ease-in-out infinite alternate; }
      @keyframes houseGhostPulse { from { opacity: .38; } to { opacity: .62; filter: drop-shadow(0 0 6px rgba(255, 150, 0, .95)); } }
      #houseScene svg g.built { opacity: 1; }
      #houseScene svg g.just-built { animation: housePop .55s ease; transform-box: fill-box; transform-origin: center; }
      @keyframes housePop { 0% { transform: scale(.82); } 55% { transform: scale(1.07); } 100% { transform: scale(1); } }
      #houseScene svg.find-phase g[data-h-part] { pointer-events: all; cursor: pointer; }
      #houseScene svg g.found-flash { animation: houseFlash .65s ease; transform-box: fill-box; transform-origin: center; }
      @keyframes houseFlash { 0% { filter: brightness(1); } 35% { filter: brightness(1.65) drop-shadow(0 0 10px rgba(255, 217, 60, .95)); transform: scale(1.06); } 100% { filter: brightness(1); } }
      #houseScene svg g.shake { animation: houseShake .4s ease; transform-box: fill-box; transform-origin: center; }
      @keyframes houseShake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
      .house-choices { display: flex; gap: 10px; justify-content: center; flex-wrap: nowrap; margin-top: 2px; min-height: 96px; }
      .house-choices:empty { min-height: 0; margin-top: 0; }
      .house-card {
        flex: 1 1 0; max-width: 160px; min-height: 96px; padding: 8px 10px; border-radius: 20px; cursor: pointer;
        background: #fff4e0; border: 3px solid #ffa94d; color: var(--ink);
        font-weight: 800; line-height: 1.1;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
        box-shadow: 0 5px 0 rgba(35,50,70,.14); transition: transform .15s ease;
      }
      .house-card:active { transform: scale(.94); }
      .house-card.right { border-color: var(--teal-dark); box-shadow: 0 0 0 5px var(--teal); background: #e6fcf5; }
      .house-card.wrong { border-color: #e8590c; background: #ffe8cc; }
      /* v109 (D): a disabled (dimmed) card swallows taps completely. */
      .house-card:disabled { opacity: .55; pointer-events: none; }
      .house-card .hc-art { width: 64px; height: 40px; flex: 0 0 auto; }
      .house-card .hc-cap2 { font-size: 19px; text-align: center; }
      .house-fly { position: fixed; z-index: 80; margin: 0; pointer-events: none; transition: transform .5s ease, opacity .5s ease; }
