/* ============================================================================
   PLAY BREAK — COMPONENTS
   Every screen is built from the same small set of pieces, so the control bar,
   the cards and the panels are literally the same code everywhere rather than
   five lookalike copies. Layout is landscape-tablet first (see the media
   queries at the foot for portrait and phone).
   ============================================================================ */

*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;}
body{
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-ui);
  font-size:var(--t-md);
  line-height:var(--lh);
  -webkit-tap-highlight-color:transparent;
}

/* Focus must be obvious and must never be removed. A child driving this with a
   switch or a keyboard has nothing else to tell them where they are. */
:focus{outline:none;}
:focus-visible{
  outline:4px solid var(--focus);
  outline-offset:3px;
  border-radius:var(--r-sm);
}

.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* ---------- app frame ---------- */
/* Every pixel not spent on chrome goes to the scene. */
.app{
  /* No width cap: the scene should own the screen. The old 1200px cap left
     huge cream margins and made the play area feel like a postage stamp. */
  max-width:none;width:100%;margin:0 auto;padding:var(--sp-1);
  display:flex;flex-direction:column;gap:var(--sp-1);
  min-height:100%;
}

/* ---------- the control bar: identical position on every screen ---------- */
/* The site-wide backbtn.js drops a fixed "← Games" pill into the top-left
   corner of EVERY game. Our own Home button lives there too, so the bar is
   padded to leave that corner alone — two stacked exits in one corner is
   confusing, and an overlapping one is unusable. */
.appbar{
  display:flex;align-items:center;gap:var(--sp-1);
  background:var(--surface);
  border-radius:var(--r-lg);
  padding:var(--sp-1) var(--sp-2) var(--sp-1) 100px;
  box-shadow:var(--lip-2) var(--lip),var(--shadow-1);
  flex-wrap:wrap;
}
.appbar .spacer{flex:1 1 auto;}
.appbar-title{
  font-size:var(--t-lg);font-weight:700;color:var(--brand-deep);
  padding:0 var(--sp-2);white-space:nowrap;
}

.ctrl{
  display:inline-flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;
  min-width:var(--tap-ctrl);min-height:var(--tap-ctrl);
  padding:var(--sp-1) var(--sp-2);
  /* cream, softly rounded, gentle depth — not a rigid bordered card */
  background:var(--surface-2);
  border:2px solid transparent;
  border-radius:var(--r-lg);
  box-shadow:var(--lip-1) var(--lip);
  font-family:inherit;font-size:var(--t-xs);font-weight:700;
  color:var(--ink);
  cursor:pointer;
  transition:transform var(--dur-fast) var(--ease),background var(--dur-fast) linear;
}
.ctrl:hover{background:var(--brand-tint);}
.ctrl:active{transform:translateY(3px);box-shadow:none;}
.ctrl[aria-pressed="true"],.ctrl.is-on{border-color:var(--brand);background:var(--brand-tint);}
.ctrl .ico{width:26px;height:26px;display:block;}
.ctrl .lab{line-height:1;letter-spacing:.02em;}
.ctrl.home{border-color:var(--brand);}

/* ---------- stage ---------- */
/* THE PLAY BUTTONS MUST NEVER FALL BELOW THE FOLD — a child will not scroll to
   find them. A vh-based guess cannot work here because the toolbar wraps to a
   different height in every activity, so instead the activity screen is a fixed
   100vh flex column: bar, instruction and toolbar take the height they need,
   and the stage gets whatever is left. Scene.fit() then measures that box and
   sizes the canvas to the largest 16:9 rectangle inside it. */
