/* ───────────────────────────────────────────────────────────────────────
   play.talk — sticker-book interface
   Words are plump sticker pills on warm cream paper. Colours follow the
   Fitzgerald Key (the AAC convention Grid uses too):
     yellow people · green doing · orange things · blue describing
     pink social · purple questions · red no/stop · grey little words
   ─────────────────────────────────────────────────────────────────────── */

:root {
  --bg: #fff7ec;
  --paper-dot: rgba(124, 94, 42, 0.05);
  --ink: #4a3520;
  --ink-soft: #a08c6c;
  --card: #fffdf7;
  --edge: #f2e7d2;

  --pronoun-b: #ffc81e;  --pronoun-f: #fff3bf;
  --name-b:    #ffc81e;  --name-f:    #fff3bf;
  --verb-b:    #51cf66;  --verb-f:    #d3f9d8;
  --noun-b:    #ff922b;  --noun-f:    #ffe8cc;
  --describe-b:#4dabf7;  --describe-f:#d0ebff;
  --social-b:  #f783ac;  --social-f:  #ffdeeb;
  --question-b:#9775fa;  --question-f:#e5dbff;
  --negation-b:#ff6b6b;  --negation-f:#ffe3e3;
  --little-b:  #cdc1ac;  --little-f:  #f4efe4;
  --unknown-b: #d8cab2;  --unknown-f: #fbf6ec;

  /* the size of everything on the talk screen: bars, badges, chips, cards,
     the sentence box and its buttons all hang off these four, and the
     scale beneath them is a per-device choice in the grown-ups settings
     (html[data-size], below) — 100 is the size the app has always had;
     a phone wants less. The gap and the inset are the spacings that
     follow the scale, so the layout tightens as a whole. */
  --scale: 1;
  --bar-h: calc(clamp(50px, 7.2vh, 66px) * var(--scale));
  --bar-fs: calc(clamp(21px, 3.3vh, 30px) * var(--scale));
  --chip-h: calc(clamp(40px, 5.6vh, 52px) * var(--scale));
  --chip-fs: calc(clamp(17px, 2.6vh, 24px) * var(--scale));
  --gap: calc(10px * var(--scale));
  --inset: calc(12px * var(--scale));
  /* the grip at the right of an old line (below): its width is what the
     fade strip and the time stamp measure themselves against too */
  --grip-w: calc(46px * var(--scale));

  --vvh: 100dvh;
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  position: fixed;
  inset: 0;
  width: 100%;
  overscroll-behavior: none;
  background: var(--bg);
}

body {
  /* The sheet says inline-start and inline-end where a thing sits at a
     side (a badge's corner, a grip's edge, a pushed-aside button), so a
     right-to-left pack (docs/languages-plan.md, phase 6) mirrors the page
     from dir alone; a centred thing (left: 50% and a transform) and a box
     stretched to both edges stay as they are, the same either way. */
  /* the language pack's face (app.js sets --face when the device is in a
     language whose script Baloo 2 has no letters for) */
  font-family: var(--face, "Baloo 2", "Comic Sans MS", system-ui, sans-serif);
  color: var(--ink);
  user-select: none;
  -webkit-user-select: none;
  /* iOS: a held picture must open our menu, not the share/save callout */
  -webkit-touch-callout: none;
}

img {
  -webkit-user-drag: none;
}

/* warm paper with faint polka dots */
#app {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  height: var(--vvh);
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(var(--paper-dot) 1.6px, transparent 1.7px) 0 0 / 26px 26px,
    linear-gradient(180deg, #fffaf1 0%, var(--bg) 28%);
  will-change: transform;
}

/* ── top bar ─────────────────────────────────────────────────────────── */
#topbar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: calc(env(safe-area-inset-top, 0px) + 8px) 16px 4px;
}

#wordmark {
  font-weight: 800;
  /* height-capped on big screens, width-capped on phones, one line always */
  font-size: clamp(15px, min(3vh, 4.8vw), 26px);
  letter-spacing: 0.5px;
  line-height: 1;
  white-space: nowrap;
}
#wordmark span { display: inline-block; }
/* the six-step cycle, begun two steps in so "play" and "talk" wear the very
   colours the home-screen icon gives them (scripts/icon.html draws the icon
   from this cycle); the dot between the words is plain ink, not a step */
#wordmark span:nth-of-type(6n + 5) { color: #ff922b; transform: rotate(-4deg); }
#wordmark span:nth-of-type(6n + 6) { color: #51cf66; transform: rotate(3deg); }
#wordmark span:nth-of-type(6n + 1) { color: #4dabf7; transform: rotate(-2deg); }
#wordmark span:nth-of-type(6n + 2) { color: #f783ac; transform: rotate(4deg); }
#wordmark span:nth-of-type(6n + 3) { color: #9775fa; transform: rotate(-3deg); }
#wordmark span:nth-of-type(6n + 4) { color: #f5b301; transform: rotate(2deg); }
#wordmark i { font-style: normal; color: var(--ink); }

#topbar-left { display: flex; align-items: center; gap: 14px; min-width: 0; }
#wordmark { cursor: pointer; }

#topbar-right { display: flex; align-items: center; gap: 12px; flex: none; }

/* the header's doors: pills the size of the tray's chips, so they stand
   nearer the word bubbles below than the small 34px pills they were
   (Simon's ask, 2026-09-08) — hung off the same variables, so they follow
   the family's size setting and the screen as everything else does */
#talk-btn,
#play-btn {
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  gap: calc(6px * var(--scale));
  height: var(--chip-h);
  padding: 0 calc(14px * var(--scale));
  border-radius: 999px;
  border: 2.5px solid var(--describe-b);
  background: var(--describe-f);
  font-size: var(--chip-fs);
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  box-shadow: 0 2px 0 rgba(96, 70, 28, 0.07);
  cursor: pointer;
}
#talk-btn:active,
#play-btn:active { transform: scale(0.95); }
/* the header doors' bespoke drawn stickers — just the word until drawn */
#talk-btn .pic,
#play-btn .pic { display: none; }
#talk-btn .pic.ready,
#play-btn .pic.ready { display: block; width: calc(var(--chip-h) * 0.6); height: calc(var(--chip-h) * 0.6); }
#talk-btn .pic img,
#play-btn .pic img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
#app:not(.list-mode):not(.game-mode) #talk-btn,
#app.game-mode #play-btn {
  background: var(--describe-b);
  color: #fff;
}
#play-btn[hidden] { display: none; }

/* the flag: the language this device is in, a pill the doors' height
   holding the pack's own drawing of its flag (lib/lang/<tag>/flag.svg);
   a tap moves to the next language the talker speaks */
#lang-btn {
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--chip-h);
  padding: 0 calc(8px * var(--scale));
  border-radius: 999px;
  border: 2.5px solid var(--describe-b);
  background: #fff;
  box-shadow: 0 2px 0 rgba(96, 70, 28, 0.07);
  cursor: pointer;
}
#lang-btn:active { transform: scale(0.95); }
#lang-btn svg { display: block; height: calc(var(--chip-h) * 0.62); width: auto; }
#lang-btn[hidden] { display: none; }
/* a word on its way into the language the device is in: the dashed edge
   the suggestion chips wear, until its translation lands */
#current-line .bar.translating { border-style: dashed; opacity: 0.75; }

/* phones: tighter header so talk, play, the dot and the cog all fit */
@media (max-width: 480px) {
  #topbar { padding-inline: 10px; }
  #topbar-left { gap: 8px; }
  #topbar-right { gap: 8px; }
  #talk-btn,
  #play-btn { padding: 0 calc(10px * var(--scale)); gap: 4px; }
  #talk-btn .pic.ready,
  #play-btn .pic.ready { width: calc(var(--chip-h) * 0.5); height: calc(var(--chip-h) * 0.5); }
}
/* the very narrowest phones need one squeeze more */
@media (max-width: 360px) {
  #topbar { padding-inline: 8px; gap: 8px; }
  #topbar-left { gap: 6px; }
  #wordmark { font-size: 14px; }
  #talk-btn,
  #play-btn { padding: 0 8px; }
  #talk-btn .pic.ready,
  #play-btn .pic.ready { width: calc(var(--chip-h) * 0.45); height: calc(var(--chip-h) * 0.45); }
}
/* a talker that speaks two languages wears the flag as well, and the doors
   say play and talk in its language (spielen and reden run longest): below
   a small tablet's width the wordmark gives way (the talk door does what a
   tap on it did), and the narrowest phones squeeze once more, so the flag
   never slides under the dot and the cog */
