    /* ===== MAZE mode: board lives in the game yard, pet walks it ===== */
    #screen-pet #gameYard { touch-action: none; }
    #screen-pet .pet-stage { touch-action: none; }
    .maze-board {
      position: absolute; z-index: 2; border-radius: 14px; overflow: hidden;
      background: linear-gradient(180deg, #eafbe7, #d8f5d0);
      /* v20: the frame is an inset shadow, NOT a border — a real border
         eats 6px of content box under the global border-box sizing
         and silently shifts the whole grid vs the layout math. */
      box-shadow: inset 0 0 0 3px #51b06a, inset 0 0 0 6px rgba(255, 255, 255, .55), 0 4px 10px rgba(30, 90, 40, .18);
    }
    /* The board is pure decoration — every maze input is handled on
       the yard — so it must never swallow taps: the win banner floats
       over the yard's lower area by design, and the full-size board
       would otherwise cover its button (Part D regression find). */
    .maze-board, .maze-board * { pointer-events: none; }
    .maze-board svg { position: absolute; inset: 0; }
    .maze-goal {
      position: absolute; z-index: 2; display: grid; place-items: center;
      background: radial-gradient(circle at 50% 62%, #fff 0 52%, #ffe3ec 72%, rgba(255, 227, 236, 0) 74%);
      animation: mazeGoalPulse 1.1s ease-in-out infinite;
    }
    .maze-goal .food-art { width: 82%; height: 82%; pointer-events: none; animation: goalHop 2.8s ease-in-out infinite; }
    /* v44: content badge on the goal dish (number / letter / word
       picture of the active menu chip) */
    .maze-goal-badge {
      position: absolute; top: -7px; right: -7px; z-index: 3;
      min-width: 26px; height: 26px; padding: 0 5px; border-radius: 999px;
      background: #ffd93c; border: 2.5px solid #e8a100; color: #5b3a00;
      font-size: 17px; font-weight: 900; line-height: 22px; text-align: center;
      box-shadow: 0 2px 5px rgba(90, 60, 0, .3);
    }
    @keyframes mazeGoalPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.07); } }
    /* v23: the maze goal (a fish for the cat) hops in a little parabolic
       arc — up, curve, land with a squash — then rests, instead of just
       sitting/pulsing statically. */
    @keyframes goalHop {
      0%, 46%, 100% { transform: translateY(0) rotate(0deg) scale(1, 1); }
      12% { transform: translateY(-24px) rotate(-7deg) scale(1, 1); }
      24% { transform: translateY(0) rotate(0deg) scale(1.08, .88); }
      30% { transform: translateY(-13px) rotate(6deg) scale(1, 1); }
      40% { transform: translateY(0) rotate(0deg) scale(1.05, .92); }
    }
    /* v57 (Addendum 11): layer 2 of 3 keeping the pet above the
       walls. Layer 1 is the inline z-index 8 mazePlacePet sets;
       layer 3 is DOM order (startMaze re-appends the pet face
       after the board). The v56 fix relied on the inline style
       alone — the rule below makes the layering a stylesheet fact
       too, so no single regression can put a wall over the pet
       again. Yard class .maze-on is toggled by setupGameStage. */
    #gameYard.maze-on .pet-face { z-index: 8; }
    /* v57 ROOT FIX: the pet face is a CHILD of the board and is
       positioned in % of the board's own box (mazePlacePet), so
       pet and walls share one geometry source and cannot desync.
       Inside the board's stacking context it still paints above
       the wall svg and the goal dish. */
    .maze-board .pet-face { position: absolute; z-index: 3; }
    .maze-toprow { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 4px; flex-wrap: wrap; }
    /* v79 (frame stability): the maze message must not grow at the
       win — a taller msg inflates the panel, the yard shrinks, and
       the ResizeObserver re-lays the board a cell smaller (measured
       chain: msg 34->40px, panel +3, yard -3, board -11). Reserve
       the two-line height from the deal, like fishMsg (v78). */
    #mode-maze .msg { min-height: 40px; }
    /* v20 (Part D, real-device feedback): the maze panel gives its
       height back to the yard so the portrait board can fill it —
       the task line is hidden (the speech bubble says the same and
       reads it aloud) and the global 124px bottom pad is trimmed
       for this panel only. */
    #screen-pet #mode-maze .task-sub { display: none; }
    #screen-pet .pet-panel:has(#mode-maze.active) #mode-maze.active { padding-bottom: 18px; }
    /* …but the win banner is absolutely positioned by a fixed offset
       from the panel's bottom edge and overlays the yard by design;
       with the trimmed panel it would land fully outside the panel
       box and be clipped away by the panel's overflow-y:auto (the
       regression pdf1 caught). Maze content never scrolls, so the
       panel can safely let the banner overflow visibly. */
    #screen-pet #mode-maze.active { overflow: visible; }
    .maze-toprow .game-score { margin: 0; min-height: 0; }
    .maze-toprow .maze-actions { margin: 0; }