.stage{
  position:relative;
  background:var(--surface);
  border-radius:var(--r-lg);
  padding:4px;
  box-shadow:var(--lip-3) var(--lip),var(--shadow-2);
  width:100%;
  display:flex;align-items:center;justify-content:center;
}
canvas.scene:focus-visible{
  outline:4px solid var(--focus); outline-offset:-4px;
}
canvas.scene{
  display:block;width:100%;aspect-ratio:16/9;
  border-radius:var(--r-lg);
  touch-action:none;
  background:var(--sky-2);
}
.app.is-activity{
  height:100vh;height:100dvh;
  max-height:100vh;max-height:100dvh;
  overflow:hidden;
}
.app.is-activity > .appbar,
.app.is-activity > .saybar,
.app.is-activity > .toolbar,
.app.is-activity > #bannerslot{ flex:0 0 auto; }
/* the stage takes everything the bar and tray do not need */
.app.is-activity > .stage{
  flex:1 1 auto;
  min-height:200px;      /* below this the scene is useless; let the page scroll */
  align-self:center;     /* JS sets the exact width; this keeps it centred */
}
/* JS sets explicit pixel width/height on the canvas in activity mode, so the
   aspect-ratio rule must not fight it. */
.app.is-activity canvas.scene{ width:auto;height:auto;aspect-ratio:auto; }

/* ---------- focusable hit zones layered over the canvas ----------
   Invisible until hovered/focused so they never compete with the artwork, but
   always present in the accessibility tree and always keyboard reachable. */
.stageoverlays{position:absolute;inset:var(--sp-2);pointer-events:none;}
.zone{
  position:absolute; margin:0; padding:0; border:none; cursor:pointer;
  background:transparent; border-radius:var(--r-md);
  pointer-events:auto;
  transition:background var(--dur-fast) linear, box-shadow var(--dur-fast) linear;
}
.zone:hover{ background:rgba(255,255,255,.22); box-shadow:inset 0 0 0 3px rgba(255,255,255,.5); }
.zone:focus-visible{ outline:4px solid var(--focus); outline-offset:2px; background:rgba(255,255,255,.18); }
.zone[disabled]{ pointer-events:none; }

/* ---------- in-stage status pill (bubble count etc.) ---------- */
/* Top RIGHT: the top left is now the progress trail's and the top centre is the
   robot's, so the badge was landing on the lamps. */
.stagebadge{
  position:absolute; top:calc(var(--sp-2) + 10px); right:calc(var(--sp-2) + 12px);
  background:rgba(255,255,255,.92);
  border-radius:var(--r-pill);
  padding:6px var(--sp-3);
  font-size:var(--t-sm); font-weight:800; color:var(--ink);
  box-shadow:var(--shadow-1);
  pointer-events:none;
}

/* ---------- instruction line: short, concrete, never inside artwork ---------- */
.saybar{
  display:flex;align-items:center;gap:var(--sp-3);
  background:var(--surface);
  border-radius:var(--r-lg);
  padding:var(--sp-3) var(--sp-4);
  box-shadow:var(--lip-2) var(--lip),var(--shadow-1);
}
.saybar .txt{flex:1;font-size:var(--t-lg);font-weight:700;line-height:var(--lh-tight);}

/* Floats over the TOP CENTRE of the scene, so it costs the stage no height at
   all. pointer-events:none so it can never block a drag underneath it.
   Centred rather than pinned to the left edge: hard against the corner the
   robot read as parked in the margin instead of talking to the child, and on a
   wide screen it sat a long way from where the eye actually is.
   Each activity's progress trail is positioned to clear it — see drawTrail(). */
/* Specificity, not source order: .saybar--bubble is declared below this and
   sets position:relative, which at equal specificity would win and drop the
   overlay back into the flex flow — where it slid off the right edge. */
.saybar.saybar--over{
  position:absolute; left:50%; transform:translateX(-50%); top:14px; z-index:4;
  pointer-events:none;
  max-width:min(46%, 520px);
  box-shadow:var(--shadow-2);
  margin:0;
  /* room for the robot's antenna — it was being clipped by the bubble edge */
  padding:10px 22px 10px 12px;
  gap:var(--sp-2);
  overflow:visible;
}
.saybar.saybar--over .robot{ width:52px; height:52px; }
.saybar.saybar--over::after{display:none;}

/* A compact speech bubble that hugs its text instead of spanning the width, so
   the stage keeps the height. The tail points down-left at the robot. */