@media (max-width: 599px) {
  #topbar:has(#lang-btn:not([hidden])) #wordmark { display: none; }
}
@media (max-width: 360px) {
  #topbar:has(#lang-btn:not([hidden])) #topbar-left { gap: 4px; }
  #topbar:has(#lang-btn:not([hidden])) #lang-btn { padding: 0 4px; }
  #topbar:has(#lang-btn:not([hidden])) #talk-btn,
  #topbar:has(#lang-btn:not([hidden])) #play-btn { padding: 0 6px; }
}

#status-dot {
  width: 11px; height: 11px;
  border-radius: 50%;
  background: #ced4da;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.7);
}
#status-dot.ok   { background: #51cf66; }
#status-dot.warn { background: #ffc81e; }
#status-dot.off  { background: #ced4da; }

#gear {
  border: 0;
  background: none;
  color: var(--ink-soft);
  opacity: 0.55;
  padding: 8px;
  cursor: pointer;
}

/* ── history of spoken lines ─────────────────────────────────────────── */
#history {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  padding: 6px 0 14px;
  scrollbar-width: thin;
  scrollbar-color: #e8dcc4 transparent;
}

.line {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  /* extra headroom so picture badges never get clipped by the row */
  padding: calc(var(--bar-h) * 0.42) 16px 8px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  border-radius: 22px;
}
.line::-webkit-scrollbar { display: none; }

#history .line { cursor: pointer; }
#history .line .bar { box-shadow: 0 2px 0 rgba(96, 70, 28, 0.06); }

/* ── day breaks ──────────────────────────────────────────────────────
   Between the last line of one day and the first of the next: a soft
   rule across the history, the new day's date small at its right just
   beneath it, and a little more air than two lines of the same day keep
   between them. The date stops short of the headroom the next line keeps
   for its picture badges (a badge rises about a third of a bar above its
   word, never past the line's top edge), so a line that runs the whole
   width can't bring a bubble up under the date. */
.day-break {
  flex: none;
  margin: 10px 16px 3px;
  padding: 3px 4px 0;
  border-top: 2px solid #e8dcc4;
  text-align: end;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink-soft);
}

/* ── word bars (the stickers) ────────────────────────────────────────── */
.bar {
  --b: var(--unknown-b);
  --f: var(--unknown-f);
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: var(--bar-h);
  padding: 0 0.62em;
  border-radius: 999px;
  border: 2.5px solid var(--b);
  background: var(--f);
  font-size: var(--bar-fs);
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  box-shadow: 0 3px 0 rgba(96, 70, 28, 0.08);
  transition: background 0.4s, border-color 0.4s;
  animation: pop 0.28s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* the hand-drawn jitter lives in the rotate property, not transform: the
   pop/bounce entrances animate transform and, filling both ways, keep their
   last frame forever — a transform rotate here would never show */
.line .bar:nth-child(3n)      { rotate: 0.7deg; }
.line .bar:nth-child(3n + 1)  { rotate: -0.6deg; }
.line .bar:nth-child(3n + 2)  { rotate: 0.3deg; }

/* Each bar's tilt makes it a stacking context, so siblings paint in DOM
   order and a badge leaning over the seam would slide UNDER the next word.
   Earlier bars must sit above later ones: a descending ladder, generous
   enough for any sentence a child will build (past thirty words the badges
   go humbly back underneath). */
.line .bar:nth-child(1)  { z-index: 30; }
.line .bar:nth-child(2)  { z-index: 29; }
.line .bar:nth-child(3)  { z-index: 28; }
.line .bar:nth-child(4)  { z-index: 27; }
.line .bar:nth-child(5)  { z-index: 26; }
.line .bar:nth-child(6)  { z-index: 25; }
.line .bar:nth-child(7)  { z-index: 24; }
.line .bar:nth-child(8)  { z-index: 23; }
.line .bar:nth-child(9)  { z-index: 22; }
.line .bar:nth-child(10) { z-index: 21; }
.line .bar:nth-child(11) { z-index: 20; }
.line .bar:nth-child(12) { z-index: 19; }
.line .bar:nth-child(13) { z-index: 18; }
.line .bar:nth-child(14) { z-index: 17; }
.line .bar:nth-child(15) { z-index: 16; }
.line .bar:nth-child(16) { z-index: 15; }
.line .bar:nth-child(17) { z-index: 14; }
.line .bar:nth-child(18) { z-index: 13; }
.line .bar:nth-child(19) { z-index: 12; }
.line .bar:nth-child(20) { z-index: 11; }
.line .bar:nth-child(21) { z-index: 10; }
.line .bar:nth-child(22) { z-index: 9; }
.line .bar:nth-child(23) { z-index: 8; }
.line .bar:nth-child(24) { z-index: 7; }
.line .bar:nth-child(25) { z-index: 6; }
.line .bar:nth-child(26) { z-index: 5; }
.line .bar:nth-child(27) { z-index: 4; }
.line .bar:nth-child(28) { z-index: 3; }
.line .bar:nth-child(29) { z-index: 2; }
.line .bar:nth-child(30) { z-index: 1; }

.pos-pronoun  { --b: var(--pronoun-b);  --f: var(--pronoun-f); }
.pos-name     { --b: var(--name-b);     --f: var(--name-f); }
.pos-verb     { --b: var(--verb-b);     --f: var(--verb-f); }
.pos-noun     { --b: var(--noun-b);     --f: var(--noun-f); }
.pos-describe { --b: var(--describe-b); --f: var(--describe-f); }
.pos-social   { --b: var(--social-b);   --f: var(--social-f); }
.pos-question { --b: var(--question-b); --f: var(--question-f); }
.pos-negation { --b: var(--negation-b); --f: var(--negation-f); }
.pos-little   { --b: var(--little-b);   --f: var(--little-f); }

/* picture = a little pill badge ringed in the word's colour — big enough
   for a finger, because holding it opens the picture picker; the padding is
   invisible extra grip. Its centre sits exactly on the bar's top-right
   corner: half the pill each side of the corner, the offsets being half its
   height (0.62bh+5px is the pill height; its width is 1.2 of that less 2px)
   pushed out by the 5px grip padding + the bar's 2.5px border */
.bar .pic {
  display: none;
  position: absolute;
  top: calc((var(--bar-h) * 0.62 + 5px) * -0.5 - 7.5px);
  inset-inline-end: calc((var(--bar-h) * 0.62 + 5px) * -0.6 - 6.5px);
  padding: 5px;
  z-index: 1;
  cursor: pointer;
}
.bar .pic img {
  display: block;
  box-sizing: border-box;
  /* an oblong a whisker slimmer than the lists page's 1.35:1 pills, so it
     spans less of the words either side; border-box keeps the old circle's
     overall height with the ring inside, and the padding is the lists'
     breathing room at badge scale */
  height: calc(var(--bar-h) * 0.62 + 5px);
  width: calc((var(--bar-h) * 0.62 + 5px) * 1.2 - 2px);
  padding: calc(var(--bar-h) * 0.04);
  border-radius: 999px;
  object-fit: contain;
  background: #fff;
  border: 2.5px solid var(--b);
  box-shadow: 0 2px 6px rgba(96, 70, 28, 0.2);
}
.bar .pic.ready { display: block; animation: pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) both; }

/* while the picture is being drawn, a little spinning ring sits where the
   badge will pop in, in the word's colour */
.bar .pic.loading { display: block; }
.bar .pic.loading::before {
  content: "";
  display: block;
  box-sizing: border-box;
  /* a circle as tall as the pill it stands in for, its box padded out to
     the pill's width by the side margins, so it spins centre-on-corner
     exactly where the picture will land */
  width: calc(var(--bar-h) * 0.62 + 5px);
  height: calc(var(--bar-h) * 0.62 + 5px);
  margin: 0 calc((var(--bar-h) * 0.62 + 5px) * 0.1 - 1px);
  border-radius: 50%;
  background: #fff;
  border: 2.5px solid var(--b);
  border-top-color: transparent;
  opacity: 0.65;
  animation: pic-spin 0.9s linear infinite;
}
@keyframes pic-spin { to { transform: rotate(360deg); } }

/* Bespoke drawn stickers sitting inline in text — hints, buttons, headings,
   tabs, toasts. Invisible while still being drawn (no spinner in running
   text), popping in beside the words once ready. Contexts with their own
   framing (the bars, the header doors, the welcome wave) override below. */
.icon-pic { display: none; }
.icon-pic.ready {
  display: inline-block;
  width: 1.2em;
  height: 1.2em;
  margin: 0 0.15em;
  vertical-align: -0.25em;
}
.icon-pic img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* the in-progress word: a solid warm edge (it wore a dashed one until the
   suggestion chips took the dashes — the bubble is where words are made,
   the chips are the offers) */
