* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

:root {
  --pink: #ff6b9d; --pink-dark: #e04e83; --bg: #fdf6ec;
  --card: #ffffff; --ink: #3d405b; --soft: #ffe3ee;
}

body {
  font-family: 'Nunito', system-ui, -apple-system, sans-serif;
  background: linear-gradient(180deg, #b8e6ff 0%, #e8f7ff 100%);
  color: var(--ink); height: 100dvh; overflow: hidden; user-select: none;
  font-weight: 600;
}

#app { display: flex; flex-direction: column; height: 100dvh; max-width: 520px; margin: 0 auto; background: var(--bg); box-shadow: 0 0 40px rgba(0,0,0,.12); }

/* Top bar */
#topbar { padding: 10px 12px; background: rgba(255,255,255,.92); border-bottom: 2px solid #f3d9e2; z-index: 20; }
#topbar-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
#coin-display { font-size: 18px; font-weight: 900; cursor: pointer; background: #fff5f8; border: 2px solid #f3d9e2; border-radius: 999px; padding: 3px 12px; box-shadow: 0 2px 7px rgba(0,0,0,.10); } /* ?v=128: the coin IS the shop button now — pill + pointer so it reads as tappable */
#sound-btn { margin-left: 2px; width: 38px; height: 38px; flex: 0 0 auto; border-radius: 50%; border: 2px solid #f3d9e2; background: #fff5f8; font-size: 18px; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 7px rgba(0,0,0,.13); } /* ?v=128: global sound on/off, top-right of the header */
/* v61: the twin switch lives in the top bar now — persistent, every room, any time */
#char-switch { display: flex; gap: 4px; background: #fff; border: 2px solid #f3d9e2; padding: 3px; border-radius: 999px; }
#char-switch .char-btn { border: 2px solid transparent; background: #fdf3f7; border-radius: 999px; padding: 4px 10px; font-family: inherit; font-size: 13px; font-weight: 800; color: var(--ink); cursor: pointer; }
#char-switch .char-btn.selected { border-color: var(--ink); background: #ffe0ed; }
#needs-bar { display: flex; gap: 8px; }
.need { flex: 1; display: flex; align-items: center; gap: 4px; }
.need-icon { font-size: 17px; }
.need-track { flex: 1; height: 11px; background: #eee7ea; border-radius: 6px; overflow: hidden; }
.need-fill { height: 100%; border-radius: 6px; background: #6fce62; width: 100%; transition: width .5s, background .5s; }
.need.mid .need-fill { background: #ffc53d; }
.need.low .need-fill { background: #ff6b6b; animation: pulse 1s infinite; }
@keyframes pulse { 50% { opacity: .5; } }

/* Scene */
#scene { flex: 1; position: relative; overflow: hidden; }
#room { position: absolute; inset: 0; }
#room-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* ?v=154 (Tomika 2026-11-03, all-rooms framing pass): the dog room's view pans to the RIGHT edge of its backdrop — the toy basket sits at the art's right edge (file x 82-100%) and the centre crop cut it out of the game entirely (diag165: no basket, no toys, just wall). The bed/toys/bone hotspots below are re-anchored to the panned painting. */
#room[data-room="dogroom"] #room-bg { object-position: 100% 0%; } /* ?v=264 (Tomika 00:03): down the REST of the way — y 20% -> 0%, the very top of the backdrop; the 20% step still showed too much floor for her */ /* ?v=259 (Tomika 23:51): the dog-room backdrop rides DOWN in the frame — object-position y 50% -> 20% shows more of the room's top (window, lights, doorway tops); 'it's too much floor here' */
#room[data-room="backyard"] #room-bg { object-position: 50% 0%; } /* ?v=192 (Tomika 16:36): the canopy scene rides lower in the frame — more of the treetop (branch, leaves, sky) shows; the slide's tip takes the crop instead · ?v=195 (Tomika 17:42): down ANOTHER 20% as far as the art allows — 0% is the very top of the 1024x1536 backdrop, so the remaining travel from 10% was ~10 points, not 20; the picture tops out here. Going lower needs a taller backdrop (more sky on top) or a zoom on this room — her call */
#night-overlay { position: absolute; inset: 0; background: rgba(18, 22, 58, .48); opacity: 0; transition: opacity 1.2s; pointer-events: none; z-index: 4; }
#room.night #night-overlay { opacity: 1; }
/* Rooms bar */
#rooms-bar { display: flex; gap: 6px; padding: 8px 10px; background: #fff; border-bottom: 2px solid var(--soft); overflow-x: auto; }
.games-btn { flex: 0 0 auto; border: 2px solid #f3d9e2; background: #fff; border-radius: 999px; padding: 6px 13px; font-family: inherit; font-size: 13.5px; font-weight: 800; color: var(--ink); cursor: pointer; } /* ?v=128: Games lives in the top scroll now */
.room-btn { flex: 1; white-space: nowrap; border: 2px solid var(--soft); background: #fff5f8; border-radius: 999px; padding: 7px 4px; font-family: inherit; font-size: 13.5px; font-weight: 800; color: var(--ink); cursor: pointer; }
.room-btn.selected { background: var(--pink); border-color: var(--pink); color: #fff; }

.hotspot { position: absolute; z-index: 3; cursor: pointer; border-radius: 12px; display: none; }
.hotspot:active { background: rgba(255,255,255,.18); }
#room[data-room="kitchen"] .hs-kitchen { display: block; }
#room[data-room="bedroom"] .hs-bedroom { display: block; }
#room[data-room="bathroom"] .hs-bathroom { display: block; }
#room[data-room="backyard"] .hs-backyard { display: block; }
#room[data-room="closet"] .hs-closet { display: block; }
#hs-c-shirt1 { left: 0; top: 37%; width: 16%; height: 26%; }
#hs-c-shirt2 { left: 13%; top: 35%; width: 20%; height: 22%; }
#hs-c-shirt3 { left: 27%; top: 43%; width: 16%; height: 30%; }
#hs-c-shirt4 { left: 39%; top: 45%; width: 12%; height: 27%; }
#hs-c-shorts1 { display: none !important; }
#hs-c-shorts2 { display: none !important; }
#hs-c-shorts3 { display: none !important; }
#hs-c-folded { left: 44%; top: 65%; width: 50%; height: 28%; }
#hs-c-shoe1 { display: none !important; }
#hs-c-shoe2 { display: none !important; }
#hs-c-shoe3 { display: none !important; }
#hs-c-shoe4 { display: none !important; }
#hs-c-mirror { left: 52%; top: 11%; width: 36%; height: 42%; }
#hs-c-dresser { left: 19%; top: 73%; width: 24%; height: 18%; }
#hs-y-grass { left: 66%; top: 25%; width: 22%; height: 17%; }
#yard-ball { position: absolute; left: 58%; bottom: 12%; z-index: 6; cursor: pointer; display: none; transition: left .5s ease-in, bottom .5s ease-in; }
#yard-ball img { width: 36px; display: block; filter: drop-shadow(0 3px 3px rgba(0,0,0,.3)); pointer-events: none; }
#room[data-room="backyard"] #yard-ball { display: block; }
#dig-mound { position: absolute; left: 4%; bottom: 13%; z-index: 6; cursor: pointer; display: none; }
#dig-mound img { width: 84px; display: block; pointer-events: none; }
#room[data-room="backyard"] #dig-mound { display: block; }
#hs-k-fridge { left: 0; top: 22%; width: 44%; height: 30%; }
#hs-k-stove { left: 14%; top: 38%; width: 18%; height: 20%; } /* ?v=278: twin of the winning rule below — the stove body and the kettle on it */
#hs-k-sink { left: 44%; top: 68%; width: 34%; height: 24%; }
#hs-k-table { left: 35%; top: 43%; width: 25%; height: 14%; } /* ?v=278: on the painted round table in the middle (was the whole right wall, where it sat UNDER the shelf zone and stole its taps) */
#hs-k-window { left: 39%; top: 37%; width: 22%; height: 10.5%; border-radius: 50%; } /* ?v=339 (Tomika 13:36): Sevyn may ONLY be called from INSIDE the glass — the ?v=278 zone covered the window's whole bounding box and overshot it (left 30/top 23 vs the painted frame at 34/33), so taps on the wall and lantern left of the window summoned her. The zone is now a circle inscribed in the painted glass (glass spans x 37-62, y 36-48.5, measured on the backdrop grid) */ /* ?v=278: re-measured on the painted art — the full round window, top arc included (was 31/30/32/15 since ?v=179) */
#hs-b-bed { left: 6%; top: 60%; width: 92%; height: 31%; }
#hs-b-dresser { left: 79%; top: 46%; width: 21%; height: 26%; }
#hs-b-piano { display: none !important; }
#hs-b-bookshelf { left: 8%; top: 20%; width: 16%; height: 52%; } /* ?v=235 (Tomika 22:09): moved right off the doorway — at left 0 the tap zone sat on the door, so shelf taps hit the doorway instead */
#hs-b-window { left: 26%; top: 16%; width: 50%; height: 40%; }
#hs-b-dogbed { left: 74%; top: 84%; width: 26%; height: 14%; }
#hs-ba-tub { left: 4%; top: 40%; width: 42%; height: 32%; } /* ?v=232: the new backdrop's barrel tub (stage x ~4-46%, bubbles + barrel y ~40-72%) */
#hs-ba-shower { left: 72%; top: 25%; width: 19%; height: 18%; } /* ?v=234: the mirror gag moves onto the REAL mirror — Chat's redo added the oval mirror above the sink (stage x ~72-91%, y ~25-43%) */
#hs-ba-light { left: 6%; top: 15%; width: 13%; height: 17%; } /* ?v=234: the wall lantern (stage x ~8-17%, y ~17-31% on the zoomed art) — tap = lights off/on: night backdrop + the room dim */
#hs-ba-sink { left: 58%; top: 43%; width: 31%; height: 28%; } /* ?v=232: the leaf sink + its stand (stage x ~58-89%, y ~43-71%) */
#hs-ba-toilet { left: 0; top: 0; width: 0; height: 0; } /* ?v=232: PARKED — the towel gag's fixture isn't in the new art (no towels, no toilet in this bathroom); the handler stays wired for if a fixture returns */
#room[data-room="living"] .hs-living { display: block; }
#hs-l-tv { left: 30%; top: 48%; width: 40%; height: 24%; } /* ?v=170: no screen to tap now — the couch is the TV-watch tap zone until Chat's animated TV lands */ /* ?v=169 (main, Tomika takeover): tap zone follows the wall-mounted TV in Chat's tall living backdrop */
#hs-l-console { left: 57%; top: 65%; width: 26%; height: 9%; }
#hs-l-couch { left: 0; top: 45%; width: 55%; height: 38%; }
#hs-l-desk { left: 0; top: 31%; width: 13%; height: 16%; }
#hs-l-rug { left: 12%; top: 78%; width: 72%; height: 17%; }
#hs-l-hammock { left: 84%; top: 40%; width: 16%; height: 36%; }
#bath-kid { position: absolute; z-index: 5; left: 7%; bottom: 38%; width: 31%; pointer-events: none; } /* ?v=235: kept in sync with the staging rule below (which wins on left/bottom/width) */
#tv-screen { position: absolute; z-index: 3; left: 61%; top: 51%; width: 20%; height: 15%; background: #263859; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 30px; box-shadow: 0 0 24px rgba(56,189,248,.55); pointer-events: none; }
.race-track { position: relative; background: #eef2f7; border-radius: 12px; height: 46px; margin: 8px 0; overflow: hidden; }
.race-car { position: absolute; left: 0; top: 4px; font-size: 32px; transition: left .12s linear; }
.race-flag { position: absolute; right: 6px; top: 8px; font-size: 24px; }
#hs-y-tree { left: 9%; top: 6%; width: 29%; height: 45%; }
#hs-y-swing { left: 0; top: 30%; width: 30%; height: 28%; } /* ?v=112: the painted plank seat + the seated figure's whole span (his sprite is tap-transparent, so taps on him land here) */
#hs-y-slide { left: 32%; top: 80%; width: 36%; height: 20%; } /* ?v=228 (Tomika 21:33): the slide tap zone is the PAINTED CHUTE ONLY — its tip at the deck's front edge + the chute run below it (stage x ~32-68%, y 80-100%). Tapping it starts the Slide Ride; everywhere else on the canopy is walkable deck. */
#hs-y-doghouse { left: 30%; top: 56%; width: 44%; height: 24%; }
#hs-y-flowers { left: 0; top: 47%; width: 21%; height: 22%; }
#hs-y-window { left: 83%; top: 37%; width: 17%; height: 26%; }
/* v65 (fix 25): the red oval dish is out of the whole app — no #dog-bowl rules remain */
/* v63 (fix 4): the red kibble bowl lives ONLY in Gigi's room — never the kitchen.
   In her room it sits at 70% so her bowl beat can actually reach it. */


/* Person */
:root { --big-shrink: 0.90; } /* ?v=122 (Tomika 09:36, PLAYTEST GATE): the BIG characters (Brodie, Harley, Hudson, Gigi) render x0.90 in the general standing/in-room scenes — her kitchen crowding fix. ONE factor: change this value (and BIG_SHRINK in game.js, its twin) to retune. The seated scene stagings (couch #sit-kid, toilet #potty-kid, swing, bath #bath-kid, bed #sleep-kid) do NOT use it — their sizes stay exactly as tuned, per her refinement. Sky/Lily (puppy 0.7) and Sevyn are not part of this factor. */
#person { position: absolute; bottom: 9%; left: 50%; transform: translateX(-50%); z-index: 10; transition: left 1.6s ease-in-out; cursor: pointer; }
#person-anim { position: relative; display: flex; flex-direction: column; align-items: center; transform-origin: bottom center; animation: breathe 3.4s ease-in-out infinite; }
@keyframes breathe { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(1.016); } }
#person-anim.walking { animation: walkBob .24s ease-in-out infinite alternate; }
@keyframes walkBob { from { transform: translateY(0); } to { transform: translateY(-8px); } }
#kid-img { height: min(calc(297px * var(--big-shrink) * 0.90), calc(41dvh * var(--big-shrink) * 0.90)); width: auto; filter: drop-shadow(0 6px 6px rgba(0,0,0,.18)); pointer-events: none; } /* ?v=251 (Tomika 23:29-23:30): the TWINS' STANDING render takes ANOTHER x0.90 in the indoor scenes — 'they look too big in these rooms... just the standing ones'. This rule is the twins' general standing/idle height (Brodie is overridden by his char-brodie rules below and is NOT in this change, per her 'not Brody'); the eating/drinking beats keep their own sizing (the #person.eating rules) — animations unchanged, exactly as she scoped it. The dogpark watcher rule below is outdoor and also unchanged. */
#room[data-room="dogpark"] #kid-img { height: min(calc(168px * var(--big-shrink)), calc(24dvh * var(--big-shrink))); }
/* ===== ?v=109: character scene scales (Tomika 19:25 + 19:34 + 19:41 + 19:44) =====
   BRODIE general scene = 0.90x today (he is bushier than the twins and
   crowds the scene at equal scale); BRODIE living room = 1.10x his ?v=104
   living render (the general 0.90 composed with a living-room 1.2222 —
   applied here as direct heights so the net is exactly 1.10); BRODIE bath
   = 1.15x today's bath render, bath scene only; EATING faces = 0.85x
   today's eat render for ALL THREE (twins' general size unchanged; for
   Brodie 252.5px is 0.85x of today's 297 — NOT 0.85 stacked on his 0.90).
   All scalings are bottom-anchored (the stagings sit on their bottom
   edge), so feet / seated bases stay planted; belly/tap math reads the
   rendered rect, so zones track automatically. */
#room.char-brodie #kid-img { height: min(calc(267.3px * var(--big-shrink)), calc(36.9dvh * var(--big-shrink))); }
#room.char-brodie[data-room="living"] #kid-img { height: min(calc(267.3px * var(--big-shrink)), calc(36.9dvh * var(--big-shrink))); } /* ?v=115 (Tomika 20:55): his living-room STANDING render matches his KITCHEN standing render exactly (was 326.7px — the ?v=109 living boost made him fill the frame); the +10% living intent survives ONLY in the couch staging via couchScaleF() */
#room.char-brodie[data-room="dogpark"] #kid-img { height: min(calc(151.2px * var(--big-shrink)), calc(21.6dvh * var(--big-shrink))); }
#person.eating #kid-img { height: min(calc(252.5px * var(--big-shrink)), calc(34.9dvh * var(--big-shrink))); }
#room.char-brodie #person.eating #kid-img { height: min(calc(252.5px * var(--big-shrink)), calc(34.9dvh * var(--big-shrink))); }
#room.char-brodie #sleep-kid { width: 30%; left: 34%; } /* ?v=173: Brodie's sleeper matches — smaller, up into the nest */ /* ?v=172: Brodie's sleeper shrunk to fit the nest too */ /* ?v=169: re-centred on the nest in Chat's tall bedroom backdrop */ /* ?v=153: Brodie's sleeper centres on the reframed nest too (his narrower frame: centre ~29%) */
#room.char-brodie[data-room="pond"] #sit-kid { width: 23.4%; }
#room.char-brodie[data-room="dogpark"] #sit-kid { width: 22.5%; }
#room.char-brodie #potty-kid { width: 32.4%; bottom: 35.5%; } /* ?v=386 (Tomika 22:02): Brodie's toilet pose up 2 more (33.5 -> 35.5); twins untouched */ /* ?v=169: seat height for the tall potty backdrop's toilet */ /* ?v=142 (Tomika 2026-11-02): Brodie's toilet render +15% (see .pk-brodie) and sits a little higher (30.5%->32%). The .pk-brodie transform stays 1.25 — now 1.25x of the NEW (0.90) base, exactly as specified: factors composed, not stacked or cancelled */
#room.char-brodie #bath-kid { width: 39.6%; } /* ?v=386 (Tomika 22:01): his SEATED tub pose +10% (36 -> 39.6) — the scrubbing/laugh (.tickling 45%) animation keeps its size exactly; twins' tub seat untouched */
#room.char-brodie #bath-kid.tickling { width: 45%; } /* ?v=233: 39% x 1.15 */
#room.char-brodie:has(#bath-kid.tickling) #bath-bubbles { width: 45%; } /* the foam layer mirrors the tickling box, as in the base rules */
#person.bounce #person-anim { animation: bounce .35s 4 alternate ease-in-out; }
#person.dance #person-anim { animation: dance .3s infinite alternate ease-in-out; }
#person.jump #person-anim { animation: jump .45s ease-out; }
@keyframes bounce { from { transform: translateY(0); } to { transform: translateY(-10px); } }
@keyframes dance { from { transform: translateY(0) scale(1, 1); } to { transform: translateY(-9px) scale(.98, 1.03); } }
@keyframes jump { 40% { transform: translateY(-34px) scale(1.05); } }

.person-hat { position: absolute; top: -14px; left: 50%; transform: translateX(-50%) rotate(-6deg); font-size: 44px; line-height: 1; z-index: 3; pointer-events: none; }
.person-hat[class*="acc-"] { pointer-events: auto; cursor: pointer; }

#gnome-bubble { position: absolute; z-index: 12; background: #fff; padding: 12px; border-radius: 50%; font-size: 13px; font-weight: 800; border: 2px solid var(--soft); box-shadow: 0 4px 12px rgba(0,0,0,.15); animation: pop .25s; display: flex; align-items: center; justify-content: center; text-align: center; box-sizing: border-box; line-height: 1.22; }
#gnome-bubble.hidden { display: none; }
#speech-bubble { position: absolute; bottom: 104%; left: 50%; transform: translateX(-50%); background: #fff; padding: 12px; border-radius: 50%; font-size: 14px; font-weight: 800; border: 2px solid var(--soft); box-shadow: 0 4px 12px rgba(0,0,0,.15); z-index: 12; animation: pop .25s; display: flex; align-items: center; justify-content: center; box-sizing: border-box; } /* ?v=113: a round chat-circle now (Tomika 19:44) — diameter is set per line by bubCircle() in game.js so the words wrap INSIDE the circle; it floats above the wrap, clear of the face */
#speech-bubble.hidden { display: none; }
@keyframes pop { from { transform: translateX(-50%) scale(.6); opacity: 0; } }
#hearts { position: absolute; top: -28px; left: 50%; transform: translateX(-50%); font-size: 18px; animation: floatUp 1.2s forwards; }
#hearts.hidden { display: none; }
#zzz { position: absolute; top: -24px; right: -16px; font-size: 26px; animation: floatUp 2s infinite; }
#zzz.hidden { display: none; }
@keyframes floatUp { 0% { transform: translateY(0); opacity: 1; } 100% { transform: translateY(-22px); opacity: 0; } }

#dog { position: absolute; bottom: 4%; left: 13%; z-index: 9; transition: left 2.4s ease-in-out; cursor: pointer; }
#room[data-room="potty"] #dog, #room[data-room="bathroom"] #dog, #room[data-room="backyard"] #dog { display: none !important; } /* ?v=308 (Tomika 11:51): the v62 cast rule sealed in the stylesheet — no dog in the potty room, the tub scene, or on the canopy, whatever any beat stages; goRoom sets the same rule in JS, this makes it unconditional (Gigi rendered in her potty screenshot despite the JS rule) */
#dog.dog-on-top { z-index: 11; } /* ?v=160: during a feed/water beat the dog renders above the main character (#person is z 10) — her eat/drink can never be occluded */
#dog-img { display: block; height: calc(176px * var(--big-shrink)); width: auto; filter: drop-shadow(0 4px 4px rgba(0,0,0,.18)); pointer-events: none; } /* v65: block kills the ~5px inline strut under the sprite */
#dog.happy #dog-img { animation: wag .25s infinite alternate; }
@keyframes wag { from { transform: translateX(-2.5px); } to { transform: translateX(2.5px); } }
#dog-bubble { position: absolute; bottom: calc(100% - 6px); left: 57%; transform: translateX(-50%); background: #fff; padding: 10px; border-radius: 50%; font-size: 12.5px; font-weight: 800; box-shadow: 0 2px 6px rgba(0,0,0,.15); display: flex; align-items: center; justify-content: center; text-align: center; box-sizing: border-box; line-height: 1.22; } /* ?v=113: round chat-circle, anchored just above her head and a step to the side — the old fixed bottom:92px pill could land on her face depending on the pose's height compensation */
#dog-bubble.hidden { display: none; }

#fx-layer { position: absolute; inset: 0; pointer-events: none; z-index: 15; overflow: hidden; }
.fx-emoji { position: absolute; font-size: 26px; animation: fxFall 1.1s ease-in forwards; }
@keyframes fxFall { 0% { transform: translateY(-10px) rotate(0); opacity: 1; } 100% { transform: translateY(120px) rotate(40deg); opacity: 0; } }
.bubble-fx { position: absolute; font-size: 22px; cursor: pointer; pointer-events: auto; animation: rise 2.6s linear forwards; }
@keyframes rise { from { transform: translateY(0); opacity: .95; } to { transform: translateY(-46vh); opacity: .5; } }

/* Action bar */
#actions { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; padding: 10px; background: rgba(255,255,255,.96); border-top: 2px solid #f3d9e2; z-index: 20; }
.action-btn { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 9px 2px; font-size: 26px; background: #fff5f9; border: 2px solid var(--soft); border-radius: 16px; cursor: pointer; font-family: inherit; transition: transform .1s, background .1s; }
.action-btn span { font-size: 11px; font-weight: 800; color: #8a5a70; }
.action-btn:active { transform: scale(.9); background: #ffe0ed; }

/* Sheets / overlays */
.sheet { position: fixed; inset: 0; background: rgba(40,20,35,.5); display: flex; align-items: flex-end; justify-content: center; z-index: 100; }
.sheet.hidden { display: none; }
.sheet-card { background: #fff; width: 100%; max-width: 520px; max-height: 86dvh; overflow-y: auto; border-radius: 26px 26px 0 0; padding: 22px 20px 30px; text-align: center; animation: slideUp .3s ease-out; }
.sheet-card.wide { text-align: center; }
@keyframes slideUp { from { transform: translateY(60px); opacity: 0; } }
.sheet-card h3 { font-size: 22px; font-weight: 900; margin-bottom: 6px; }
.sheet-sub { color: #8b8fa3; font-size: 14px; margin: 8px 0; }
.sheet-card input { width: 100%; padding: 13px; margin: 4px 0 10px; border: 2px solid var(--soft); border-radius: 14px; font-size: 16px; font-family: inherit; font-weight: 700; text-align: center; }
.welcome-emoji { font-size: 52px; margin-bottom: 4px; }

.big-btn { background: var(--pink); color: #fff; border: none; padding: 15px 30px; border-radius: 16px; font-size: 18px; font-weight: 900; font-family: inherit; cursor: pointer; margin-top: 10px; box-shadow: 0 4px 0 var(--pink-dark); width: 100%; }
.big-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 var(--pink-dark); }
.close-btn { background: #f1f2f6; color: var(--ink); border: none; padding: 12px 26px; border-radius: 14px; font-size: 15px; font-weight: 800; font-family: inherit; cursor: pointer; margin-top: 14px; }
.mini-btn { background: #e8f4ff; color: #2f6fb2; border: none; padding: 10px 18px; border-radius: 12px; font-size: 14px; font-weight: 800; font-family: inherit; cursor: pointer; margin-top: 10px; display: block; margin-left: auto; margin-right: auto; }

.snack-grid, .game-grid, .shop-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 12px; }
.snack-btn, .game-btn, .shop-btn, .drink-btn { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 14px 4px; font-size: 34px; background: #fff8fb; border: 2px solid var(--soft); border-radius: 18px; cursor: pointer; font-family: inherit; transition: transform .1s; }
.snack-btn:active, .game-btn:active, .shop-btn:active, .drink-btn:active { transform: scale(.93); }
.snack-btn span, .game-btn span, .shop-btn span, .drink-btn span { font-size: 12px; font-weight: 800; color: #8a5a70; }
.shop-btn.owned { border-color: #95e08a; background: #f2fff0; }
.shop-btn.cant { opacity: .45; }

.swatch-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin: 8px 0 4px; }
.swatch { width: 42px; height: 42px; border-radius: 50%; border: 3px solid #eee; cursor: pointer; }
.swatch.selected { border-color: var(--ink); transform: scale(1.12); }
.hat-btn { font-size: 24px; padding: 8px 12px; background: #fff8fb; border: 2px solid var(--soft); border-radius: 14px; cursor: pointer; font-family: inherit; font-weight: 800; }
.hat-btn.selected { border-color: var(--ink); background: #ffe0ed; }

/* Piano */
#piano-keys { display: flex; gap: 3px; margin: 14px 0 4px; justify-content: center; }
.pkey { flex: 1; max-width: 46px; height: 110px; background: #fff; border: 2px solid #3d405b; border-radius: 0 0 8px 8px; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 8px; cursor: pointer; font-weight: 900; }
.pkey.target { background: #38bdf8; box-shadow: 0 0 16px #38bdf8, inset 0 0 8px rgba(255,255,255,.55); color: #fff; }
.pkey.playing { background: var(--soft); }
.pkey.good { background: #95e08a !important; }
.pkey.bad { background: #ff9a9a !important; }

/* Memory */
#memory-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 12px; }
.mem-card { aspect-ratio: 1; font-size: 30px; background: linear-gradient(145deg, #ff8fab, #ff6b9d); border: none; border-radius: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: transform .2s; font-family: inherit; }
.mem-card .face { display: none; }
.mem-card.flipped { background: #fff3f7; border: 2px solid var(--soft); }
.mem-card.flipped .face { display: block; }
.mem-card .back { color: #fff; font-size: 22px; font-weight: 900; }
.mem-card.flipped .back { display: none; }
.mem-card.matched { background: #d8ffd2; border: 2px solid #95e08a; }

/* Catch */
#catch-area { position: relative; height: 300px; background: linear-gradient(180deg, #cdeffd, #eafbf0); border-radius: 16px; overflow: hidden; margin-top: 10px; touch-action: none; }
#gk-stage { position: relative; height: 320px; border-radius: 16px; overflow: hidden; margin-top: 10px; background: #7a5a38 url('../assets/th-treedoor-p.jpg') center / cover; touch-action: manipulation; }
#gk-img { position: absolute; left: 50%; bottom: 3%; transform: translateX(-50%); width: 46%; pointer-events: none; }
#gk-bug { position: absolute; font-size: 36px; line-height: 1; cursor: pointer; z-index: 3; animation: gkBob .45s ease-in-out infinite alternate; transition: left .16s ease-in, top .16s ease-in, transform .16s ease-in, opacity .16s; }
@keyframes gkBob { from { margin-top: 0; } to { margin-top: -9px; } }
#gk-spark { position: absolute; font-size: 28px; pointer-events: none; z-index: 4; }
#catch-basket { position: absolute; bottom: 8px; left: 50%; font-size: 40px; transform: translateX(-50%); }
.falling { position: absolute; font-size: 28px; top: -30px; }

/* Fridge, books, school */
#fridge-img { width: 100%; border-radius: 14px; margin-bottom: 10px; }
.book-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
.book-btn { display: flex; align-items: center; gap: 10px; background: #fff5f8; border: 2px solid var(--soft); border-radius: 14px; padding: 10px 14px; font-family: inherit; font-size: 16px; font-weight: 800; color: var(--ink); cursor: pointer; text-align: left; }
.book-btn:active { transform: scale(.97); }
#book-art { font-size: 74px; text-align: center; padding: 16px 0 6px; letter-spacing: 4px; }
#book-text { font-size: 19px; font-weight: 700; text-align: center; line-height: 1.45; min-height: 84px; padding: 0 6px; }
#book-page-num { text-align: center; color: #b08; font-weight: 800; font-size: 13px; }
.book-nav { display: flex; gap: 10px; justify-content: center; margin: 8px 0 12px; }
.mini-btn { background: var(--pink); color: #fff; border: none; border-radius: 999px; padding: 10px 20px; font-family: inherit; font-size: 15px; font-weight: 800; cursor: pointer; }
.mini-btn:disabled { opacity: .35; }
#school-art { font-size: 40px; text-align: center; letter-spacing: 3px; min-height: 52px; padding-top: 8px; }
#school-q { font-size: 30px; font-weight: 800; text-align: center; margin: 6px 0 12px; }
#school-progress { text-align: center; font-weight: 800; color: #b08; }
#school-answers { display: flex; gap: 10px; justify-content: center; }
.school-ans { background: #fff5f8; border: 3px solid var(--soft); border-radius: 16px; width: 74px; height: 62px; font-family: inherit; font-size: 26px; font-weight: 800; color: var(--ink); cursor: pointer; }
.school-ans.right { background: #b8f7c1; border-color: #4caf50; }
.school-ans.wrong { background: #ffc9c9; border-color: #e63946; }
#school-feedback { text-align: center; font-size: 18px; font-weight: 800; min-height: 26px; margin-top: 10px; }
#fridge-wrap { position: relative; }
#fridge-wrap #fridge-img { margin-bottom: 0; display: block; }
.fridge-spot { position: absolute; background: transparent; border: none; border-radius: 12px; cursor: pointer; padding: 0; }
.fridge-spot:active { background: rgba(255, 214, 64, .35); }
#sleep-kid { position: absolute; z-index: 5; left: 32%; bottom: 34%; width: 33%; /* ?v=175 (Tomika 11:47): sleeper up some more — deeper into the nest */ /* ?v=173: twins' sleeper smaller + UP into the nest basin (Tomika's phone screenshot: Harley overflowed the bed) */ /* ?v=172: sleeper shrunk to fit IN the nest (was 48% and overflowed it) */ pointer-events: none; } /* ?v=153 (Tomika 2026-11-03): the sleeper now centres on the NEST in the reframed bedroom view (centre ~29%, was 50% under the old centre crop — she read off-centre in the nest); the dog's sleeper staging is NOT touched (her order: the dog asleep in front of the bed stays exactly as-is) */
#sleep-dog { position: absolute; z-index: 5; left: 1%; bottom: 2%; width: 30%; pointer-events: none; }
.toy { position: absolute; z-index: 4; font-size: 42px; cursor: pointer; filter: drop-shadow(0 3px 3px rgba(0,0,0,.25)); user-select: none; -webkit-user-select: none; }
#toy-ball { left: 22%; bottom: 8%; }
#toy-kite { left: 66%; bottom: 57%; font-size: 46px; }
#toy-plant2 { left: 6%; bottom: 11%; }
#toy-rocket { left: 80%; bottom: 10%; font-size: 36px; } /* ?v=174 (Tomika 11:25): OUT of the window/nest — floor, right side; she can long-press-drag it anywhere now */
/* v86: in-scene signs (the dog-room feeding sign + the kitchen chart) —
   DOM elements in the scene's own language, shown only in their room */
.scene-sign { display: none; position: absolute; z-index: 6; cursor: pointer; font-weight: 900; text-align: center; box-shadow: 0 3px 8px rgba(0,0,0,.3); }
#kitchen-chart { left: 25%; top: 3.5%; background: #fff8ea; color: var(--ink); border: 3px solid #b9834f; border-radius: 8px; padding: 6px 12px; font-size: 13px; }
/* ?v=99 (Tomika 17:21): the dog food bag on the kitchen floor, bottom-right
   against the wall. Kitchen only via .hs-kitchen. ?v=102: the drawn SVG is
   replaced by Tomika's designed "Pups' Treehouse" kibble bag — her verified
   transparent PNG (assets/chat-original/props/kibble-bag-pups-treehouse-
   TRANSPARENT.png; transparent-padding trim only, pixels untouched). */
#dogfood-bag { display: none; position: absolute; right: 2%; bottom: 1.5%; width: 17%; z-index: 6; cursor: pointer; } /* ?v=276 (Tomika 00:58): the ?v=275 shrink is called off — 'that's okay, don't reduce it' — the bag stays at its original 17% */ /* ?v=107 (Tomika 19:27): the layering rule joins the cooler — z 6 already renders the bag BELOW the character layer (dog 9 / person 10), so everyone passes in FRONT of it; the tap stays alive via its new .hotspot class (forwardTapInner forwards a character's non-belly tap down to it) — same solution as the drinks-room cooler */
#dogfood-bag img { display: block; width: 100%; height: auto; filter: drop-shadow(0 5px 5px rgba(0,0,0,.28)); }
@keyframes kiteSway { 0%,100% { transform: translate(0,0) rotate(-6deg); } 50% { transform: translate(16px,-30px) rotate(9deg); } }
#toy-kite.flying { animation: kiteSway 1.2s ease-in-out 4; }
@keyframes kickBall { 0% { transform: translate(0,0) rotate(0); } 30% { transform: translate(110px,-64px) rotate(130deg); } 62% { transform: translate(200px,0) rotate(250deg); } 100% { transform: translate(0,0) rotate(360deg); } }
#toy-ball.kicked { animation: kickBall 1.1s ease-in-out; }
#toy-rocket.launch { transition: transform 1.1s ease-in, opacity 1.1s ease-in; transform: translateY(-290px); opacity: 0; }
@keyframes growPulse { 50% { transform: scale(1.35); } }
#toy-plant2.grow { animation: growPulse .5s ease-in-out 3; }

.hidden { display: none !important; }

/* --- Squirrel era: touch zones, accessories, bird --- */
.tzone { position: absolute; z-index: 4; cursor: pointer; border-radius: 40%; pointer-events: none; } /* v69 (fix 31): the legacy zones ate every click under them (stopPropagation + unconditional ladder) — a tap THROUGH Harley onto Gigi died here and Harley tickled instead. Hit-testing now lives in #person's belly rule; the zones are inert overlays. */
#zone-head { left: 0; top: 24%; width: 68%; height: 34%; }
#zone-belly { left: 10%; top: 58%; width: 48%; height: 27%; }
#zone-feet { left: 8%; top: 85%; width: 50%; height: 15%; }
.person-hat.acc-flower { top: 21%; left: 3%; transform: rotate(-8deg); font-size: 42px; }
.person-hat.acc-leaf { top: 3%; left: 31%; transform: rotate(-8deg); font-size: 46px; }
.person-hat.acc-scarf { top: 54%; left: 28%; transform: none; font-size: 46px; }
#bird { position: absolute; width: 19%; z-index: 8; cursor: pointer; }
#bird img { width: 100%; display: block; filter: drop-shadow(0 4px 5px rgba(0,0,0,.25)); pointer-events: none; }
#bird.perched { animation: birdBob 1.8s ease-in-out infinite; }
#bird.flap #bird-img { animation: birdFlap .15s ease-in-out infinite alternate; }
@keyframes birdFlap { from { transform: translateY(0); } to { transform: translateY(-7px); } }
#bird.song { animation: birdSong 1.2s ease-in-out; }
@keyframes birdSong { 0% { transform: translate(0, 0); } 25% { transform: translate(-7px, -9px); } 55% { transform: translate(6px, -13px); } 80% { transform: translate(-3px, -5px); } 100% { transform: translate(0, 0); } }
@keyframes birdBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
#bird-say { position: absolute; bottom: 103%; left: 54%; transform: translateX(-50%); background: #fff; border: 2px solid #bde3ff; padding: 10px; border-radius: 50%; font-size: 12px; font-weight: 800; box-shadow: 0 2px 6px rgba(0,0,0,.15); color: var(--ink); display: flex; align-items: center; justify-content: center; text-align: center; box-sizing: border-box; line-height: 1.22; } /* ?v=113: round chat-circle like the others */
#bird-say.hidden { display: none; }
#room[data-room="backyard"] #dig-mound { display: none; }
#room[data-room="backyard"] #yard-ball { bottom: 24%; }


/* ===== v5 mega-round: new rooms, games, scrub bath, sit TV, dog needs ===== */
#room[data-room="dogroom"] .hs-dogroom { display: block; }
#room[data-room="ground"] .hs-ground { display: block; }
/* v65 (fix 26): #hs-c-bow removed — the bow accessory is retired (backdrop art stays) */
#hs-d-bed { left: 0%; top: 32%; width: 48%; height: 22%; } /* ?v=177 (Tomika 12:09): REZONED onto the PAINTED cushion at the 110% framing (cushion spans x 0-47%, y ~34-46%) — the old zone sat at y 44-84%, covering the rug/floor in FRONT of the bed, so cushion taps hit nothing */ /* ?v=175: the painted bed spans stage x ~2-44%, y ~44-58%; the zone reaches DOWN to the floor line because the height cap holds a dragged dog at floor level — her centre enters the zone at floor depth and the nap stages her up into the bed (same overshoot discipline as the old rect) */ /* ?v=154: re-anchored to the panned backdrop — the painted bed's visible body now spans the left ~62% of the stage */
#hs-d-food { left: 1%; top: 73%; width: 16%; height: 21%; }
#hs-d-water { left: 16%; top: 73%; width: 15%; height: 21%; }
#hs-d-toys { left: 57%; top: 35%; width: 24%; height: 14%; } /* ?v=177: REZONED onto the painted basket at 110% (basket x ~59-79%, y ~37-46%) */ /* ?v=175: the toy basket in the tall art sits at stage x ~63-79%, y ~44-57% */ /* ?v=154: sits on the toy basket, which the panned view puts at stage x ~75-100% */
#hs-d-bone { left: 68%; top: 35%; width: 8%; height: 6%; } /* ?v=177: shrunk to JUST the bone poking from the basket top (x ~69-75%, y ~36-39% at 110%) — the old zone covered the basket's whole top half and stole toy-chest taps */ /* ?v=175: the bone pokes from the basket's top at stage x ~66-72%, y ~43-48% */ /* ?v=154: the bone poking from the basket's top lands at stage x ~78-90% in the panned view */
#hs-g-opening { left: 73%; top: 46%; width: 27%; height: 40%; }
#hs-g-pond { left: 0; top: 55%; width: 26%; height: 25%; }
#hs-g-slide { left: 45%; top: 40%; width: 42%; height: 20%; } /* ?v=175: the tall clearing art puts the slide on the RIGHT — lower chute + mouth at stage x ~45-87%, y ~40-60% */
#hs-g-flowers { left: 75%; top: 79%; width: 25%; height: 19%; }
#hs-g-grass { left: 25%; top: 79%; width: 45%; height: 19%; }
/* ?v=141 CLEARING LIFE (Tomika 2026-11-02 09:43): foreground critters in the clearing — above the hotspots (z 3) so their own taps land, below the characters (9/10) so the gang walks in front of them. Shown only in the ground room. Art is placeholder SVG (see index.html) pending Tomika's verdict. */
#room[data-room="treedoor"] .clear-critter { display: block; } /* ?v=269 (Tomika 00:31): the gecko lives on the MAZE-ENTRANCE tree (the tree door), not in the clearing — her room gate moves with her */
.clear-critter { z-index: 4; }
.clear-critter svg { display: block; width: 100%; height: auto; pointer-events: none; filter: drop-shadow(0 3px 3px rgba(0,0,0,.3)); }
#clear-lizard { left: 47%; top: 29%; width: 20%; transition: left .8s ease, top .8s ease; } /* ?v=271 (Tomika 00:45, tree-door screenshot): she WAS at the knot but invisible — 14% and green-on-moss. Home perch moves to the brown door ARCH (contrast) and she grows to 20% */ /* ?v=269: home spot = the spiral knot on the tree-door trunk, a touch bigger again for the big trunk · ?v=268: Chat's gecko is a full-body sitter (the flat SVG was 9%) — and the spot-to-spot glide slows to match the climbing frames */
#clear-lizard img { width: 100%; display: block; }
#clear-lizard .liz-tongue { opacity: 0; transition: opacity .12s; }
#clear-lizard.flick .liz-tongue { opacity: 1; }
#clear-lizard.pushing { animation: lizPush .4s ease-in-out 3; }
@keyframes lizPush { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
#tree-door-glow { position: absolute; left: 73%; top: 46%; width: 27%; height: 40%; z-index: 2; pointer-events: none; opacity: 0; transition: opacity .5s; background: radial-gradient(ellipse at 50% 60%, rgba(255,220,130,.85) 0%, rgba(255,200,90,.35) 45%, rgba(255,200,90,0) 75%); }
#room:not([data-room="ground"]) #tree-door-glow { display: none; }
#tree-door-glow.lit { opacity: 1; }
#tree-door-glow.wobble { animation: doorWobble .6s ease-in-out 1; }
@keyframes doorWobble { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(1.5deg); } 75% { transform: rotate(-1.5deg); } }
#room[data-room="backyard"] #yard-ball, #room[data-room="backyard"] #dig-mound { display: none !important; }
#room[data-room="ground"] #dig-mound { display: block; bottom: 6%; }
#room[data-room="backyard"] #toy-ball { bottom: 25%; }
#room[data-room="backyard"] #toy-plant2 { bottom: 26%; }
#canopy-slide { position: absolute; right: 1%; top: 35%; font-size: 54px; z-index: 7; cursor: pointer; display: none; filter: drop-shadow(0 4px 4px rgba(0,0,0,.3)); animation: slideNudge 1.6s ease-in-out infinite; }
#room[data-room="backyard"] #canopy-slide { display: block; }
@keyframes slideNudge { 0%,100% { transform: translateY(0); } 50% { transform: translateY(7px); } }
#sit-kid { position: absolute; left: 11%; bottom: 36%; width: 33%; z-index: 8; pointer-events: none; filter: drop-shadow(0 4px 4px rgba(0,0,0,.2)); }
/* ?v=386 REPAIR: the two seat rules below spent the evening as text INSIDE the base rule above (its closing brace had landed three lines late, so the parser ate both rules as bad declarations and the couch seats ran on the base 11%/36% all along — the ?v=375/376 seat moves never actually applied; the width factors in game.js did, which is what read as 'sizes are better'). They are real rules now: the seats Tomika approved finally take effect. */
#room:not(.char-brodie)[data-room="living"] #sit-kid { left: 14%; bottom: 38%; } /* ?v=386 (Tomika 20:29-20:30): the SQUIRRELS sit +3 right and +2 up on the couch (base 11%/36%) */
#room.char-brodie[data-room="living"] #sit-kid { left: 13.5%; bottom: 39%; } /* ?v=414 (Tomika 04:49): Brodie's SITTING poses down 2 and left 1 (from 14.5%/41%) — sitting poses only; his lying pose stays pinned where she called it perfect (couchShow, 12.5%/34.5%) */
#sit-dog { position: absolute; left: 47%; bottom: 5%; width: 44%; z-index: 8; pointer-events: none; filter: drop-shadow(0 4px 4px rgba(0,0,0,.2)); }
#soap { position: absolute; left: 68%; bottom: 50%; font-size: 42px; z-index: 9; cursor: grab; touch-action: none; filter: drop-shadow(0 3px 3px rgba(0,0,0,.25)); }
#clean-wrap { position: absolute; top: 3%; left: 50%; transform: translateX(-50%); width: 58%; max-width: 260px; background: rgba(255,255,255,.92); border-radius: 999px; padding: 5px 10px; z-index: 9; text-align: center; font-size: 12px; font-weight: 800; }
#clean-fill { height: 10px; width: 0%; border-radius: 999px; background: linear-gradient(90deg, #7bed9f, #2ed573); transition: width .2s; margin-bottom: 2px; }
#dog-stats { position: absolute; left: 3%; top: 2%; z-index: 9; background: rgba(255,255,255,.9); border-radius: 14px; padding: 6px 9px; width: 126px; display: none; box-shadow: 0 3px 8px rgba(0,0,0,.15); }
#room[data-room="dogroom"] #dog-stats { display: block !important; }
.ds-row { display: flex; align-items: center; gap: 5px; margin: 2px 0; font-size: 13px; }
.ds-bar { flex: 1; height: 9px; background: #eee; border-radius: 999px; overflow: hidden; }
.ds-bar div { height: 100%; border-radius: 999px; transition: width .3s; }
#ds-food { background: #ff9f43; }
#ds-water { background: #48dbfb; }
#speech-bubble { max-width: 76vw; white-space: normal; text-align: center; line-height: 1.25; }
#speech-bubble, #dog-bubble, #bird-say, .friend-bubble, #brodie-bubble { pointer-events: none; } /* ?v=177 (Tomika 12:09): speech bubbles are DISPLAY ONLY — the kid's big round bubble floats right over the dog-room bed, and while any line was showing it swallowed every cushion tap ('the tappable bed doesn't work'). Bubbles never take a tap again, in any room. */
/* v65 (fix 26): no .acc-bow rules — the bow accessory is retired for good */
.person-hat.acc-berry { top: 29%; left: 5%; transform: none; font-size: 36px; }
.person-hat.acc-acorn { top: 14%; left: 27%; transform: none; font-size: 40px; }
.person-hat.acc-butterfly { top: 19%; left: 57%; transform: none; font-size: 38px; }
#sleep-dog { left: 58.1%; bottom: 22%; width: 31%; } /* ?v=231: the pre-JS fallback wears the new 20%-smaller Gigi tuple (31% at left 58.1) so the first staged frame never flashes the old size */ /* ?v=174: matches the rescaled Gigi sleep tuple so there's no jump when the cycle takes over; up on the rug */ /* ?v=173 (Tomika 11:18): the dog sleeper moves UP + in so she lies ON the rug (Sky spilled off its edge) */ /* ?v=172: dog sleeper refit to the 130% bedroom — on the rug, in front of the bed */ /* ?v=172 (Tomika 11:13): the dog's sleeper shrunk so she fits ON the rug in front of the bed (was 47% and spilled off it) */ /* v63 (fix 10): her curled length now matches her dog-img sleep render (~180px) — was 39% and read small */
#zzz { animation: zFloat 2.6s ease-in-out infinite; }
@keyframes zFloat { 0% { transform: translateY(6px) scale(.8); opacity: .4; } 55% { opacity: 1; } 100% { transform: translateY(-26px) scale(1.25); opacity: .35; } }
.mad { animation: madShake .45s !important; }
@keyframes madShake { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }
#dog.walking #dog-img { animation: dogBob .28s infinite alternate; }
@keyframes dogBob { from { transform: translateY(0); } to { transform: translateY(-10px); } }
#bath-kid.scrubbing { animation: scrubWig .18s infinite alternate; }
@keyframes scrubWig { from { transform: translateX(-1.5px); } to { transform: translateX(1.5px); } }
#room.slide-next { animation: roomInR .38s ease; }
#room.slide-prev { animation: roomInL .38s ease; }
@keyframes roomInR { from { transform: translateX(30px); opacity: .35; } to { transform: none; opacity: 1; } }
@keyframes roomInL { from { transform: translateX(-30px); opacity: .35; } to { transform: none; opacity: 1; } }
#slide-stage { position: relative; height: 330px; border-radius: 16px; overflow: hidden; background: linear-gradient(#79bfe8 0%, #bfe8ff 45%, #8ed081 100%); touch-action: none; }
#slide-path { position: absolute; left: 50%; top: 0; width: 112px; height: 100%; transform: translateX(-50%); background: repeating-linear-gradient(180deg, #cf9250 0 46px, #c08340 46px 92px); border-radius: 44px; opacity: .9; }
.slide-item { position: absolute; font-size: 30px; transform: translate(-50%, -50%); z-index: 2; pointer-events: none; }
#slide-kid { position: absolute; width: 79px; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 3; pointer-events: none; filter: drop-shadow(0 4px 4px rgba(0,0,0,.25)); }
#slide-dog { position: absolute; width: 58px; bottom: 104px; left: 57%; transform: translateX(-50%); z-index: 2; pointer-events: none; filter: drop-shadow(0 3px 3px rgba(0,0,0,.25)); }
#slide-hud { position: absolute; top: 8px; left: 50%; transform: translateX(-50%); background: rgba(255,255,255,.92); padding: 4px 14px; border-radius: 999px; font-weight: 900; z-index: 4; white-space: nowrap; }
#slide-stage.bump { animation: madShake .3s; }
#maze-canvas { width: 100%; max-width: 306px; display: block; margin: 0 auto; border-radius: 12px; background: #7a4a21; }
#maze-btns { text-align: center; margin-top: 8px; }
#maze-btns button { font-size: 24px; padding: 7px 18px; margin: 2px; border-radius: 12px; border: 2px solid #f3c6d3; background: #fff; cursor: pointer; }

/* ===== v6: scarf fix, outdoor neighborhood, slide image, arrows ===== */
.person-hat.acc-scarf { top: 63%; left: 29%; }
/* Divergent fork bath staging: Chat's bath pose is a full-body sit, so she is
   scaled up, sunk lower, and clipped at mid-foam — the bubble line crosses her
   lower body and she reads as sitting IN the tub (the main game gets this from
   a bust-cropped sprite; here it is staging only, artwork untouched). */
#bath-kid { left: 7%; bottom: 38%; width: 31%; clip-path: inset(0 0 20% 0); } /* ?v=238 (Tomika 22:17): the old 38% clip cut her body at 62% of the box — at the shrunk staging that line floats ABOVE the foam mound, leaving a see-through band where her neck/chest should be (her "still transparent parts", proven on her enlarged screenshot: the body ends in a straight line and the wall shows through below it). The clip now keeps 80% of the sprite, so the cut hides well under the bubbles; the foam window + cluster (z-6/7) cover everything below the bubble line as before */
/* Divergent fork bath round 2: a FOREGROUND foam layer — the bathroom backdrop
   itself, rendered identically to #room-bg but clipped to the foam band and
   stacked ABOVE the sprite (z-6 vs her z-5), so the painted bubbles overlap her
   lower body and fill the gaps beside her. The band's top edge is a scalloped
   polygon (46.8–50%) so the foam meets her body along a mound contour, not a
   straight cut. Pixel-aligned by construction: same image, same cover
   rendering — invisible except where it covers her. */
#bath-foam { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; z-index: 6; pointer-events: none; clip-path: polygon(2% 52%, 10% 50%, 20% 51.5%, 30% 49.5%, 40% 51%, 46% 52.5%, 46% 58%, 2% 58%); } /* ?v=242 (Tomika 22:28): 'make sure you're showing some of her chest all the way up' — the bubble line drops from her chin/neck (?v=236: 44-47.5%) to her CHEST (49.5-52.5%): head, neck and upper chest render above the foam, and the window + bubbles take over at mid-chest like a real soak. Bottom edge (58%) unchanged */
/* Scrubbing tickles: while the laugh sprite is swapped in, restage her so the
   laughing head lands at the same size/place as the bath head (the laugh trim
   box is wider/shorter than the bath box). Body sinks behind the foam layer. */
#bath-kid.tickling { width: 39%; left: 3%; bottom: 41%; clip-path: inset(0 0 16% 0); } /* ?v=238: the tickle swap's clip deepens with the base rule (28%->16%) so its cut also hides under the foam */
/* Divergent fork bath round 3 (v52, Tomika's prescription: "just add bubbles
   to that current image of her, so it blends in with the bubbles in the
   tub"): a foam cluster ANCHORED TO THE SPRITE — this element carries the
   exact staging box of #bath-kid (same left/bottom/width, height from the
   bath sprite's aspect ratio), so it tracks her on ANY viewport aspect,
   unlike the backdrop-anchored #bath-foam band, whose fixed polygon drifts
   off her clip line on tall phones and left a straight cut edge + gap.
   The SVG cluster sits across her lower body at the clip line (62% of the
   sprite box), spans 135% of her width, and its two staggered rows of
   varied circles give irregular top AND bottom edges — no straight lines.
   z-7: above #bath-kid (5) and #bath-foam (6), below the soap (9). */
#bath-bubbles { position: absolute; left: 0%; bottom: 33%; width: 44%; aspect-ratio: 317/464; z-index: 7; pointer-events: none; } /* ?v=235: the cluster re-seated on her raised clip line (~48%) */
.tub-bub { position: absolute; pointer-events: none; z-index: 8; line-height: 1; } /* ?v=386: the tub's own ambient bubbles — spawned over the water only (the room-wide fx() sprayed them by the sink and on faces) */
#bath-bubbles svg { position: absolute; left: 2%; width: 104%; top: 57.5%; height: 11%; display: block; } /* ?v=386 (Tomika 22:45: 'the bubbles look weird around the tub'): the strip slims and lifts to the waterline — at 16% tall its bottom row of big uniform circles hung BELOW the rim as a scalloped curtain on the barrel front; circles shrunk ~28% in the SVG itself and the mass softened (.85 -> .78) so the painted backdrop foam blends through */ /* ?v=242: the strip drops with the foam window (top 52%->63%) so the bubble line crosses her CHEST, not her chin — head, neck and upper chest stay visible above it · ?v=243 (Tomika 22:32 'the bubbles look weird'): the strip was 135% wide bleeding -17.5% left, so foam blobs spilled past the tub onto the walls, and its tall flat-white band covered the backdrop's own shaded bubbles — two foam styles stacked. Now contained to the tub bowl (left 2%, width 104%), slimmer (height 16%) and a touch higher (top 61%), so the painted bubbles show around it and blend */
/* Tickle staging: the laugh sprite's box is wider/shorter — follow it to the
   shared waterline (clip line at 72% of that box) so the cluster stays put
   when the face swaps. Synced off the sprite's own class via :has. */
#room:has(#bath-kid.tickling) #bath-bubbles { left: 3%; bottom: 41%; width: 39%; aspect-ratio: 429/464; } /* ?v=235: mirrors the raised tickling box */
#room:has(#bath-kid.tickling) #bath-bubbles svg { top: 61%; height: 13.5%; } /* ?v=386: slims with the base strip */ /* ?v=242: the tickle state's strip drops to the chest line with the base rule · ?v=243: slims with it */
/* Scrub wiggle: she wiggles while scrubbed — her bubbles wiggle with her. */
#room:has(#bath-kid.scrubbing) #bath-bubbles { animation: scrubWig .18s infinite alternate; }
#soap { left: 7%; bottom: 45%; font-size: 38px; }
#canopy-slide { right: -3%; top: 46%; width: 47%; height: auto; font-size: 0; animation: none; z-index: 7; filter: drop-shadow(0 5px 5px rgba(0,0,0,.25)); }
#room[data-room="pond"] .hs-pond { display: block; }
#room[data-room="dogpark"] .hs-dogpark { display: block; }
#room[data-room="treedoor"] .hs-treedoor { display: block; }
#hs-d-rug { left: 5%; top: 54%; width: 55%; height: 8%; } /* ?v=175: the rug in the tall art lies in front of the bed, stage x ~6-58%, y ~54-62% */
#hs-p-water { left: 2%; top: 27%; width: 76%; height: 20%; } /* ?v=175: the tall pond art puts the water at stage y ~27-47% */
#hs-p-dock { left: 73%; top: 37%; width: 27%; height: 13%; } /* ?v=175: the dock platform sits at stage x ~73-100%, y ~37-50% */
#hs-p-reeds { left: 0; top: 20%; width: 14%; height: 14%; } /* ?v=175: the cattails stand at the pond's left edge, stage y ~20-34% */
#hs-dp-hoop { left: 3%; top: 21%; width: 13%; height: 17%; } /* ?v=245: re-anchored on Tomika's new park art — the agility hoop at the fence's left end */
#hs-dp-tunnel { left: 22%; top: 21%; width: 20%; height: 11%; } /* ?v=245: the blue tunnel, back-left on the new art */
#hs-dp-ramp { left: 57%; top: 21%; width: 19%; height: 11%; } /* ?v=245: the wooden ramp, back-right on the new art */
#hs-dp-bench { left: 80%; top: 20%; width: 18%; height: 18%; } /* ?v=245: the bench at the fence's right end on the new art */
#hs-dp-balls { left: 30%; top: 45%; width: 45%; height: 25%; } /* ?v=245: the open field — the ball party zone follows the taller field */
#hs-t-door { left: 35%; top: 34%; width: 30%; height: 32%; }
#hs-t-steps { left: 18%; top: 64%; width: 64%; height: 18%; }
#hs-t-shrooms { left: 76%; top: 64%; width: 22%; height: 18%; }
#walk-left, #walk-right { position: absolute; top: 46%; z-index: 11; width: 46px; height: 46px; border-radius: 50%; background: rgba(255,255,255,.88); font-size: 30px; font-weight: 900; color: #b06a8a; display: none; align-items: center; justify-content: center; box-shadow: 0 3px 9px rgba(0,0,0,.25); cursor: pointer; }
#walk-left { left: 6px; }
#walk-right { right: 6px; }
#room[data-room="pond"] #sit-kid { left: 64%; bottom: 33%; width: 26%; }
#room[data-room="dogpark"] #sit-kid { left: 84%; bottom: 66%; width: 16%; } /* ?v=245: her park seat is the new art's bench (fence's right end, far side) — she sits there at that depth's scale, outside the pen as always */
#room[data-room="backyard"] #sit-kid { left: -1%; bottom: 52%; width: 33%; } /* ?v=254 (Tomika 23:37): the swing staging moves right (+3) and up (+2) at her word */ /* ?v=112: the canopy SWING seat — the painted plank's surface measures at ~58% room-bottom and its centre at ~12.5% room width (part of the plank is cropped by the cover-fit, like the deck's left edge); the frame bottom sits ~8% under the rump line (the ?v=104 couch calibration), feet dangling toward the deck. Width 33% = just under the twins' 36% toilet-sit render (Tomika 19:38: swing-sit sits slightly under each character's own toilet size) */
#room.char-brodie[data-room="backyard"] #sit-kid { left: -6%; bottom: 54%; width: 37%; }
#room:not(.char-brodie)[data-room="backyard"] #sit-kid { bottom: 56.5%; } /* ?v=414 (Tomika 11:23): Hudson + Harley UP about 4-5 on the swing (shared seat 52% -> 56.5%); Brodie's swing seat stays at his own 54% */ /* ?v=386 (Tomika 22:12): Brodie on the swing up 2 more (the shared swing seat is 52%) */ /* Brodie: 37% vs his potty render of 46.6% effective (32.4% x 1.4375, ?v=142) — the same ~0.91x step down, re-centred on the plank */
#dog.jumping #dog-img { animation: dogJump .55s ease; }
.park-friend.jumping img { animation: dogJump .55s ease; } /* ?v=266: the park friends jump with the same beat (the hydrant splash) */
@keyframes dogJump { 40% { transform: translateY(-42px); } }
#room[data-room="backyard"] #toy-ball { left: 30%; bottom: 26.5%; }

/* v7: slide sits IN the deck edge — only its top shows, middle of the edge */
#canopy-slide { left: 35%; right: auto; top: auto; bottom: -4%; width: 45%; }

/* v8: Lily Pad Leap */
#hs-p-lily { left: 12%; top: 29%; width: 44%; height: 16%; } /* ?v=175: sits on the lily pads (stage y ~29-45%) so the Leap invitation lives on the water */
#lily-canvas { width: 100%; max-width: 340px; display: block; margin: 0 auto; border-radius: 12px; background: #3d9dbd; }
#lily-hud { text-align: center; font-size: 17px; margin-bottom: 4px; }
#lily-btns { text-align: center; margin-top: 8px; }
#lily-btns button { font-size: 24px; padding: 7px 18px; margin: 2px; border-radius: 12px; border: 2px solid #f3c6d3; background: #fff; cursor: pointer; }

/* v9: front-facing wide slide, connected to the canopy edge */
#canopy-slide { left: 22%; right: auto; top: auto; bottom: -1%; width: 56%; }
/* ?v=120: the slide's live tap target is a narrow strip down its MIDDLE
   (the slide spans room-x 22-78%, so the strip is 44-56%); the slide
   image itself takes no taps, so its top and edges are inert. */
#slide-strip { position: absolute; left: 44%; bottom: -1%; width: 12%; height: 28%; z-index: 8; cursor: pointer; display: none; }
#room[data-room="backyard"] #slide-strip { display: block; }
#room[data-room="backyard"] #canopy-slide { pointer-events: none; }
/* v9: the ball stays behind the rope too */
#room[data-room="backyard"] #toy-ball { bottom: 30%; }

/* v11: the show plays INSIDE the TV's glass */
#tv-screen { left: 65.5%; top: 54%; width: 19%; height: 11.5%; border-radius: 14px / 12px; font-size: 21px; overflow: hidden; box-shadow: inset 0 0 12px rgba(0,0,0,.45), 0 0 18px rgba(56,189,248,.45); }
#bird-img { color: transparent; }

/* v13: no box on the TV — the show plays on the TV's own screen, just the star moving */
#tv-screen { background: transparent !important; box-shadow: none; filter: drop-shadow(0 2px 2px rgba(0,0,0,.4)); }
.tv-drv { animation: tvDrv 1.1s ease-in-out infinite alternate; }
.tv-lnch { animation: tvLnch 2.2s ease-in-out infinite; }
.tv-stomp { animation: tvStomp .7s ease-in-out infinite; }
.tv-swim { animation: tvDrv 2.6s ease-in-out infinite alternate; }
@keyframes tvDrv { from { transform: translate(-17%, 0); } to { transform: translate(17%, -4%); } }
@keyframes tvLnch { 0%, 100% { transform: translateY(24%); } 55% { transform: translateY(-26%); } }
@keyframes tvStomp { 0%, 100% { transform: translateY(0); } 30% { transform: translateY(-15%); } }

/* v15: dog park balls + Gigi's Obstacle Dash */
.park-ball { position: absolute; width: 9%; z-index: 6; display: none; cursor: pointer; touch-action: none; filter: drop-shadow(0 3px 3px rgba(0,0,0,.3)); } /* ?v=192: touch-action none so a long-press drag of a ball never becomes a page swipe */
#room[data-room="dogpark"] .park-ball { display: block; }

/* v54 (divergent): dog-park friends + tennis-ball fetch — Chat's cast art */
.park-friend { position: absolute; z-index: 12; display: none; cursor: pointer; touch-action: none; transition: transform .12s ease; }
.park-friend.dragging { z-index: 20; transform: scale(1.07); cursor: grabbing; }
#room[data-room="dogpark"] .park-friend { display: block; }
.park-friend img { height: 117px; width: auto; display: block; filter: drop-shadow(0 3px 3px rgba(0,0,0,.22)); pointer-events: none; animation: friendBob 2.6s ease-in-out infinite alternate; }
#friend-3 img { height: 106px; animation-duration: 3.1s; }
#friend-2 { left: 68%; bottom: 33%; }
#friend-3 { left: 76%; bottom: 31%; }
@keyframes friendBob { from { transform: translateY(0); } to { transform: translateY(-4px); } }
.park-friend.bounce img { animation: friendBounce .55s ease; }
@keyframes friendBounce { 35% { transform: translateY(-20px); } 70% { transform: translateY(2px); } }
.park-friend.hop img { animation: friendHop .6s ease; }
@keyframes friendHop { 0% { transform: translateY(0); } 20% { transform: translateY(-7px); } 45% { transform: translateY(-32px); } 63% { transform: translateY(-11px); } 82% { transform: translateY(1px); } 100% { transform: translateY(0); } }
.park-friend.hop-big img { animation: friendHopBig .62s ease; }
@keyframes friendHopBig { 0% { transform: translateY(0); } 25% { transform: translateY(-10px); } 48% { transform: translateY(-46px); } 68% { transform: translateY(-14px); } 100% { transform: translateY(0); } }
.park-friend.bow img { animation: friendBow .85s ease; }
#dog.bow #dog-img { animation: friendBow .85s ease; }
@keyframes friendBow { 0% { transform: scaleY(1); } 25% { transform: scaleY(.76) translateY(6px); } 45% { transform: scaleY(.76) translateY(6px); } 65% { transform: scaleY(.8) translateY(4px); } 100% { transform: scaleY(1); } }
.park-friend img { transform-origin: 50% 92%; }
.friend-fx { position: absolute; bottom: 80%; font-size: 15px; line-height: 1; pointer-events: none; z-index: 3; animation: friendFxRise 1.1s ease-out forwards; }
@keyframes friendFxRise { 0% { transform: translateY(0) scale(.7); opacity: 0; } 25% { opacity: 1; } 100% { transform: translateY(-32px) scale(1.15); opacity: 0; } }
.friend-bubble { position: absolute; bottom: 102%; left: 54%; transform: translateX(-50%); background: #fff; padding: 10px; border-radius: 50%; font-size: 12px; font-weight: 800; box-shadow: 0 2px 6px rgba(0,0,0,.15); color: var(--ink); display: flex; align-items: center; justify-content: center; text-align: center; box-sizing: border-box; line-height: 1.22; } /* ?v=113: round chat-circle like the others */
.friend-bubble.hidden { display: none; }

/* v59 (divergent): Brodie the red panda lives in the forest clearing */
#brodie { position: absolute; left: 9%; bottom: 31%; z-index: 11; /* ?v=254 (Tomika 23:37): his default deck spot moves right (+3) and up (+2) with the scene's other stagings — a spot she has dragged him to still wins, as always */ display: none; cursor: pointer; touch-action: none; transition: transform .12s ease; } /* v88: z 6 -> 11, ABOVE #person (z 10) — his home spot sits inside the kid's 275px hitbox footprint, so whenever she stood over him he painted UNDER her sprite; curled in his sleep pose he was covered completely and "vanished" (only his sleep bar showed), and touches on his body grabbed her instead. As the pet at her feet he now draws — and hit-tests — in front of her. (The v65/v78 load heals were real but separate: every pose file was always healthy.) */
#room[data-room="backyard"] #brodie { display: block; }
#brodie.dragging { z-index: 20; transform: scale(1.06); cursor: grabbing; }
#brodie-img { height: calc(93.6px * var(--big-shrink)); width: auto; display: block; filter: drop-shadow(0 4px 4px rgba(0,0,0,.2)); pointer-events: none; } /* ?v=109: 104px x 0.90 — Brodie's general scene scale reaches his companion sprite too (a scene sprite, not a UI icon) */
#brodie.hop #brodie-img { animation: friendHop .6s ease; }
#brodie-bubble { position: absolute; bottom: 102%; left: 54%; transform: translateX(-50%); background: #fff; padding: 10px; border-radius: 50%; font-size: 12px; font-weight: 800; box-shadow: 0 2px 6px rgba(0,0,0,.15); color: var(--ink); display: flex; align-items: center; justify-content: center; text-align: center; box-sizing: border-box; line-height: 1.22; } /* ?v=113: round chat-circle like the others */
#brodie-bubble.hidden { display: none; }

/* v59 (divergent): Gigi's ladder potty marks — pee fades, poop waits for a tap */
.potty-mark { position: absolute; width: 12%; z-index: 11; cursor: pointer; filter: drop-shadow(0 2px 2px rgba(0,0,0,.25)); }
.potty-mark.fading { transition: opacity 1.2s ease; opacity: 0; }
.potty-mark.away { transition: opacity .5s, transform .5s; opacity: 0; transform: scale(.35) translateY(-10px); }
#fetch-ball { position: absolute; left: 84%; bottom: 6%; width: 11%; z-index: 6; display: none; cursor: pointer; filter: drop-shadow(0 3px 3px rgba(0,0,0,.3)); }
#room[data-room="dogpark"] #fetch-ball { display: block; }
#fetch-ball-2 { position: absolute; left: 10%; bottom: 30%; width: 11%; z-index: 6; display: none; cursor: pointer; filter: drop-shadow(0 3px 3px rgba(0,0,0,.3)); }
#room[data-room="dogpark"] #fetch-ball-2 { display: block; }
#dog.fetch-hopping #dog-img { animation: fetchHop .34s ease-in-out infinite alternate; }
@keyframes fetchHop { from { transform: translateY(0); } to { transform: translateY(-26px); } }
#gigi-hud { text-align: center; font-size: 16px; margin-bottom: 5px; }
#gigi-stage { position: relative; height: 280px; border-radius: 14px; overflow: hidden; background: linear-gradient(#aee3ff 0 62%, #8ed081 62% 100%); touch-action: none; }
#gigi-track { position: absolute; left: 0; right: 0; bottom: 22%; height: 8%; background: #c98d5a; border-top: 3px solid #a9743f; }
#gigi-runner { position: absolute; left: 14%; width: 54px; z-index: 3; }
.gigi-ob { position: absolute; z-index: 2; }
.ob-log { font-size: 30px; line-height: 1; }
.ob-tunnel { width: 62px; height: 54px; background: #4aa3d8; border-radius: 31px 31px 0 0; box-shadow: inset 0 -18px 0 -6px #2c6e99, inset 0 0 0 4px #7cc0e8; }
.ob-ramp { width: 52px; height: 30px; background: #b07a45; clip-path: polygon(0 100%, 100% 100%, 100% 25%); }

/* v17: profile running Gigi is wider than tall */
#gigi-runner { width: 76px; }

/* v21: the kite lives in a forest tree now, ball down in the clearing */
#room[data-room="ground"] #toy-kite { left: 71%; bottom: 63%; transform: rotate(16deg); }
#room[data-room="ground"] #toy-ball { left: 18%; bottom: 7%; }

/* v22: tap the big forest tree to call Sevyn */
#hs-g-tree { left: 2%; top: 2%; width: 40%; height: 30%; }

/* ?v=106: the v26 bedroom guitar rule is REMOVED with the prop (Tomika
   19:32 — the guitar belongs to the future music room, not this scene). */

/* v27: first-person 3D tree maze */
#maze-3d { width: 100%; height: 300px; border-radius: 12px; overflow: hidden; background: #243014; touch-action: none; }
#maze-3d canvas { display: block; }
#maze-hud { text-align: center; font-weight: 700; margin-top: 7px; color: #7a4d16; font-size: 16px; }
/* v72 (fix 28): the explorer's minimap — hidden outside the fullscreen 3D maze */
#maze-map { display: none; }
.fs3d #maze-map { display: block; position: absolute; top: calc(248px + env(safe-area-inset-top)); left: 10px; /* ?v=302: drops below the lowered header text block (see the #maze-game h3 rule below) */ z-index: 6; width: 106px; height: 130px; background: rgba(16,24,10,.72); border: 2px solid rgba(255,255,255,.55); border-radius: 10px; }

/* v28: window hotspot onto the actual window; lantern + lamp are light switches */
#hs-k-window { left: 39%; top: 37%; width: 22%; height: 10.5%; border-radius: 50%; } /* ?v=339 (Tomika 13:36): Sevyn may ONLY be called from INSIDE the glass — the ?v=278 zone covered the window's whole bounding box and overshot it (left 30/top 23 vs the painted frame at 34/33), so taps on the wall and lantern left of the window summoned her. The zone is now a circle inscribed in the painted glass (glass spans x 37-62, y 36-48.5, measured on the backdrop grid) */ /* ?v=278: twin of the rule above — both copies carry the re-measured zone */
#hs-k-pantry { left: 62%; top: 22%; width: 27%; height: 22%; } /* ?v=278 (Tomika 01:01): the WHOLE right shelf unit — both jar rows and everything on them (was a 12%-tall strip at 66/32; the upper shelf fell outside it) */
#hs-k-lantern { left: 26%; top: 22%; width: 10%; height: 18%; } /* ?v=278: ON the painted lantern, left of the window (was 48/6 — up on the window's bare wall, which is why her lantern taps never landed) */
#hs-b-lamp { left: 73%; top: 51%; width: 12%; height: 13%; } /* ?v=174: lamp position in the 110% framing */ /* ?v=173: lamp position in the 120% framing */ /* ?v=172: lamp position in the 130% framing */ /* ?v=172: lamp position in the 145% framing */ /* ?v=172: lamp position in the 160% framing */ /* ?v=171: lamp position in the 140% framing */ /* ?v=169: tap zone on the mushroom lamp in the tall bedroom backdrop */ /* ?v=153: re-centred on the mushroom lamp in the reframed view (painted lamp centre ~83% of stage; the tap zone sits over its cap — the group-bedtime control must stay easy to see and tap) */
#dark-overlay { position: absolute; inset: 0; background: rgba(12, 14, 44, .55); opacity: 0; transition: opacity .8s; pointer-events: none; z-index: 45; }
#room.lights-off #dark-overlay { opacity: 1; }

/* v30: pantry goes full-bleed and big */
.pantry-card { max-width: 660px; max-height: 95dvh; padding: 14px 0 16px; }
.pantry-card h3 { margin-bottom: 0; }
.pantry-card .sheet-sub { margin: 2px 0 8px; }
.pantry-card #fridge-wrap { margin: 0; }
.pantry-card #fridge-img { border-radius: 10px; }
.pantry-card .close-btn { margin-top: 12px; }
/* 2D/3D chooser + Dash 3D */
.pick-row { display: flex; gap: 10px; margin: 10px 0 4px; }
.pick-btn { flex: 1; font-family: inherit; font-size: 32px; font-weight: 900; padding: 16px 6px 12px; border-radius: 18px; border: 2px solid var(--soft); background: #fff8fb; color: #5a3a4d; cursor: pointer; }
.pick-btn span { display: block; font-size: 13px; font-weight: 800; color: #8a5a70; margin-top: 2px; }
.pick-btn:active { transform: scale(.96); }
#dash3d { width: 100%; height: 300px; border-radius: 12px; overflow: hidden; background: #9fd8ef; touch-action: none; }
#lily3d { width: 100%; height: 300px; border-radius: 12px; overflow: hidden; background: #3592b3; touch-action: none; }
#slide3d { width: 100%; height: 300px; border-radius: 12px; overflow: hidden; background: #8fd4f2; touch-action: none; }
#lily3d-hud { text-align: center; font-size: 17px; margin-bottom: 4px; }
#lily3d-btns { text-align: center; margin-top: 8px; }
#lily3d-btns button { font-size: 24px; padding: 7px 18px; margin: 2px; border-radius: 12px; border: 2px solid #f3c6d3; background: #fff; cursor: pointer; }
#slide3d-hud { text-align: center; font-size: 16px; font-weight: 800; color: #7a4a21; margin: 6px 0 2px; }
#dash3d canvas { display: block; }
#dash3d-hud { text-align: center; font-size: 16px; margin-bottom: 5px; }
.dash3d-row { display: flex; gap: 10px; justify-content: center; align-items: center; margin-top: 10px; }
.jump-btn { font-family: inherit; font-size: 20px; font-weight: 900; padding: 13px 30px; border-radius: 999px; border: none; background: #4aa3d8; color: #fff; cursor: pointer; box-shadow: 0 4px 0 #2c6e99; }
.jump-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 #2c6e99; }
#maze-canvas { border-radius: 12px; max-width: 100%; }

/* v32: potty, cooking, teeth */
#potty-kid { position: absolute; left: 32%; bottom: 32%; width: 36%; /* ?v=169: seat height for the tall potty backdrop */ z-index: 7; }
/* ?v=97 (Tomika 17:30): Brodie reads too small on the toilet — his seated
   presentation grows x1.25 around his seat contact point (origin low on
   the sprite, where his rump meets the seat) so he stays properly seated:
   no floating, no sinking. Twins' sprite carries no class = unchanged. */
#potty-kid.pk-brodie { --pks: 1.4375; transform: scale(1.4375); transform-origin: 50% 80%; } /* ?v=142 (Tomika 2026-11-02): Brodie's toilet render +15% — 1.25 x 1.15 = 1.4375, so the end render is 32.4% x 1.4375 = 46.6% of room width (was 40.5%). Twins untouched (36%, no scale). */
#brush-cup { position: absolute; left: 80%; bottom: 51%; width: 5%; display: none; z-index: 6; cursor: pointer; touch-action: none; } /* ?v=233 (Tomika 21:57): the toothbrush was way too big — 8.5% -> 5% wide, seated on the zoomed leaf sink's right rim; touch-action none so the long-press drag (?v=233, game.js) owns the gesture */
#room[data-room="bathroom"] #brush-cup { display: block; z-index: 7; }
#room[data-room="potty"] .hs-potty { display: block; }
#room[data-room="closet"] .hs-closet { display: block; }
#hs-du-skylight { left: 20%; top: 0%; width: 60%; height: 15%; }
#hs-du-window { left: 0%; top: 17%; width: 21%; height: 23%; }
#hs-potty-toilet { left: 34%; top: 44%; width: 36%; height: 27%; } /* ?v=308: re-aimed for the 100% framing */ /* ?v=305: re-aimed at the toilet on the new backdrop (seat ~5 points lower on stage) */
#hs-po-lantern { left: 67%; top: 32%; width: 15%; height: 12%; } /* ?v=308: re-aimed for the 100% framing */ /* ?v=305: the painted wall lantern is a light switch (day/night pair) */
#hs-po-skylight { left: 6%; top: 0%; width: 88%; height: 22%; } /* ?v=308: re-aimed for the 100% framing */ /* ?v=305: the painted skylight is also a light switch, like the music room */ /* ?v=188 (Tomika 14:42): the zone WAS most of the room (54x71) — mid-room taps and drag-releases kept sitting her on the toilet. Now it hugs the painted toilet (tank + bowl in the 120% framing); sitting also fires from a drop right on the seat (dropZoneCheck) */
#hs-k-stove { left: 14%; top: 38%; width: 18%; height: 20%; } /* ?v=278 (Tomika 01:01): the stove AND what's on it (the kettle) open the cooking game — the zone now covers the stove top (was 5/52, the stove's base and the floor) */
.potty-dance { animation: pottyWiggle .5s ease-in-out 2; }
@keyframes pottyWiggle { 0%, 100% { transform: translateX(0) scale(var(--pks, 1)); } 25% { transform: translateX(-4px) scale(var(--pks, 1)); } 75% { transform: translateX(4px) scale(var(--pks, 1)); } }
.cook-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.cook-grid .snack-btn.picked { background: #ffe9a8; border-color: #f2b134; }
#cook-bowl { font-size: 40px; min-height: 52px; letter-spacing: 2px; }
#cook-chop-item { font-size: 92px; cursor: pointer; user-select: none; -webkit-user-select: none; line-height: 1.15; }
#cook-chop-count { font-weight: 800; color: #8a5a70; margin-bottom: 6px; }
#cook-pot { touch-action: none; }
#cook-steam { height: 26px; font-size: 20px; }
#cook-pot-emoji { font-size: 104px; line-height: 1.1; }
#cook-fire { font-size: 34px; margin-top: -14px; transition: font-size .3s; }
.cook-track { margin: 12px auto 4px; max-width: 260px; }
#cook-action { margin-top: 10px; }
/* v46: teeth — Tom-style opening beat, zoom into a full-screen mouth */
.teeth-card { padding: 16px 12px 22px; }
#teeth-stage { position: relative; width: 100%; max-width: 430px; aspect-ratio: 4 / 5; margin: 0 auto; border-radius: 16px; overflow: hidden; background: #cfe8f2 url('../assets/th-bathroom-p.jpg') center / cover; }
#teeth-game { align-items: stretch; } /* ?v=235 (Tomika 22:08): the teeth game goes FULL SCREEN — all the way up, not a low sheet */
#teeth-game .teeth-card { height: 100dvh; max-height: none; border-radius: 0; display: flex; flex-direction: column; overflow-y: auto; } /* ?v=244: on a very short screen the card scrolls — the stage's 4:5 is never the thing that gives */
#teeth-game #teeth-stage { margin: auto; flex: none; width: min(100%, 430px); } /* ?v=239 (Tomika 22:22): her face rendered WIDE — the fullscreen card's flex column was SHRINKING the stage vertically when the title/chips/progress/Done crowded it, breaking the stage's 4:5 and stretching the canvas (and her face) sideways. The stage refuses to shrink (flex: none). ?v=244 (Tomika 22:35): the ?v=239 height-based width cap made the whole game SMALLER on her phone — not asked for. The cap is gone: full card width (the old 430px ceiling), full size; the vertical room comes from removing the patient picker row (her same message), and the card scrolls on very short screens rather than ever squashing the stage */
/* v61: teeth progress capsule — modeled on the bath's #clean-wrap scrub bar:
   same floating pill at the top of the scene, same green fill language */
#teeth-clean-wrap { position: absolute; top: 3%; left: 50%; transform: translateX(-50%); width: 58%; max-width: 260px; background: rgba(255,255,255,.92); border-radius: 999px; padding: 5px 10px; z-index: 9; text-align: center; font-size: 12px; font-weight: 800; color: var(--ink); }
#teeth-clean-fill { height: 10px; width: 0%; border-radius: 999px; background: linear-gradient(90deg, #7bed9f, #2ed573); transition: width .2s; margin-bottom: 2px; }
#teeth-clean-wrap.done { background: #eafff1; box-shadow: 0 0 0 2px #2ed573 inset, 0 2px 8px rgba(0,0,0,.18); }
/* v59: patient picker — dental clean-head portraits · ?v=244 (Tomika 22:35): REMOVED at her order — the teeth game always plays the character you're currently playing; no switching patients mid-game. The row is display:none (the builder JS stays harmless) and the freed height is what lets the stage run full width without crowding */
#teeth-patients { display: none; }
#teeth-patients::-webkit-scrollbar { display: none; }
.tpat { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 2px; background: none; border: none; cursor: pointer; font-family: inherit; padding: 0; }
.tpat img { width: 52px; height: 52px; object-fit: cover; border-radius: 50%; border: 3px solid #e8d8e0; background: #fdf3e3; pointer-events: none; }
.tpat span { font-size: 11px; font-weight: 800; color: #8a5a70; }
.tpat.selected img { border-color: #f2b134; box-shadow: 0 0 0 2px #ffe9a8; }
.tpat.selected span { color: #5a3a4d; }
#teeth-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; opacity: 0; transform: scale(1.14); transition: opacity .45s ease, transform .8s cubic-bezier(.2,.7,.3,1); }
#teeth-stage.open #teeth-canvas { opacity: 1; transform: scale(1); }
#teeth-stage.win-zoom #teeth-canvas { transform: scale(.86); opacity: 0; transition: opacity .55s ease, transform .55s ease; }
#teeth-intro { position: absolute; inset: 0; z-index: 2; background-size: cover; background-position: center; transform-origin: 46% 32%; transition: transform .85s cubic-bezier(.55,.06,.75,.4), opacity .5s ease; }
#teeth-stage.zooming #teeth-intro { transform: scale(3.4); opacity: 0; }
#teeth-stage.open #teeth-intro { opacity: 0; visibility: hidden; transition: opacity .3s ease, visibility 0s linear .3s; }
#teeth-stage.win-zoom #teeth-intro { opacity: 1; visibility: visible; transform: scale(1); transition: opacity .5s ease, transform .5s ease, visibility 0s; }
#teeth-hero { position: absolute; bottom: 0; left: 47%; transform: translateX(-50%); height: 97%; pointer-events: none; }
.teeth-fly { position: absolute; z-index: 3; pointer-events: none; filter: drop-shadow(0 1px 1px rgba(0,0,0,.35)); }
.fly-a { left: 30%; top: 10%; font-size: 17px; animation: flyOrbitA 2.6s linear infinite; }
.fly-b { left: 57%; top: 5%; font-size: 13px; animation: flyOrbitB 3.4s linear infinite; }
@keyframes flyOrbitA { 0% { transform: translate(0,0) rotate(0deg); } 25% { transform: translate(17px,-7px) rotate(16deg); } 50% { transform: translate(3px,-14px) rotate(-8deg); } 75% { transform: translate(-15px,-5px) rotate(-16deg); } 100% { transform: translate(0,0) rotate(0deg); } }
@keyframes flyOrbitB { 0% { transform: translate(0,0) rotate(0deg); } 25% { transform: translate(-14px,6px) rotate(-14deg); } 50% { transform: translate(-3px,13px) rotate(8deg); } 75% { transform: translate(13px,4px) rotate(14deg); } 100% { transform: translate(0,0) rotate(0deg); } }
#teeth-foam { position: absolute; inset: 0; z-index: 3; pointer-events: none; overflow: hidden; transition: opacity .5s ease; }
#teeth-stage.win-zoom #teeth-foam { opacity: 0; }
.foam-dot { position: absolute; border-radius: 50%; background: radial-gradient(circle at 35% 32%, #ffffff, rgba(255,255,255,.88) 55%, rgba(205,232,255,.55)); box-shadow: inset -2px -2px 4px rgba(150,195,235,.55), inset 1px 1px 2px #fff, 0 1px 2px rgba(110,150,195,.25); animation: foamPop 1s ease-out forwards; }
@keyframes foamPop { 0% { transform: scale(.35) translate(0,0); opacity: 0; } 15% { opacity: 1; } 55% { transform: scale(1.05) translate(var(--dx, 0px), -5px); opacity: 1; } 100% { transform: scale(1.4) translate(var(--dx, 0px), -13px); opacity: 0; } }
#teeth-brush { position: absolute; right: 4px; bottom: 4px; width: 50px; z-index: 5; cursor: grab; touch-action: none; transform: rotate(3deg); filter: drop-shadow(0 3px 3px rgba(0,0,0,.3)); transition: opacity .3s ease; }
#teeth-stage:not(.open) #teeth-brush { right: 6%; bottom: 5%; width: 17%; transform: none; animation: brushInvite 1.7s ease-in-out infinite; }
#teeth-stage.zooming #teeth-brush { opacity: 0; animation: none; }
#teeth-stage.win-zoom #teeth-brush { animation: none; }
@keyframes brushInvite { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
#teeth-brush.held { position: fixed; z-index: 300; transform: none; animation: none; }
#teeth-brush.buzzing { animation: buzzShake .07s linear infinite; }
#teeth-brush.hint-wiggle { animation: pottyWiggle .5s ease-in-out 2; }
@keyframes buzzShake { 0% { margin-left: 0; } 50% { margin-left: 2px; } 100% { margin-left: -1px; } }
#teeth-progress { font-weight: 900; margin-top: 8px; color: #5a3a4d; }
.opt-btn { font-family: inherit; font-size: 17px; font-weight: 900; padding: 12px 22px; border-radius: 999px; border: none; background: #ff9d5c; color: #fff; cursor: pointer; box-shadow: 0 4px 0 #d96f2e; margin: 6px auto 2px; display: block; }
.opt-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 #d96f2e; }
.opt-btn.hidden { display: none; }

#need-potty { cursor: pointer; }

#ds-potty { background: #a55eea; }
#poop { position: absolute; left: 62%; bottom: 4.5%; font-size: 36px; z-index: 8; cursor: pointer; animation: poopPulse 1s ease-in-out infinite; }
/* v41: Gigi potty spots — one in every forest scene */
.potty-spot { z-index: 7; }
#spot-pond { left: 74%; top: 70%; width: 24%; height: 24%; }
/* v63 (fix 19): the park's potty spot IS the fire hydrant prop now — a real
   tappable hydrant at the pen's right edge; when Gigi is bursting it glows. */
#spot-dogpark { left: 2.5%; top: auto; bottom: 1.2%; width: 139px; height: auto; } /* ?v=342: Chat's hydrant (adopted, 'use all of Chat's items') is a squarer canvas than the Starfire — width 92px -> 139px holds the hydrant's on-screen HEIGHT exactly */ /* ?v=129 (Tomika 10:42): nudged down toward the bottom-left into the space the retired bottom bar freed */
#spot-dogpark img { width: 100%; display: block; filter: drop-shadow(0 3px 3px rgba(0,0,0,.3)); pointer-events: none; } /* v65 (fix 27): the real hydrant prop, sized like a hydrant next to the dogs */
#spot-dogpark:active { background: transparent; }
#spot-dogpark.hinting { filter: drop-shadow(0 0 12px rgba(255,190,40,.95)) brightness(1.1); }
#spot-treedoor { left: 2%; top: 68%; width: 24%; height: 25%; }
.spot-hint { display: none; position: absolute; right: 6%; top: 4%; font-size: 19px; background: rgba(255,255,255,.92); border-radius: 999px; padding: 2px 7px; box-shadow: 0 2px 8px rgba(0,0,0,.25); animation: poopPulse 1s ease-in-out infinite; }
.potty-spot.hinting .spot-hint { display: block; }
#dog.squatting #dog-img { transform: scaleY(.9); }
#dog-img { transition: transform .3s; }
#dog.good-shake { animation: dogShake .45s ease-in-out; }
@keyframes dogShake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }
.animated-poop { position: absolute; font-size: 30px; z-index: 8; pointer-events: none; animation: poopDrop .45s ease-in forwards; }
.animated-poop.wiggle { animation: poopWiggle .45s ease-in-out 2; }
.animated-poop.away { transition: opacity .5s, transform .5s; opacity: 0; transform: scale(.35) translateY(-10px); }
@keyframes poopDrop { 0% { transform: translateY(-18px) scale(1.2, .75); opacity: 0; } 55% { transform: translateY(2px) scale(.85, 1.18); opacity: 1; } 78% { transform: translateY(0) scale(1.12, .88); } 100% { transform: translateY(0) scale(1, 1); opacity: 1; } }
@keyframes poopWiggle { 0%, 100% { transform: rotate(0); } 30% { transform: rotate(-7deg); } 70% { transform: rotate(7deg); } }
@keyframes poopPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.15); } }

/* Cook 3D kitchen */
#cook3d-game .sheet-card { max-width: 680px; }
#c3-stage { position: relative; width: 100%; aspect-ratio: 1344 / 703; border-radius: 16px; overflow: hidden; background: #8a5a33; touch-action: none; }
#c3-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
#c3-zoom { position: absolute; inset: 0; transform-origin: 76% 52%; transition: transform .7s cubic-bezier(.2,.7,.3,1); }
#c3-zoom.zoomed { transform: scale(2.5); }
/* v52: contents box IS the pot mouth (measured on cook-bg.jpg: centre
   (76%, 51.5%), rx 9.5% of stage width, ry ~4.2% of stage height) and the
   ellipse clip means no piece can ever spill over the rim or front wall. */
#c3-contents { position: absolute; left: 66.5%; top: 47.3%; width: 19%; height: 8.6%; z-index: 3; pointer-events: none; animation: c3bob 2.6s ease-in-out infinite; clip-path: ellipse(50% 50% at 50% 50%); }
.c3-soup { position: absolute; left: 2%; right: 2%; top: 4%; bottom: 4%; border-radius: 50%; background: radial-gradient(ellipse at 50% 38%, #f6c177, #cf8438 62%, #a55a24 92%); box-shadow: inset 0 -3px 6px rgba(0,0,0,.25); }
.c3-pieces { position: absolute; inset: 0; animation: c3jiggle var(--jigd, 1.1s) ease-in-out infinite; }
.c3-pieces img { position: absolute; width: 40%; filter: drop-shadow(0 1px 1px rgba(0,0,0,.3)); }
@keyframes c3bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2.5px); } }
@keyframes c3jiggle { 0%, 100% { transform: translate(calc(var(--amp, .4px) * -1), 0) rotate(-.8deg); } 50% { transform: translate(var(--amp, .4px), calc(var(--amp, .4px) * -.7)) rotate(1deg); } }
#c3-tray { display: flex; gap: 6px; justify-content: center; margin-top: 10px; min-height: 74px; }
.c3-tray-item { width: 15%; max-width: 76px; cursor: grab; text-align: center; touch-action: none; -webkit-user-select: none; user-select: none; }
.c3-tray-item img { width: 100%; pointer-events: none; }
.c3-tray-item span { display: block; font-size: 11px; font-weight: 800; color: #7a4a21; }
.c3-tray-item.used { opacity: .25; pointer-events: none; }
.c3-item { position: absolute; width: 23%; aspect-ratio: 1.15; z-index: 5; cursor: pointer; touch-action: none; }
.c3-item::after { content: ''; position: absolute; inset: -10px; } /* v48: finger-sized hit pad around each small board item */
.c3-item.waiting { opacity: .45; filter: saturate(.55); } /* v48 sequential chopping: non-active items wait dimmed */
.c3-item.active { z-index: 9; border-radius: 14px; box-shadow: 0 0 0 3px rgba(255,190,60,.95), 0 0 18px rgba(255,200,60,.8); animation: c3pulse .7s ease-in-out infinite alternate; } /* z-index 9: the active item wins hit-testing where the packed items' hit pads overlap */
@keyframes c3pulse { from { transform: scale(1); } to { transform: scale(1.06); } }
.c3-item .half { position: absolute; top: 0; width: 50.5%; height: 100%; overflow: hidden; transition: transform .18s; }
.c3-item .hl { left: 0; } .c3-item .hr { right: 0; }
.c3-item .half img { position: absolute; top: 0; width: 198%; height: 100%; max-width: none; }
.c3-item .hl img { left: 0; } .c3-item .hr img { left: -98%; }
.c3-item.chop1 .hl { transform: translateX(-5px) rotate(-5deg); }
.c3-item.chop1 .hr { transform: translateX(5px) rotate(5deg); }
.c3-item.chop2 .hl { transform: translateX(-11px) rotate(-10deg); }
.c3-item.chop2 .hr { transform: translateX(11px) rotate(10deg); }
.c3-item .pile { display: none; position: absolute; inset: 0; }
.c3-item.diced .half { display: none; }
.c3-item.diced .pile { display: block; }
.c3-item .pile img { position: absolute; width: 58%; }
.c3-item .pile img:nth-child(1) { left: 2%; top: 22%; transform: rotate(-10deg); }
.c3-item .pile img:nth-child(2) { left: 38%; top: 30%; transform: rotate(8deg); }
.c3-item .pile img:nth-child(3) { left: 20%; top: 2%; transform: rotate(18deg); }
.c3-drag { position: fixed; z-index: 120; pointer-events: none; filter: drop-shadow(0 10px 10px rgba(0,0,0,.35)); }
#c3-knife { position: absolute; width: 33%; left: 15%; top: 30%; z-index: 7; transform: rotate(6deg); transition: left .16s, top .16s, transform .13s; pointer-events: none; filter: drop-shadow(0 4px 4px rgba(0,0,0,.3)); } /* v73 (fix 8): home is right above the chopping board (board zone: items land left 6-42%, top 48-63%) — it used to sit on the window at 55%/3% */
#c3-spoon { position: absolute; width: 29%; right: 1%; bottom: 2%; z-index: 7; transform: rotate(-12deg); cursor: grab; touch-action: none; filter: drop-shadow(0 4px 4px rgba(0,0,0,.3)); }
#c3-spoon.rest-pot { left: 76%; top: 51%; right: auto; bottom: auto; transform: translate(-76%, -50%) rotate(35deg); transform-origin: 76% 50%; }
#c3-spoon.held { position: absolute; z-index: 81; pointer-events: none; transform: translate(-76%, -50%) rotate(35deg); transform-origin: 76% 50%; cursor: grabbing; }
#c3-fire { position: absolute; left: 63%; top: 69%; width: 26%; z-index: 4; display: flex; justify-content: center; gap: 2px; pointer-events: none; }
#c3-fire span { font-size: clamp(15px, 4.6vw, 30px); animation: c3flick .38s ease-in-out infinite alternate; }
#c3-fire span:nth-child(2) { animation-delay: .14s; }
#c3-fire span:nth-child(3) { animation-delay: .27s; }
@keyframes c3flick { from { transform: scale(1) translateY(0); } to { transform: scale(1.18) translateY(-3px); } }
#c3-knob { position: absolute; left: 66%; top: 76%; z-index: 9; border: none; border-radius: 999px; background: #ff6b4a; color: #fff; font-weight: 900; font-size: clamp(13px, 3.6vw, 17px); padding: 10px 16px; cursor: pointer; box-shadow: 0 4px 0 #d14a2e; animation: poopPulse 1s ease-in-out infinite; }
.c3-puff { position: absolute; width: 16px; height: 16px; border-radius: 50%; background: rgba(255,255,255,.85); pointer-events: none; animation: c3rise 1.15s ease-out forwards; z-index: 6; }
@keyframes c3rise { to { transform: translateY(-56px) scale(2.1); opacity: 0; } }
#c3-progwrap { height: 14px; background: #f3e3d3; border-radius: 999px; margin-top: 10px; position: relative; overflow: visible; }
#c3-prog { height: 100%; width: 0%; background: linear-gradient(90deg, #ffb35c, #ff7847); border-radius: 999px; transition: width .2s; }
#c3-progwrap span { position: absolute; right: 8px; top: -1px; font-size: 11px; font-weight: 800; color: #a35b1e; }
#c3-steam { position: absolute; inset: 0; pointer-events: none; }

/* v60: 3D games take the WHOLE screen — no card, no page chrome, HUD and
   controls float over the canvas, big ✕ exit in the corner */
.sheet.fs3d { background: #000; align-items: stretch; justify-content: center; }
.sheet.fs3d .sheet-card { width: 100%; max-width: none; height: 100dvh; max-height: 100dvh; border-radius: 0; padding: 0; overflow: hidden; position: relative; background: #000; animation: none; }
.fs3d #lily3d, .fs3d #slide3d, .fs3d #dash3d, .fs3d #maze-3d { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 0; }
.fs3d #maze-canvas { position: absolute; inset: 0; margin: auto; max-height: 100%; }
.fs3d .sheet-card h3 { position: absolute; top: calc(10px + env(safe-area-inset-top)); left: 0; right: 0; z-index: 6; margin: 0; padding: 0 64px; color: #fff; font-size: 21px; text-shadow: 0 2px 10px rgba(0,0,0,.5); pointer-events: none; }
.fs3d .sheet-sub { position: absolute; top: calc(42px + env(safe-area-inset-top)); left: 50%; transform: translateX(-50%); z-index: 6; width: max-content; max-width: 92%; margin: 0; padding: 6px 12px; border-radius: 999px; background: rgba(20,30,40,.48); color: #fff; font-size: 13px; pointer-events: none; }
.fs3d #lily3d-hud, .fs3d #slide3d-hud, .fs3d #dash3d-hud, .fs3d #maze-hud { position: absolute; left: 50%; transform: translateX(-50%); z-index: 6; margin: 0; width: max-content; max-width: 94%; color: #fff; font-size: 17px; font-weight: 800; text-shadow: 0 2px 8px rgba(0,0,0,.55); pointer-events: none; }
.fs3d #lily3d-hud { top: calc(78px + env(safe-area-inset-top)); }
.fs3d #slide3d-hud { top: calc(96px + env(safe-area-inset-top)); }
.fs3d #dash3d-hud { top: calc(112px + env(safe-area-inset-top)); } /* ?v=414: below the 3-line sprite-build subtitle */
.fs3d #maze-hud { top: auto; bottom: calc(164px + env(safe-area-inset-bottom)); font-size: 15px; } /* ?v=302 (Tomika 11:24): the progress line rides UP clear of the arrow pad (128px left it under the ⬆️ button) so it can be read */
.fs3d #lily3d-btns, .fs3d #maze-btns { position: absolute; left: 0; right: 0; bottom: calc(18px + env(safe-area-inset-bottom)); z-index: 7; margin: 0; }
/* ?v=132 fix: the fullscreen maze canvas (absolute inset 0) used to paint
   over #maze-hint — the button sat in normal flow with no z-index, so a
   real tap hit the canvas and the gnome hint never fired (diag139). Float
   it top-left, above the canvas, beside the ✕ exit. */
#maze-game h3 { top: calc(58px + env(safe-area-inset-top)); } /* ?v=302 (Tomika 11:24): the maze's title + intro text come DOWN below the hint-pill/exit row — they were riding at the very top edge, blocked behind the pill and the browser chrome. Maze-scoped: the other fullscreen games keep their headers. */
#maze-game .sheet-sub { top: calc(92px + env(safe-area-inset-top)); }
.fs3d #maze-hint { position: absolute; top: calc(10px + env(safe-area-inset-top)); left: 12px; z-index: 10; margin: 0; padding: 10px 16px; border-radius: 999px; border: none; background: rgba(255,255,255,.94); color: #444; font-size: 15px; font-weight: 900; font-family: inherit; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,.35); }
.fs3d #lily3d-btns button, .fs3d #maze-btns button { font-size: 30px; padding: 13px 26px; margin: 3px; border-radius: 16px; border: 2px solid rgba(243,198,211,.9); background: rgba(255,255,255,.94); box-shadow: 0 4px 14px rgba(0,0,0,.3); }
.fs3d .dash3d-row { position: absolute; left: 0; right: 0; bottom: calc(22px + env(safe-area-inset-bottom)); z-index: 7; margin: 0; }
.fs3d #dash3d-jump { font-size: 23px; padding: 17px 52px; }
.fs3d #lily3d-quit, .fs3d #slide3d-quit, .fs3d #maze-quit, .fs3d #dash3d-quit { display: none; }
.fs-exit { position: absolute; top: calc(10px + env(safe-area-inset-top)); right: 12px; z-index: 10; width: 46px; height: 46px; border-radius: 50%; border: none; background: rgba(255,255,255,.94); color: #444; font-size: 21px; font-weight: 900; font-family: inherit; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,.35); }
.fs-exit:active { transform: scale(.92); }
/* v85: a floating energy bar hovers over every sleeper, climbing live —
   Tomika: while they're sleeping you should SEE the bar going up */
.sleep-bar { position: absolute; z-index: 30; width: 86px; transform: translate(-50%, -100%); display: flex; align-items: center; gap: 4px; background: rgba(255,255,255,.93); border-radius: 999px; padding: 3px 8px 3px 6px; box-shadow: 0 3px 10px rgba(60,30,80,.28); pointer-events: none; font-size: 12px; line-height: 1; }
.sleep-bar .sb-track { flex: 1; height: 7px; background: #e8e2f2; border-radius: 99px; overflow: hidden; }
.sleep-bar .sb-fill { height: 100%; background: linear-gradient(90deg, #feca57, #ff9f43); border-radius: 99px; transition: width .45s ease; }
/* v89 (Tomika 13:48): at 80%+ energy the floating sleep bar turns GREEN */
.sleep-bar .sb-fill.full { background: linear-gradient(90deg, #8edd55, #27ae60); }
/* v84: Fix 15 fullscreen cooking — Chat's painted scene is the stage;
   every zone is positioned in % of the painting so it maps 1:1 at any size */
/* v90: the stage IS Chat's revised stove plates — ONE background plate per
   burner state (off / left / right / both), flicker-swapped by JS
   (ckPlateRefresh) at ~150ms while any burner is lit. The knobs are
   painted INTO the plates; the pot is a placed sprite on the left grate. */
#ck-stage { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); height: min(100%, calc(100vw * 1672 / 941)); aspect-ratio: 941 / 1672; background: url('../assets/cookfs/stove-off.webp') center / 100% 100% no-repeat; }
/* v89/v90: shelf raised so the foods SIT ON the painted plank (the new
   plate's beam spans ~7.3-13.2%) instead of hanging below it into the window */
#ck-shelf { position: absolute; top: 2.2%; left: 0; width: 100%; height: 10.8%; display: flex; gap: 2%; overflow-x: auto; overflow-y: hidden; padding: 0 2%; align-items: flex-end; scrollbar-width: none; z-index: 3; touch-action: pan-x; }
#ck-shelf::-webkit-scrollbar { display: none; }
.ck-food { flex: 0 0 auto; height: 96%; cursor: pointer; touch-action: pan-x; }
.ck-food img { height: 100%; width: auto; display: block; filter: drop-shadow(0 3px 3px rgba(0,0,0,.35)); }
#ck-board-food { position: absolute; left: 50%; top: 36.1%; transform: translateX(-50%); width: 36%; z-index: 2; cursor: pointer; touch-action: none; }
#ck-board-food.ready { cursor: grab; filter: drop-shadow(0 0 10px rgba(255,190,60,.95)); }
#ck-board-food.held { z-index: 6; cursor: grabbing; }
#ck-knife { position: absolute; left: 52%; top: 26.5%; width: 47%; z-index: 3; transform: rotate(8deg); cursor: grab; touch-action: none; transition: left .18s ease, top .18s ease, transform .12s ease; filter: drop-shadow(0 4px 5px rgba(0,0,0,.3)); }
#ck-knife.held { transition: none; cursor: grabbing; }
#ck-knife.chop { transform: rotate(-14deg) translateY(6%); }
/* v90: the new plates have NO painted pot — Chat's copper pot prop is a
   placed sprite sitting on the LEFT burner grate (base rests on the grate
   arms, centred on the burner cap at ~(26.2%, 77%)). The v89 flame-ring
   sprites are gone: the flames live in the plates now. */
#ck-pot { position: absolute; left: 6.1%; top: 61.4%; width: 40.3%; z-index: 1; pointer-events: auto; cursor: grab; touch-action: none; }
/* v94 (Tomika 16:49): the plates' painted flame fringe sits exactly
   where the pot's belly covers it (proven ?v=91: rotation runs, flame
   100% occluded), so she approved the additive route — Chat's flame-ring
   graphic (assets/cookfs/flames.png) wrapped around the POT's base,
   shown only while the pot's burner (fireL) is lit. The ring is centred
   on the pot (x 26.2%); its flame ellipse (~72% of its canvas) spans
   ~46% of the stage — just wider than the pot body (~40%) — so blue
   flame peeks out both sides and licks along the base. It shares the
   pot's z and sits BEFORE it in the DOM, so the pot's belly covers the
   ring's middle like a real burner. Flicker = a scale/opacity shimmer
   anchored at the flame band's base; NO rotation anywhere. */
#ck-flames { position: absolute; left: -1.8%; top: 64.3%; width: 56%; z-index: 1; pointer-events: none; transform-origin: 50% 82%; animation: ckFlameFlicker .48s ease-in-out infinite; }
@keyframes ckFlameFlicker {
  0% { transform: scale(1, 1); opacity: .93; }
  28% { transform: scale(1.018, 1.05); opacity: 1; }
  52% { transform: scale(.992, .962); opacity: .88; }
  76% { transform: scale(1.01, 1.032); opacity: .97; }
  100% { transform: scale(1, 1); opacity: .93; }
}
/* v89/v90: the pot's contents — a porridge surface + the chopped pieces,
   clipped to an ellipse over the placed pot's mouth.
   v91 (Tomika 16:20): the fill rode right at the rim, brimming over the
   top edge — dropped 2.8% of stage height so the surface sits DOWN inside
   the mouth (centre ~(26.2%, 72.4%) — inside the pot's interior, its front
   edge meeting the inner wall just above the rim's front line) */
#ck-potfood { position: absolute; left: 13.9%; top: 68.9%; width: 24.5%; height: 2.6%; z-index: 2; pointer-events: none; }
#ck-potfood .ck-surface { position: absolute; inset: 5% 2.5%; border-radius: 50%; background: radial-gradient(ellipse at 50% 42%, #f6c177, #cf8438 62%, #a55a24); box-shadow: inset 0 3px 7px rgba(90,40,0,.5); opacity: 0; transition: opacity .35s ease; }
#ck-potfood .ck-surface.on { opacity: 1; }
#ck-potfood .ck-surface.rich { background: radial-gradient(ellipse at 50% 42%, #eda858, #b96f27 62%, #8a4a1c); }
#ck-potfood .ck-surface.honeyed { background: radial-gradient(ellipse at 50% 42%, #ffd98a, #e09a3e 62%, #a55a24); }
#ck-potfood .ck-pieces { position: absolute; inset: 5% 2.5%; border-radius: 50%; overflow: hidden; }
#ck-potfood .ck-pieces img { position: absolute; width: 32%; filter: drop-shadow(0 1px 1px rgba(90,40,0,.45)); }
.ck-drag-ghost { position: fixed; z-index: 400; pointer-events: none; transform: translate(-50%, -50%); opacity: .95; filter: drop-shadow(0 6px 8px rgba(0,0,0,.4)); }
#ck-steam { position: absolute; left: 16.2%; top: 57.5%; width: 20%; z-index: 3; opacity: .9; pointer-events: none; }
#ck-splash { position: absolute; left: 13.2%; top: 61.5%; width: 26%; z-index: 4; pointer-events: none; }
#ck-pour { position: absolute; left: 15.2%; top: 58%; width: 22%; z-index: 4; pointer-events: none; }
/* v89: the ladle RESTS by the pot, grabbable — stirring is done WITH it.
   v90: it lies across the counter fascia under the board's left corner,
   handle reaching toward the pot on the left burner */
#ck-ladle { position: absolute; left: 3%; top: 53.5%; width: 26%; z-index: 3; cursor: grab; touch-action: none; transition: left .18s ease, top .18s ease; filter: drop-shadow(0 3px 4px rgba(0,0,0,.3)); }
#ck-ladle.held { transition: none; z-index: 6; cursor: grabbing; }
#ck-ladle.stir { animation: ckStir 1.1s ease-in-out; }
@keyframes ckStir { 0% { transform: rotate(0); } 25% { transform: rotate(-9deg) translateX(4%); } 50% { transform: rotate(7deg); } 75% { transform: rotate(-9deg) translateX(4%); } 100% { transform: rotate(0); } }
#ck-bowl { position: absolute; left: 30%; top: 35.5%; width: 40%; z-index: 3; filter: drop-shadow(0 5px 8px rgba(0,0,0,.3)); padding: 22px; margin: -22px; cursor: pointer; -webkit-tap-highlight-color: transparent; } /* v96: padding+negative margin = a generous invisible touch margin around the bowl (it is a tap target for the eat beat); the soup image itself does not move */
#ck-bowl-steam { position: absolute; left: 40%; top: 29%; width: 20%; z-index: 4; pointer-events: none; opacity: 0; animation: ckWisps 2.8s ease-in-out infinite; } /* ?v=158 (Tomika 2026-11-03): gentle steam wisps over the SERVED bowl while it waits to be eaten — whichever collection bowl was served; pointer-events none so the bowl keeps its eat tap. Anchored so the wisp bases start at the bowl's surface (first capture had them floating too high above the rim) */
@keyframes ckWisps { 0% { transform: translate(0, 6px) scale(.96); opacity: 0; } 35% { opacity: .8; } 70% { opacity: .55; } 100% { transform: translate(3px, -12px) scale(1.04); opacity: 0; } }
/* v90 (Tomika 13:42): the knobs are WORKING controls — and in the revised
   plates they are PAINTED IN (brass knobs at (24%, 92.6%) and (75.5%, 92.6%)),
   so NO medallion sprites: these generous transparent tap zones sit over
   the painted knobs, and burner state reads through the flame plates +
   fire crackle + the amber ring an ON knob wears (hugging the knob,
   covering nothing of it). */
#ck-knob, #ck-knob2 { position: absolute; width: 17%; height: 11.5%; transform: translate(-50%, -50%); background: none; border: none; padding: 0; cursor: pointer; z-index: 4; border-radius: 50%; }
#ck-knob { left: 24%; top: 92.8%; }
#ck-knob2 { left: 75.5%; top: 92.8%; }
#ck-knob.on::after, #ck-knob2.on::after { content: ''; position: absolute; inset: 9% 13%; border-radius: 50%; box-shadow: 0 0 0 3px rgba(255, 176, 64, .92), 0 0 18px 7px rgba(255, 122, 0, .5); pointer-events: none; }
#ck-hud { position: absolute; top: 59%; left: 50%; transform: translateX(-50%); z-index: 4; background: rgba(255,255,255,.88); border-radius: 999px; padding: 4px 12px; font-weight: 900; font-size: 14px; color: var(--ink); white-space: nowrap; } /* v96 (Tomika 17:20): the pot counter moved OFF the shelf's left end (it covered the apple) to the counter fascia UNDER the cutting board — centred under the board, clear of the board, the ladle rest (left), the pot (below-left) and both burners */
/* v89 (Tomika 13:45): the prompt pill lives directly UNDER the shelf row,
   hugging it — never down on the stove again */
#ck-sub { position: absolute; left: 50%; transform: translateX(-50%); top: 13.9%; z-index: 4; width: max-content; max-width: 92%; background: rgba(255,255,255,.92); border-radius: 14px; padding: 7px 14px; font-weight: 800; font-size: 16.5px; color: var(--ink); text-align: center; margin: 0; } /* ?v=116 (Tomika): instruction text a little bigger (14.5px -> 16.5px), same box footprint */
/* v89: Serve/Eat sit in the clear space BETWEEN the two knobs (~34-62%),
   covering no knob, burner or pot */
#ck-actions { position: absolute; left: 48%; transform: translateX(-50%); bottom: 1.6%; z-index: 4; display: flex; gap: 10px; }
#ck-actions button { font-family: inherit; font-weight: 900; font-size: 16px; border: none; border-radius: 999px; padding: 10px 18px; background: #ffb340; color: #4a2f10; box-shadow: 0 3px 8px rgba(0,0,0,.3); cursor: pointer; }
#ck-actions #ck-serve { background: #7ac74f; color: #173d05; }
#ck-stage .fs-exit { top: auto; bottom: 10px; right: 10px; width: 38px; height: 38px; background: #58b368; color: #fff; font-size: 17px; } /* v96 (Tomika 17:20): the ✕ leaves the top-right (it covered the shelf's blueberries) for the BOTTOM-RIGHT corner — GREEN and smaller (38px vs 46px), still a real touch target, clear of the right knob's zone (ends at 85% width) and of the Serve/Eat bar (centred ~33-63%) */
#hs-k-drinks { left: 79%; top: 70%; width: 19%; height: 20%; }

/* v100: the DRINKS ROOM (Tomika 17:24; shell v3 approved 17:43; water
   cooler KEPT 18:12). Zones follow design/drinks-room/LAYOUT.md on the
   painted shell: cooler bottom-left of the cabinet, brewer on the counter
   centre, sink at the counter's right corner, cooler bottle on the right
   floor. Characters stand on the woven mat.
   v101 (Tomika 18:27): the brewer + mug move to the OTHER side — the right
   stretch of counter between the window and the sink — and DOWN onto the
   countertop line (the painted front lip sits ~56.3% of backdrop height;
   sprite frame bottoms sit ~1% / ~0.5% below their visible bases, so frame
   bottoms 43% / 43.5% ground the visible bases on the surface, no hover).
   The mug mirrors to the brewer's front-left, clear of the sink zone
   (starts at 67%) and its fill glass; the coffee zone follows the brewer
   and ends at 65%, so it never steals a sink tap. */
#room[data-room="drinks"] .hs-drinks { display: block; }
/* ?v=148 MUSIC ROOM (Tomika 2026-11-02): piano against the left wall, guitar zone by its right end, Sevyn's mic corner by the window. Props sit below the characters (z 2, like the drinks cooler); the piano beat's seated figure (#mu-stage-kid, z 5) plays in front of the keys. Staging elements are beat-scoped AND room-scoped — a room change hides them. */
#room[data-room="music"] .hs-music { display: block; }
#mu-piano { left: 0%; bottom: 36%; width: 66%; z-index: 2; } /* ?v=177 (Tomika 12:10): the piano a little bit bigger (60% -> 66%); base stays planted at bottom 36% on the wall seam */ /* ?v=152 (Tomika 2026-11-03): the piano sat mid-floor at 14% — now ALL the way back against the wall (its base at the wall/floor seam of the backdrop) */
#mu-piano img { display: block; width: 100%; height: auto; }
#hs-mu-guitar { left: 54%; top: 47%; width: 20%; height: 20%; } /* ?v=152: the guitar tap zone rises with the piano's right end */
#hs-mu-skylight { left: 12%; top: 0%; width: 76%; height: 21%; } /* ?v=261: the painted skylight is the music room's light switch — tap swaps the day/night backdrops */
#mu-micstand { left: 70%; bottom: 30%; width: 20%; z-index: 2; } /* ?v=152: the mic stand moves back toward the wall/railing line with the piano */
#mu-micstand img { display: block; width: 100%; height: auto; }
#mu-sevyn { position: absolute; left: 66%; bottom: 30%; width: 30%; z-index: 8; pointer-events: none; } /* ?v=152: Sevyn sings AT the mic — her staging follows the stand back */
#mu-stage-kid { position: absolute; left: 26%; bottom: 34%; width: 40%; z-index: 5; pointer-events: none; } /* ?v=152: the bench pose stays 2% in front of the piano's base, as before */
#room:not([data-room="music"]) #mu-stage-kid, #room:not([data-room="music"]) #mu-sevyn { display: none !important; }
#room:not([data-room="music"]) #mu-piano, #room:not([data-room="music"]) #mu-micstand { display: none !important; }
.clear-prop { position: absolute; z-index: 4; display: none; }
#room[data-room="ground"] .clear-prop { display: block; }
#clear-frog { left: 11%; bottom: 5%; width: 8%; cursor: pointer; }
#clear-gnome { left: -5.7%; bottom: 28%; width: 35.7%; pointer-events: auto; cursor: pointer; }
#clear-mushrooms { left: 84%; bottom: 9%; width: 12.6%; cursor: pointer; }
#clear-leaves { left: 55%; bottom: 4%; width: 27%; cursor: pointer; }
#clear-leaves.rustling { animation: leafRustle .6s ease-in-out; }
@keyframes leafRustle { 0%,100% { transform: translate(0,0); } 20% { transform: translate(-1.2%, -1%); } 40% { transform: translate(1%, .5%); } 60% { transform: translate(-.8%, -.5%); } 80% { transform: translate(.6%, 0); } }
#clear-mushrooms.boinging, .tree-shroom.boinging { animation: mushSquash .45s ease; transform-origin: 50% 100%; }
.tree-shroom { position: absolute; z-index: 4; display: none; cursor: pointer; }
#room[data-room="treedoor"] .tree-shroom { display: block; }
.tree-shroom.flip { scale: -1 1; }
.tree-shroom[data-sh="1"] { left: 71%; bottom: 21%; width: 8.5%; }
.tree-shroom[data-sh="2"] { left: 25%; bottom: 54%; width: 6.5%; }
.tree-shroom[data-sh="3"] { left: 57%; bottom: 46%; width: 5.5%; }
.tree-shroom[data-sh="4"] { left: 15%; bottom: 13%; width: 5%; }
.tree-shroom[data-sh="5"] { left: 85%; bottom: 7%; width: 9.5%; }
@keyframes mushSquash { 0% { transform: scale(1, 1); } 35% { transform: scale(1.07, .8); } 65% { transform: scale(.96, 1.07); } 100% { transform: scale(1, 1); } }
#clear-frog.hopping { animation: frogHop .48s ease; }
@keyframes frogHop { 0%, 100% { transform: translateY(0); } 35% { transform: translateY(-38%); } 70% { transform: translateY(0); } 82% { transform: translateY(-12%); } } /* ?v=285: the clearing frog's tap hop — translation only, no spin (house rule) */ /* ?v=283 (Tomika 08:28): the piano and mic stand exist ONLY in the music room — a hard display gate, so no script state can ever show them anywhere else */
.dr-prop { display: none; position: absolute; pointer-events: none; }
#hs-dr-cooler { left: 19%; top: 54%; width: 23%; height: 20%; } /* ?v=241: re-anchored on Tomika's new backdrop — the mini-fridge under the counter's left section (was the old shell art's fridge spot) */
#hs-dr-coffee { left: 38%; top: 37%; width: 27%; height: 21%; }
#hs-dr-sink { left: 0; top: 0; width: 0; height: 0; } /* ?v=241: PARKED — Tomika's new drinks backdrop has no sink (bare counter); the glass-fill beat retires with the fixture, handler kept. The bathroom towel gag precedent */
#hs-dr-lantern { left: 23%; top: 23%; width: 12%; height: 15%; } /* ?v=118: the painted hanging lantern · ?v=241: nudged onto the new backdrop's hanging lantern (it hangs a little lower in her art) */
#hs-dr-window { left: 35%; top: 22%; width: 37%; height: 28%; } /* ?v=118: the painted round window; sits BEFORE the cooler/coffee zones in the DOM, so where it overlaps the coffee zone (counter height) the machine keeps its taps and the window takes the rest · ?v=241: re-anchored on the new backdrop's round window */
#dr-lantern-glow { display: none; position: absolute; left: 15%; top: 12%; width: 28%; height: 30%; z-index: 46; pointer-events: none; opacity: 0; transition: opacity .8s; border-radius: 50%; background: radial-gradient(circle, rgba(255,224,160,.95) 0%, rgba(255,186,92,.45) 32%, rgba(255,170,70,0) 68%); } /* ?v=118: the lantern's warm glow — the one bright thing when the room goes dark (above #dark-overlay z 45, never intercepts a tap) */
#room.lights-off #dr-lantern-glow { opacity: 1; }
#dr-brewer { left: 40%; bottom: 43.5%; width: 21%; z-index: 4; } /* ?v=139 (Tomika 2026-11-02 11:49): nudged LEFT on the counter + 10% bigger (19%->21%, 43%->40%); bottom unchanged so the base stays on the countertop lip line. The coffee hotspot (38-65%) still covers the machine (40-61%). ?v=103: Tomika's stump espresso machine (634x946, padding trimmed) replaces the smoothie brewer in the same grounded spot — visible base on the countertop lip line (~56.3%); its own painted wooden cup replaces the separate mug sprite, which is retired */
/* ?v=155 brew-sequence stage motion (the stage FRAMES swap the prop's src when Chat's art lands; these classes carry the stage until then — and the grind shake stays as an accent with them): grind = the grinder's working shudder; drip and steam need no prop motion (the brew sound and the steam curl prop carry them). */
#dr-brewer.cf-grind { animation: cfGrind .12s linear infinite; }
@keyframes cfGrind { 0% { transform: translateX(0); } 25% { transform: translateX(.7px); } 75% { transform: translateX(-.7px); } 100% { transform: translateX(0); } }
#dr-steam { left: 45%; bottom: 48.6%; width: 10%; z-index: 5; animation: drSteam 1.1s ease-in-out infinite; } /* ?v=139: follows the machine left (cup centre 52.5%->50.5% of room width, so steam 47%->45% keeps it over the cup). ?v=103: re-aimed over the stump machine's own cup (cup centre ~52.5% of room width, rim ~48.9% from bottom) */
@keyframes drSteam { 0%, 100% { opacity: .3; transform: translateY(0); } 50% { opacity: .95; transform: translateY(-5px); } }
#dr-glass { left: 79.5%; bottom: 44.5%; width: 7%; z-index: 5; }
/* ?v=103: Tomika's stump water cooler (451x1193, padding trimmed) replaces
   the drawn SVG placeholder — same right-floor spot (zone 4), sized by
   height (~33% of room height) so it reads as the floor appliance it is */
#water-cooler { display: none; position: absolute; left: -2%; bottom: 1.2%; width: 30%; z-index: 4; cursor: pointer; /* ?v=179 (Tomika 12:37): z 4 — the cooler prop now out-ranks the hotspot layer (z 3) on its OWN body: the kid's fridge zone (#hs-dr-cooler) overlapped the cooler's top, so pressing the cooler opened the kid's drink menu and the dog never drank ('Sky just sits there'). The fridge keeps the rest of its front; characters (z 8-10) still walk in front of the cooler */ } /* ?v=149 (Tomika 20:20): DOWN + LEFT a little more — still read too far right/high on her phone; spans -2% to 28% now, bowl spot moves 33% -> 30% with it. ?v=138 (Tomika 2026-11-02 11:49): bigger, left, down — it crowded the drinks fridge; bottom space freed by the nav removal. Spans 0-30% now, so the dog's bowl spot (33%, set in the cooler tap handler) still sits beside it. ?v=125 (Tomika 10:25): SIZE RESTORE — the +5% is on the PRE-MOVE scale (?v=103: width 18% with the tall stump art, subject H/W 2.667 -> rendered subject height 48.0 room-%). The new right-facing art is squatter (H/W 1.767), so at 18-19% width it stood a third shorter — "barely there". Width 28.5% restores the pre-move height x1.05 (= 50.4 room-%). Drinks room only — this element exists nowhere else. ?v=124 (Tomika 10:14): right-facing sprite swap. ?v=107 (Tomika 19:24 + 19:34): RELOCATED to the LEFT bottom corner, beside the built-in fridge — on the right it blocked the entry walkway — and rendered with the mirrored sprite (water-cooler-stump-flip.png, her flip mechanism) so its tap/cup face INTO the room. Layering: z 2 puts it BELOW the character layer (bird 8 / dog 9 / person 10) — nobody ever walks BEHIND it — and below the hotspot layer (z 3), so where its body overlaps the built-in fridge's menu zone (#hs-dr-cooler, x 10-34%) the FRIDGE zone stays on top and fully tappable. Its taps stay alive two ways: direct taps land on the element itself, and taps that land on a character standing over it forward down to it because it carries .hotspot (forwardTapInner's scenery scan) — the ?v=101 z-12 blanket war is over: the cooler no longer needs to out-rank the dog, the tap forwards through her (belly taps still hers, per convention). */
#water-cooler img { display: block; width: 100%; height: auto; }
#water-cooler.glug img { animation: wcGlug .45s ease-in-out 3; }
@keyframes wcGlug { 0%, 100% { transform: translateY(0); } 30% { transform: translateY(-2.5%); } 60% { transform: translateY(1%); } }

/* v80: Gigi's corner button, five dog levels, thought bubbles */
#dog-switch { position: relative; display: flex; } /* ?v=128: the dog picker sits in the header row, paired with the character picker */
#gigi-btn { position: static; z-index: 60; display: flex; align-items: center; gap: 5px; background: #fff5f8; border: 2px solid var(--soft, #f3d9e2); border-radius: 999px; padding: 3px 11px 3px 4px; font-family: inherit; font-size: 13.5px; font-weight: 800; color: var(--ink); cursor: pointer; box-shadow: 0 2px 7px rgba(0,0,0,.16); }
#gigi-btn img { width: 23px; height: 23px; border-radius: 50%; object-fit: cover; object-position: top; background: #ffeef4; }
/* ?v=128: the 94px right pad made room for the old FIXED dog button — the pickers live in the row now, the pad is gone */
.ds-ico { display: inline-flex; align-items: center; justify-content: center; width: 15px; }
.ds-ico-img { width: 14px; height: 14px; object-fit: contain; }
#ds-energy { background: #feca57; }
#ds-play { background: #1dd1a1; }
.dl-card { width: min(320px, 88vw); }
.dl-line { text-align: center; font-weight: 800; font-size: 15px; margin: 0 0 10px; color: var(--ink); min-height: 20px; }
.cl-fill { height: 100%; border-radius: 6px; transition: width .3s ease; }
/* v82 (Tomika): long-presses belong to the game, never the browser — no
   save-image callout, no drag-ghost on any character sprite */
#app img { -webkit-touch-callout: none; -webkit-user-drag: none; user-select: none; -webkit-user-select: none; }
.dl-rows .ds-row { font-size: 16px; margin: 7px 0; }
.dl-rows .ds-bar { height: 12px; }
#dl-food { background: #ff9f43; }
#dl-water { background: #48dbfb; }
#dl-energy { background: #feca57; }
#dl-play { background: #1dd1a1; }
#dl-potty { background: #a55eea; }
.thought-bubble { position: absolute; bottom: 103%; left: 50%; transform: translateX(-50%); background: #fff; border: 2px dashed #aebfd8; border-radius: 50%; padding: 12px; font-size: 12px; font-weight: 700; color: var(--ink); white-space: normal; text-align: center; max-width: 68vw; z-index: 30; box-shadow: 0 3px 8px rgba(0,0,0,.12); pointer-events: none; display: flex; align-items: center; justify-content: center; box-sizing: border-box; line-height: 1.22; } /* ?v=113: a round thought-circle (the dashed trail dots below stay) — compact, diameter set per line by bubCircle(); the wide pill that sat over Gigi's face is gone */
.thought-bubble::before { content: ''; position: absolute; left: 32%; bottom: -8px; width: 8px; height: 8px; background: #fff; border: 2px dashed #aebfd8; border-radius: 50%; }
.thought-bubble::after { content: ''; position: absolute; left: 40%; bottom: -15px; width: 5px; height: 5px; background: #fff; border: 2px dashed #aebfd8; border-radius: 50%; }

/* v93: character + dog DROPDOWNS (Tomika 16:30) — each button wears the
   current pick; the panel lists thumb-sized rows, viewport-safe */
#char-switch { position: relative; }
#char-btn { display: flex; align-items: center; gap: 6px; border: 2px solid transparent; background: #fdf3f7; border-radius: 999px; padding: 3px 10px 3px 4px; font-family: inherit; font-size: 13.5px; font-weight: 800; color: var(--ink); cursor: pointer; }
#char-btn img { width: 23px; height: 23px; border-radius: 50%; object-fit: cover; object-position: top; background: #ffeef4; }
.dd-caret { font-size: 10px; opacity: .65; }
.pick-menu { position: absolute; min-width: 178px; background: #fff; border: 2px solid #f3d9e2; border-radius: 18px; box-shadow: 0 10px 26px rgba(90, 40, 60, .22); padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.pick-row { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 52px; padding: 7px 12px; border: none; border-radius: 13px; background: none; font-family: inherit; font-size: 16px; font-weight: 800; color: var(--ink); cursor: pointer; text-align: left; }
.pick-row:active { background: #ffe0ed; }
.pick-row img { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; object-position: top; background: #ffeef4; }
#char-menu { top: calc(100% + 6px); right: 0; z-index: 40; }
#dog-menu { top: calc(100% + 6px); right: 0; z-index: 65; } /* ?v=128: drops from the in-row dog picker, like the char menu */

/* ?v=116 (Tomika): the park treat bowl — a prop BELOW the character layer (z 2, like the cooler/bag); its .hotspot class keeps its tap alive through the tap-forwarding rules */
#treat-bowl { right: 5%; bottom: 4%; width: 19%; z-index: 7; border-radius: 0; } /* ?v=116 fix: was left:5%/z-index 2 — the exact corner AND under the hydrant spot (z 3), so the hydrant swallowed every tap; bottom-right floor is open grass. ?v=117 fix: z 4 -> 7 — the tennis ball's REST spot (x84/b6, z 6) sits on the bowl's centre and swallowed every bowl tap (probe-verified: elementsFromPoint = #fetch-ball over #treat-bowl); 7 keeps the bowl below the character layer (z 9) with its tap alive, per the prop doctrine */
#treat-bowl img { width: 100%; display: block; pointer-events: none; }
/* ?v=116 (Tomika 20:40): oval speech bubbles carry a TAIL — a small rotated square tucked under the oval, on the side toward the speaker (data-side set by bubFlip in game.js: l = tail at the left, r = at the right, c = centre) */
#gnome-bubble::after, #speech-bubble::after, #dog-bubble::after, #bird-say::after, .friend-bubble::after, #brodie-bubble::after { content: ''; position: absolute; bottom: -6px; left: 46%; width: 13px; height: 13px; background: #fff; transform: rotate(45deg); border-right: 2px solid var(--soft); border-bottom: 2px solid var(--soft); border-bottom-right-radius: 4px; }
#bird-say::after { border-right-color: #bde3ff; border-bottom-color: #bde3ff; }
#gnome-bubble[data-side="l"]::after, #speech-bubble[data-side="l"]::after, #dog-bubble[data-side="l"]::after, #bird-say[data-side="l"]::after, .friend-bubble[data-side="l"]::after, #brodie-bubble[data-side="l"]::after { left: 20%; }
#gnome-bubble[data-side="r"]::after, #speech-bubble[data-side="r"]::after, #dog-bubble[data-side="r"]::after, #bird-say[data-side="r"]::after, .friend-bubble[data-side="r"]::after, #brodie-bubble[data-side="r"]::after { left: auto; right: 20%; }
.thought-bubble[data-side="r"]::before { left: auto; right: 32%; }
.thought-bubble[data-side="r"]::after { left: auto; right: 40%; }

/* ?v=128: the puppy room's nightstand + night light (placeholder prop for a Chat art pass) — the dog's individual bedtime tap */

/* ?v=130: favorites — the levels cards name them, and a favorite moment bursts hearts over the character */
.levels-fav { margin: 2px 0 8px; font-size: 13.5px; font-weight: 800; color: #e75480; text-align: center; }
.heart-fx { position: absolute; z-index: 31; pointer-events: none; font-size: 19px; opacity: 0; animation: heartRise 1.5s ease-out forwards; }
@keyframes heartRise { 0% { opacity: 0; transform: translateY(6px) scale(.6); } 18% { opacity: 1; } 100% { opacity: 0; transform: translateY(-66px) scale(1.15); } }

/* ?v=169 (main): in Chat's tall living backdrop the TV is a wall-mounted flat screen — the screen element carries its own body/bezel */
#tv-screen { display: none !important; } /* ?v=170 (main, Tomika 11:05): the emoji-show screen is REMOVED — Chat is building a proper animated TV; the couch/watch flow stays as its scaffold */

/* ?v=170 (main, Tomika 11:05): the four tall-art rooms read too small — zoom their backdrops 20% (centre crop). Anchors re-based to this framing below/in game.js. */
#room[data-room="potty"] #room-bg { transform: scale(1.0); } /* ?v=308 (Tomika 11:51): zoomed OUT for the new skylight art — at 120% the side doorways were cropped off her phone; 100% is the full cover framing, the widest this art can show */ /* potty stays at +20% (Tomika: "the potty room was fine") */
#room[data-room="kitchen"] #room-bg { transform: scale(1.1); } /* ?v=258 (Tomika 23:50): kitchen zooms OUT 10 more points (120->110) — she wants more of the doorways; this supersedes the ?v=174 'locked perfect' at her direct word, screenshots in hand */ /* ?v=173 (Tomika 11:19): kitchen pans OUT 20 points (140->120) */
#room[data-room="living"] #room-bg { transform: scale(1.2); } /* ?v=258 (Tomika 23:50): living zooms OUT 15 more points (135->120) — more of the doorways, her word with the room on screen */ /* ?v=173 (Tomika 11:18): living zooms OUT 10 points (145->135) so the doorways show */
#room[data-room="bedroom"] #room-bg { transform: scale(1.1); }
#room[data-room="bathroom"] #room-bg { transform: scale(1.1); } /* ?v=233 (Tomika 21:57): the new bathroom backdrop zooms in a tad — 10 points, like the bedroom/dogroom */
#room[data-room="bathroom"] #bath-foam { transform: scale(1.1); } /* ?v=233: the foam layer is a pixel-locked clone of the backdrop — it takes the same zoom or the bubble band drifts off the tub */
#room[data-room="dogroom"] #room-bg { transform: scale(1.1); } /* ?v=177 (Tomika 12:09): dog room zooms in 10 more points (100->110) like the others */ /* ?v=174 (Tomika 11:24): bedroom out 10 more points (120->110) — FINAL; kitchen stays 120 (locked perfect), living 135, potty 120 */ /* ?v=173 (Tomika 11:18): bedroom zooms OUT 10 more points (130->120) for the doorways */ /* ?v=172 (Tomika 11:15): bedroom zoomed back OUT to 130% so the doorways peek in; the bed fit comes from the smaller sleepers, not the zoom */

/* ?v=174 (Tomika 11:25): long-press-draggable room items */
.movable { touch-action: none; }
.item-moving { filter: drop-shadow(0 0 12px rgba(255, 176, 66, .95)); transform: scale(1.06); z-index: 30 !important; }

/* ?v=203 (Tomika 18:39): bought decor props + prop residence overrides — a
   prop shows only in the room it lives in; the !important pair outranks the
   per-room fixture rules so a carried prop appears wherever she drops it. */
.prop-decor { position: absolute; z-index: 4; cursor: pointer; user-select: none; -webkit-user-select: none; touch-action: none; filter: drop-shadow(0 3px 3px rgba(0,0,0,.25)); }
#prop-stumptable { filter: none; } /* ?v=414 (Tomika 04:44): she spotted the shadow pooled under her stump side table and asked for it gone — the art itself carries no baked shadow (checked: 29 dark px in the bottom band); it was the decor drop-shadow. Off for this table only. */
#prop-tv { z-index: 5; }
.prop-here { display: block !important; }
.prop-away { display: none !important; }

/* ?v=204 (Tomika 18:53): the removal ✕ — appears while a bought prop is
   lifted; drag the prop onto it and let go to remove the prop and refund
   its full shop price. Pure display: the drag's own pointer math reads its
   rect, so it never intercepts touches. */
#prop-trash { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 40; display: flex; align-items: center; gap: 7px; background: rgba(255,255,255,.96); color: #c2436b; border: 3px solid #e56a8d; border-radius: 999px; padding: 8px 16px; font-weight: 900; font-size: 17px; box-shadow: 0 6px 18px rgba(0,0,0,.3); pointer-events: none; }
#prop-trash .x { font-size: 22px; line-height: 1; }
#prop-trash.armed { background: #e5484d; color: #fff; border-color: #b02a2e; transform: translateX(-50%) scale(1.15); }

/* ?v=329: TELESCOPE SKY — the first DISCOVERY SCENE (Tomika 12:52): a full-screen learn-view opened from something touchable in a room. This pattern (overlay + tappable subjects + fact card + Tell-me-more) is the template for a discovery scene in EVERY room. */
#sky-view { position: fixed; inset: 0; z-index: 70; background: linear-gradient(180deg, #060a24 0%, #0d1440 45%, #1b2350 78%, #2a2a55 100%); background-image: url('../../assets/chat/planets/sky-backdrop.png?v=355'); background-size: cover; background-position: center; overflow: hidden; }
#sky-stars { position: absolute; inset: 0; }
.sky-star { position: absolute; background: #fff; border-radius: 50%; animation: skyTwinkle 2.6s ease-in-out infinite; }
@keyframes skyTwinkle { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
#sky-shoot { position: absolute; width: 90px; height: 2px; background: linear-gradient(90deg, #fff, transparent); border-radius: 2px; opacity: 0; transform: rotate(-24deg); }
#sky-shoot.go { animation: skyShoot 1.1s ease-out; }
@keyframes skyShoot { 0% { opacity: 0; margin-left: 0; } 12% { opacity: 1; } 100% { opacity: 0; margin-left: -46%; margin-top: 20%; } }
.sky-body { position: absolute; border-radius: 50%; border: none; background: transparent; cursor: pointer; padding: 0; transition: box-shadow .25s, scale .25s; animation: skyBob 5.2s ease-in-out infinite; overflow: visible; }
@keyframes skyBob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
#sky-venus { animation-delay: .6s; } #sky-earth { animation-delay: 1.2s; } #sky-mars { animation-delay: 1.8s; } #sky-jupiter { animation-delay: 2.4s; } #sky-saturn { animation-delay: 3s; } #sky-uranus { animation-delay: 3.6s; } #sky-neptune { animation-delay: 4.2s; } #sky-mercury { animation-delay: 4.8s; }
.sky-body.found { box-shadow: 0 0 0 3px rgba(255,235,160,.9), 0 0 26px 8px rgba(255,240,180,.45); }
.sky-body:active { scale: 1.12; }
.sky-body .sky-name { position: absolute; left: 50%; top: calc(100% + 5px); transform: translateX(-50%); color: #dfe8ff; font-size: 11px; font-weight: 800; text-shadow: 0 1px 4px #000; white-space: nowrap; }
#sky-moon { background: transparent; box-shadow: none; }
#sky-mercury { background: transparent; box-shadow: none; }
#sky-venus { background: transparent; box-shadow: none; }
#sky-earth { background: transparent; box-shadow: none; }
#sky-mars { background: transparent; box-shadow: none; }
#sky-jupiter { background: transparent; box-shadow: none; }
#sky-saturn { background: transparent; box-shadow: none; }
#sky-uranus { background: transparent; box-shadow: none; }
#sky-neptune { background: transparent; box-shadow: none; }
#sky-title { position: absolute; top: 14px; left: 0; right: 0; text-align: center; color: #eef3ff; font-size: 21px; font-weight: 900; text-shadow: 0 2px 8px #000; }
#sky-close { position: absolute; top: 12px; right: 12px; z-index: 5; }
#sky-card { position: absolute; z-index: 4; left: 4%; right: 4%; bottom: 14px; background: rgba(255,255,255,.97); border-radius: 20px; padding: 14px 42px 12px 16px; box-shadow: 0 10px 30px rgba(0,0,0,.5); }
.card-x { position: absolute; top: 8px; right: 8px; width: 30px; height: 30px; border-radius: 50%; border: none; background: #eef0f7; color: #5a6482; font-size: 15px; font-weight: 900; cursor: pointer; line-height: 1; } .card-x:active { background: #dde2f0; transform: scale(.92); }
#sky-card-name { font-size: 20px; font-weight: 900; color: #28305e; }
#sky-card-tag { font-size: 13.5px; font-weight: 700; color: #7a6cff; margin: 1px 0 5px; }
#sky-card-fact { font-size: 15.5px; line-height: 1.42; color: #333a55; min-height: 44px; }
#sky-card-row { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
#sky-more { background: #6c5ce7; color: #fff; border: none; border-radius: 999px; padding: 9px 18px; font-size: 15px; font-weight: 900; cursor: pointer; }
#sky-count { margin-left: auto; font-size: 13px; font-weight: 800; color: #8a90b8; }

/* ?v=330: DISCOVERY SCENES — the generic learn engine (Tomika 12:56): every scene gets at least TWO educational taps, on things with NO existing action (the piano keeps playing the piano). Subjects are medallions in a themed full-screen view; the fact card + Tell-me-more pattern matches the telescope sky. */
#learn-view { position: fixed; inset: 0; z-index: 70; overflow: hidden; }
.learn-th-birds { background: linear-gradient(180deg, #ffe9b8 0%, #ffd9a0 34%, #a8d8a8 100%); background-image: url('../assets/learn-birds-bg.jpg'); background-size: cover; background-position: center; } /* ?v=414: painted Bird Guide backdrop (Muse-generated at Tomika's request 11:18) — morning sky, birdhouse + feeder in the branches, meadow below; the gradient stays as the fallback */
.learn-th-pond { background: linear-gradient(180deg, #bfeef5 0%, #6fc7d9 45%, #2e7d9b 100%); }
.learn-th-forest { background: linear-gradient(180deg, #d9f2c2 0%, #8fce7e 45%, #3e7d44 100%); }
.learn-th-kitchen { background: linear-gradient(180deg, #fff1d6 0%, #ffd9a8 50%, #e8a56b 100%); }
#learn-title { position: absolute; top: 14px; left: 0; right: 0; text-align: center; color: #fff; font-size: 21px; font-weight: 900; text-shadow: 0 2px 8px rgba(0,0,0,.45); }
#learn-close { position: absolute; top: 12px; right: 12px; z-index: 5; }
.learn-subj { position: absolute; width: 64px; height: 64px; border-radius: 50%; background: rgba(255,255,255,.92); border: 3px solid rgba(255,255,255,.65); font-size: 33px; cursor: pointer; box-shadow: 0 6px 16px rgba(0,0,0,.28); transition: scale .2s, box-shadow .2s; }
.learn-subj:active { scale: 1.14; }
.learn-subj.found { box-shadow: 0 0 0 3px #ffe98a, 0 0 22px 7px rgba(255,233,138,.55); }
.learn-subj.has-bird { width: 96px; height: 96px; background: transparent; border-color: transparent; box-shadow: none; }
.learn-subj.has-bird.found { box-shadow: 0 0 22px 8px rgba(255,233,138,.6); }
.learn-bird { width: 100%; height: 100%; object-fit: contain; display: block; pointer-events: none; }
.learn-subj .ls-name { position: absolute; left: 50%; top: calc(100% + 4px); transform: translateX(-50%); font-size: 11.5px; font-weight: 800; color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,.6); white-space: nowrap; }
#learn-card { position: absolute; left: 4%; right: 4%; bottom: 14px; background: rgba(255,255,255,.97); border-radius: 20px; padding: 14px 42px 12px 16px; box-shadow: 0 10px 30px rgba(0,0,0,.5); }
#learn-card-name { font-size: 20px; font-weight: 900; color: #28305e; }
#learn-card-tag { font-size: 13.5px; font-weight: 700; color: #7a6cff; margin: 1px 0 5px; }
#learn-card-fact { font-size: 15.5px; line-height: 1.42; color: #333a55; min-height: 44px; }
#learn-card-row { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
#learn-more { background: #6c5ce7; color: #fff; border: none; border-radius: 999px; padding: 9px 18px; font-size: 15px; font-weight: 900; cursor: pointer; }
#learn-count { margin-left: auto; font-size: 13px; font-weight: 800; color: #8a90b8; }
#hs-l-birds { left: 2%; top: 5%; width: 30%; height: 20%; }
#hs-l-pond { left: 24%; top: 45%; width: 52%; height: 27%; }
#hs-l-forest { left: 3%; top: 11%; width: 26%; height: 27%; }
#hs-l-kitchen { left: 23%; top: 37%; width: 32%; height: 19%; }

/* ?v=332: FROG HOP — the Frogger-type mini-game (Tomika 13:11) starring the clearing frog with Chat's crouch/hop/landing frames. Woodland rules: dodge the rolling logs on the path, ride the lily pads across the stream, no-fail — a bonk or a splash just sends him back to the home bank. */
#fg-stage { position: relative; width: 100%; aspect-ratio: 7 / 9; border-radius: 16px; overflow: hidden; background: linear-gradient(180deg, #7ec8e3 0 22%, #a8d88e 22% 33%, #c9a06b 33% 66%, #a8d88e 66% 100%); box-shadow: inset 0 0 0 3px rgba(255,255,255,.35); }
#fg-goals { position: absolute; left: 0; right: 0; top: 1%; display: flex; justify-content: space-around; font-size: 26px; }
#fg-goals .fg-padslot { opacity: .55; filter: grayscale(.3); transition: all .3s; }
#fg-goals .fg-padslot.got { opacity: 1; filter: none; transform: scale(1.25); }
.fg-obj { position: absolute; height: 9.5%; display: flex; align-items: center; justify-content: center; font-size: 24px; letter-spacing: 2px; pointer-events: none; }
.fg-log { background: rgba(146, 96, 52, .35); border-radius: 999px; }
.fg-drift { background: rgba(255,255,255,.18); border-radius: 999px; }
#fg-frog { position: absolute; width: 12.5%; z-index: 6; pointer-events: none; transition: left .16s ease, top .16s ease; }
#fg-pad { display: flex; justify-content: center; gap: 10px; margin: 12px 0 4px; }
.fg-btn { width: 62px; height: 52px; font-size: 24px; border-radius: 14px; border: 2px solid #b8c9a3; background: #f4f9ec; cursor: pointer; }
.fg-btn:active { background: #dff0c8; transform: scale(.94); }

/* ?v=338: CANOPY CRITTERS — Chat's flying owl and bumblebee (Canopy Creature Actions). The owl flies in, perches on the big branch, then flies off; the bee drifts around the flower corner. Wildlife layer: behind the characters, in front of the backdrop. */
#canopy-owl { position: absolute; width: 12.5%; left: 33%; bottom: 66%; z-index: 7; cursor: pointer; transition: left 3.4s linear, bottom 3.4s ease-in-out, opacity .5s; display: none; }
#room[data-room="backyard"] #canopy-owl { display: block; }
#canopy-owl.away { opacity: 0; pointer-events: none; }
#canopy-owl img { width: 100%; display: block; }
#canopy-bee { position: absolute; width: 6%; left: 12%; bottom: 24%; z-index: 7; cursor: pointer; transition: left 2.6s ease-in-out, bottom 2.6s ease-in-out; display: none; }
#room[data-room="backyard"] #canopy-bee { display: block; }
#canopy-bee img { width: 100%; display: block; }
/* ?v=414: POND WILDLIFE — up to three bees drifting flower to flower (smaller toward the back flowers), and a frog that sometimes hops through. */
#hs-sky { position: absolute; left: 0; top: 0; width: 100%; height: 30%; z-index: 3; display: none; cursor: pointer; }
#room[data-room="backyard"] #hs-sky, #room[data-room="ground"] #hs-sky, #room[data-room="pond"] #hs-sky, #room[data-room="dogpark"] #hs-sky, #room[data-room="treedoor"] #hs-sky { display: block; }
/* ?v=414: Lily Pad Leap sprite stage (Chat's pack) */
#lily3d { background: #2e7ea6; }
#lily3d .lp-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
#lily3d .lp-el { position: absolute; transform: translate(-50%, -50%); pointer-events: none; }
#lily3d .lp-ripple { z-index: 1; opacity: .5; }
#lily3d .lp-plat { z-index: 2; }
#lily3d .lp-turtle { z-index: 2; }
#lily3d .lp-tbody { position: absolute; left: 50%; top: 50%; width: 92%; height: 62%; transform: translate(-50%, -50%); background: #3f8a5f; border-radius: 50%; box-shadow: inset 0 -6px 0 rgba(0,0,0,.12); }
#lily3d .lp-thead { position: absolute; top: 50%; width: 30%; height: 34%; transform: translateY(-50%); background: #4da56e; border-radius: 50%; }
#lily3d .lp-grass { position: absolute; transform: translate(-50%, -50%); z-index: 1; background: linear-gradient(#93d473, #69ad4e); border-radius: 50%; box-shadow: 0 3px 0 rgba(46, 110, 60, .35); }
#lily3d .lp-decor { z-index: 3; }
#lily3d .lp-bank { z-index: 4; transform: translate(-50%, -100%); }
#lily3d .lp-frog { z-index: 5; transform: translate(-50%, -100%); }
#lily3d .lp-splash { z-index: 6; }
#lily3d .lp-bug { z-index: 3; transition: none; }
@keyframes lpBob { from { translate: 0 0; } to { translate: 0 -7px; } }
#lily3d .lp-bob { animation: lpBob 2.6s ease-in-out infinite alternate; }
#lily3d-btns { display: flex; justify-content: space-between; align-items: flex-end; padding: 0 10px; }
#lily3d-btns .lily-dpad { display: grid; grid-template-columns: repeat(3, 54px); grid-template-rows: 54px 54px; gap: 4px; }
#lily3d-btns .lily-dpad .lily-b-up { grid-column: 2; grid-row: 1; }
#lily3d-btns .lily-dpad button { grid-row: 2; }
#lily3d-btns button { background: none !important; border: none !important; box-shadow: none !important; padding: 0 !important; margin: 0 !important; cursor: pointer; }
#lily3d-btns button img { width: 54px; height: 54px; display: block; filter: drop-shadow(0 3px 6px rgba(0,0,0,.35)); }
#lily3d-btns .lily-b-jump img { width: 82px; height: 82px; }
#lily3d-btns button:active img { transform: scale(.93); }
#sky-bird { position: absolute; z-index: 6; display: none; cursor: pointer; }
#sky-bird.on { display: block; }
#sky-bird.perched { animation: none; }
.pond-bee { position: absolute; z-index: 7; cursor: pointer; display: none; }
#room[data-room="pond"] .pond-bee.on { display: block; }
.pond-bee img { width: 100%; display: block; animation: beeBob399 1.1s ease-in-out infinite; }
@keyframes beeBob399 { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7%); } }
#pond-frog { position: absolute; z-index: 6; width: 9%; display: none; cursor: pointer; }
#room[data-room="pond"] #pond-frog.on { display: block; }
.pond-fly { position: absolute; z-index: 7; cursor: pointer; display: none; width: 6%; }
#room[data-room="pond"] .pond-fly.on { display: block; }
.pond-fly svg { width: 100%; display: block; animation: beeBob399 1.35s ease-in-out infinite; }
.pond-fly .wingL { transform-box: fill-box; transform-origin: 100% 50%; animation: flap399 .17s ease-in-out infinite alternate; }
.pond-fly .wingR { transform-box: fill-box; transform-origin: 0% 50%; animation: flap399 .17s ease-in-out infinite alternate; }
@keyframes flap399 { from { transform: scaleX(1); } to { transform: scaleX(.34); } }
.pond-fly.excited .wingL, .pond-fly.excited .wingR { animation-duration: .07s; }
#pond-dragon { position: absolute; z-index: 7; cursor: pointer; display: none; width: 8.5%; }
#room[data-room="pond"] #pond-dragon.on { display: block; }
#pond-dragon svg { width: 100%; display: block; }
#pond-dragon .dwings { transform-box: fill-box; transform-origin: 50% 50%; animation: dw399 .13s ease-in-out infinite alternate; }
@keyframes dw399 { from { transform: scaleY(1); } to { transform: scaleY(.72); } }
#pond-lady { position: absolute; z-index: 6; cursor: pointer; display: none; width: 4.4%; }
#room[data-room="pond"] #pond-lady.on { display: block; }
#pond-lady svg { width: 100%; display: block; }
#pond-lady.crawling svg { animation: wig399 .38s ease-in-out infinite alternate; }
@keyframes wig399 { from { transform: rotate(-2.4deg); } to { transform: rotate(2.4deg); } }
#fact-pop { position: absolute; z-index: 30; left: 4%; right: 4%; bottom: 14px; background: rgba(255,255,255,.97); border-radius: 20px; padding: 14px 42px 12px 16px; box-shadow: 0 10px 30px rgba(0,0,0,.35); }
#fact-pop-name { font-size: 19px; font-weight: 900; color: #28305e; }
#fact-pop-text { font-size: 15px; font-weight: 600; color: #444c6e; margin-top: 2px; line-height: 1.35; }

/* ?v=343: PAW SALON mini-game — Chat's Nail Salon kit + his paw art. Soak -> dry -> file -> paint. Nail spots measured on the paw art; polish colors live as overlays on the nails so a finished paw keeps the kid's exact colors. */
#pw-stage { position: relative; width: min(80%, 330px); margin: 8px auto 2px; }
#pw-pawwrap { position: relative; width: 100%; }
#pw-paw { width: 100%; display: block; }
#pw-pawwrap.dipping #pw-paw { animation: pwDip .8s ease; }
@keyframes pwDip { 0%, 100% { transform: translateY(0); } 40% { transform: translateY(4.5%); } }
#pw-pawwrap.warm #pw-paw { filter: drop-shadow(0 0 14px rgba(255, 170, 60, .9)); }
.pw-nail { position: absolute; width: 10.5%; height: 11.5%; border-radius: 50% 50% 50% 50% / 62% 62% 38% 38%; transform: translate(-50%, -50%) rotate(var(--tilt, 0deg)); opacity: 0; pointer-events: none; z-index: 3; }
.pw-nail.filed { opacity: .38; background: #ffffff; }
.pw-nail.painted { opacity: .9; }
.pw-nailtap { position: absolute; width: 17%; height: 17%; border-radius: 50%; transform: translate(-50%, -50%); background: transparent; border: none; z-index: 4; cursor: pointer; padding: 0; }
#pw-bowl { position: absolute; left: 50%; bottom: -9%; transform: translateX(-50%); width: 76%; z-index: 5; cursor: pointer; }
#pw-towel { position: absolute; left: -7%; bottom: -4%; width: 38%; z-index: 5; }
#pw-dryer { position: absolute; right: -9%; top: -4%; width: 36%; z-index: 5; cursor: pointer; }
#pw-file { position: absolute; width: 30%; z-index: 6; left: 35%; top: 2%; transition: left .25s ease, top .25s ease; }
#pw-file.sawing { animation: pwSaw .35s ease; }
@keyframes pwSaw { 0%, 100% { transform: translateX(0) rotate(-8deg); } 50% { transform: translateX(9%) rotate(-2deg); } }
#pw-brush { position: absolute; right: -6%; bottom: 12%; width: 30%; z-index: 5; }
#pw-colors { display: flex; justify-content: center; gap: 10px; margin: 12px 0 2px; }
#pw-colors.hidden { display: none; }
.pw-color { width: 52px; height: 64px; border: 3px solid transparent; border-radius: 12px; background: rgba(255,255,255,.65); cursor: pointer; padding: 3px; }
.pw-color.sel { border-color: #ff9d2e; background: #fff; }
.pw-color img { width: 100%; height: 100%; object-fit: contain; display: block; }
#pw-actions { text-align: center; margin-top: 8px; }

/* ?v=344: PLAYGROUND tap animations (Chat's Playground Interactive Props). Objects only — rocking a swing or a seesaw is what these toys DO; the no-spin rule is about characters and stays untouched. */
#prop-pgswing.playing { animation: pgRock 1.7s ease-in-out; transform-origin: 50% 0%; }
@keyframes pgRock { 0%, 100% { transform: rotate(0deg); } 25% { transform: rotate(3.5deg); } 65% { transform: rotate(-3deg); } }
#prop-pgseesaw.playing { animation: pgTeeter 1.2s ease-in-out; transform-origin: 50% 92%; }
@keyframes pgTeeter { 0%, 100% { transform: rotate(0deg); } 30% { transform: rotate(4.5deg); } 70% { transform: rotate(-4.5deg); } }
#prop-pgmerry.playing { animation: pgMerry 1.4s ease-in-out; transform-origin: 50% 60%; }
@keyframes pgMerry { 0%, 100% { transform: rotate(0deg) scale(1); } 30% { transform: rotate(5deg) scale(1.03); } 70% { transform: rotate(-4deg) scale(1.02); } }
#prop-pghop.playing { animation: pgHopSeq .9s ease; }
@keyframes pgHopSeq { 0%, 100% { transform: translateY(0); } 20% { transform: translateY(-4%); } 40% { transform: translateY(0); } 60% { transform: translateY(-6%); } 80% { transform: translateY(0); } }

/* ?v=345: the Berry Blender whirs when tapped. */
#prop-cfblender.playing { animation: cfBlend .7s linear; }
@keyframes cfBlend { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-1.5%); } 40% { transform: translateX(1.5%); } 60% { transform: translateX(-1%); } 80% { transform: translateX(1%); } }

.sky-body .sky-img { position: absolute; inset: -13%; width: 126%; height: 126%; display: block; pointer-events: none; }
.sky-body .sky-name { position: relative; z-index: 1; text-shadow: 0 1px 6px rgba(0,0,20,.85), 0 0 2px rgba(0,0,20,.9); }
.sky-body.sel { scale: 1.28; box-shadow: 0 0 0 3px rgba(255,244,190,.95), 0 0 34px 10px rgba(255,240,180,.5); z-index: 3; }
.sky-sparkle { position: absolute; pointer-events: none; animation: skyTwinkle 2.8s ease-in-out infinite; }
@keyframes skyTwinkle { 0%,100% { opacity: .25; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.12); } }

#ck-pot2 { position: absolute; left: 53.7%; top: 61.4%; width: 40.3%; z-index: 1; touch-action: none; cursor: grab; }
#ck-pot2-x { position: absolute; z-index: 6; width: 34px; height: 34px; border-radius: 50%; border: 2px solid #fff; background: #e05252; color: #fff; font-size: 17px; font-weight: 900; cursor: pointer; box-shadow: 0 3px 8px rgba(0,0,0,.35); }

#room[data-room="ground"] #hs-g-pond { display: none !important; }
#room[data-room="ground"] #clear-mushrooms { display: none; }
#room[data-room="treedoor"] #clear-mushrooms { display: block; left: 6%; bottom: 5%; }
#hs-t-tree { left: 0%; top: 30%; width: 20%; height: 34%; }
#hs-t-seasons { left: 80%; top: 0%; width: 20%; height: 18%; }
.learn-th-trees { background: linear-gradient(180deg, #e6f4c5 0%, #a5d68f 45%, #4a7d3a 100%); }
.learn-th-seasons { background: linear-gradient(180deg, #ffe3ec 0%, #ffc98f 45%, #b8d4f0 100%); }
.learn-th-shrooms { background: linear-gradient(180deg, #ffe9c9 0%, #cf9a62 45%, #6b4a2f 100%); }
@keyframes gnomeHop { 0%,100% { translate: 0 0; } 35% { translate: 0 -12px; } 65% { translate: 0 -4px; } }
#clear-gnome.gnome-hop { animation: gnomeHop .6s ease; }
#dig-mound { touch-action: none; }

@keyframes lizHop { 0%,100% { scale: 1; } 40% { scale: 1.13; } }
#clear-lizard.liz-hop { animation: lizHop .5s ease; }

/* ?v=414: Gigi's Obstacle Dash - the sprite build (Chat's corrected pack) */
#dash3d { background: linear-gradient(#79c7ee 0%, #a8dcf5 42%, #cdeeb9 58%, #9ccb6b 100%); }
.gd-sky { position: absolute; inset: 0; z-index: 0; }
.gd-cloud { position: absolute; width: 21%; height: 4.5%; background: #fff; border-radius: 999px; opacity: .92; filter: blur(1px); box-shadow: 14px 5px 0 -2px #fff, -13px 6px 0 -3px #fff; }
.gd-far { position: absolute; left: 0; right: 0; bottom: 24.5%; height: 21%; z-index: 1; overflow: hidden; }
.gd-prop { position: absolute; bottom: 0; }
.gd-fence { position: absolute; left: 0; right: 0; bottom: 21%; height: 6.5%; z-index: 2; background-image: url('../assets/chat/dash/tile-fence.png'); background-repeat: repeat-x; background-size: auto 100%; opacity: .95; }
.gd-ground { position: absolute; left: 0; right: 0; bottom: 0; height: 24.5%; z-index: 1; background-image: url('../assets/chat/dash/tile-grass.jpg'); background-repeat: repeat; }
.gd-track { position: absolute; left: 0; right: 0; bottom: 8.2%; height: 10.5%; z-index: 2; background-image: url('../assets/chat/dash/tile-dirt.jpg'); background-repeat: repeat-x; background-size: auto 100%; box-shadow: 0 -2px 0 rgba(90,55,20,.18), 0 3px 0 rgba(90,55,20,.15); }
.gd-gigi { position: absolute; width: 31%; z-index: 5; pointer-events: none; }
.gd-ob { position: absolute; z-index: 4; pointer-events: none; }
.gd-log { width: 30%; }
.gd-gate { width: 56%; }
.gd-puddle { width: 31%; }
.gd-item { position: absolute; width: 10.5%; z-index: 4; pointer-events: none; }
.gd-fx { position: absolute; z-index: 6; pointer-events: none; }
.gd-finish { position: absolute; width: 13%; z-index: 3; text-align: center; pointer-events: none; }
.gd-fin-flag { font-size: 34px; line-height: 1; }
.gd-fin-post { width: 8px; height: 74px; margin: 0 auto; background: repeating-linear-gradient(180deg, #fff 0 9px, #222 9px 18px); border-radius: 3px; }
.gd-jumpbtn { background: none; border: none; padding: 0; cursor: pointer; line-height: 0; }
.gd-jumpbtn img { width: 148px; max-width: 38vw; display: block; }
.gd-duckbtn { font-size: 19px; font-weight: 800; color: #fff; background: linear-gradient(#f7a83c, #e07f13); border: 3px solid #fff; border-radius: 999px; padding: 11px 20px; box-shadow: 0 3px 0 rgba(120,60,0,.45); cursor: pointer; }