.saybar--bubble{
  align-self:flex-start;
  width:auto; max-width:min(100%, 760px);
  padding:var(--sp-2) var(--sp-4) var(--sp-2) var(--sp-2);
  border-radius:var(--r-xl);
  position:relative;
  margin-bottom:calc(var(--sp-2) + 4px);
}
.saybar--bubble .txt{font-size:var(--t-md);}
.saybar--bubble{padding:var(--sp-1) var(--sp-3) var(--sp-1) var(--sp-1);margin-bottom:var(--sp-2);}
.saybar--bubble::after{
  content:''; position:absolute; left:52px; bottom:-9px;
  width:18px; height:18px; background:var(--surface);
  border-bottom-right-radius:4px;
  transform:rotate(45deg) skew(6deg,6deg);
  box-shadow:3px 3px 0 var(--lip);
}

/* ---------- the guide robot ---------- */
.robot{width:88px;height:88px;flex:0 0 auto;display:block;object-fit:contain;
  /* the pose files are <img> now — stop a drag from lifting them off the page */
  user-select:none;-webkit-user-drag:none;}
.robot--lg{width:150px;height:150px;}
.robot--sm{width:62px;height:62px;}
/* a slow, small bob — never a bounce, and switched off entirely in Still */
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
:root[data-motion="full"] .robot--anim{animation:bob 3.6s ease-in-out infinite;}

/* ---------- activity cards (main menu) ---------- */
/* ▶ THE PLAY BOXES ARE SIZED TO THE SCREEN, not to a fixed 200px.
   They were capped three ways at once - a 200px card floor, a 150px ceiling on
   the picture inside it, and a 210px column - so on a 1440x900 laptop five
   cards used 248px of an 900px page and the rest sat empty. Each cap is now a
   clamp() that grows with the viewport and still collapses on a phone.

   ▶ ONLY THE HEIGHT GROWS. Raising the 210px column floor was tried and is
   wrong: at 1280 and 1024 it wraps five cards onto TWO rows and the menu starts
   to scroll, and a child will not scroll to find a game. Keep the floor low
   enough that all five stay on one row on a laptop. */
.cardgrid{
  display:grid;gap:var(--sp-4);
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
}
.actcard{
  display:flex;flex-direction:column;align-items:stretch;gap:0;
  background:var(--surface);
  border:4px solid var(--line);
  border-radius:var(--r-xl);
  box-shadow:var(--lip-3) var(--lip),var(--shadow-1);
  padding:0;overflow:hidden;cursor:pointer;
  font-family:inherit;color:inherit;text-align:center;
  min-height:clamp(190px, 42vh, 400px);
  transition:transform var(--dur) var(--ease),border-color var(--dur-fast) linear;
}
/* The menu is a full-height column, so the row of play boxes takes the space
   the old layout left empty below it and sits centred rather than jammed under
   the robot. Activity mode has its own 100vh rule further up and is untouched. */
.app:not(.is-activity){min-height:100vh;min-height:100dvh;}
.app:not(.is-activity) .cardgrid{flex:1 1 auto;align-content:center;}
/* Below ~1100px the five boxes no longer fit one row, so they wrap to two and a
   tall card starts pushing the menu off the bottom. A child will not scroll to
   find a game, so the cards give height back exactly where that happens. */
@media (max-width:1100px) and (min-width:761px){
  .actcard{min-height:clamp(180px, 26vh, 280px);}
  .actcard .art img{max-height:clamp(130px, 18vh, 200px);}
}
.actcard:hover{border-color:var(--brand);}
.actcard:active{transform:translateY(4px);box-shadow:var(--lip-1) var(--lip);}
.actcard .art{
  flex:1;display:flex;align-items:center;justify-content:center;
  background:var(--brand-tint);padding:var(--sp-4);
}
.actcard .art svg,
.actcard .art img{width:100%;height:auto;max-height:clamp(140px, 31vh, 310px);object-fit:contain;}
.actcard .name{
  padding:var(--sp-3);font-size:var(--t-xl);font-weight:800;
  color:#fff;background:var(--brand);
}
.actcard[data-tone="trains"]  .name{background:var(--toy-red);}
.actcard[data-tone="blocks"]  .name{background:var(--toy-green);}
.actcard[data-tone="bubbles"] .name{background:var(--toy-blue);}
.actcard[data-tone="cars"]    .name{background:var(--toy-amber);}
.actcard[data-tone="spinners"].name,
.actcard[data-tone="spinners"] .name{background:var(--toy-purple);}