.bar.typing {
  background: #fff;
  border: 2.5px solid #e3d5bd;
  box-shadow: none;
  min-width: 2.8em;
  color: var(--ink);
  /* the bar's 10px gap stands between a word and its picture — the bubble
     has neither, and it left the caret hanging a space off the last letter */
  gap: 0;
}
.bar.typing .hint { color: #d5c6a8; font-weight: 600; font-size: 0.85em; }

/* the picture-lists door: a grid of little picture cards stands right
   beside the typing bubble as a bubble of its own — the same height, the
   same solid warm border, and a whole pill for a finger to find. The
   icon-bar shape (with the list stickers below) does the
   drawing-fills-the-pill work. */
.bar.lists-door {
  border-color: #e3d5bd;
  box-shadow: none;
  cursor: pointer;
}
/* mid-word, when the door opens on the words that start with the letters
   typed rather than on the categories, it is an offer like the chips — dashed */
.bar.lists-door.matching { border-style: dashed; }

.caret {
  display: inline-block;
  width: 3px;
  height: 1.05em;
  margin: 0 1px 0 2px; /* a hair off the last letter, like a real caret */
  border-radius: 2px;
  background: #ff922b;
  animation: blink 1.05s steps(1) infinite;
}

/* ── the tray: current sentence + suggestions, sits above the keyboard ── */
#tray {
  flex: none;
  background: var(--card);
  border-top: 2px solid var(--edge);
  border-radius: 26px 26px 0 0;
  box-shadow: 0 -10px 30px rgba(124, 94, 42, 0.10);
  padding-bottom: max(env(safe-area-inset-bottom, 0px), 8px);
}

#current-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
#current-wrap::-webkit-scrollbar { display: none; }

#current-line {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: calc(var(--bar-h) * 0.42) 16px 8px;
  min-width: 100%;
}

#strip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 16px 12px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  min-height: calc(var(--chip-h) + 16px);
}
#strip::-webkit-scrollbar { display: none; }
/* auto suggest off (a grown-ups switch): no strip at all, the sentence
   sitting a little further off the tray's bottom edge in its place */
/* (with auto suggest off the strip keeps only the picture-lists door —
   renderStrip adds no chips — so it is never hidden) */

.chip {
  --b: var(--unknown-b);
  --f: var(--unknown-f);
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--chip-h);
  padding: 0 0.9em;
  border-radius: 999px;
  /* dashed: a chip is an offer, not a word yet — the typing bubble and
     the lists door beside it keep the solid edges */
  border: 2px dashed var(--b);
  background: var(--f);
  font-family: inherit;
  font-size: var(--chip-fs);
  font-weight: 700;
  color: var(--ink);
  box-shadow: 0 2px 0 rgba(96, 70, 28, 0.07);
  cursor: pointer;
  animation: pop 0.22s ease-out both;
}
/* no :active transform on #history .line: the line is full-width with its
   stickers at the left, so a centre-origin scale read as the bubble sliding
   right whenever a finger rested on it */
/* press feedback uses the scale property so it composes with the pop
   entrance, whose fill-mode both holds transform: scale(1) forever */
.chip:active, .bar:active { scale: 0.96; }
/* the rest of a word still to be typed — on a suggestion chip, and on the
   bars of the mid-word picture list, which colour their letters the same */
.chip .done, .list-row .txt .done { color: var(--ink-soft); font-weight: 600; }

/* ── list mode ───────────────────────────────────────────────────────── */
/* the list screen swaps in for the history + tray; nothing else changes */
#list-screen { display: none; }
#app.list-mode #history { display: none; }
/* (a play page opened over the lists keeps its tray — game mode, below) */
#app.list-mode:not(.playing) #tray { display: none; }
#app.list-mode #list-screen {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 6px 0 max(env(safe-area-inset-bottom, 0px), 20px);
  scrollbar-width: thin;
  scrollbar-color: #e8dcc4 transparent;
}

.list-row { cursor: pointer; }
.list-row .bar { box-shadow: 0 2px 0 rgba(96, 70, 28, 0.06); }
.list-row:active .bar { scale: 0.97; } /* scale, not transform — see .bar:active */
/* a starred row wears the little gold star after its name — a drawn sticker
   like everything else, so it stays invisible until it has arrived */
.list-row .pic.star-mark {
  flex: none;
  width: calc(var(--bar-h) * 0.5);
  height: calc(var(--bar-h) * 0.5);
  margin: 0;
}
.list-row.said .bar {
  animation: bounce 0.55s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* the picture bubble — same size as a text bubble, image filling the pill.
   icon-bar is its bespoke-drawn-icon twin (category and game stickers) */
.bar.pic-bar,
.bar.icon-bar {
  width: calc(var(--bar-h) * 1.35);
  flex: none;
  justify-content: center;
  padding: 0;
  overflow: hidden;
  background: #fff;
}
.pic-bar .pic,
.icon-bar .pic {
  display: block;
  position: static;
  width: 100%;
  height: 100%;
  /* breathing room so the picture shows whole, not cropped to the pill */
  padding: calc(var(--bar-h) * 0.08);
}
/* in list mode the spinner sits centred in the picture bubble */
.pic-bar .pic.loading,
.icon-bar .pic.loading {
  display: flex;
  align-items: center;
  justify-content: center;
}
.pic-bar .pic img,
.icon-bar .pic img {
  width: 100%;
  height: 100%;
  padding: 0; /* the badge-scale breathing room stays out of the big pills */
  border: 0;
  border-radius: 0;
  box-shadow: none;
  object-fit: contain;
}
/* drawn icons arrive cut-out (transparent) — never re-flood them white */
.icon-bar .pic img { background: transparent; }

.list-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(var(--bar-h) * 0.36) 16px 8px;
}
/* the one small control on a screen of big bars — give little fingers room */
.back-chip { flex: none; padding: 0 1.2em; }

.list-empty {
  margin: auto;
  text-align: center;
  max-width: 26ch;
  padding: 22px 28px;
  background: #fffdf7;
  border: 2.5px solid var(--edge);
  border-radius: 26px;
  font-size: 19px;
  font-weight: 600;
  color: var(--ink-soft);
}

/* ── game mode ───────────────────────────────────────────────────────── */
/* like list mode, the games swap in for everything below the top bar;
   these rules sit after list mode's so a game wins while both are open */
#game-screen { display: none; }
#app.game-mode #history,
#app.game-mode #list-screen { display: none; }
/* the tray stays for a running page (Simon's ask: the page's words in the
   very tray of the talk page, with everything the tray can do); the shelf
   has no use for it */
#app.game-mode:not(.playing) #tray { display: none; }
#app.game-mode #game-screen {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 6px 0 0;
  scrollbar-width: thin;
  scrollbar-color: #e8dcc4 transparent;
}

/* the running game: a big friendly card holding the sandboxed iframe */
.game-frame-wrap {
  position: relative; /* the thinking bubble sits over the frame */
  flex: 1;
  min-height: 0;
  display: flex;
  padding: 4px 12px max(env(safe-area-inset-bottom, 0px), 12px);
}
/* with the tray beneath, the frame sits close over it (the tray keeps the
   safe area) */
#app.playing .game-frame-wrap { padding-bottom: 4px; }
/* a game's title wordmark riding its name pill, once it has arrived —
   on the shelf rows and above the running game alike. The strip arrives
   see-through; on the shelf it rides inside the row's white capsule as
   every tappable thing does, while above a running game it floats free
   of any pill. No fixed width either way: the strip sits at its natural
   width for its height, and only when the row truly runs out of room
   does it shrink to fit — squeezing it sooner bunched the letters */
