/* Sunny Day — warm ivory, teal, lavender, coral. Mobile-first, large buttons. */
:root {
  --ivory: #FFF8EC;
  --ivory-2: #FFF1D6;
  --teal: #0E7C7B;
  --teal-dark: #0A5E5D;
  --lav: #B8A9E8;
  --lav-dark: #6C5BB5;
  --coral: #FF7A66;
  --coral-dark: #D94F3D;
  --ink: #17334D;
  --muted: #33506B;
  --card: #FFFFFF;
  --line: #EADFC8;
  --radius: 18px;
  /* v15 Liquid Glass tokens — from West's approved preview scene
     (example-liquid-glass.html, 2026-11-11): the Sunny sky, frosted
     translucent surfaces, sunny-orange primary actions. --ink and
     --muted moved to the preview's ink pair (#17334D / #33506B);
     both are DARKER than the values they replace, so text contrast
     improves everywhere they are used. */
  --sky-1: #6FBDF2;
  --sky-2: #A8DCFF;
  --sky-3: #FFE9BE;
  --glass-bg: linear-gradient(135deg, rgba(255,255,255,0.58), rgba(255,255,255,0.22));
  --glass-bg-strong: linear-gradient(135deg, rgba(255,255,255,0.88), rgba(255,255,255,0.62));
  --glass-border: rgba(255,255,255,0.7);
  --glass-blur: blur(18px) saturate(1.7);
  --glass-shadow: inset 0 1px 0 rgba(255,255,255,0.9), inset 0 -1px 0 rgba(255,255,255,0.25), 0 12px 32px rgba(31,68,120,0.18);
  --glass-radius: 26px;
  --sun-bg: linear-gradient(180deg, #FFB52E, #F59E0B);
  --sun-shadow: inset 0 1px 0 rgba(255,255,255,0.7), 0 8px 20px rgba(245,158,11,0.35);
  --row-bg: rgba(255,255,255,0.38);
  --row-border: rgba(255,255,255,0.6);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: "Avenir Next", "Segoe UI", system-ui, -apple-system, sans-serif;
  /* v15: the page itself is the preview's Sunny sky (the kid-stage
     body rule further down still wins on the Buddy screen: black). */
  background: linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 42%, var(--sky-3) 100%);
  background-attachment: fixed;
  color: var(--ink);
  min-height: 100vh;
}
#topbar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 16px; position: sticky; top: 0; z-index: 20;
  background: rgba(255, 248, 236, 0.92); backdrop-filter: blur(8px);
  border-bottom: 2px solid var(--line);
}
.brand { font-weight: 800; font-size: 20px; color: var(--teal-dark); }
#app { max-width: 760px; margin: 0 auto; padding: 16px 14px 60px; }
.view { display: block; }
.hidden { display: none !important; }
.card {
  background: var(--card); border: 2px solid var(--line); border-radius: var(--radius);
  padding: 18px; margin-bottom: 16px; box-shadow: 0 4px 18px rgba(34, 48, 58, 0.06);
}
.hero { background: linear-gradient(135deg, #FFF 0%, #F3EEFF 60%, #FFE9E4 100%); }
h1 { font-size: 30px; margin: 4px 0 8px; }
h2 { font-size: 22px; margin: 6px 0 10px; }
h3 { font-size: 17px; margin: 14px 0 6px; }
.lede { font-size: 17px; line-height: 1.5; }
.fineprint { color: var(--muted); font-size: 13px; line-height: 1.45; }
.fineprint.center { text-align: center; }
.error { background: #FFE3DE; color: #8C2415; border: 1.5px solid var(--coral);
  border-radius: 12px; padding: 10px 12px; margin-top: 10px; font-size: 14px; }
.notice { background: #E7F5F4; color: var(--teal-dark); border: 1.5px solid var(--teal);
  border-radius: 12px; padding: 10px 12px; margin: 10px 0; font-size: 14px; }
.warn { background: #FFF3D6; color: #7A5410; border: 1.5px solid #E3B341; }
.form label { display: block; font-weight: 600; margin: 12px 0 4px; font-size: 15px; }
input[type="text"], input[type="email"], input[type="password"], input[type="time"],
input[type="number"], select, textarea {
  width: 100%; padding: 13px 14px; font-size: 16px; border: 2px solid var(--line);
  border-radius: 12px; background: #FFFDF8; color: var(--ink);
}
input:focus, select:focus, textarea:focus { outline: 3px solid var(--lav); border-color: var(--lav-dark); }
.check { display: block; margin: 12px 0; font-size: 15px; line-height: 1.45; }
.check input { width: 22px; height: 22px; vertical-align: middle; margin-right: 8px; accent-color: var(--teal); }
.btn {
  appearance: none; border: 2px solid var(--teal); background: #fff; color: var(--teal-dark);
  font-weight: 700; font-size: 15px; padding: 11px 16px; border-radius: 999px; cursor: pointer;
  min-height: 48px;
}
.btn:active { transform: scale(0.98); }
.btn.primary { background: var(--teal); color: #fff; border-color: var(--teal-dark); }
.btn.coral { background: var(--coral); color: #fff; border-color: var(--coral-dark); }
.btn.lav { background: var(--lav); color: #2E2260; border-color: var(--lav-dark); }
.btn.danger { background: #fff; color: var(--coral-dark); border-color: var(--coral-dark); }
.btn.small { min-height: 38px; padding: 6px 12px; font-size: 13.5px; }
.btn.big { width: 100%; font-size: 16.5px; margin-top: 12px; }
.btn.huge { width: 100%; font-size: 19px; min-height: 60px; margin-top: 8px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.tabs .tab {
  flex: 1 1 auto; border: 2px solid var(--line); background: #fff; border-radius: 999px;
  padding: 11px 10px; font-weight: 700; font-size: 15px; cursor: pointer; min-height: 48px; color: var(--muted);
}
.tabs .tab.active { background: var(--teal); color: #fff; border-color: var(--teal-dark); }
.tabs.small .tab { flex: 1; }
.kidtabs .tab { font-size: 16.5px; min-height: 56px; }
.kidtabs .tab.active { background: var(--lav-dark); border-color: #4c3d8f; }
.docbox { background: #FFFDF6; border: 2px solid var(--line); border-radius: 12px; padding: 12px; font-size: 14px; line-height: 1.55; max-height: 220px; overflow: auto; }
.allowances { background: #F3EEFF; border: 2px solid var(--lav); border-radius: 12px; padding: 12px 14px; font-size: 15px; line-height: 1.6; margin-bottom: 12px; }
.divider { text-align: center; color: var(--muted); margin: 14px 0 4px; font-weight: 700; }
.planrow { display: grid; gap: 4px; }
.meter { background: #F1EAD9; border-radius: 999px; height: 16px; overflow: hidden; margin: 4px 0 10px; }
.meter > div { background: var(--teal); height: 100%; }
.meter.coral > div { background: var(--coral); }
.meter.lav > div { background: var(--lav-dark); }
.kidcard { border: 2px solid var(--line); border-radius: var(--radius); padding: 14px; margin-bottom: 12px; background: #FFFDF8; }
.row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.row > * { flex: 0 1 auto; }
.grow { flex: 1 1 auto; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 520px) { .grid2 { grid-template-columns: 1fr; } }
.pill { display: inline-block; background: #F3EEFF; color: #4c3d8f; border-radius: 999px; padding: 3px 10px; font-size: 12.5px; font-weight: 700; margin: 2px 3px 2px 0; }
.pill.teal { background: #E7F5F4; color: var(--teal-dark); }
.pill.coral { background: #FFE9E4; color: var(--coral-dark); }
.step-row { display: flex; align-items: center; gap: 10px; padding: 10px; border: 2px solid var(--line); border-radius: 14px; margin-bottom: 8px; background: #fff; font-size: 16.5px; }
.step-row.done { background: #E7F5F4; border-color: var(--teal); }
.step-row .check-btn { margin-left: auto; }
.big-text { font-size: 18px; line-height: 1.6; }
.bookpage { font-size: 20px; line-height: 1.65; background: #FFFDF6; border: 2px solid var(--line); border-radius: var(--radius); padding: 20px; min-height: 180px; }
.bookpage .w { cursor: pointer; border-radius: 4px; }
.bookpage .w:active { background: var(--lav); }
.choice-btn { display: block; width: 100%; text-align: left; margin-top: 10px; }
#child-header { display: flex; align-items: center; gap: 10px; row-gap: 8px; margin-bottom: 10px; flex-wrap: wrap; position: relative; }
/* Kid top bar layout (v6, 2026-11-10): a DETERMINISTIC two-row flex.
   Row 1: Menu (left) … mute, Exit (right — the mute button carries
   the auto margin that pushes the pair over). Row 2: the listening
   indicator, flex-basis 100%, so it can NEVER share a row with the
   buttons. V5 let the header wrap by chance: at ~390px the Exit pill
   wrapped onto a second row exactly where the absolutely-positioned
   corner mute button sat (top:64px in the Buddy tab), and the two
   overlapped in West's live screenshot. All four controls keep their
   colours/labels; the mute button is IN FLOW here now (it used to be
   rendered inside the Buddy tab and pinned by coordinates). The
   no-overlap contract at 360–430px is asserted by tests/layout.test.js. */
#btn-kid-menu { order: 1; }
#btn-mute { order: 2; margin-left: auto; flex: none; }
#btn-exit-child { order: 3; }
/* v19 (West, 2026-11-11): the indicator still OWNS row 2 (flex-basis
   100% forces the wrap, so it can never share the buttons' row and
   never overlap them), but its BOX is no longer the full-width strip
   West photographed — max-width: max-content shrinks the bar to a
   compact pill around its icon + label, and the auto inline margins
   centre that pill in its row. flex-grow stops at the max-width
   clamp, so the row guarantee and the pill coexist. */
#mic-indicator { order: 4; flex: 1 1 100%; justify-content: center; max-width: max-content; margin-inline: auto; }
/* Kid menu: every auxiliary kid-mode action lives behind this one panel
   (Buddy screen itself shows only Sunny, the mic, and the message box).
   Anchored inside the header so it always drops BELOW the header controls
   and can never cover the Exit button or the corner mute button. */
.kid-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 35; background: #fff; border: 2px solid var(--line); border-radius: 18px; box-shadow: 0 12px 32px rgba(20, 25, 35, 0.28); padding: 8px; display: flex; flex-direction: column; gap: 4px; min-width: 208px; max-width: 250px; max-height: 70vh; overflow-y: auto; }
.kid-menu.hidden { display: none; }
.kmenu-item { text-align: left; background: #FFF9EC; border: 2px solid var(--line); border-radius: 12px; padding: 12px 14px; font-size: 16.5px; font-weight: 700; cursor: pointer; min-height: 48px; font-family: inherit; color: inherit; }
.kmenu-item:active { transform: scale(0.98); }
#kid-menu-note { margin: 6px 4px 2px; }
/* Mute: the ONE always-visible kid control besides mic/message —
   in-flow in the kid top bar (v6; see the layout contract at
   #child-header), instantly reachable. */
#ctab-buddy { position: relative; }
/* Liquid Glass (West's name for it): translucent frosted glass, blurred
   backdrop, a bright rim along the top edge. The mute button is the
   reference instance; the full-bleed kid screen's corner controls reuse
   the same treatment (see the kid-stage section below). */
/* Liquid Glass controls, COLOUR-TINTED per control (West, 2026-11-09
   22:13 PT): "black and white on different colors on the buttons that
   are Liquid Glass" — each control gets its OWN jewel-tone tint in the
   glass, with its glyph in black or white, whichever contrasts. The
   glass itself (translucent colour + blur(14px) saturate(1.6) + rim
   light) is unchanged; the controls stay small and quiet in the
   corners. Mapping (also in README): mute = AMBER, black glyph (RUBY,
   white glyph, while muted) · stop-speaking = AMETHYST, white glyph ·
   kid Menu = SAPPHIRE, white · Exit = RUBY, white · listening
   indicator = EMERALD, white · kid menu panel = SAPPHIRE glass. */
.mute-btn { position: static; width: 48px; height: 48px; border-radius: 50%; border: 1.5px solid rgba(255, 209, 102, 0.65); background: linear-gradient(160deg, rgba(245, 180, 40, 0.55), rgba(199, 128, 10, 0.48)); -webkit-backdrop-filter: blur(14px) saturate(1.6); backdrop-filter: blur(14px) saturate(1.6); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -10px 16px rgba(255, 255, 255, 0.07), 0 4px 14px rgba(245, 180, 40, 0.3); color: #221806; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.mute-btn svg { width: 21px; height: 21px; display: block; }
.mute-btn[aria-pressed="true"] { background: linear-gradient(160deg, rgba(226, 63, 95, 0.6), rgba(159, 24, 54, 0.52)); border-color: rgba(253, 164, 175, 0.6); color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 4px 14px rgba(226, 63, 95, 0.32); }
/* Stop-speaking: appears under the mute button only while Sunny talks. */
.stop-speak-btn { position: absolute; top: 66px; right: 10px; z-index: 30; min-height: 40px; padding: 6px 14px; border-radius: 999px; border: 1.5px solid rgba(196, 164, 255, 0.6); background: linear-gradient(160deg, rgba(150, 100, 230, 0.52), rgba(95, 55, 180, 0.46)); -webkit-backdrop-filter: blur(14px) saturate(1.6); backdrop-filter: blur(14px) saturate(1.6); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 4px 14px rgba(124, 78, 196, 0.32); color: #fff; font-size: 14px; font-weight: 700; cursor: pointer; }
.mic { background: var(--coral); color: #fff; font-weight: 700; border-radius: 999px; padding: 6px 8px 6px 14px; display: flex; gap: 8px; align-items: center; }
.stage { border-radius: 24px; padding: 18px 10px 8px; text-align: center; position: relative; overflow: hidden; transition: background 0.5s; }
.stage.room-night { background: radial-gradient(ellipse at 50% 30%, #2C3E70 0%, #131C38 70%); color: #fff; }
.stage.room-garden { background: linear-gradient(180deg, #BDE7FF 0%, #DFF7C8 100%); }
.stage.room-space { background: radial-gradient(ellipse at 50% 20%, #3B2E6E 0%, #0B0620 75%); color: #fff; }
.stage.room-beach { background: linear-gradient(180deg, #AEE6FF 0%, #FFE9B8 100%); }
/* SPACE STAGE (West, 2026-11-09): Sunny's companion window is crystal
   black with shining stars. Pure CSS/SVG — no image asset, no canvas:
   two tiled radial-gradient star layers on ::before (varied sizes and
   brightness), a few bright four-point "shine" stars with a soft cross
   glint (inline SVG) on ::after, a subtle slow twinkle, and Sunny's
   golden glow + light pool popping against the black. The twinkle is
   disabled under reduced motion; the stars stay, static. */
.comp-window { position: relative; overflow: hidden; background: radial-gradient(ellipse at 50% 42%, #070812 0%, #020208 55%, #000000 100%); border-radius: 26px; padding: 10px 6px 2px; margin: 2px 4px 10px; box-shadow: inset 0 0 28px rgba(0, 0, 0, 0.65); }
.comp-window::before { content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image:
    radial-gradient(1.6px 1.6px at 22px 30px, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.1px 1.1px at 68px 84px, rgba(214, 231, 255, 0.85), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.3px 1.3px at 110px 18px, rgba(255, 244, 205, 0.9), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1px 1px at 138px 66px, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.8px 1.8px at 44px 118px, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1px 1px at 96px 132px, rgba(200, 220, 255, 0.75), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.2px 1.2px at 152px 122px, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0) 70%),
    radial-gradient(0.9px 0.9px at 8px 96px, rgba(255, 255, 255, 0.65), rgba(255, 255, 255, 0) 70%);
  background-size: 170px 150px;
  animation: starTwinkle 4.2s ease-in-out infinite alternate; }
.comp-window::after { content: ''; position: absolute; inset: 0; pointer-events: none; background-repeat: no-repeat; background-size: 100% 100%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 240' preserveAspectRatio='none'%3E%3Ccircle cx='52' cy='44' r='13' fill='%23FFF3B0' opacity='0.13'/%3E%3Ccircle cx='306' cy='66' r='10' fill='%23FFF3B0' opacity='0.11'/%3E%3Cg fill='%23FFFFFF'%3E%3Cpath d='M52 32 l2.8 8.2 8.2 2.8 -8.2 2.8 -2.8 8.2 -2.8 -8.2 -8.2 -2.8 8.2 -2.8 Z'/%3E%3Cpath d='M306 57 l2.1 6.1 6.1 2.1 -6.1 2.1 -2.1 6.1 -2.1 -6.1 -6.1 -2.1 6.1 -2.1 Z' opacity='0.9'/%3E%3Cpath d='M262 176 l1.8 5.2 5.2 1.8 -5.2 1.8 -1.8 5.2 -1.8 -5.2 -5.2 -1.8 5.2 -1.8 Z' opacity='0.8'/%3E%3Cpath d='M96 198 l1.5 4.3 4.3 1.5 -4.3 1.5 -1.5 4.3 -1.5 -4.3 -4.3 -1.5 4.3 -1.5 Z' opacity='0.75'/%3E%3C/g%3E%3C/svg%3E");
  animation: starTwinkle 3.1s ease-in-out infinite alternate-reverse; }
@keyframes starTwinkle { from { opacity: 0.55; } to { opacity: 1; } }
.comp-window > svg, .comp-window > .companion-photo-host { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) {
  .comp-window::before, .comp-window::after { animation: none; }
}
.caption { min-height: 30px; font-size: 17px; font-weight: 600; padding: 8px 6px; }
.chatlog { max-height: 260px; overflow: auto; text-align: left; padding: 6px; }
/* v14 heard-you echo: the child's own final words, shown back so the
   speaker can verify Sunny heard right. Ephemeral caption chip above
   the chat log — display-only, never part of the log itself. Long
   utterances wrap and scroll inside the chip; the fade is opacity
   only (no movement, no flash) and disappears under reduced motion. */
.heard { display: flex; gap: 8px; align-items: baseline; margin: 0 0 8px; padding: 10px 14px; border-radius: 16px; background: #F3EEFF; border: 2px solid var(--lav-dark); font-size: 17px; line-height: 1.45; text-align: left; overflow-wrap: anywhere; max-height: 5.8em; overflow-y: auto; opacity: 1; transition: opacity 0.7s ease; }
.heard.fading { opacity: 0; }
.heard-label { font-weight: 700; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) {
  .heard { transition: none; }
}
.bubble { max-width: 85%; padding: 10px 14px; border-radius: 16px; margin: 6px 0; font-size: 16.5px; line-height: 1.45; }
.bubble.kid { background: #F3EEFF; margin-left: auto; }
.bubble.buddy { background: #fff; border: 2px solid var(--line); }
.chatrow { display: flex; gap: 8px; margin-top: 8px; }
.chatrow input { flex: 1; }
.ptt { min-width: 64px; font-size: 22px; }
.toast { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); background: var(--ink); color: #fff; padding: 12px 18px; border-radius: 999px; font-weight: 600; z-index: 50; max-width: 92vw; text-align: center; }
.modal-back { position: fixed; inset: 0; background: rgba(20, 25, 35, 0.55); z-index: 40; display: flex; align-items: flex-end; justify-content: center; }
.modal { background: #fff; border-radius: 22px 22px 0 0; padding: 20px; width: 100%; max-width: 560px; max-height: 88vh; overflow: auto; }
.wardrobe { display: flex; gap: 8px; flex-wrap: wrap; }
.wardrobe button { border: 2px solid var(--line); background: #fff; border-radius: 999px; padding: 9px 13px; font-weight: 700; cursor: pointer; min-height: 44px; }
.wardrobe button.sel { background: var(--lav); border-color: var(--lav-dark); color: #2E2260; }
table.kv { width: 100%; border-collapse: collapse; font-size: 14.5px; }
table.kv td { padding: 6px 4px; border-bottom: 1px solid var(--line); vertical-align: top; }
.reduced-motion * { animation: none !important; transition: none !important; }

/* ——— Photo companion (COMPANION_MODE = 'photo') ———
   West's snippet CSS, verbatim: */
.sun-companion {
  width: min(100%, 480px);
  margin: 0 auto;
}

.sun-photo {
  display: block;
  width: 100%;
  height: auto;
  object-fit: contain;
  border-radius: 24px;
}

/* ——— Mascot companion (COMPANION_MODE = 'mascot', v3) ———
   The glossy 3D mascot from West's approved art. Expression STILLS are
   stacked and crossfaded (opacity only — a face swap, never facial
   animation or lip-sync); all real motion stays in the host layers,
   exactly like photo/video mode.
   BIGGER (West, 2026-11-09): the mascot renders ~20% larger than the
   photo/video presentation — the scoped cap rises 480px → 576px, and
   on the kid stage he goes full-bleed edge-to-edge (see the kid-stage
   rule) instead of sitting inside the padded column like the photo.
   The 390px no-overflow contract is harness-verified. */
.companion-mascot-host .sun-companion { width: min(100%, 576px); }
.sun-companion-mascot { position: relative; aspect-ratio: 1 / 1; }
.mascot-face {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; opacity: 0; pointer-events: none;
  transition: opacity 0.3s ease;
}
.mascot-face.active { opacity: 1; }
/* v9: expression faces crossfade at the gentler 300ms above, but the
   MOUTH frames and the blink must stay crisp — a 300ms fade would
   smear lip-sync (frames hold ~90ms) and soften the blink. */
.mascot-face[data-face="mouth-closed"],
.mascot-face[data-face="mouth-wide"],
.mascot-face[data-face="blink"] { transition-duration: 0.08s; }
/* GLOW (West, 2026-11-09): a warm golden radial halo behind him on the
   black stage — bigger and warmer than the photo mode's extra halo.
   It pulses while thinking via the shared photo-host rule, and the
   reactive controller breathes its opacity like the other modes. */
.companion-photo-host.companion-mascot-host .comp-glow {
  inset: -18% -18%;
  background: radial-gradient(circle, rgba(255, 216, 84, 0.66) 0%, rgba(255, 186, 40, 0.36) 42%, rgba(255, 186, 40, 0) 70%);
}

/* The photo host reuses the vector companion's nested layer structure, so
   float / tilt / spin / bounce compose the same way. The photo's glow is
   baked into the picture; .comp-glow is a soft extra halo behind it that
   only pulses gently (thinking / reduced-motion celebrate). */
.companion-photo-host { position: relative; touch-action: manipulation; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.companion-photo-host .comp-glow {
  position: absolute; inset: -4% -3%; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(255, 210, 63, 0.5) 0%, rgba(255, 190, 40, 0.22) 45%, rgba(255, 190, 40, 0) 70%);
}
.companion-photo-host .comp-bob { position: relative; animation: photoBob 3.6s ease-in-out infinite; }
@keyframes photoBob {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  25% { transform: translateY(-5px) rotate(1.1deg); }
  50% { transform: translateY(-7px) rotate(0deg); }
  75% { transform: translateY(-5px) rotate(-1.1deg); }
}
.companion-photo-host .layer-spin,
.companion-photo-host .layer-tilt,
.companion-photo-host .layer-bounce,
.companion-photo-host .layer-rock { transform-origin: 50% 50%; }
.companion-photo-host .layer-sparkles { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
/* Voice-reactive layer (TTS build): the reactive controller writes
   transform/filter here — whole-image lean/bob/glow, never a mouth.
   Pivot low-centre so the lean reads as leaning toward the child. */
.companion-photo-host .layer-react { transform-origin: 50% 62%; will-change: transform, filter; }
.companion-photo-host .spark-dot {
  position: absolute; left: 50%; top: 50%; width: 9px; height: 9px; border-radius: 50%;
  box-shadow: 0 0 6px rgba(255, 210, 63, 0.8);
}
.companion-photo-host .spark-star {
  position: absolute; color: #FFE873; font-size: 20px; line-height: 1;
  text-shadow: 0 0 8px rgba(255, 210, 63, 0.9);
}
/* State motions, mirroring the vector companion's: speaking tilts on
   .layer-tilt (the mouth is baked into the photo — the tilts carry the
   expressiveness), thinking adds a slow tilt plus a gentle glow pulse. */
.companion-photo-host[data-state="speaking"] .layer-tilt { animation: photoTalkTilt 1.5s ease-in-out infinite alternate; }
@keyframes photoTalkTilt { from { transform: rotate(-2.6deg); } to { transform: rotate(2.6deg); } }
.companion-photo-host[data-state="listening"] .comp-bob { animation: photoListen 1.1s ease-in-out infinite; }
@keyframes photoListen { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.03); } }
.companion-photo-host[data-state="thinking"] .layer-tilt { animation: photoThinkTilt 2.8s ease-in-out infinite alternate; }
@keyframes photoThinkTilt { from { transform: rotate(-4.6deg); } to { transform: rotate(4.6deg); } }
.companion-photo-host[data-state="thinking"] .comp-glow { animation: photoHaloPulse 2.4s ease-in-out infinite alternate; }
@keyframes photoHaloPulse { from { opacity: 0.55; transform: scale(0.97); } to { opacity: 1; transform: scale(1.05); } }
.companion-photo-host[data-state="thinking"] .sun-companion { animation: photoGlowPulse 2.4s ease-in-out infinite alternate; }
@keyframes photoGlowPulse {
  from { filter: drop-shadow(0 0 5px rgba(255, 205, 60, 0.3)); }
  to { filter: drop-shadow(0 0 16px rgba(255, 205, 60, 0.65)); }
}
.companion-photo-host[data-state="sleeping"] .comp-bob { animation-duration: 5.5s; }
/* Photo/video hosts cannot wear drawn headphones — a muted badge is
   shown instead when the app mutes Sunny (vector mode draws real
   headphones on the character). */
.companion-photo-host.fx-muted::after { content: '🔇 Muted'; position: absolute; top: 10px; right: 10px; z-index: 4; background: rgba(179, 38, 30, 0.92); color: #fff; font-weight: 700; font-size: 13.5px; padding: 5px 10px; border-radius: 999px; }
@media (prefers-reduced-motion: reduce) {
  .companion-photo-host .comp-bob,
  .companion-photo-host .layer-tilt,
  .companion-photo-host .comp-glow,
  .companion-photo-host .sun-companion { animation: none !important; }
}

/* ——— Video companion (COMPANION_MODE = 'video') ———
   The video host reuses the photo host wholesale (same classes, nested
   layers, state CSS and one-shot translations). Only the media element
   differs: the animated loop is laid exactly over the still photo,
   which sizes the frame, paints instantly, and remains as the
   automatic fallback if the video cannot play. */
.sun-companion-video { position: relative; }
.sun-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; border-radius: 24px; display: block;
}

/* ——— FULL-BLEED KID STAGE (West, 2026-11-09: "cover the whole screen
   with the diamond black. Just leave text box only.") ———
   Active only while the kid-mode Buddy tab is on screen (body.kid-stage,
   toggled by the app): the diamond-black space stage runs edge-to-edge
   over the ENTIRE viewport. The app topbar (logo, Parent Dashboard /
   Sign out), the cream page, the kid header bar and the chat card chrome
   all disappear. What remains: Sunny's stage, his caption, the message
   box with Send — plus small Liquid Glass corner controls (menu, exit,
   mute, mic indicator) that stay quiet against the black. Parent and
   owner screens are untouched: the class is removed the moment the app
   leaves the Buddy tab. */
body.kid-stage { background: #000; }
body.kid-stage #topbar { display: none; }
body.kid-stage #app { max-width: none; padding: 0; }
body.kid-stage #view-child {
  position: relative; min-height: 100dvh; overflow: hidden;
  background: radial-gradient(ellipse at 50% 38%, #0A0C1E 0%, #04050F 52%, #000000 100%);
}
/* The starfield, promoted from the companion window to the whole
   screen (same two layers as .comp-window: tiled star dots + shine
   stars). Fixed so it covers the viewport even if content scrolls. */
body.kid-stage #view-child::before { content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    radial-gradient(1.6px 1.6px at 22px 30px, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.1px 1.1px at 68px 84px, rgba(214, 231, 255, 0.85), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.3px 1.3px at 110px 18px, rgba(255, 244, 205, 0.9), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1px 1px at 138px 66px, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.8px 1.8px at 44px 118px, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1px 1px at 96px 132px, rgba(200, 220, 255, 0.75), rgba(255, 255, 255, 0) 70%),
    radial-gradient(1.2px 1.2px at 152px 122px, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0) 70%),
    radial-gradient(0.9px 0.9px at 8px 96px, rgba(255, 255, 255, 0.65), rgba(255, 255, 255, 0) 70%);
  background-size: 170px 150px;
  animation: starTwinkle 4.2s ease-in-out infinite alternate; }
body.kid-stage #view-child::after { content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none; background-repeat: no-repeat; background-size: 100% 100%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 390 844' preserveAspectRatio='none'%3E%3Ccircle cx='52' cy='44' r='13' fill='%23FFF3B0' opacity='0.13'/%3E%3Ccircle cx='330' cy='120' r='10' fill='%23FFF3B0' opacity='0.11'/%3E%3Cg fill='%23FFFFFF'%3E%3Cpath d='M52 32 l2.8 8.2 8.2 2.8 -8.2 2.8 -2.8 8.2 -2.8 -8.2 -8.2 -2.8 8.2 -2.8 Z'/%3E%3Cpath d='M330 111 l2.1 6.1 6.1 2.1 -6.1 2.1 -2.1 6.1 -2.1 -6.1 -6.1 -2.1 6.1 -2.1 Z' opacity='0.9'/%3E%3Cpath d='M300 640 l1.8 5.2 5.2 1.8 -5.2 1.8 -1.8 5.2 -1.8 -5.2 -5.2 -1.8 5.2 -1.8 Z' opacity='0.8'/%3E%3Cpath d='M70 700 l1.5 4.3 4.3 1.5 -4.3 1.5 -1.5 4.3 -1.5 -4.3 -4.3 -1.5 4.3 -1.5 Z' opacity='0.75'/%3E%3C/g%3E%3C/svg%3E");
  animation: starTwinkle 3.1s ease-in-out infinite alternate-reverse; }
body.kid-stage.reduced-motion #view-child::before,
body.kid-stage.reduced-motion #view-child::after { animation: none; }
@media (prefers-reduced-motion: reduce) {
  body.kid-stage #view-child::before, body.kid-stage #view-child::after { animation: none; }
}
/* Header becomes a transparent overlay of glass corner controls. */
body.kid-stage #child-header { position: absolute; top: 0; left: 0; right: 0; z-index: 40; margin: 0; padding: max(10px, env(safe-area-inset-top)) 12px 0; background: transparent; }
/* Colour tints (see the mapping note at .mute-btn): Menu = SAPPHIRE,
   Exit = RUBY — same glass recipe, different jewel tone per control. */
body.kid-stage #btn-kid-menu,
body.kid-stage #btn-exit-child {
  min-height: 40px; padding: 6px 14px; font-size: 13px; border-radius: 999px;
  color: #fff;
  -webkit-backdrop-filter: blur(14px) saturate(1.6); backdrop-filter: blur(14px) saturate(1.6);
  opacity: 0.95;
}
body.kid-stage #btn-kid-menu {
  border: 1.5px solid rgba(147, 197, 253, 0.55);
  background: linear-gradient(160deg, rgba(64, 120, 230, 0.55), rgba(30, 64, 175, 0.46));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), inset 0 -8px 14px rgba(255, 255, 255, 0.06), 0 4px 14px rgba(37, 99, 235, 0.32);
}
body.kid-stage #btn-exit-child {
  border: 1.5px solid rgba(253, 164, 175, 0.55);
  background: linear-gradient(160deg, rgba(226, 63, 95, 0.55), rgba(159, 24, 54, 0.46));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), inset 0 -8px 14px rgba(255, 255, 255, 0.06), 0 4px 14px rgba(226, 63, 95, 0.3);
}
/* v19 correction (West, 2026-11-11): there is deliberately NO
   kid-stage size rule for the push-to-talk button here. v18 shrank
   it to the Exit pill's metrics on a misread of West's ask — his
   photo showed the "listen button" is the green Listening bar
   (#mic-indicator), not Talk. The v18 rule is reverted: the Talk
   button wears the base .btn size again (min-height 48px). */
/* Listening indicator = EMERALD glass, white glyph.
   v19 (West, 2026-11-11): this is the element West called the
   "listen button" — the pill now wears the SAME size as the Exit
   button (the shared glass-pill metrics: min-height 40px, padding
   6px 14px, font-size 13px), and the layout rule above makes its
   box a compact centred pill instead of a full-width strip.
   Emerald identity, mic icon, label and show/hide behaviour are
   unchanged. tests/v19.test.js pins the pairing. */
body.kid-stage #mic-indicator.mic {
  background: linear-gradient(160deg, rgba(25, 175, 118, 0.5), rgba(8, 115, 72, 0.46)); border: 1.5px solid rgba(110, 231, 183, 0.55); color: #fff;
  -webkit-backdrop-filter: blur(14px) saturate(1.6); backdrop-filter: blur(14px) saturate(1.6);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 4px 14px rgba(16, 160, 100, 0.3);
  min-height: 40px; font-size: 13px; padding: 6px 14px;
}
.mic .mic-ic { width: 15px; height: 15px; flex: none; display: block; }
body.kid-stage #btn-mic-stop { display: none; } /* the corner mute does this job here */
/* The kid menu panel wears the Menu button's SAPPHIRE glass. */
body.kid-stage .kid-menu {
  background: linear-gradient(165deg, rgba(38, 80, 180, 0.45), rgba(10, 14, 34, 0.78)); border-color: rgba(147, 197, 253, 0.4); color: #fff;
  -webkit-backdrop-filter: blur(16px) saturate(1.5); backdrop-filter: blur(16px) saturate(1.5);
  box-shadow: 0 10px 30px rgba(10, 20, 60, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
body.kid-stage .kmenu-item { background: rgba(147, 197, 253, 0.14); border-color: rgba(147, 197, 253, 0.3); color: #fff; }
body.kid-stage .kmenu-item:hover, body.kid-stage .kmenu-item.active { background: rgba(147, 197, 253, 0.3); }
body.kid-stage #kid-menu-note { color: rgba(255, 255, 255, 0.75); }
/* The Buddy column fills the viewport: stage, caption, then the chat
   surface pinned low with the message box at the bottom. */
body.kid-stage #ctab-buddy { position: relative; z-index: 1; min-height: 100dvh; display: flex; flex-direction: column; padding: 60px 12px max(12px, env(safe-area-inset-bottom)); }
body.kid-stage #ctab-buddy .stage { background: transparent; border-radius: 0; padding: 6px 0 0; overflow: visible; }
body.kid-stage #ctab-buddy .comp-window { background: transparent; border-radius: 0; box-shadow: none; margin: 0; padding: 0; overflow: visible; }
body.kid-stage #ctab-buddy .comp-window::before,
body.kid-stage #ctab-buddy .comp-window::after { display: none; } /* the screen IS the window now */
body.kid-stage #ctab-buddy .caption { color: #fff; text-shadow: 0 1px 10px rgba(0, 0, 0, 0.65); }
/* Stop-speaking floats over the stage's top-right; its offset clears
   the tallest possible header (safe-area inset + row 1 + the
   indicator's own row) so it can never sit on a header control. */
body.kid-stage #ctab-buddy .stop-speak-btn { top: calc(max(10px, env(safe-area-inset-top)) + 106px); right: 12px; }
/* Mascot on the kid stage: full-bleed edge-to-edge (the photo sits in
   the padded column) — with the +20% scoped cap this is the "a little
   bigger" West asked for. The glow keeps no horizontal bleed here so
   the 390px no-overflow contract holds. */
body.kid-stage #ctab-buddy .companion-mascot-host { width: calc(100% + 24px); margin-inline: -12px; }
body.kid-stage #ctab-buddy .companion-mascot-host .comp-glow { inset: -14% 0; }
body.kid-stage #sayit-slot .card { background: rgba(24, 18, 30, 0.72); border-color: rgba(255, 122, 102, 0.65); color: #fff; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }
body.kid-stage #ctab-buddy > .card { background: transparent; border: none; box-shadow: none; padding: 4px 2px 0; margin: auto 0 0; }
body.kid-stage #ctab-buddy .chatlog { max-height: 24dvh; }
body.kid-stage #ctab-buddy .bubble.kid { background: rgba(184, 169, 232, 0.30); color: #fff; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
body.kid-stage #ctab-buddy .heard { background: rgba(184, 169, 232, 0.30); border-color: rgba(255, 255, 255, 0.28); color: #fff; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
body.kid-stage #ctab-buddy .bubble.buddy { background: rgba(255, 255, 255, 0.10); border: 1.5px solid rgba(255, 255, 255, 0.28); color: #fff; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
body.kid-stage #ctab-buddy .chatrow input {
  background: rgba(255, 255, 255, 0.10); border-color: rgba(255, 255, 255, 0.38); color: #fff;
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
}
body.kid-stage #ctab-buddy .chatrow input::placeholder { color: rgba(255, 255, 255, 0.62); }

/* ================= v7: tricks, dress-up, activities, audio-era UI =====
   Mobile-first, large touch targets, transform/opacity motion only.
   NO strobing anywhere: the fastest loop here is the gentle sidekick
   bob (2.6s) and the breathing circle (4s in / 4s out). */

/* --- trick layers (DOM/SVG elements created by tricks.js) --- */
.trick-layer { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
.trick-star { position: absolute; transform: translate(-50%, -50%); animation: trickStarFly 0.95s ease-out both; will-change: transform, opacity; }
@keyframes trickStarFly {
  0% { transform: translate(-50%, -50%) scale(0.3) rotate(0deg); opacity: 0; }
  25% { opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(1.15) rotate(40deg); opacity: 0; }
}
.peek-hand { position: absolute; top: 16%; font-size: 92px; line-height: 1; filter: drop-shadow(0 4px 8px rgba(0,0,0,0.25)); }
.peek-hand.left { left: 6%; transform: rotate(-14deg); }
.peek-hand.right { right: 6%; transform: scaleX(-1) rotate(-14deg); }
.trick-illus { position: absolute; left: 50%; transform: translateX(-50%); pointer-events: none; }
.rainbow-svg { top: 0; width: 92%; height: auto; }
.magic-hat { bottom: 4%; width: 150px; text-align: center; }
.magic-object { position: absolute; left: 50%; bottom: 118px; transform: translateX(-50%); font-size: 54px; animation: magicPop 0.5s cubic-bezier(.2,1.6,.4,1) both; }
@keyframes magicPop { from { transform: translateX(-50%) scale(0.2); opacity: 0; } to { transform: translateX(-50%) scale(1); opacity: 1; } }
.juggle-star { top: 8%; font-size: 40px; }
.sunshine-glow { position: absolute; inset: -12%; background: radial-gradient(circle at 50% 42%, rgba(255, 217, 61, 0.55) 0%, rgba(255, 217, 61, 0.18) 42%, transparent 68%); animation: sunshinePulse 1.6s ease-in-out both; }
@keyframes sunshinePulse { 0% { opacity: 0; transform: scale(0.8); } 35% { opacity: 1; } 100% { opacity: 0; transform: scale(1.06); } }
.trick-calm-glow { box-shadow: inset 0 0 0 4px rgba(255, 217, 61, 0.85), inset 0 0 60px rgba(255, 217, 61, 0.35); border-radius: 24px; }
/* High-five is interactive (touch + keyboard), so its layer takes events. */
.highfive-layer { pointer-events: auto; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 10px; padding-bottom: 14px; }
.highfive-prompt { background: rgba(20, 24, 40, 0.82); color: #fff; border-radius: 999px; padding: 8px 16px; font-weight: 700; font-size: 17px; }
.highfive-btn { font-size: 21px; padding: 14px 26px; border-radius: 999px; border: 3px solid #fff; background: #ffd93d; color: #22242e; font-weight: 800; cursor: pointer; box-shadow: 0 6px 18px rgba(0,0,0,0.3); }
.highfive-btn:focus-visible { outline: 4px solid #4d96ff; outline-offset: 3px; }
.highfive-done { font-size: 15px; padding: 10px 18px; border-radius: 999px; border: 2px solid rgba(255,255,255,0.7); background: rgba(20,24,40,0.6); color: #fff; cursor: pointer; }

/* --- dress-up overlay (inside the mascot host; rides host motion) --- */
/* V11: the layer is mounted INSIDE the face container (.sun-companion),
   so inset:0 == the face box. Every item fills the layer: the item's
   200x200 SVG canvas IS the placement (glyphs are drawn at face
   coordinates). V7-V10 instead shrank/offset each item a second time
   (top/width below), double-transforming them off the face — the
   sunglasses landed below the chin on wide screens and never showed;
   only the helmet (the one item left at inset:0) was visible, as a
   bare rim. No per-item offsets remain — by design. */
.dressup-layer { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
.dressup-item { position: absolute; inset: 0; width: 100%; height: 100%; }
.dressup-item svg { width: 100%; height: 100%; display: block; }
/* Dress-up sheet: a worn item is VISIBLY marked (v11 — before, the
   aria-pressed state had no visual style at all). */
.trick-grid button[data-dress][aria-pressed="true"] { background: #E7DFFC; box-shadow: inset 0 0 0 2px #7b5ea7; }
.trick-grid button[data-dress][aria-pressed="true"]::after { content: ' ✓ on Sunny'; font-weight: 700; color: #5b417f; }
/* Mute headphones (v11): mounted inside the face container like the
   dress-up layer, but ABOVE it (z 7 > 6) — the mute signal must show
   even over a space helmet. In mascot mode the headphones REPLACE the
   🔇 badge; photo/video modes (no overlay) keep the badge. */
.mute-headphones { position: absolute; inset: 0; pointer-events: none; z-index: 7; }
.mute-headphones svg { width: 100%; height: 100%; display: block; }
.companion-photo-host.companion-mascot-host.fx-muted::after { content: none; }

/* --- sidekick buddy (stage corner) --- */
.sidekick { position: absolute; right: 10px; bottom: 44px; width: 64px; height: 64px; z-index: 4; pointer-events: none; filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3)); }
.sidekick svg { width: 100%; height: 100%; display: block; }

/* --- stage mood states --- */
#buddy-stage.sleepy-dim { filter: brightness(0.72) saturate(0.85); }
body.bedtime-mode #view-child { filter: brightness(0.82); }
#play-stage.bedtime-dim { filter: brightness(0.8); }

/* --- activities --- */
.trick-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.trick-grid .btn { flex: 1 1 42%; min-height: 52px; font-size: 16px; }
.act-box { background: var(--card, #fff); border-radius: 20px; padding: 16px; margin-bottom: 14px; box-shadow: 0 4px 16px rgba(20,24,40,0.12); }
.act-box h3 { margin-top: 0; }
.act-line { font-size: 18px; line-height: 1.45; }
.act-status { font-size: 26px; font-weight: 800; text-align: center; padding: 10px 0; }
.bubble-field { position: relative; height: 240px; margin-top: 10px; }
.bubble { position: absolute; width: 84px; height: 84px; border-radius: 50%; border: 3px solid rgba(120,180,255,0.75); background: radial-gradient(circle at 32% 30%, rgba(255,255,255,0.95), rgba(160,205,255,0.45) 60%, rgba(120,180,255,0.3)); font-size: 34px; font-weight: 800; color: #173a5e; cursor: pointer; animation: bubbleFloat 3.4s ease-in-out infinite alternate; }
@keyframes bubbleFloat { from { margin-top: 0; } to { margin-top: -12px; } }
.bubble.popped { opacity: 0.15; transform: scale(1.25); pointer-events: none; }
.rhythm-pads { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 12px 0; }
.rhythm-pad { font-size: 44px; padding: 20px 0; border-radius: 18px; border: 3px solid #d9deeb; background: #f4f6fd; cursor: pointer; transition: background 0.15s, transform 0.15s; }
.rhythm-pad.lit { background: #ffd93d; transform: scale(1.06); border-color: #e8a90c; }
.star-groups { text-align: center; padding: 8px 0; }
.star-row { font-size: 30px; letter-spacing: 3px; color: #e8a90c; }
.star-row.blue { color: #4d96ff; }
.star-row.big { font-size: 40px; }
.star-op { font-size: 30px; font-weight: 800; }
.word-answer { font-size: 30px; font-weight: 800; letter-spacing: 6px; text-align: center; min-height: 44px; cursor: pointer; }
.word-tiles { display: flex; gap: 10px; justify-content: center; margin: 10px 0 14px; }
.word-tile { font-size: 30px; font-weight: 800; width: 58px; height: 58px; border-radius: 14px; border: 3px solid #9b5de5; background: #f3eafd; cursor: pointer; }
.word-tile:disabled { opacity: 0.3; }
.breath-circle { width: 130px; height: 130px; margin: 14px auto; border-radius: 50%; background: radial-gradient(circle at 40% 35%, #ffe98a, #ffb84d); transition: transform 4s ease-in-out; box-shadow: 0 0 44px rgba(255, 190, 60, 0.55); }
.breath-circle.grow { transform: scale(1.45); }
.color-swatch { width: 92px; height: 92px; border-radius: 22px; margin: 8px auto; border: 3px solid rgba(20,24,40,0.15); }
.draw-canvas { width: 100%; max-width: 640px; height: auto; border: 3px solid #d9deeb; border-radius: 14px; background: #fff; touch-action: none; display: block; margin: 8px auto; }
.draw-colors { gap: 8px; }
.draw-color { width: 40px; height: 40px; border-radius: 50%; border: 3px solid rgba(20,24,40,0.25); cursor: pointer; padding: 0; }
.draw-desc-label { display: block; margin: 10px 0; font-weight: 600; }
.draw-desc-label input { width: 100%; margin-top: 6px; }
.calm-box { background: linear-gradient(180deg, #fdf6e0, #eef6ff); }

/* --- parent Sunny settings --- */
.sunny-settings label { display: block; margin: 10px 0; }
.sunny-settings input[type="range"] { width: 100%; }
.act-toggles { border: 2px solid #e3e7f2; border-radius: 14px; margin: 12px 0; }
.drawings-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.drawing-thumb { margin: 0; width: 150px; }
.drawing-thumb img { width: 150px; height: auto; border-radius: 10px; border: 2px solid #e3e7f2; display: block; }

/* Reduced motion: activity loops calm down too (§13). */
body.reduced-motion .bubble { animation: none; }
body.reduced-motion .breath-circle { transition: none; }

/* ================= v8: Show Me visual panel ==========================
   Ported from West's Sunny_Show_Me.html. The panel lives INSIDE the
   Buddy stage; opening it adds .showing to #buddy-stage, which
   shrinks the REAL companion window into the stage's top corner
   (the demo's 70px corner treatment, at 84px here) — the mascot's
   face/mouth layers live inside that window, so Sunny keeps
   reacting while small, and removing the class restores him to
   full size exactly. The panel's 96px top padding is the clearance
   under the corner Sunny (6px offset + 84px box = 90 < 96): panel
   content can never slide under him, at any width. */
#buddy-stage .comp-window { transition: width .5s ease, height .5s ease; }
#buddy-stage.showing .comp-window { position: absolute; top: 6px; right: 6px; width: 84px; height: 84px; margin: 0; padding: 0; z-index: 5; }
#buddy-stage.showing .comp-window > svg { width: 100%; height: 100%; }
body.kid-stage #ctab-buddy #buddy-stage.showing .companion-mascot-host { width: 100%; margin-inline: 0; }
/* While the panel is open, the floating stop-speaking button drops
   below the corner Sunny instead of sitting on him (+96px = the
   corner's 90px footprint + slack, same inset expression as the
   default rule so safe-area handling is identical). */
body.kid-stage #ctab-buddy:has(> #buddy-stage.showing) .stop-speak-btn { top: calc(max(10px, env(safe-area-inset-top)) + 202px); }
.visual-panel { background: #fff; color: #173b44; border-radius: 24px; padding-top: 96px; padding-inline: 18px; padding-bottom: 16px; margin: 4px 4px 10px; text-align: left; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25); }
.visual-panel h2 { margin: 0 0 8px; font-size: 24px; }
.visual-panel h2:focus-visible { outline: 3px solid #007f87; outline-offset: 2px; }
#visual-picture { width: 100%; height: auto; display: block; }
#visual-panel #frog { transform-box: fill-box; transform-origin: center; }
#visual-panel #frog.jumping { animation: visualJump .7s ease; }
@keyframes visualJump { 50% { transform: translateY(-55px) rotate(-5deg); } }
#visual-panel #legs.selected { stroke: #e47800; stroke-width: 6; }
.visual-credit { display: block; font-size: 14px; color: #47646b; margin-top: 6px; }
.visual-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0 4px; }
.visual-actions .btn { min-height: 48px; }
@media (prefers-reduced-motion: reduce) {
  #buddy-stage .comp-window { transition: none; }
  #visual-panel #frog.jumping { animation: none; }
}
body.reduced-motion #buddy-stage .comp-window { transition: none; }
body.reduced-motion #visual-panel #frog.jumping { animation: none; }

/* ================= v10: media search screen ==========================
   The <sunny-media-panel> host carries no styling of its own — its
   shadow card matches .visual-panel (white, 24px radius, 96px top
   clearance under the corner Sunny) and the mascot shrink is the
   SAME #buddy-stage.showing rule as the frog panel: one mechanism,
   two surfaces, never open at once (app.js coordinator). */
#sunny-media { display: block; }

/* ================= v25: Homework Chalkboard ==========================
   West (2026-11-11): during homework the visual is a classroom
   with a CHALKBOARD — "That's what teachers use in school" — the
   problem pinned at the top, worked steps written in chalk as
   Sunny tutors. The panel matches .visual-panel's frame (white
   card, 96px top clearance under the corner Sunny from the SAME
   #buddy-stage.showing shrink). The room reuses the house
   classroom palette (classroom_day: wall #f7e3c2, floor #d9a066,
   wood #a9805a); the board itself is a dark-green chalkboard in a
   wooden frame. Chalk text uses SYSTEM handwriting fonts only —
   no external font downloads, ever. Steps start unwritten
   (opacity 0) and the .shown class writes them on with a gentle
   fade/slide; .calm (bedtime mode OR the child's reduced-motion
   setting — the host applies the class) and the reduced-motion
   media queries remove the transition entirely: steps simply
   appear. */
.board-panel { background: #fff; color: #173b44; border-radius: 24px; padding-top: 96px; padding-inline: 18px; padding-bottom: 16px; margin: 4px 4px 10px; text-align: left; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25); }
.board-panel h2 { margin: 0 0 8px; font-size: 24px; }
.board-panel h2:focus-visible { outline: 3px solid #007f87; outline-offset: 2px; }
.board-room { position: relative; border-radius: 14px; overflow: hidden; background: linear-gradient(180deg, #f7e3c2 0%, #f7e3c2 68%, #d9a066 68%, #d9a066 100%); padding: 20px 14px 24px; box-shadow: inset 0 -26px 30px rgba(122, 74, 33, 0.18), inset 0 14px 22px rgba(255, 255, 255, 0.30); }
.board-room-art { position: absolute; inset: 0; width: 100%; height: 100%; }
.chalkboard { position: relative; max-width: 520px; margin: 26px auto 4px; min-height: 198px; padding: 16px 20px 26px; background: linear-gradient(156deg, rgba(255, 255, 255, 0.13) 0%, rgba(255, 255, 255, 0.04) 34%, rgba(255, 255, 255, 0) 56%), linear-gradient(180deg, #35543f 0%, #2c4a38 100%); border: 10px solid #8a5a2b; border-radius: 6px; box-shadow: inset 0 0 0 3px #6e4520, inset 0 0 44px rgba(0, 0, 0, 0.35), inset 0 2px 3px rgba(255, 255, 255, 0.20), 0 12px 24px rgba(59, 38, 16, 0.32); outline: 2px solid rgba(110, 69, 32, 0.55); outline-offset: -13px; }
.board-problem { font-family: 'Segoe Print', 'Bradley Hand', 'Comic Sans MS', 'Chalkboard SE', cursive; color: #fdfdf4; font-size: clamp(22px, 5.6vw, 32px); line-height: 1.25; text-align: center; text-shadow: 0 0 2px rgba(255, 255, 255, 0.55); padding-bottom: 10px; margin-bottom: 12px; border-bottom: 2px dashed rgba(255, 255, 255, 0.45); }
.board-steps { list-style: none; margin: 0; padding: 0; }
.board-step { font-family: 'Segoe Print', 'Bradley Hand', 'Comic Sans MS', 'Chalkboard SE', cursive; color: #f2f7e9; font-size: clamp(17px, 4.4vw, 24px); line-height: 1.35; margin: 7px 0; opacity: 0; transform: translateY(5px); transition: opacity 0.45s ease, transform 0.45s ease; text-shadow: 0 0 2px rgba(255, 255, 255, 0.4); }
.board-step:nth-child(3n+2) { color: #ffe9a8; }
.board-step:nth-child(3n) { color: #cfe8ff; }
.board-step.shown { opacity: 1; transform: none; }
.board-panel.calm .board-step { transition: none; }
.board-tray { position: absolute; left: 14px; right: 14px; bottom: 7px; display: flex; justify-content: space-between; align-items: center; }
.board-chalk { width: 34px; height: 5px; border-radius: 3px; background: #f5f5ef; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); }
.board-eraser { width: 32px; height: 11px; border-radius: 3px; background: linear-gradient(180deg, #7a4a21 0 55%, #4c4c4c 55% 100%); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); }
.board-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0 4px; }
.board-actions .btn { min-height: 48px; }
@media (prefers-reduced-motion: reduce) {
  .board-step { transition: none; }
}
body.reduced-motion .board-step { transition: none; }

/* ================= v15: Liquid Glass site reskin =====================
   West approved the look on 2026-11-11 from the preview scene: the
   kid screen's frosted glass, extended across the WHOLE site —
   landing/auth, onboarding, the parent dashboard and every parent
   tab (Cartoons review included), the owner dashboard, and the kid
   screens outside the full-bleed stage. Frosted translucent surfaces
   (blur 18px, saturate 1.7), a bright rim along the top edge, white
   hairline borders, soft blue-grey shadows, over the Sunny sky with
   soft colour blobs behind the glass.

   Presentation ONLY — no markup, behaviour or geometry changes: the
   kid top-bar metrics, the corner-Sunny geometry and every
   test-parsed value above are untouched, and the kid-stage
   jewel-tinted glass rules keep their higher specificity (they are
   NOT overridden here). Glass goes on bars, cards, panels, sheets
   and controls; surfaces that protect readability stay solid or
   near-solid: the privacy pages' cards (below), the consent docbox,
   book pages, error/notice/warn boxes, the heard-you echo chip and
   the cartoon caption bar are deliberately left as they were.
   Fallbacks: where backdrop-filter is unsupported the surfaces go
   near-opaque; under prefers-reduced-transparency the blur, the
   blobs and the translucency all switch off to solid fills. The
   glass adds no motion of its own, so reduced-motion needs nothing
   new here. */

/* --- the sky's colour blobs (static; fixed behind everything) --- */
body::before, body::after {
  content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
}
body::before {
  background: radial-gradient(circle 170px at 93% -4%, rgba(255, 214, 110, 0.8), rgba(255, 214, 110, 0) 70%);
}
body::after {
  background:
    radial-gradient(circle 135px at -3% 60%, rgba(143, 227, 192, 0.55), rgba(143, 227, 192, 0) 70%),
    radial-gradient(circle 110px at 90% 103%, rgba(255, 179, 200, 0.5), rgba(255, 179, 200, 0) 70%);
}
body.kid-stage::before, body.kid-stage::after { display: none; }
#app { position: relative; z-index: 1; }

/* --- top bar: the preview's floating glass pill --- */
#topbar {
  top: 8px; margin: 10px 12px 0; padding: 10px 16px; border-radius: 999px;
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 10px 26px rgba(31, 68, 120, 0.16);
}
.brand { color: var(--ink); }

/* --- cards and panels: the core glass surface --- */
.card {
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border); border-radius: var(--glass-radius);
  box-shadow: var(--glass-shadow);
}
.hero {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.66), rgba(243, 238, 255, 0.34) 60%, rgba(255, 233, 228, 0.34));
}
/* Long-form reading stays near-solid: the privacy pages' cards. */
#view-privacy .card, #view-privacy-kids .card {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.9));
}
.kidcard {
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border); border-radius: 20px;
  box-shadow: var(--glass-shadow);
}
.act-box {
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
}
/* Inner rows ride lighter glass, like the preview's dashboard rows. */
.step-row {
  background: var(--row-bg); border: 1px solid var(--row-border);
  -webkit-backdrop-filter: blur(12px) saturate(1.5); backdrop-filter: blur(12px) saturate(1.5);
}
.step-row.done { background: rgba(231, 245, 244, 0.55); border-color: rgba(14, 124, 123, 0.45); }
.allowances {
  background: rgba(184, 169, 232, 0.28); border: 1px solid var(--row-border);
  -webkit-backdrop-filter: blur(12px) saturate(1.5); backdrop-filter: blur(12px) saturate(1.5);
}
.act-toggles { border-color: rgba(255, 255, 255, 0.6); background: rgba(255, 255, 255, 0.22); }
table.kv td { border-bottom-color: rgba(23, 51, 77, 0.14); }
.drawing-thumb img { border-color: rgba(255, 255, 255, 0.7); }

/* --- buttons: glass ghost, sunny primary (the preview's CTA) --- */
.btn {
  background: rgba(255, 255, 255, 0.32); border: 1px solid rgba(255, 255, 255, 0.7); color: var(--ink);
  -webkit-backdrop-filter: blur(10px) saturate(1.5); backdrop-filter: blur(10px) saturate(1.5);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75), 0 4px 14px rgba(31, 68, 120, 0.12);
}
.btn.primary {
  background: var(--sun-bg); color: #fff; border-color: rgba(255, 255, 255, 0.85);
  box-shadow: var(--sun-shadow);
}
.btn.coral {
  background: linear-gradient(180deg, #FF8A75, #FF7A66); color: #fff; border-color: rgba(255, 255, 255, 0.8);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.65), 0 8px 20px rgba(255, 122, 102, 0.32);
}
.btn.lav {
  background: linear-gradient(180deg, #CDBFF5, #B8A9E8); color: #2E2260; border-color: rgba(255, 255, 255, 0.8);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7), 0 8px 20px rgba(108, 91, 181, 0.28);
}
.btn.danger { background: rgba(255, 255, 255, 0.4); color: var(--coral-dark); border-color: rgba(217, 79, 61, 0.55); }

/* --- tabs: glass pills; the active tab is the sunny pill --- */
.tabs .tab {
  background: rgba(255, 255, 255, 0.32); border: 1px solid rgba(255, 255, 255, 0.65); color: var(--muted);
  -webkit-backdrop-filter: blur(10px) saturate(1.5); backdrop-filter: blur(10px) saturate(1.5);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.tabs .tab.active {
  background: var(--sun-bg); color: #fff; border-color: rgba(255, 255, 255, 0.85);
  box-shadow: var(--sun-shadow);
}
.kidtabs .tab.active {
  background: linear-gradient(180deg, #8B76D6, #6C5BB5); border-color: rgba(255, 255, 255, 0.8);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 8px 20px rgba(108, 91, 181, 0.3);
}

/* --- form fields: frosted wells on the glass --- */
input[type="text"], input[type="email"], input[type="password"], input[type="time"],
input[type="number"], select, textarea {
  background: rgba(255, 255, 255, 0.55); border: 2px solid rgba(255, 255, 255, 0.8);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 4px rgba(31, 68, 120, 0.08);
}
.check input { accent-color: #E8890C; }

/* --- pills and meters --- */
.pill { background: rgba(255, 255, 255, 0.45); border: 1px solid rgba(255, 255, 255, 0.7); color: var(--ink); }
.pill.teal { background: rgba(231, 245, 244, 0.6); color: var(--teal-dark); }
.pill.coral { background: rgba(255, 233, 228, 0.6); color: var(--coral-dark); }
.meter { background: rgba(255, 255, 255, 0.45); border: 1px solid rgba(255, 255, 255, 0.55); }

/* --- kid menu (light screens; the stage keeps its sapphire glass) --- */
.kid-menu {
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.6));
  border: 1px solid rgba(255, 255, 255, 0.8);
  -webkit-backdrop-filter: blur(16px) saturate(1.5); backdrop-filter: blur(16px) saturate(1.5);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 12px 32px rgba(31, 68, 120, 0.22);
}
.kmenu-item { background: rgba(255, 255, 255, 0.42); border: 1px solid rgba(255, 255, 255, 0.65); }

/* --- listening indicator (light screens): the stage's emerald glass --- */
.mic {
  background: linear-gradient(160deg, rgba(25, 175, 118, 0.88), rgba(8, 115, 72, 0.88));
  border: 1.5px solid rgba(110, 231, 183, 0.6);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 4px 14px rgba(16, 160, 100, 0.3);
}

/* --- chat bubbles on light screens (stage variants unchanged) --- */
.bubble.buddy {
  background: rgba(255, 255, 255, 0.55); border: 1.5px solid rgba(255, 255, 255, 0.75);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.bubble.kid {
  background: rgba(184, 169, 232, 0.32);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}

/* --- Show Me panel: near-solid glass (reading surface) --- */
.visual-panel {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.93), rgba(255, 255, 255, 0.84));
  border: 1px solid rgba(255, 255, 255, 0.7);
  -webkit-backdrop-filter: blur(14px) saturate(1.5); backdrop-filter: blur(14px) saturate(1.5);
}

/* --- sheets, wardrobe, toast --- */
.modal {
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.92), rgba(255, 255, 255, 0.78));
  -webkit-backdrop-filter: blur(20px) saturate(1.6); backdrop-filter: blur(20px) saturate(1.6);
  border-top: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
.wardrobe button {
  background: rgba(255, 255, 255, 0.4); border: 1px solid rgba(255, 255, 255, 0.7);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.wardrobe button.sel { background: rgba(184, 169, 232, 0.5); border-color: rgba(108, 91, 181, 0.6); color: #2E2260; }
.rhythm-pad { background: rgba(255, 255, 255, 0.5); border-color: rgba(255, 255, 255, 0.75); }
.word-tile { background: rgba(243, 234, 253, 0.55); }
.toast {
  background: rgba(23, 51, 77, 0.85); border: 1px solid rgba(255, 255, 255, 0.25);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}

/* --- fallback: no backdrop-filter support — surfaces go near-opaque --- */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .card, .kidcard, .act-box { background: linear-gradient(135deg, rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0.86)); }
  #topbar { background: rgba(255, 255, 255, 0.95); }
  .modal { background: rgba(255, 255, 255, 0.98); }
  .kid-menu { background: rgba(255, 255, 255, 0.97); }
  .visual-panel { background: #FFFFFF; }
  .btn, .tabs .tab { background: rgba(255, 255, 255, 0.9); }
  .btn.primary, .tabs .tab.active { background: var(--sun-bg); }
}

/* --- fallback: reduced transparency — solid fills, no blur, no blobs --- */
@media (prefers-reduced-transparency: reduce) {
  body::before, body::after { display: none; }
  body { background: linear-gradient(180deg, #DFF2FF 0%, #EDF8FF 45%, #FFF6DE 100%); }
  #topbar, .card, .kidcard, .act-box, .modal, .kid-menu, .btn, .tabs .tab, .step-row,
  .visual-panel, .bubble.buddy, .bubble.kid, .toast,
  input[type="text"], input[type="email"], input[type="password"], input[type="time"],
  input[type="number"], select, textarea {
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  #topbar, .card, .kidcard, .act-box, .modal, .kid-menu, .visual-panel, .step-row { background: #FFFFFF; }
  .hero { background: linear-gradient(135deg, #FFFFFF 0%, #F3EEFF 60%, #FFE9E4 100%); }
  .btn, .tabs .tab { background: #FFFFFF; }
  .btn.primary, .tabs .tab.active { background: var(--sun-bg); }
  .btn.coral { background: linear-gradient(180deg, #FF8A75, #FF7A66); }
  .btn.lav { background: linear-gradient(180deg, #CDBFF5, #B8A9E8); }
  .kidtabs .tab.active { background: linear-gradient(180deg, #8B76D6, #6C5BB5); }
  .step-row.done { background: #E7F5F4; }
  input[type="text"], input[type="email"], input[type="password"], input[type="time"],
  input[type="number"], select, textarea { background: #FFFFFF; border-color: #D8E4EC; }
  .bubble.buddy { background: #FFFFFF; }
  .bubble.kid { background: #F3EEFF; }
  .toast { background: var(--ink); }
}