/* ---------- activity controls: the keyboard path to everything on canvas ---------- */
.toolbar{
  /* centred, not left-anchored: on a wide screen the groups used to end about
     three-quarters across and leave a dead strip on the right, which read as
     "everything is too far to the left" (17 Aug) */
  display:flex;flex-wrap:wrap;gap:var(--sp-1);justify-content:center;
  background:var(--surface);
  border-radius:var(--r-lg);
  padding:var(--sp-1) var(--sp-2);
  box-shadow:var(--lip-2) var(--lip),var(--shadow-1);
}
.toolgroup{
  display:flex;flex-wrap:wrap;gap:var(--sp-1);align-items:center;
  padding:0 var(--sp-2);border-radius:var(--r-md);
}
.toolgroup + .toolgroup{border-left:3px solid var(--line);}
.toolgroup > .gl{
  font-size:var(--t-xs);font-weight:800;text-transform:uppercase;
  letter-spacing:.06em;color:var(--ink-soft);width:100%;
}
.tool{
  display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  min-width:var(--tap-min);min-height:var(--tap-min);
  padding:var(--sp-1) var(--sp-2);
  background:var(--surface-2);
  border:2px solid transparent;
  border-radius:var(--r-lg);
  box-shadow:var(--lip-1) var(--lip);
  font-family:inherit;font-size:var(--t-sm);font-weight:700;color:var(--ink);
  cursor:pointer;
  transition:transform var(--dur-fast) var(--ease);
}
.tool .ti{width:28px;height:28px;display:block;color:var(--ink);}
.tool[aria-pressed="true"] .ti{color:var(--brand-deep);}
.tool:hover{background:var(--brand-tint);}
.tool:active{transform:translateY(3px);box-shadow:none;}
.tool[aria-pressed="true"]{border-color:var(--brand);background:var(--brand-tint);box-shadow:var(--lip-1) var(--lip),0 0 0 1px var(--brand) inset;}
.tool[disabled]{opacity:.42;cursor:default;box-shadow:none;}
.tool .swatch{width:26px;height:26px;border-radius:var(--r-pill);border:2px solid rgba(0,0,0,.18);}

/* Illustrated stop buttons: a picture of the PLACE, not a line glyph. Sized
   well above the 28px icons so a child can tell the wash from the station at a
   glance, and `contain` so a wide prop (the bridge) and a tall one (the signal)
   both sit inside the same square without distorting. */
.tool .stopart{
  width:52px;height:52px;display:block;object-fit:contain;
  filter:drop-shadow(0 2px 2px rgba(0,0,0,.16));
  transition:transform var(--dur-fast) var(--ease);
}
.tool[aria-pressed="true"] .stopart{transform:scale(1.08);}
/* ============================================================================
   SHORT SCREENS: GIVE THE HEIGHT BACK TO THE GAME
   Measured on an iPad in landscape (1024x768): the app bar and the tool tray
   together took 275px - 36% of the whole screen - leaving the scene only
   826x464. In portrait the tray wrapped to 289px on its own. The child is
   looking at the picture, not at the buttons, so on any short viewport every
   control gives back what it can.

   ▶ NOTHING HERE GOES BELOW 44px. That is the accessibility floor for a tap
   target and this game is played by children with motor difficulties; the
   defaults (56/64) are generous on purpose and only come down to 46/48.
   ▶ The illustrated STOP buttons shrink least. They are pictures of places and
   are how a pre-reader chooses where to go, so they stay the biggest thing in
   the tray.
   ============================================================================ */