.bar.logo-bar {
  flex: 0 1 auto;
  min-width: 0;
  padding: 0 0.5em;
  /* the see-through strip on the capsule's own white */
  background: #fff;
}
.bar.logo-bar img {
  display: block;
  height: calc(var(--bar-h) * 0.74);
  width: auto;
  max-width: 100%;
  object-fit: contain;
}
/* the play header's title floats on the paper — no fill, no rim, no shadow */
.list-head .bar.logo-bar {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
.game-frame {
  flex: 1;
  width: 100%;
  border: 2.5px solid var(--edge);
  border-radius: 22px;
  background: var(--bg);
}
/* "leave this play page?" — the question a back tap on a running page
   asks first: a cream wash in the frame's own shape over the page, a
   small card in the middle, stay in green and leave in red wearing the
   back arrow. Above the thinking bubble. */
.leave-ask {
  position: absolute;
  inset: 4px 12px max(env(safe-area-inset-bottom, 0px), 12px);
  z-index: 6;
  border-radius: 22px;
  background: rgba(255, 247, 236, 0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.leave-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 20px 22px;
  max-width: 340px;
  border-radius: 22px;
  border: 2.5px solid var(--edge);
  background: var(--card);
  box-shadow: 0 4px 0 rgba(96, 70, 28, 0.08);
  text-align: center;
  animation: pop 0.22s ease-out both;
}
.leave-card b { font-size: 22px; }
.leave-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
.leave-actions .chip { padding: 0 1.2em; }

/* ── trying a game (grown-ups preview, over the panel) ───────────────── */
#game-preview {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--bg);
  padding: max(env(safe-area-inset-top, 0px), 12px) 14px
    max(env(safe-area-inset-bottom, 0px), 12px);
}
.gp-top {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 19px;
}
.gp-top .gp-note { font-size: 14px; font-weight: 600; color: var(--ink-soft); }
.gp-frame-wrap { position: relative; flex: 1; min-height: 0; display: flex; }
.gp-top .gp-logo {
  height: 32px;
  width: auto;
  max-width: 100%;
  /* keep the drawn proportions when the header runs out of room — a bare
     width clamp squashed the lettering */
  object-fit: contain;
  vertical-align: middle;
  /* the strip is see-through now — no chip behind it */
  background: transparent;
}
.gp-error {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  background: var(--negation-f);
  border: 2px solid var(--negation-b);
  border-radius: 14px;
  padding: 8px 12px;
}
.gp-error[hidden] { display: none; }
.gp-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.gp-actions .btn { flex: 1; min-width: 130px; }

/* ── the instructions pad (game build & fix, word meanings) ──────────────
   A near full-screen textarea, sized to the visible viewport (--vvh follows
   the phone keyboard), so every line being written stays in view. */
#compose-pad {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--vvh, 100dvh);
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--bg);
  padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 12px;
}
#compose-pad h2 { margin: 0; font-size: 22px; font-weight: 800; }
#compose-pad .compose-word,
#compose-pad textarea {
  font-family: inherit;
  font-size: 17px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink);
  background: #fff;
  border: 2px solid var(--edge);
  border-radius: 14px;
  padding: 12px 14px;
}
#compose-pad textarea {
  flex: 1;
  min-height: 0;
  resize: none;
}
#compose-pad .compose-word:focus,
#compose-pad textarea:focus { outline: none; border-color: var(--describe-b); }
.compose-actions { display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }

/* the tabs' one big door each to the pad */
#game-make-btn, #hint-add-btn { width: 100%; margin-top: 10px; }
#game-make-btn[disabled] { opacity: 0.6; cursor: default; }

/* ── games in the grown-ups panel ────────────────────────────────────── */
.game-admin-row { align-items: center; gap: 8px; }
.game-admin-row .game-btn,
.gp-error .game-btn {
  flex: none;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  background: #fff;
  border: 2px solid var(--edge);
  border-radius: 999px;
  padding: 4px 12px;
  cursor: pointer;
  white-space: nowrap;
}
.game-admin-row .game-btn.danger {
  border-color: var(--negation-b);
  background: var(--negation-f);
}
.game-admin-row .game-btn.generate,
.gp-error .game-btn.generate {
  border-color: var(--pronoun-b);
  background: var(--pronoun-f);
}
.game-admin-row .busy { animation: gently 1.2s ease-in-out infinite; }
.game-admin-row .game-err { color: #c92a2a; }
@keyframes gently { 50% { opacity: 0.45; } }

/* ── speaking feedback ───────────────────────────────────────────────── */
.line.speaking {
  background: linear-gradient(90deg, rgba(255, 243, 191, 0.7), rgba(255, 243, 191, 0.35));
}
.line.speaking .bar {
  animation: bounce 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  animation-delay: var(--d, 0s);
}

/* ── welcome card ────────────────────────────────────────────────────── */
#welcome {
  margin: auto;
  text-align: center;
  max-width: 30ch;
  padding: 26px 30px;
  background: #fffdf7;
  border: 2.5px solid var(--edge);
  border-radius: 26px;
  box-shadow: 0 4px 0 rgba(96, 70, 28, 0.06);
  transform: rotate(-0.5deg);
}
/* the welcome wave — a bespoke drawn sticker that pops in when it's ready */
#welcome .wave { display: block; margin-bottom: 6px; }
#welcome .wave .pic { display: none; }
#welcome .wave .pic.ready { display: block; width: 64px; height: 64px; margin: 0 auto; }
#welcome .wave .pic img { width: 100%; height: 100%; object-fit: contain; }
#welcome h1 { margin: 0 0 6px; font-size: 26px; font-weight: 800; }
#welcome p { margin: 0; font-size: 18px; font-weight: 600; color: var(--ink-soft); line-height: 1.45; }

/* ── hidden real input (brings up the native keyboard) ───────────────── */
#ghost {
  position: fixed;
  left: 10px;
  bottom: 10px;
  width: 2px;
  height: 2px;
  padding: 0;
  border: 0;
  outline: 0;
  opacity: 0.01;
  font-size: 16px; /* stops iOS zooming on focus */
  background: transparent;
  color: transparent;
  caret-color: transparent;
  pointer-events: none;
}

/* ── toast + delete bubble ───────────────────────────────────────────── */
/* both centre with the translate property, not transform: their pop
   entrance animates transform and its fill-mode both holds scale(1) forever,
   so a transform translateX(-50%) here never applied — every popup hung
   from mid-screen rightward by half its own width */
#toast {
  position: fixed;
  left: 50%;
  bottom: 30%;
  translate: -50%;
  background: var(--ink);
  color: #fff7ec;
  font-weight: 700;
  font-size: 16px;
  padding: 10px 18px;
  border-radius: 999px;
  box-shadow: 0 6px 18px rgba(74, 53, 32, 0.3);
  /* above the game preview (z 70) AND the composer pad (z 80): a refused
     list save reopens the pad with the grown-up's text, and the toast
     saying why must not vanish behind it */
  z-index: 90;
  animation: pop 0.2s ease-out both;
}

.delete-bubble {
  position: fixed;
  left: 50%;
  translate: -50%;
  display: flex;
  /* up to four buttons now (delete + unjoin + join + keep, on a joined pair
     mid-line): on a narrow screen they fold into rows. width must be said
     out loud — left:50% makes shrink-to-fit size against only half the
     screen, folding the bubble long before it needs to — and the 30px
     radius (the same pill as 999px at one row's height) stays a rounded
     box when folded, where a true pill would curve across the corner buttons */
  flex-wrap: wrap;
  justify-content: center;
  width: max-content;
  max-width: 94vw;
  gap: 10px;
  background: #fffdf7;
  border: 2.5px solid var(--edge);
  border-radius: 30px;
  padding: 8px;
  box-shadow: 0 10px 30px rgba(74, 53, 32, 0.25);
  z-index: 55;
  animation: pop 0.18s ease-out both;
}
.delete-bubble button {
  font-family: inherit;
  font-size: 17px;
  font-weight: 700;
  border-radius: 999px;
  padding: 8px 18px;
  cursor: pointer;
  border: 2px solid var(--edge);
  background: #fff;
  color: var(--ink);
  white-space: nowrap;
}
.delete-bubble button.danger {
  border-color: var(--negation-b);
  background: var(--negation-f);
}
/* on a phone, slimmer buttons let the four-wide bubble fold into two rows
   of two instead of three ragged rows */
@media (max-width: 430px) {
  .delete-bubble button {
    padding: 8px 13px;
  }
}

/* ── the picture viewer ──────────────────────────────────────────────────
   tap a word's little picture and it opens big in the middle of the
   screen; every picture the word has rides the same snapping strip, so a
   sideways swipe wanders through them, and any tap puts it away */
#pic-viewer {
  position: fixed;
  inset: 0;
  z-index: 60; /* over the hold bubbles, under the toast */
  background: rgba(67, 48, 26, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
#pic-viewer .viewer-strip {
  position: absolute;
  inset: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
#pic-viewer .viewer-strip::-webkit-scrollbar { display: none; }
#pic-viewer .viewer-slide {
  flex: none;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  scroll-snap-align: center;
}
#pic-viewer .viewer-slide img {
  max-width: min(82vw, 68vh);
  max-height: min(82vw, 68vh);
  animation: pop 0.22s ease-out both;
}
/* a picture still on its way wears the familiar spinning ring */
#pic-viewer .viewer-slide.loading::before {
  content: "";
  width: 54px;
  height: 54px;
  box-sizing: border-box;
  border-radius: 50%;
  border: 4px solid rgba(255, 247, 236, 0.8);
  border-top-color: transparent;
  animation: pic-spin 0.9s linear infinite;
}
#pic-viewer .viewer-word {
  position: absolute;
  left: 50%;
  bottom: max(env(safe-area-inset-bottom, 0px), 20px);
  transform: translateX(-50%);
  background: #fffdf7;
  border: 2.5px solid var(--edge);
  border-radius: 999px;
  padding: 8px 22px;
  font-size: 20px;
  font-weight: 800;
  color: var(--ink);
  box-shadow: 0 6px 18px rgba(74, 53, 32, 0.3);
}
#pic-viewer .viewer-dots {
  position: absolute;
  left: 50%;
  bottom: calc(max(env(safe-area-inset-bottom, 0px), 20px) + 58px);
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
}
#pic-viewer .viewer-dots .dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgba(255, 247, 236, 0.45);
}
#pic-viewer .viewer-dots .dot.on { background: #fff7ec; }