/* Height covers landscape tablets and laptops; width covers portrait ones,
   where the tray wraps to three rows and costs even more. iPad Pro is 1024
   tall and iPad portrait is 820 wide, so both need naming. */
@media (max-height:1050px), (max-width:1100px){
  .app.is-activity{ --tap-min:46px; --tap-ctrl:48px; }

  /* app bar: Home / Sound / Motion / Timer / Settings */
  .app.is-activity .appbar{ padding:2px var(--sp-1) 2px 92px; gap:2px; }
  .app.is-activity .ctrl{ padding:2px var(--sp-1); gap:1px; font-size:11px; }
  .app.is-activity .ctrl .ico{ width:21px;height:21px; }

  /* the choice trays: Choose a car, Choose a color */
  .app.is-activity .toolbar{ padding:2px var(--sp-1); gap:2px; }
  .app.is-activity .toolgroup{ padding:0 var(--sp-1); gap:2px; }
  .app.is-activity .toolgroup > .gl{ font-size:10px; letter-spacing:.04em; }
  .app.is-activity .tool{ padding:2px var(--sp-1); gap:1px; font-size:11px; }
  .app.is-activity .tool .ti{ width:22px;height:22px; }
  .app.is-activity .tool .swatch{ width:21px;height:21px; }
  .app.is-activity .tool .stopart{ width:40px;height:40px; }
}
@media (max-height:860px), (max-width:900px){
  .app.is-activity .tool .stopart{ width:34px;height:34px; }
  .app.is-activity .toolgroup > .gl{ font-size:9px; }
}

@media (prefers-reduced-motion:reduce){
  .tool .stopart{transition:none;}
  .tool[aria-pressed="true"] .stopart{transform:none;}
}

/* ---------- timer ---------- */
.timerwrap{display:flex;align-items:center;gap:var(--sp-2);}
.timerring{width:var(--tap-ctrl);height:var(--tap-ctrl);display:block;}
.timerring .track{fill:none;stroke:var(--surface-2);stroke-width:9;}
.timerring .fill{
  fill:none;stroke:var(--go);stroke-width:9;stroke-linecap:round;
  transform:rotate(-90deg);transform-origin:50% 50%;
  transition:stroke-dashoffset 1s linear,stroke var(--dur) linear;
}
.timerring.warn .fill{stroke:var(--warn);}
.timerring.last .fill{stroke:var(--alert);}
.timerring .num{
  font-family:var(--font-ui);font-size:15px;font-weight:800;
  fill:var(--ink);text-anchor:middle;dominant-baseline:central;
}

/* A warning appears in place and stays. It never covers the activity and never
   interrupts what the child is doing. */
.banner{
  display:flex;align-items:center;gap:var(--sp-3);
  background:var(--surface);
  border:3px solid var(--warn);
  border-left-width:10px;
  border-radius:var(--r-lg);
  padding:var(--sp-3) var(--sp-4);
  font-size:var(--t-lg);font-weight:800;color:var(--ink);
  box-shadow:var(--shadow-1);
}
.banner.last{border-color:var(--alert);}

/* ---------- panels / dialogs ---------- */
dialog.panel{
  border:none;padding:0;background:transparent;max-width:min(760px,94vw);width:100%;
  /* The reset at the top of this file sets *{margin:0}, which silently kills the
     margin:auto a native <dialog> relies on to centre itself. Put it back. */
  margin:auto;
}
dialog.panel::backdrop{background:rgba(43,39,64,.45);}
.panel-in{
  background:var(--surface);
  border-radius:var(--r-xl);
  box-shadow:var(--shadow-3);
  padding:var(--sp-5);
  max-height:88vh;overflow:auto;
}
.panel-head{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-4);}
.panel-head h2{font-size:var(--t-xl);color:var(--brand-deep);flex:1;}

.optset{margin-bottom:var(--sp-5);}
.optset > .legend{
  font-size:var(--t-xs);font-weight:800;text-transform:uppercase;letter-spacing:.08em;
  color:var(--brand-deep);margin-bottom:var(--sp-2);display:block;
}
.optrow{display:flex;gap:var(--sp-2);flex-wrap:wrap;}
.opt{
  flex:1 1 120px;
  min-height:var(--tap-min);
  padding:var(--sp-3);
  background:var(--surface-2);
  border:3px solid var(--line);
  border-radius:var(--r-md);
  font-family:inherit;font-size:var(--t-md);font-weight:700;color:var(--ink);
  cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  transition:transform var(--dur-fast) var(--ease);
}
.opt .oi{font-size:22px;line-height:1;}
.opt small{font-size:var(--t-xs);font-weight:600;color:var(--ink-soft);}
.opt[aria-checked="true"]{
  background:var(--brand);border-color:var(--brand);color:#fff;
}
.opt[aria-checked="true"] small{color:rgba(255,255,255,.85);}
.opt:active{transform:translateY(2px);}

/* ---------- big friendly buttons ---------- */
.big{
  min-height:72px;
  padding:var(--sp-4) var(--sp-6);
  border:none;border-radius:var(--r-lg);
  font-family:inherit;font-size:var(--t-xl);font-weight:800;
  color:#fff;background:var(--brand);
  box-shadow:var(--lip-3) var(--brand-deep);
  cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-3);
  transition:transform var(--dur-fast) var(--ease);
}
.big:active{transform:translateY(4px);box-shadow:var(--lip-1) var(--brand-deep);}
.big.go{background:var(--go);box-shadow:var(--lip-3) var(--go-deep);}
.big.go:active{box-shadow:var(--lip-1) var(--go-deep);}
.big.quiet{background:var(--surface-2);color:var(--ink);box-shadow:var(--lip-3) var(--lip);}
.big.quiet:active{box-shadow:var(--lip-1) var(--lip);}

/* ---------- finished screen ---------- */
.finish{
  text-align:center;
  background:var(--surface);
  border-radius:var(--r-xl);
  box-shadow:var(--lip-3) var(--lip),var(--shadow-2);
  padding:var(--sp-7) var(--sp-5);
  display:flex;flex-direction:column;align-items:center;gap:var(--sp-4);
}
.finish h2{font-size:var(--t-2xl);color:var(--brand-deep);}
.finish .choices{display:flex;gap:var(--sp-4);flex-wrap:wrap;justify-content:center;}

/* A gentle wave, not a burst. Calm mode gets this; Playful adds drifting shapes
   drawn on the canvas instead of anything that flashes. */
@keyframes wave{0%,100%{transform:rotate(0)}25%{transform:rotate(11deg)}75%{transform:rotate(-7deg)}}
:root:not([data-motion="still"]) .waving{animation:wave 1.6s ease-in-out 2;transform-origin:60% 80%;}

/* ---------- responsive ----------
   Landscape tablet is the primary target and gets the layout above. Portrait
   and phone reflow the bar and shrink the scene, but nothing is ever hidden and
   no tap target drops below --tap-min. */
@media (max-width:900px){
  :root{--t-xl:23px; --t-2xl:30px; --t-3xl:40px;}
  .appbar-title{display:none;}          /* the screen name is in .saybar anyway */
  canvas.scene{aspect-ratio:4/3;}
}
@media (max-width:620px){
  :root{--tap-ctrl:56px;}
  .app{padding:var(--sp-2);gap:var(--sp-2);}
  .ctrl .lab{font-size:10px;}
  .saybar .txt{font-size:var(--t-md);}
  .cardgrid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--sp-3);}
  .actcard{min-height:150px;}
  .actcard .art img{max-height:150px;}
  .actcard .name{font-size:var(--t-md);padding:var(--sp-2);}
  .robot{width:64px;height:64px;}
  .toolgroup + .toolgroup{border-left:none;border-top:3px solid var(--line);padding-top:var(--sp-2);width:100%;}
}
/* Short landscape phones: keep the scene from pushing the toolbar off-screen. */
@media (max-height:520px) and (orientation:landscape){
  canvas.scene{aspect-ratio:auto;height:min(52vh,320px);}
}