/* ── grown-ups panel ─────────────────────────────────────────────────── */
#overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  background: rgba(67, 48, 26, 0.4);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}
#overlay[hidden] { display: none; }

.sheet {
  width: 100%;
  max-width: 460px;
  max-height: 86vh;
  overflow-y: auto;
  overflow-x: hidden;
  background: #fffdf7;
  border-radius: 26px;
  padding: 22px;
  box-shadow: 0 24px 60px rgba(40, 26, 8, 0.35);
}
/* the grown-ups sheet is tabbed and roomier, so desktop needn't scroll */
.sheet.gp { max-width: 700px; }
.sheet h2 { margin: 0 0 14px; font-size: 24px; font-weight: 800; }
.tabbar {
  display: flex;
  gap: 8px;
  margin: 0 0 6px;
  overflow-x: auto; /* one line always — narrow phones scroll it sideways */
  padding-bottom: 4px; /* room so a scrollbar never sits on the pills */
  scrollbar-width: thin;
}
.tabbar .tab {
  flex: none;
  white-space: nowrap;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  border: 2.5px solid var(--edge);
  border-radius: 999px;
  padding: 8px 16px;
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
}
.tabbar .tab[aria-selected="true"] {
  border-color: var(--describe-b);
  background: var(--describe-f);
  color: var(--ink);
}
.tabpane[hidden] { display: none; }
/* long lists scroll inside their tab, so the sheet itself stays put */
#hint-list, #games-admin, #clist-admin { max-height: 44vh; overflow-y: auto; }
.sheet .prow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1.5px solid var(--edge);
  font-size: 17px;
  font-weight: 600;
}
.sheet .prow:last-of-type { border-bottom: 0; }
.sheet label { flex: 1; min-width: 0; }
.sheet select, .sheet input[type="text"], .sheet input[type="password"],
#game-preview input[type="text"] {
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
  background: #fff;
  border: 2px solid var(--edge);
  border-radius: 14px;
  padding: 9px 12px;
  max-width: 56%;
}
.sheet input[type="checkbox"] { width: 24px; height: 24px; accent-color: #51cf66; }
/* the button colours, Simon's rules: normal white · selected blue ·
   done/confirm green · delete red · generate yellow — and one line each */
.sheet .btn, #game-preview .btn, #compose-pad .btn, #anim-sheet .btn, #home-guide .btn {
  font-family: inherit;
  font-size: 17px;
  font-weight: 700;
  border-radius: 999px;
  padding: 10px 20px;
  cursor: pointer;
  border: 2.5px solid var(--edge);
  background: #fff;
  color: var(--ink);
  white-space: nowrap;
}
.sheet .btn.danger, #game-preview .btn.danger, #compose-pad .btn.danger, #anim-sheet .btn.danger, #home-guide .btn.danger { border-color: var(--negation-b); background: var(--negation-f); }
.sheet .btn.primary, #game-preview .btn.primary, #compose-pad .btn.primary, #anim-sheet .btn.primary, #home-guide .btn.primary { border-color: var(--verb-b); background: var(--verb-f); }
/* a button that generates something wears yellow under its diamond dust
   (after .primary, so generate wins on buttons carrying both) */
.sheet .btn.generate, #game-preview .btn.generate, #compose-pad .btn.generate, #anim-sheet .btn.generate { border-color: var(--pronoun-b); background: var(--pronoun-f); }
/* selected = blue, as everywhere (the family-list picker's toggles) */
.sheet .btn.sel { border-color: var(--describe-b); background: var(--describe-f); }
/* the family-list picker: one full-width row per list, its sticker leading */
.sheet .clist-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 8px 0;
  text-align: start;
}

/* ── the home-screen guide (a game's own page, games.js) ────────────────
   floats over the running game teaching the last tap; sits ABOVE the
   family-code sheet (50) on purpose — adding the icon needs no code */
#home-guide {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgba(74, 53, 32, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
#home-guide .guide-card {
  background: #fffdf7;
  border: 2px solid var(--edge);
  border-radius: 20px;
  padding: 18px;
  width: min(92vw, 420px);
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 12px 40px rgba(74, 53, 32, 0.25);
}
#home-guide .guide-card b { font-size: 20px; }
#home-guide .guide-note { font-size: 15px; color: var(--ink-soft); }
#home-guide .guide-body { display: flex; flex-direction: column; gap: 12px; }
#home-guide .guide-steps { display: flex; flex-direction: column; gap: 10px; }
#home-guide .guide-step {
  display: flex;
  align-items: center;
  gap: 12px;
  font-weight: 700;
  text-align: start;
}
#home-guide .guide-step .pic {
  width: 46px;
  height: 46px;
  flex: none;
}
#home-guide .guide-step .pic img { width: 100%; height: 100%; object-fit: contain; }
#home-guide .guide-actions { display: flex; gap: 8px; }
#home-guide .guide-actions .btn { flex: 1; }
/* the shareable address in the panel's home-screen sheet — one tap selects
   the lot, and long addresses fold rather than overflow */
#anim-sheet .home-addr {
  user-select: all;
  -webkit-user-select: all;
  overflow-wrap: anywhere;
  font-size: 13px;
}

/* ── bring-it-to-life sheet (hold a drawn sprite in a game preview) ────── */
#anim-sheet {
  position: fixed;
  inset: 0;
  z-index: 80; /* above the preview it was opened over */
  background: rgba(74, 53, 32, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
#anim-sheet .anim-card {
  background: var(--card, #fffdf7);
  border: 2px solid var(--edge);
  border-radius: 20px;
  padding: 18px;
  width: min(92vw, 420px);
  /* a card taller than the screen (a picture with many pictures and
     animations, on a phone) scrolls within it rather than running off
     both ends; its parts keep their height, so the card is what scrolls */
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 12px 40px rgba(74, 53, 32, 0.25);
}
#anim-sheet .anim-card > * { flex-shrink: 0; }
#anim-sheet .anim-card b { font-size: 20px; }
#anim-sheet .anim-what { font-size: 15px; color: var(--ink-soft); }
#anim-sheet .anim-hint {
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
  background: #fff;
  border: 2px solid var(--edge);
  border-radius: 14px;
  padding: 9px 12px;
}
#anim-sheet .anim-lens { display: flex; gap: 8px; }
#anim-sheet .anim-lens .btn { flex: 1; }
#anim-sheet .anim-lens .btn.sel { border-color: var(--describe-b); background: var(--describe-f); }
#anim-sheet .anim-actions { display: flex; gap: 8px; flex-wrap: wrap; }
#anim-sheet .anim-actions .btn { flex: 1; min-width: 110px; }
/* the collection sheet borrows the word-picture picker's tiles. The grid
   scrolls, and scrolling clips — so it carries enough padding that the
   corner badges (ticks, pencils, manner icons) overhanging each tile stay
   inside the clip edge instead of being sliced off. */
#anim-sheet .pic-grid {
  max-height: 42vh;
  overflow-y: auto;
  padding: 12px 12px 12px 4px;
}
/* a picture being redrawn: it keeps its place, fading gently in and out
   like a working sprite in the game, under a centred spinner — and takes
   no taps until the new version arrives */
#anim-sheet .pic-tile.editing { cursor: default; }
#anim-sheet .pic-tile.editing img { animation: gently 1.2s ease-in-out infinite; }
#anim-sheet .pic-tile.editing::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 3.5px solid var(--describe-b);
  border-top-color: transparent;
  animation: pic-spin 0.9s linear infinite;
}
/* a picture whose animation is being made right now */
#anim-sheet .pic-tile .busy {
  position: absolute;
  top: -9px;
  inset-inline-end: -9px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid var(--describe-b);
  border-top-color: transparent;
  animation: pic-spin 0.9s linear infinite;
}
/* the images button wears the same spinner while anything is in the oven */
.gp-actions .btn.busy::after {
  content: "";
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-inline-start: 8px;
  vertical-align: -2px;
  border-radius: 50%;
  border: 2.5px solid var(--describe-b);
  border-top-color: transparent;
  animation: pic-spin 0.9s linear infinite;
}
#anim-sheet .anim-plays { display: flex; gap: 8px; }
#anim-sheet .anim-plays .btn { flex: 1; font-size: 14px; padding: 8px 6px; min-width: 0; }
#anim-sheet .anim-plays .btn.sel { border-color: var(--describe-b); background: var(--describe-f); }
#anim-sheet .anim-plays .btn:disabled { opacity: 0.4; cursor: default; }
/* how often "sometimes" is — a slider between frequent and rare, under the
   manner buttons while the sometimes-animation is selected */
#anim-sheet .anim-rate { display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 700; color: var(--ink-soft); }
#anim-sheet .anim-rate input[type="range"] { flex: 1; min-width: 0; margin: 0; accent-color: var(--describe-b); }
#anim-sheet .anim-actions .btn:disabled { opacity: 0.4; cursor: default; }
#anim-sheet .anim-note { font-size: 13px; color: var(--ink-soft); margin: 0; }
/* the quiet debug door under the manner buttons, and the card it opens:
   the full sheets that went to and came back from the image model */
#anim-sheet .anim-debug-row { display: flex; gap: 8px; flex-wrap: wrap; }
#anim-sheet .anim-debug-link {
  align-self: flex-start;
  font-size: 12px;
  padding: 4px 10px;
  color: var(--ink-soft);
}
#anim-sheet .anim-debug-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 62vh;
  overflow-y: auto;
}
#anim-sheet .anim-debug-body img {
  width: 100%;
  border: 2px solid var(--edge);
  border-radius: 12px;
  background: #fff;
}
#anim-sheet .anim-sub { font-size: 13px; font-weight: 700; color: var(--ink-soft); }
/* the delete badge on an unchosen picture revision — red where the pencil
   is friendly, and up at the top corner where a tick would sit */
#anim-sheet .pic-tile .wand.bin {
  bottom: auto;
  top: -9px;
  border-color: var(--negation-b);
  color: var(--negation-b);
}
/* each manner of playing wears its icon — on the buttons and on the tile
   that serves it (the plain tile carries the manners that stay still) */
#anim-sheet .anim-plays .ricon { margin-inline-end: 6px; }
#anim-sheet .pic-tile .roles {
  position: absolute;
  top: -9px;
  inset-inline-start: 6px;
  display: flex;
  gap: 3px;
}
#anim-sheet .pic-tile .role-badge {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--describe-b); /* a held manner is a selected state */
  box-shadow: 0 2px 5px rgba(96, 70, 28, 0.25);
}
.sheet .statusline { display: flex; align-items: center; gap: 8px; font-size: 15px; }
.sheet .dot { width: 10px; height: 10px; border-radius: 50%; background: #ced4da; flex: none; }
.sheet .dot.on { background: #51cf66; }
.sheet .note { font-size: 14px; font-weight: 500; color: var(--ink-soft); margin: 10px 0 0; line-height: 1.4; }
.sheet .note.verline { font-size: 12px; text-align: center; opacity: 0.7; }
.sheet .note.verline:empty { display: none; }
.sheet .sheet-actions { display: flex; justify-content: space-between; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
/* the grown-ups panel's done stays in the bottom-right corner even on the
   play tab, where clear history steps away and would leave it at the left */
.sheet.gp .sheet-actions [data-act="close"] { margin-inline-start: auto; }
.sheet h3 { margin: 16px 0 0; font-size: 18px; font-weight: 800; }

/* ── family word meanings (grown-ups panel) ──────────────────────────── */
.hint-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1.5px solid var(--edge);
  font-size: 15px;
  font-weight: 500;
}
.hint-row b { flex: none; }
.hint-row span { flex: 1; color: var(--ink-soft); overflow-wrap: anywhere; }
.hint-row .hint-del {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 15px;
  color: var(--ink-soft);
  padding: 2px 6px;
}

/* ── picture picker (hold a word's picture) ──────────────────────────── */
.pic-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding: 4px 2px 10px;
}
.pic-tile {
  position: relative;
  width: 92px;
  height: 92px;
  flex: none;
  border-radius: 20px;
  border: 2.5px solid var(--edge);
  background: #fff;
  padding: 6px;
  cursor: pointer;
}
.pic-tile img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 12px;
}
.pic-tile:active { transform: scale(0.95); }
.pic-tile.sel {
  border-color: var(--describe-b);
  box-shadow: 0 0 0 3px var(--describe-f);
}
.pic-tile .tick {
  position: absolute;
  top: -9px;
  inset-inline-end: -9px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--verb-b);
  box-shadow: 0 2px 5px rgba(96, 70, 28, 0.25);
}
/* the pencil badge — tap to change a picture or an animation; a photo's
   diamond-dust variant (.generate) redraws it in the sticker style */
.pic-tile .wand {
  position: absolute;
  bottom: -9px;
  inset-inline-end: -9px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--describe-b);
  box-shadow: 0 2px 5px rgba(96, 70, 28, 0.25);
  cursor: pointer;
}
.pic-tile .wand.generate { border-color: var(--pronoun-b); }
.pic-tile .wand:active { transform: scale(0.9); }
/* the corner badges hold drawn stickers now, not glyphs — each sticker
   fills its little circle (overriding the inline text sizing above) */
.pic-tile .tick .icon-pic.ready,
.pic-tile .wand .icon-pic.ready,
#anim-sheet .pic-tile .role-badge .icon-pic.ready {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 2px;
}
/* the game's look — backdrop and logo, shown apart and never animated;
   tapping one opens its change form */
.pic-tile.look.wide { width: 156px; } /* both halves are landscape */
.pic-tile.look img { object-fit: cover; }
.pic-tile.look.logo img { object-fit: contain; } /* the wordmark shows whole */
/* the little named ribbon — the look tiles' captions, the word itself
   under a word's picture in a page's images sheet (a long compound is
   clipped with a dot-dot-dot rather than spilling out of its tile), and
   the "talk page" mark in a word picture's own sheet */
.pic-tile .cap {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 4px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  background: rgba(74, 53, 32, 0.55);
  border-radius: 8px;
  padding: 1px 3px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
/* a picture still being drawn — same spinner the stickers use */
.pic-tile.gen {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: default;
}
.pic-tile.gen::before {
  content: "";
  display: block;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 3.5px solid var(--describe-b);
  border-top-color: transparent;
  opacity: 0.7;
  animation: pic-spin 0.9s linear infinite;
}
/* the laptop-camera view: live preview + snap, right inside the sheet */
#pic-cam-view { margin: 4px 0 10px; }
#pic-cam-view video {
  display: block;
  width: 100%;
  max-height: 300px;
  border-radius: 18px;
  background: #111;
}
.cam-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  margin-top: 10px;
  flex-wrap: wrap;
}

.pic-desc-row {
  display: flex;
  gap: 8px;
  margin: 2px 0 8px;
}
.pic-desc-row input[type="text"] {
  flex: 1;
  max-width: none;
  min-width: 0; /* let flex shrink them, or narrow sheets scroll sideways */
}
.pic-actions {
  flex-wrap: wrap;
  justify-content: flex-start;
}
.pic-actions .btn.primary { margin-inline-start: auto; }

/* unlock screen */
.sheet.unlock { text-align: center; }
.sheet.unlock input { max-width: 100%; width: 100%; text-align: center; font-size: 22px; margin: 12px 0; }
/* the demo's first sheet: a breath between its notes and the tap */
.sheet.unlock .note + .btn { margin-top: 16px; }
/* the pairing sheet's second way in (the code by email) stands a little
   apart from the pad */
.sheet.unlock .numpad + .note { margin-top: 22px; }

/* ── the number pad: six dots, then keys like a phone's passcode screen
   (the pairing sheet, and setting the family code in the grown-ups panel) */
.numpad { display: flex; flex-direction: column; align-items: center; gap: 18px; margin: 16px 0 6px; }
.numpad .dots { display: flex; gap: 14px; }
.numpad .dot { width: 14px; height: 14px; border-radius: 50%; border: 2.5px solid var(--ink-soft); box-sizing: border-box; }
.numpad .dot.on { background: var(--ink-soft); }
.numpad .keys { display: grid; grid-template-columns: repeat(3, 68px); gap: 12px 22px; justify-content: center; }
.numpad .key {
  width: 68px; height: 68px; border-radius: 50%; padding: 0;
  border: 2.5px solid var(--edge); background: #fff; color: inherit;
  font: inherit; font-size: 28px; font-weight: 800; line-height: 1;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.numpad .key:active { background: var(--edge); }
.numpad .key.blank { visibility: hidden; }
.numpad .key.del svg { width: 30px; height: 30px; }
#code-pad { padding: 4px 0 10px; }
#code-pad .note { text-align: center; }
/* the demo's own sheet (what it is, and the way to sign up): its two
   buttons side by side in the middle */
.sheet.demo .sheet-actions { justify-content: center; }

/* ── animations ──────────────────────────────────────────────────────── */
@keyframes pop {
  from { transform: scale(0.6); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}
@keyframes blink { 50% { opacity: 0; } }
@keyframes bounce {
  0%   { transform: translateY(0); }
  35%  { transform: translateY(-14%) scale(1.05); }
  70%  { transform: translateY(2%) scale(1); }
  100% { transform: translateY(0); }
}
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-7px); }
  50% { transform: translateX(6px); }
  75% { transform: translateX(-4px); }
}
.shake { animation: shake 0.3s ease-in-out; }

@keyframes arrive {
  from { transform: translateY(14px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
.line.arrive, .day-break.arrive { animation: arrive 0.3s ease-out; }

@keyframes fadeout {
  to { opacity: 0; transform: scale(0.92); }
}
.line.removing { animation: fadeout 0.25s ease-in both; }

@media (prefers-reduced-motion: reduce) {
  .bar, .chip, .line.arrive, .day-break.arrive, .line.speaking .bar { animation: none !important; }
}

/* tighter top bar when the screen is short (phone landscape) */
@media (max-height: 460px) {
  #topbar { padding-top: 4px; }
  #wordmark { font-size: 18px; }
  #welcome { padding: 14px 22px; }
  #welcome .wave .pic.ready { width: 40px; height: 40px; }
}

/* ── moving words ────────────────────────────────────────────────────── */
/* Every old line stands in a white card of its own, as on the demo page —
   the history a stack of cards on the paper with a little air between.
   The words scroll in a box inside the card and clip to its rounded edge
   (the card's overflow does the clipping, so a bar sliding under the edge
   follows the curve of the corner), with the green face that says the
   whole line standing clear of them at the right — never over them. The
   box keeps the line's old top padding (the headroom for the picture
   badges, which must stay inside the card now that it clips), and the
   face is set level with the bars, which sit below that headroom. */
#history { gap: var(--gap); padding: var(--gap) 0 14px; }
#history .line {
  width: auto;
  margin: 0 var(--inset);
  padding: 0;
  gap: 0;
  background: var(--card);
  border: 2.5px solid var(--edge);
  border-radius: 22px;
  box-shadow: 0 3px 0 rgba(96, 70, 28, 0.08);
  overflow: hidden;
}
.day-break { margin: 4px 14px 0; }
.line .words {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: calc(var(--bar-h) * 0.42) calc(2 * var(--inset)) var(--inset) calc(16px * var(--scale));
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* nothing else on the scroller: a mask sat here once, and WebKit is not
     to be trusted to keep scrolling a masked box under a finger — the
     right-edge fade is painted over the card instead (below) */
}
.line .words::-webkit-scrollbar { display: none; }
/* a long line fades out under its right edge rather than being sliced off
   against the face: a strip of the card's own colour painted over the end
   of the words box, touching nothing (the box's extra right padding keeps
   a short line's last word clear of it); it steps aside while the line is
   spoken, since the card is yellow then */
#history .line { position: relative; }
#history .line::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  /* the words box ends where the grip begins: the grip is --grip-w wide
     with an inset's margin at its right, so the strip's right edge sits
     exactly there */
  inset-inline-end: calc(var(--grip-w) + var(--inset));
  width: calc(28px * var(--scale));
  /* above the bars (their badge ladder climbs to 30) and below the face
     (40) — painted beneath the bars it faded nothing, and a word was still
     sliced hard against the face */
  z-index: 35;
  pointer-events: none;
  background: linear-gradient(to right, rgba(255, 253, 247, 0), var(--card));
}
#history .line.speaking::after { display: none; }
/* the grip at the right of an old line: a soft tab, level with the bars
   (the card's headroom above is the stamp's), wearing six drawn dots in
   the tab's own dark beige — held a moment, the line lifts under the
   finger to be carried down into the tray; a tap says the line again, as
   a tap anywhere on the line does. A finger that moves at once is
   scrolling the history, so the browser keeps the touch (no touch-action
   here — the drag engine claims the move only once the hold has lifted
   the line). Its width is --grip-w (:root), which the fade strip and the
   stamp share. */
.grip {
  flex: none;
  margin: calc(var(--bar-h) * 0.42 - var(--inset)) var(--inset) 0 0;
  width: var(--grip-w);
  height: var(--bar-h);
  border-radius: 999px;
  background: #f3ebd9;
  color: #cbbc9c; /* the dots' ink (the bubble silhouette's beige before them) */
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  z-index: 40;
}
/* the dots: sized with everything else, and never what the pointer is
   talking to */
.grip svg {
  width: calc(16px * var(--scale));
  height: calc(28px * var(--scale));
  display: block;
  pointer-events: none;
}
body.dragging .grip { cursor: grabbing; }
/* what is carried: a clone riding with the pointer, lifted a little */
.drag-clone { position: fixed; left: 0; top: 0; z-index: 80; pointer-events: none; will-change: translate; }
.drag-clone .carried {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  margin: 0;
  transition: transform 0.16s ease, box-shadow 0.16s ease;
}
.drag-clone .bar, .drag-clone .pic { animation: none; }
.drag-clone .bar.carried.up { transform: scale(1.08); box-shadow: 0 14px 26px rgba(96, 70, 28, 0.28); }
.drag-clone .line.carried {
  margin: 0;
  background: var(--card);
  border: 2.5px solid var(--edge);
  border-radius: 22px;
  overflow: hidden;
}
.drag-clone .line.carried.up { transform: scale(1.01); box-shadow: 0 16px 30px rgba(96, 70, 28, 0.22); }
.drag-clone.landing { transition: translate 0.22s cubic-bezier(0.2, 0.8, 0.2, 1); }
.drag-clone.poof { opacity: 0; scale: 0.6; transition: opacity 0.2s ease-in, scale 0.2s ease-in; }
/* where a carried sentence word will land: its own bar, faded to a dashed
   outline; carried off the tray, the slot closes up */
.bar.slot { opacity: 0.35; border-style: dashed; background: transparent; box-shadow: none; }
.bar.slot .pic { visibility: hidden; }
.bar.slot.out { display: none; }
/* an old word, or a whole old line, being copied: dimmed while carried */
.bar.lifted, .line.lifted { opacity: 0.55; }
/* a whole line over the tray: the sentence box opens to take its words */
#current-wrap:has(#current-line.receiving) { border-style: dashed; border-color: var(--verb-b); }
body.dragging, body.dragging .bar, body.dragging #history .line { cursor: grabbing; }
/* a bar flying from an old line to its copy in the sentence */
.bar.flying {
  position: fixed;
  z-index: 80;
  pointer-events: none;
  margin: 0;
  box-sizing: border-box;
  animation: none;
  transition: translate 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.bar.flying .pic { animation: none; }

/* ── the tray, in the demo page's shape ──────────────────────────────
   The offers stand above — the picture-lists door first, then the chips —
   and beneath them the white box the sentence is built in, with send
   beside it. The box never wraps: the sentence scrolls sideways inside it
   as it always did, clipped to the box's rounded edge, and the typing
   bubble is borderless in it, filling its blank part, the caret blinking
   at the end of the words whether or not the keyboard is up. */
/* the offers row: the strip (door first, then the chips, scrolling) and,
   at its right so it stands over send, the red cross that clears the
   sentence — red because it undoes, like delete and cancel */
#offers {
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: var(--gap) calc(14px * var(--scale)) 4px;
}
#strip {
  flex: 1;
  min-width: 0;
  padding: 0;
  gap: var(--gap);
  min-height: var(--chip-h);
}
#clear {
  flex: none;
  width: var(--chip-h);
  height: var(--chip-h);
  border-radius: 50%;
  border: 2px solid var(--negation-b);
  background: var(--negation-f);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-family: inherit;
  font-size: calc(var(--chip-h) * 0.5);
  font-weight: 800;
  line-height: 1;
  color: var(--ink);
  box-shadow: 0 2px 0 rgba(96, 70, 28, 0.07);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#clear:active { scale: 0.92; }
/* the cross is only there while there is something to clear — an empty box
   with a red cross beside it read as a dismiss button for the tray — and
   pops in with the first word or letter */
#clear { animation: pop 0.22s ease-out both; }
#clear[hidden] { display: none; }
#clear .icon-pic.ready { width: 58%; height: 58%; margin: 0; }
#clear .icon-pic.ready ~ .glyph { display: none; }
/* the sentence going: its bars shrink away before the box empties */
#current-line .bar.gone { opacity: 0; scale: 0.6; transition: opacity 0.18s ease-in, scale 0.18s ease-in; }
.chip.lists-door {
  padding: 0 10px;
  border-style: solid;
  border-color: #e3d5bd;
  background: #fff;
  animation: none;
}
.chip.lists-door.matching { border-style: dashed; }
.chip.lists-door .icon-pic.ready {
  width: calc(var(--chip-h) - 12px);
  height: calc(var(--chip-h) - 12px);
  margin: 0;
  vertical-align: middle;
}
#barrow {
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: 4px calc(14px * var(--scale)) calc(8px * var(--scale));
}
#current-wrap {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  background: #fff;
  border: 2.5px solid var(--edge);
  border-radius: 26px;
  cursor: text;
  transition: border-color 0.2s;
}
#current-line {
  display: flex;
  width: 100%;
  min-width: 0;
  gap: var(--gap);
  padding: calc(var(--bar-h) * 0.42) var(--inset) calc(8px * var(--scale));
  border-radius: 0;
}
.bar { gap: var(--gap); }
#current-line .bar.typing {
  flex: 1;
  min-width: 2.8em;
  justify-content: flex-start;
  padding-inline-start: 4px;
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  /* no hand-drawn tilt on this one: it spans the box's blank part, and a
     fraction of a degree about the centre of something that wide lifted
     or dropped its left end, where the letters and the caret sit, by
     several pixels, a different way with every word finished (the caret
     looked to land at random, Simon found); the words keep their tilt,
     being only a word wide */
  rotate: 0deg;
}
/* the say button: the round speech bubble — the header's sticker as a
   silhouette — in a green darker than the button's own,
   over the word "say", standing as tall as the sentence box beside it
   (Simon's asks, after the boy's silhouette over "say it": the bubble
   alone first, then the word back beneath it) */
#send {
  flex: none;
  align-self: stretch;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(2px * var(--scale));
  min-width: calc(var(--bar-h) * 1.35);
  padding: calc(4px * var(--scale)) calc(14px * var(--scale));
  border-radius: 999px;
  border: 2.5px solid var(--verb-b);
  background: var(--verb-f);
  font-family: inherit;
  font-size: var(--chip-fs);
  font-weight: 800;
  line-height: 1;
  color: var(--ink);
  box-shadow: 0 3px 0 rgba(96, 70, 28, 0.08);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#send .pic.ready {
  display: block;
  width: calc(var(--bar-h) * 0.62);
  height: calc(var(--bar-h) * 0.62);
  margin: 0;
  background: #34a34a; /* a shade lighter than the arrows' dark green */
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
  pointer-events: none;
}
#send .pic img { display: none; } /* the mask is the picture here */
#send:active { scale: 0.95; }

/* ── size: a per-device choice, four steps ───────────────────────────
   Chosen in the grown-ups settings and kept on the device (the phone and
   the iPad want different answers). Each step is one line: add another to
   try it. */
html[data-size="70"] { --scale: 0.7; }
html[data-size="85"] { --scale: 0.85; }
html[data-size="115"] { --scale: 1.15; }
/* the size row: its label has the line to itself and the five buttons
   stand beneath it, each a letter with its percentage small below */
.sheet .prow.size { flex-direction: column; align-items: stretch; gap: 8px; }
.sheet .prow.size label { flex: none; }
/* five of them fit one row of a phone's sheet, just */
/* the languages row: the label has the line to itself and the ticks wrap
   beneath it, two to a line on a phone (in one row the ticks wrapped over
   the label, Simon's phone) */
.sheet .prow.langs { flex-direction: column; align-items: stretch; gap: 8px; }
.sheet .prow.langs > label { flex: none; }
.sheet #langs-ticks { display: flex; flex-wrap: wrap; gap: 8px 12px; }
.sheet #langs-ticks label.tick { flex: 0 0 calc(50% - 6px); min-width: 0; display: inline-flex; align-items: center; gap: 6px; }
.sheet .size-row { display: flex; flex-wrap: wrap; gap: 6px; }
.sheet .size-row .btn {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  min-width: 48px;
  padding: 6px 9px 5px;
  font-size: 16px;
  line-height: 1.1;
}
.sheet .size-row .btn small { font-size: 11px; font-weight: 600; color: var(--ink-soft); }

/* when a line was said: a small faint stamp above its face, in the headroom
   the card keeps above the bars — light beige, so it sits back into the
   card and is there when looked for. Centred over the face, sized with
   everything else, and never a tap target of its own. */
.line .stamp {
  position: absolute;
  top: calc(5px * var(--scale));
  /* a box from the card's right edge to the grip's left edge, so its
     centred text stands over the grip's centre */
  inset-inline-end: 0;
  width: calc(var(--grip-w) + 2 * var(--inset));
  text-align: center;
  font-size: calc(13px * var(--scale));
  font-weight: 700;
  line-height: 1;
  color: #d2c5a8;
  pointer-events: none;
  z-index: 41;
}

/* ── the tray under a play page ───────────────────────────────────────
   A running play page keeps the talk page's own tray beneath it (Simon's
   ask, after a word bar of its own): the box holds the page's line — the
   picture the child last touched, the words the page put there, a line of
   bubbles tapped on the page — with everything the tray can do, and
   beside say a green send button that carries the line to the talk page's
   sentence. games.js lends the tray and adds the button. The button is
   say's twin: the same shape and green, the arrow sticker over the word as
   say wears its speech bubble. */
#send-talk {
  display: none;
  flex: none;
  align-self: stretch;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(2px * var(--scale));
  min-width: calc(var(--bar-h) * 1.35);
  padding: calc(4px * var(--scale)) calc(14px * var(--scale));
  border-radius: 999px;
  border: 2.5px solid var(--verb-b);
  background: var(--verb-f);
  font-family: inherit;
  font-size: var(--chip-fs);
  font-weight: 800;
  line-height: 1;
  color: var(--ink);
  box-shadow: 0 3px 0 rgba(96, 70, 28, 0.08);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: opacity 0.25s;
}
#app.playing #send-talk,
#game-preview #send-talk { display: inline-flex; }
#send-talk .icon-pic.ready {
  display: block;
  width: calc(var(--bar-h) * 0.62);
  height: calc(var(--bar-h) * 0.62);
  margin: 0;
  background: #34a34a;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
  pointer-events: none;
}
#send-talk .icon-pic img { display: none; } /* the mask is the picture */
#send-talk:active { scale: 0.95; }
/* nothing to send yet: the button waits, faded */
#tray.idle #send-talk { opacity: 0.45; }
/* the tray inside a grown-up's preview: between the frame and the
   buttons, as it sits under the child's page, without the screen-bottom
   room it keeps in the app */
#game-preview #tray {
  padding-bottom: 0;
  border-radius: 26px;
  border: 2px solid var(--edge);
  box-shadow: none;
}
/* the words on their way: a copy of the box's bars flies up to the talk
   button and fades as it lands (the preview's send) */
.fly-word {
  position: fixed;
  z-index: 60;
  display: flex;
  gap: var(--gap);
  pointer-events: none;
  transition: transform 0.55s cubic-bezier(0.4, 0, 0.6, 1), opacity 0.55s ease-in;
}
/* how many words wait in the sentence while a play page is open */
#talk-btn { position: relative; }
#talk-btn .tray-count {
  position: absolute;
  top: -9px;
  inset-inline-end: -9px;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  border: 2px solid var(--verb-b);
  background: var(--verb-f);
  color: var(--ink);
  font-size: 13px;
  font-weight: 800;
  line-height: 18px;
  text-align: center;
  animation: pop 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

/* ── the thinking bubble ─────────────────────────────────────────────────
   The one way the app shows a wait to the child (Simon's rule: never a
   loading word, never a getting-ready screen): a drawn thought bubble
   trail of three little clouds that appears one at a time from the bottom
   up, the small cloud first, the medium one above it, the big one on top.
   It is a moving picture made
   the way the play pages' are (scripts/make-thinking.js: a still from the
   image model, tiled and animated by the animations' model, cut into three
   650px frames): the frames side by side in /icons/thinking.png, worn as a
   background and stepped along by think-steps, so the whole animation is
   one picture and one rule, no shapes drawn here (the CSS cloud with its
   dots and puffs went on 2026-09-19, at Simon's ask). The play pages carry
   the same picture inside their sandbox (SHIM_CSS in games.js mirrors
   this, the strip handed across as data on the first beat — keep the two
   alike). */
.thinking-bubble {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: 18em;
  height: 18em;
  font-size: clamp(14px, 2.4vmin, 22px);
  pointer-events: none;
  z-index: 5;
  background: url("/icons/thinking.png") 0 0 / 300% 100% no-repeat;
  animation: think-steps 1.35s steps(3, end) infinite, think-float 2.4s ease-in-out infinite;
}
/* three frames across the strip, shown in turn: the strip is three times
   the box wide, and its position steps 0, 50 and 100 percent */
@keyframes think-steps {
  from { background-position-x: 0%; }
  to { background-position-x: 150%; }
}
@keyframes think-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-0.3em); }
}
