/* ============================================================
   Sensory Speak
   A single-page AAC board. No page navigation, ever — everything
   the child needs is on one screen.
   ============================================================ */

/* ---------- 1. Theme tokens ---------------------------------- */

:root {
  --bg:        #f4f1ea;
  --fg:        #1b1b1b;
  --panel:     #ffffff;
  --panel-fg:  #1b1b1b;
  --muted:     #6b6b6b;
  --line:      #d9d4c8;
  --accent:    #2f6f4f;
  --accent-fg: #ffffff;
  --danger:    #b3261e;

  /* Button surface. Themes override these; color modes override them per
     button via an inline --btn-bg. */
  --btn-bg:     #ffffff;
  --btn-fg:     #1b1b1b;
  --btn-border: #c9c4b8;

  --border-w: 3px;
  --radius:   18px;
  --gap:      10px;
  --label-size: clamp(0.85rem, 2.2vw, 1.35rem);
  --symbol-size: clamp(1.8rem, 7vw, 4.2rem);
  --focus: #0a84ff;

  /* Set to 1 by the high-contrast themes so buttons can drop their own
     colors and adopt the theme's pairing instead. */
  --hc: 0;
}

/* High contrast pairings. These are the four combinations low-vision users are
   most often prescribed; we keep the pure pairing on the button faces and push
   part-of-speech color into a stripe so no information is lost. */

.theme-hc-bw {   /* white on black — the most common prescription */
  --bg: #000000; --fg: #ffffff; --panel: #000000; --panel-fg: #ffffff;
  --muted: #c8c8c8; --line: #ffffff; --accent: #ffffff; --accent-fg: #000000;
  --btn-bg: #000000; --btn-fg: #ffffff; --btn-border: #ffffff;
  --danger: #ff6b6b; --focus: #ffd400; --hc: 1;
}

.theme-hc-wb {   /* black on white */
  --bg: #ffffff; --fg: #000000; --panel: #ffffff; --panel-fg: #000000;
  --muted: #333333; --line: #000000; --accent: #000000; --accent-fg: #ffffff;
  --btn-bg: #ffffff; --btn-fg: #000000; --btn-border: #000000;
  --danger: #b3261e; --focus: #0000ff; --hc: 1;
}

.theme-hc-yb {   /* yellow on black — highest luminance contrast available */
  --bg: #000000; --fg: #ffe500; --panel: #000000; --panel-fg: #ffe500;
  --muted: #d4c200; --line: #ffe500; --accent: #ffe500; --accent-fg: #000000;
  --btn-bg: #000000; --btn-fg: #ffe500; --btn-border: #ffe500;
  --danger: #ff6b6b; --focus: #ffffff; --hc: 1;
}

.theme-hc-by {   /* black on yellow */
  --bg: #ffe500; --fg: #000000; --panel: #ffe500; --panel-fg: #000000;
  --muted: #3a3400; --line: #000000; --accent: #000000; --accent-fg: #ffe500;
  --btn-bg: #ffe500; --btn-fg: #000000; --btn-border: #000000;
  --danger: #8b0000; --focus: #0000ff; --hc: 1;
}

/* ---------- 2. Base ------------------------------------------ */

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;                  /* the board must never scroll away */
  background: var(--bg);
  color: var(--fg);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
}

/* A communication board is not a document: text selection and long-press
   callouts only ever get in a child's way. */
body {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
}

button { font: inherit; color: inherit; cursor: pointer; }

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

:focus-visible { outline: 4px solid var(--focus); outline-offset: 3px; }

/* ---------- 3. App shell ------------------------------------- */

#app { display: flex; flex-direction: column; height: 100%; }

.screen { display: none; height: 100%; flex-direction: column; }
.screen.active { display: flex; }

/* ---------- 4. The board ------------------------------------- */

#board-screen { position: relative; }

.board-area {
  flex: 1;
  display: flex;
  min-height: 0;
  min-width: 0;
}

.board-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  position: relative;
  /* A small, fixed frame. The gap between buttons used to double as the
     frame, which on a phone spent a third of the width on empty margins. */
  padding: clamp(6px, 1.5vmin, 14px);
  min-height: 0;
  min-width: 0;
}

/* ---- side rail ----
   A navigation column down one edge, the way TD Snap keeps its page tools
   beside the board. With folders placed here it holds Home and every folder;
   otherwise only the pages a family has pinned. Narrow on purpose: it must
   not eat the space the words need. */

#sidebar {
  display: none;
  flex-direction: column;
  gap: 6px;
  padding: 8px 6px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  flex: none;
  width: 104px;
  /* The rail is a different KIND of thing from the board — it moves you
     somewhere rather than saying a word — so it gets its own tinted surface
     and a real edge instead of the same white panel and hairline as
     everything else. Tinting toward the accent means it follows whatever
     theme is set, including the high-contrast ones, instead of hard-coding a
     color that only works on the default background. */
  background: var(--panel);
  background: color-mix(in srgb, var(--accent) 14%, var(--panel));
  border-right: 3px solid var(--line);
  border-right: 3px solid color-mix(in srgb, var(--accent) 50%, var(--line));
  box-shadow: inset -9px 0 14px -12px rgba(0, 0, 0, .55);
}
body.rail-left  #sidebar { display: flex; }
body.rail-right #sidebar {
  display: flex;
  order: 2;
  border-right: none;
  border-left: 3px solid var(--line);
  border-left: 3px solid color-mix(in srgb, var(--accent) 50%, var(--line));
  box-shadow: inset 9px 0 14px -12px rgba(0, 0, 0, .55);
}
body.rail-right .board-wrap { order: 1; }
/* Navigation and page lock live in the rail when folders use that placement,
   so the header does not need duplicate controls. */
body.folders-in-rail #home-btn,
body.folders-in-rail #page-lock-btn { display: none !important; }

@media (max-width: 560px) {
  #sidebar { width: 74px; padding: 6px 4px; gap: 5px; }
}

.rail-btn {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 6px 4px;
  border: 2px solid var(--btn-border);
  border-radius: 14px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-weight: 700;
  font-size: 0.74rem;
  line-height: 1.1;
  text-align: center;
  /* Share the column's height so a full set of folders fits without
     scrolling wherever it can, and scrolls only when it truly cannot. */
  flex: 1 1 0;
  min-height: 56px;
  max-height: 112px;
  overflow: hidden;
  word-break: break-word;
}
.rail-btn .rail-icon { font-size: 1.55rem; line-height: 1; display: grid; place-items: center; }
.rail-btn .rail-icon img { width: 1.6em; height: 1.6em; object-fit: cover; border-radius: 6px; }
.rail-btn .rail-label {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.rail-btn.current {
  box-shadow: inset 0 0 0 3px var(--focus), 0 0 0 2px var(--focus);
}
.rail-btn.home {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}
@media (max-width: 560px) {
  .rail-btn { font-size: 0.66rem; min-height: 50px; border-radius: 12px; padding: 5px 3px; }
  .rail-btn .rail-icon { font-size: 1.3rem; }
  /* Phone: folders sit in a bottom rail so the grid keeps the full width. */
  .board-area { flex-direction: column; }
  body.rail-left #sidebar,
  body.rail-right #sidebar {
    order: 2;
    width: 100%;
    flex-direction: row;
    height: auto;
    min-height: 64px;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    gap: 6px;
    border-right: none;
    border-left: none;
    border-top: 3px solid color-mix(in srgb, var(--accent) 50%, var(--line));
    box-shadow: inset 0 9px 14px -12px rgba(0, 0, 0, .55);
  }
  body.rail-left .board-wrap,
  body.rail-right .board-wrap { order: 1; }
  body.rail-left #sidebar .rail-btn,
  body.rail-right #sidebar .rail-btn {
    flex: 1 0 auto;
    min-width: 64px;
    max-width: 104px;
    min-height: 56px;
    max-height: 72px;
  }
}

#grid {
  flex: 1;
  display: grid;
  gap: var(--gap);
  min-height: 0;
  width: 100%;
  align-content: stretch;
  justify-content: stretch;
}
body.motion-board #grid.motion-free { display: block; position: relative; overflow: hidden; }
body.motion-board #grid.motion-free .cell:not(.blank) {
  position: absolute; border-radius: 50%; touch-action: none; z-index: 1;
  box-sizing: border-box; padding: 10px;
}
body.motion-board #grid.motion-free .cell.motion-dragging { z-index: 4; opacity: .8; }
body.motion-board #grid.motion-free .motion-scene-photo {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  pointer-events: none; z-index: 0;
}
.rail-btn.lock { background:var(--panel); border-color:var(--accent); }
.rail-btn.lock[aria-pressed="true"] {
  background:var(--accent);
  color:var(--accent-fg);
  box-shadow:inset 0 0 0 3px var(--focus);
}

/* Stitch vocabulary. The keyboard owns the home canvas; MY WORDS opens the
   ordinary, fully editable Patchwork board built during setup. */
#grid.keyboard-grid {
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1.2vmin, 12px);
  padding: clamp(6px, 1.4vmin, 14px);
  overflow: auto;
  align-items: stretch;
  justify-content: stretch;
}
.keyboard-predictions {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(5px, 1vmin, 10px);
  flex: 0 0 clamp(48px, 12%, 78px);
}
.keyboard-prediction,
.keyboard-key {
  border: max(2px, var(--border-w, 2px)) solid var(--btn-fg, #17202a);
  border-radius: clamp(8px, 1.5vmin, 16px);
  color: var(--btn-fg, #17202a);
  background: var(--btn-bg, #fffdf8);
  box-shadow: 0 2px 4px rgba(0, 0, 0, .12);
  font: inherit;
  font-weight: 800;
  min-width: 0;
  touch-action: manipulation;
}
.keyboard-prediction {
  background: #e9ddff;
  color: #301364;
  font-size: clamp(.88rem, 2.8vmin, 1.45rem);
  overflow: hidden;
  text-overflow: ellipsis;
}
.keyboard-prediction:disabled { opacity: .42; }
.keyboard-letters {
  display: flex;
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  flex-direction: column;
  justify-content: stretch;
  gap: clamp(5px, 1vmin, 10px);
}
.keyboard-row {
  display: flex;
  flex: 1 1 0;
  justify-content: center;
  gap: clamp(3px, .75vmin, 8px);
  min-height: 42px;
}
.keyboard-letter {
  flex: 1 1 0;
  max-width: 10%;
  font-size: clamp(1.35rem, 5.2vmin, 2.8rem);
}
.keyboard-qwerty .keyboard-row:nth-child(2) { padding-inline: 4%; }
.keyboard-qwerty .keyboard-row:nth-child(3),
.keyboard-abc .keyboard-row:nth-child(3) { padding-inline: 12%; }
.keyboard-phrase {
  position: absolute;
  bottom: 0;
  width: 11%;
  height: calc((100% - clamp(10px, 2vmin, 20px)) / 3);
  padding: 4px;
  background: #d9f2df;
  color: #12462b;
  border-color: #2f855a;
  font-size: clamp(.58rem, 1.65vmin, .96rem);
  line-height: 1.08;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  z-index: 1;
}
.keyboard-phrase[hidden] { display: none; }
.keyboard-phrase-left { left: 0; }
.keyboard-phrase-right { right: 0; }
.keyboard-tools {
  display: grid;
  grid-template-columns: minmax(90px, 1.2fr) minmax(140px, 3fr) minmax(48px, .65fr) minmax(48px, .65fr) minmax(64px, .9fr);
  gap: clamp(5px, 1vmin, 10px);
  flex: 0 0 clamp(52px, 14%, 86px);
}
.keyboard-tools .keyboard-key { font-size: clamp(.92rem, 2.7vmin, 1.4rem); }
.keyboard-words {
  display: flex;
  align-items: center;
  justify-content: center;
  padding-inline: 10px;
  background: #c9efd3;
  color: #12462b;
  border-color: #2f855a;
}
.keyboard-words strong { text-align: center; line-height: 1; }
.keyboard-space { background: #f3efe7; }
.keyboard-delete { background: #ffe0df; color: #681c17; }
.keyboard-key:active,
.keyboard-prediction:active { transform: scale(.97); filter: brightness(.94); }

@media (max-width: 620px) {
  .keyboard-tools { grid-template-columns: minmax(76px, 1.15fr) minmax(95px, 2.2fr) repeat(3, minmax(42px, .7fr)); }
  .keyboard-words { padding-inline: 6px; }
  .keyboard-phrase { font-size: clamp(.5rem, 1.8vmin, .78rem); }
}

.folder-empty {
  position: absolute;
  inset: 12px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  text-align: center;
  padding: 18px;
  border-radius: 18px;
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  border: 2px dashed color-mix(in srgb, var(--accent) 35%, var(--line));
  color: var(--fg);
}
.folder-empty p {
  margin: 0;
  max-width: 28rem;
  font-size: 1rem;
  font-weight: 650;
  line-height: 1.45;
}

/* Gap presets — the CSS fallback used until board.js has measured the grid.
   The real gap is a share of the button size, computed per render, so that
   "very wide" means very wide on an iPad and merely wide on a phone instead
   of starving the buttons. These clamps track the same intent. */
.gap-tight  { --gap: 4px; }
.gap-normal { --gap: clamp(6px, 1.5vmin, 14px); }
.gap-wide   { --gap: clamp(10px, 3.5vmin, 36px); }
.gap-xwide  { --gap: clamp(14px, 6vmin, 60px); }

/* In checkerboard mode the empty tracks are already doing the separating.
   Letting the gap run at its full width on top of them stacks two kinds of
   space and leaves the buttons themselves too small to read, so the wider
   settings are capped here. The visible separation is unchanged. */
body.checker.gap-wide  { --gap: 8px; }
body.checker.gap-xwide { --gap: 12px; }

/* Border weight presets, for low vision. */
.border-normal { --border-w: 3px; }
.border-thick  { --border-w: 6px; }
.border-xthick { --border-w: 10px; }

/* Text size presets. These are multipliers rather than absolute sizes: the
   real size comes from the button itself (see the container query block
   below), so a preset means "bigger relative to the button" and never pushes
   the word out of a short button. */
.text-sm { --sym-scale: 0.78; --lab-scale: 0.8; }
.text-md { --sym-scale: 1;    --lab-scale: 1; }
.text-lg { --sym-scale: 1.1;  --lab-scale: 1.3; }
.text-xl { --sym-scale: 1.2;  --lab-scale: 1.65; }

/* Fallback for browsers without container query units: scale with the
   viewport, as before. Overridden below wherever containers are supported. */
.text-sm { --label-size: clamp(0.7rem, 1.7vw, 1rem);     --symbol-size: clamp(1.2rem, 4.5vw, 2.4rem); }
.text-md { --label-size: clamp(0.85rem, 2.2vw, 1.35rem); --symbol-size: clamp(1.6rem, 6vw, 3.4rem); }
.text-lg { --label-size: clamp(1.05rem, 3.2vw, 2rem);    --symbol-size: clamp(2rem, 7vw, 4.2rem); }
.text-xl { --label-size: clamp(1.3rem, 4.4vw, 3rem);     --symbol-size: clamp(2.4rem, 8vw, 5rem); }

/* --- the button itself --- */

.cell {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25em;
  padding: 0.4em;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  /* Border and corner radius follow the button's own size (--cell is set by
     board.js), so a small phone button keeps a crisp square shape instead of
     turning into a thick-rimmed circle. */
  border: min(var(--border-w), calc(var(--cell, 120px) * 0.06)) solid var(--btn-border);
  border-radius: min(var(--radius), calc(var(--cell, 120px) * 0.16));
  background: var(--btn-bg);
  color: var(--btn-fg);
  text-align: center;
  line-height: 1.1;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
  transition: transform 90ms ease-out, filter 90ms ease-out;
}
.cell:active { filter: brightness(0.92); transform: scale(0.985); }
.cell:active.has-pictogram,
.cell:active.has-photo { filter: brightness(0.96) saturate(1.08); }

/* A blank cell in checkerboard mode. Not a button — it exists purely to give
   each real button a moat of empty space. */
.cell.blank {
  border: none;
  background: transparent;
  box-shadow: none;
  pointer-events: none;
}

.cell .symbol {
  /* An explicit height, not a font-size that flex may squash, so the space
     left over for the word is knowable before the word is laid out. */
  height: var(--sym-h, var(--symbol-size));
  font-size: var(--sym-h, var(--symbol-size));
  line-height: 1;
  flex: 0 0 auto;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.cell .symbol img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 8px;
}

.cell.has-pictogram .symbol img.pictogram {
  width: auto;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
}

/* A real photograph fills a rounded frame across the face of the button,
   rather than floating like a symbol. */
.cell.has-photo .symbol { width: 100%; }
.cell.has-photo .symbol img.photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: min(12px, calc(var(--cell, 120px) * 0.09));
}

/* The invisible switch that gives an iPhone its haptic tick (see haptics.js).
   It must sit under the finger, above the button's own content — and never
   escape its host, so the host is always the switch's containing block. */
.haptic-host { position: relative; }
.haptic-switch {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  -webkit-appearance: switch;
  appearance: auto;
  opacity: 0;
  cursor: inherit;
  z-index: 3;
}

.cell .label {
  font-size: var(--label-size);
  font-weight: 700;
  letter-spacing: 0.01em;
  /* Never break a word across lines. A child learning to recognize "come"
     must not be shown "com / e" — the text shrinks to fit instead (see --len
     below). Multi-word labels still wrap at the space. */
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
  /* Never shrink and never get clipped — this is the part that communicates.
     A definite width gives the fit pass in board.js something to measure
     wrapping against. */
  flex: 0 0 auto;
  width: 100%;
}
/* Applied by the fit pass when a word needs the button's side padding to fit
   at the smallest legible size. */
.cell.tight-pad { padding-left: 1px; padding-right: 1px; }

/* Circles get no aspect relaxation — stretch one and it stops being a circle,
   then strands the space it was meant to occupy. 14.65% is the inset that
   turns a circle into its largest inscribed square, the only region where
   content is safe wherever it lands. tight-pad (4%) is only applied after
   the fitter has already hidden the picture and hit the minimum font size. */
.shape-circle .cell { border-radius: 50%; padding: 14.65%; }
.shape-circle .cell.tight-pad { padding: 4%; }
.shape-circle .cell .pos-stripe {
  top: 0; left: 0; right: 0; bottom: 0;
  height: auto;
  background: transparent;
  border-radius: 50%;
  box-shadow: inset 0 0 0 max(4px, calc(var(--cell, 120px) * 0.08)) var(--pos-color, transparent);
}
.shape-circle .cell .folder-tab { display: none; }
.shape-circle .cell .folder-more { right: 18%; bottom: 14%; }
.meaning-motion .cell .symbol { overflow: visible; }

/* Applied by the fit pass only when a word is wider than the button at the
   smallest legible size even without padding — a hyphenated word beats a
   clipped one. */
.cell .label.break-anywhere {
  overflow-wrap: anywhere;
  word-break: break-word;
  hyphens: auto;
}

/* ---- Content sized to the BUTTON, not the viewport --------------------
   Without this, a wide screen makes the emoji enormous no matter how short
   the button is, and the word gets pushed out of the bottom of the button.
   Container query units measure the button itself, so a short wide button in
   checkerboard / motor-planning mode gets a proportionate symbol and a word that always fits. */
@supports (container-type: size) {
  .cell { container-type: size; container-name: cell; }

  /* --len   = characters in the label's LONGEST word
     --lines = how many lines the label will wrap onto
     Both are set per button in board.js. The width term divides the button's
     width by the longest word so that word always fits on one line; the height
     term divides the available height by the number of lines. Whichever is
     smaller wins, so the text shrinks to fit rather than breaking or clipping. */
  /* These are the LARGEST sizes the preset would like, expressed relative to
     the button. They are a starting point only: the fit pass in board.js
     measures the text as actually laid out and shrinks it where it does not
     fit, which is the only reliable way to know where a browser will wrap. */
  .cell .symbol { --sym-h: min(calc(48cqh * var(--sym-scale, 1)), 36cqw); }
  .cell.has-photo .symbol { --sym-h: min(calc(52cqh * var(--sym-scale, 1)), 72cqw); }
  .cell .label  { font-size: calc(18cqh * var(--lab-scale, 1)); }

  .sym-none .cell .label { font-size: calc(48cqh * var(--lab-scale, 1)); }

  /* Very narrow buttons: the word needs the width more than the picture does. */
  @container cell (max-width: 76px) {
    .symbol { display: none; }
  }
}

/* Symbols off: the label takes the whole face and grows to fill it. Chosen by
   families whose child reads text faster than they parse a picture, and by
   low-vision users for whom one big word beats a small word under a symbol. */
.sym-none .cell .symbol { display: none; }
.sym-none .cell .label  { font-size: calc(var(--label-size) * 1.7); }

/* Part-of-speech stripe. In normal themes this is decorative reinforcement; in
   high contrast it carries the color information that the button face gave up. */
.cell .pos-stripe {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: max(4px, calc(var(--cell, 120px) * 0.08));
  background: var(--pos-color, transparent);
}
.cell.no-stripe .pos-stripe { display: none; }

/* --- folders ---
   A folder has to be tellable from a word at a glance and without relying on
   color, since the color is already carrying other meaning. It reads like a
   card with something behind it: a tab along the top edge, a stacked shadow,
   and a corner chevron — three redundant, non-color cues, none of which
   fight the word itself the way an underline or a doubled border did. */

.cell.is-folder {
  box-shadow: 3px 3px 0 0 var(--ink, rgba(0, 0, 0, 0.22));
}

.cell .folder-tab {
  position: absolute;
  top: 0;
  left: 12%;
  width: 32%;
  height: max(4px, calc(var(--cell, 120px) * 0.07));
  background: var(--ink, rgba(0, 0, 0, 0.26));
  border-radius: 0 0 6px 6px;
}

.cell .folder-more {
  position: absolute;
  right: 6%;
  bottom: 2%;
  font-size: clamp(11px, calc(var(--cell, 120px) * 0.18), 26px);
  font-weight: 900;
  line-height: 1;
  opacity: 0.7;
}

/* Under a high-contrast pairing the ink is the theme's foreground, and the
   part-of-speech stripe already occupies the top edge. */
[class*="theme-hc-"] .cell.is-folder { box-shadow: 3px 3px 0 0 var(--btn-fg); }
[class*="theme-hc-"] .cell .folder-tab { display: none; }

/* --- pressed / active states --- */

.cell.is-armed {                      /* first tap of auditory preview */
  filter: brightness(1.12);
  box-shadow: inset 0 0 0 4px var(--focus);
}

.cell.is-holding {                    /* hold-to-activate in progress */
  transform: scale(0.97);
}

/* The hold progress bar gives a visible countdown so a hold never feels like
   the button simply failed to respond. */
.cell .hold-bar {
  position: absolute;
  bottom: 0; left: 0;
  height: 6px;
  width: 0;
  background: var(--focus);
}

/* ---------- 5. Activation animations ------------------------- */
/* Each is a separate class so a child can be given exactly one, and it can be
   swapped without touching anything else. */

@keyframes ss-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-7px) rotate(-1.5deg); }
  30% { transform: translateX(7px) rotate(1.5deg); }
  45% { transform: translateX(-6px) rotate(-1deg); }
  60% { transform: translateX(6px) rotate(1deg); }
  80% { transform: translateX(-3px); }
}

@keyframes ss-pulse {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.14); }
  70%  { transform: scale(0.97); }
  100% { transform: scale(1); }
}

@keyframes ss-bounce {
  0%   { transform: translateY(0); }
  30%  { transform: translateY(-16px); }
  55%  { transform: translateY(0); }
  72%  { transform: translateY(-7px); }
  100% { transform: translateY(0); }
}

@keyframes ss-glow {
  0%   { box-shadow: 0 0 0 0 var(--glow-color, #fff), 0 0 0 0 var(--glow-color, #fff); }
  35%  { box-shadow: 0 0 26px 12px var(--glow-color, #fff), inset 0 0 22px 4px var(--glow-color, #fff); }
  100% { box-shadow: 0 0 0 0 transparent, inset 0 0 0 0 transparent; }
}

@keyframes ss-flash {
  0%, 100% { filter: brightness(1); }
  30%      { filter: brightness(2.1); }
  60%      { filter: brightness(1.3); }
}

.anim-shake  { animation: ss-shake  420ms ease-in-out; }
.anim-pulse  { animation: ss-pulse  380ms cubic-bezier(.34,1.56,.64,1); }
.anim-bounce { animation: ss-bounce 520ms cubic-bezier(.28,.84,.42,1); }
.anim-glow   { animation: ss-glow   620ms ease-out; }
.anim-flash  { animation: ss-flash  420ms ease-out; }

/* Respect the OS setting, but only for the decorative case: if a child's
   feedback depends on the animation, the app's own setting wins and we simply
   shorten it rather than removing it. */
@media (prefers-reduced-motion: reduce) {
  .cell { transition: none; }
  .anim-shake, .anim-pulse, .anim-bounce, .anim-glow, .anim-flash {
    animation-duration: 180ms;
  }
}

/* ---------- 6. Message bar ----------------------------------- */

#top-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px var(--gap);
  border-bottom: var(--border-w) solid var(--line);
  min-height: 64px;
  flex: none;
}

#page-title {
  font-weight: 800;
  font-size: 1.05rem;
  white-space: nowrap;
  opacity: 0.75;
}
/* On Home the title is redundant — the board itself says where you are. */
body.at-home #page-title { display: none; }

/* The words themselves. Typography and box are set by the message field in
   patchwork.css — this rule deliberately sets NEITHER font-size nor
   min-height, because it used to do both and fought the field for the header's
   height (a viewport-scaled 25px here pushed a 44px field to 51px and the bar
   over its 60px budget). The label inherits its size from the Speak button. */
#message-text {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* With the strip switched off, Home and Stop still hold their places. */
body:not(.show-message-bar) #message-text,
body:not(.show-message-bar) #msg-speak,
body:not(.show-message-bar) #msg-back,
body:not(.show-message-bar) #msg-clear { display: none; }
body:not(.show-message-bar) #page-title { flex: 1; }

.msg-btn {
  border: var(--border-w) solid var(--line);
  background: var(--panel);
  color: var(--panel-fg);
  border-radius: 12px;
  padding: 10px 14px;
  font-weight: 700;
  white-space: nowrap;
  flex: none;
  min-height: 48px;
}

/* Stop is the one control an adult reaches for while a child is mid-spiral,
   so it is colored to be findable without reading it. */
.msg-btn.stop {
  background: var(--danger);
  color: #fff;
  border-color: var(--danger);
}
.theme-hc-bw .msg-btn.stop, .theme-hc-yb .msg-btn.stop {
  background: #000; color: var(--danger); border-color: var(--danger);
}

.msg-btn.home {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}

/* ---------- 7. Adult controls -------------------------------- */
/* Deliberately small, low-contrast and corner-anchored: an adult can find it,
   a child exploring the board is unlikely to land on it. The long-press gate
   in app.js is the real lock. */

#adult-btn {
  position: absolute;
  right: 6px;
  bottom: 6px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 2px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  opacity: 0.45;
  font-size: 18px;
  display: grid;
  place-items: center;
  z-index: 20;
}
#adult-btn:hover { opacity: 1; }

#hold-hint {
  position: absolute;
  right: 56px;
  bottom: 14px;
  background: var(--panel);
  color: var(--panel-fg);
  border: 2px solid var(--line);
  border-radius: 10px;
  padding: 6px 10px;
  font-size: 13px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms;
  z-index: 20;
}
#hold-hint.show { opacity: 1; }

/* ---------- 8. Panels & setup -------------------------------- */

.panel {
  position: fixed;
  inset: 0;
  background: var(--bg);
  color: var(--fg);
  z-index: 50;
  display: none;
  flex-direction: column;
  overflow: hidden;
}
.panel.open { display: flex !important; }

.panel-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 2px solid var(--line);
  background: var(--panel);
}
.panel-head h2 { margin: 0; font-size: 1.15rem; flex: 1; }

.panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  -webkit-overflow-scrolling: touch;
  /* Reaching the end of a settings list must not hand the scroll to the
     document: on Android that is pull-to-refresh, which reloads the board
     out from under the adult. */
  overscroll-behavior: contain;
}

.panel-foot {
  display: flex;
  gap: 10px;
  padding: 12px 16px;
  border-top: 2px solid var(--line);
  background: var(--panel);
}

.btn {
  border: 2px solid var(--line);
  background: var(--panel);
  color: var(--panel-fg);
  border-radius: 12px;
  padding: 12px 16px;
  font-weight: 600;
  min-height: 48px;
}
.btn.primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.btn.danger  { color: var(--danger); border-color: var(--danger); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn.grow { flex: 1; }

fieldset {
  border: 2px solid var(--line);
  border-radius: 14px;
  margin: 0 0 18px;
  padding: 14px;
}
legend { font-weight: 700; padding: 0 8px; font-size: 1rem; }

.field { margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }
.field > label { display: block; font-weight: 600; margin-bottom: 6px; }
.field .hint { display: block; font-weight: 400; color: var(--muted); font-size: 0.85rem; margin-top: 4px; }
.field-error {
  display: block; margin: 6px 0 0; color: var(--danger); font-weight: 600; font-size: 0.9rem;
}
.field-error[hidden] { display: none; }
input.invalid, input[aria-invalid="true"] { border-color: var(--danger); }

select, input[type="text"], input[type="number"], textarea {
  width: 100%;
  padding: 11px 12px;
  border: 2px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  color: var(--panel-fg);
  font: inherit;
  min-height: 48px;
}

input[type="range"] { width: 100%; }

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.row > * { flex: 1 1 auto; }

/* Segmented choice control — used everywhere a setting has 2-5 options. */
.seg { position: relative; display: flex; flex-wrap: wrap; gap: 8px; width: 100%; }
.seg input { position: absolute; opacity: 0; pointer-events: none; }
.seg label {
  flex: 1 1 auto;
  text-align: center;
  padding: 11px 12px;
  border: 2px solid var(--line);
  border-radius: 10px;
  font-weight: 600;
  min-height: 48px;
  display: grid;
  place-items: center;
  background: var(--panel);
}
.seg input:checked + label {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}
.seg input:focus-visible + label { outline: 4px solid var(--focus); outline-offset: 2px; }

.switch { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.switch input { width: 26px; height: 26px; flex: none; accent-color: var(--accent); }
.switch span { font-weight: 600; }

/* --- word picker --- */

.wordgroup { margin-bottom: 20px; }
.wordgroup h4 { margin: 0 0 2px; font-size: 1rem; }
.wordgroup .grouphint { margin: 0 0 10px; color: var(--muted); font-size: 0.85rem; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 13px;
  border: 2px solid var(--line);
  border-radius: 999px;
  background: var(--panel);
  color: var(--panel-fg);
  font-weight: 600;
  min-height: 46px;
}
.chip[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}
.chip .dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--pos-color, transparent);
  border: 1px solid rgba(0,0,0,.25);
  flex: none;
}
.chip .star { font-size: 0.75rem; opacity: 0.9; }

/* --- folder picker --- */

.folder-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px;
  border: 2px solid var(--line);
  border-radius: 12px;
  margin-bottom: 8px;
}
.setup-folder-actions { flex-wrap: wrap; margin: 10px 0; }
.folder-group-title { margin: 20px 0 8px; color: var(--fg); font-size: 1rem; }
.folder-group-title:first-child { margin-top: 8px; }
.folder-badge { display: inline-block; margin-left: 8px; padding: 2px 7px; border-radius: 999px; background: var(--accent); color: var(--accent-fg); font-size: .68rem; vertical-align: middle; }
.folder-swatch {
  width: 42px; height: 42px;
  border-radius: 10px;
  display: grid; place-items: center;
  font-size: 22px;
  flex: none;
  border: 2px solid rgba(0,0,0,.2);
}
.folder-sub {
  display: block;
  font-weight: 400;
  font-size: 0.82rem;
  color: var(--muted);
}

/* --- picture picker --- */

.pic-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 10px;
  margin-top: 12px;
}
.pic-grid button {
  border: 3px solid var(--line);
  border-radius: 12px;
  background: #fff;
  padding: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.pic-grid button[aria-pressed="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent);
}
.pic-grid img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
}
.pic-grid .cap {
  font-size: 0.72rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.pic-preview img {
  max-width: 180px;
  max-height: 180px;
  border: 2px solid var(--line);
  border-radius: 12px;
  margin-top: 12px;
}

.pic-spinner { color: var(--muted); font-style: italic; }

/* --- intent coverage report --- */

.intent-warn, .intent-ok {
  border: 2px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 16px;
}
.intent-warn { border-color: var(--danger); }
.intent-warn p { margin: 0 0 8px; }
.intent-warn ul { margin: 0 0 8px; padding-left: 20px; }
.intent-warn li { margin-bottom: 6px; }
.intent-ok { border-color: var(--accent); margin: 0 0 16px; }
.intent-ok { color: var(--accent); font-weight: 600; }

#selection-count {
  position: sticky;
  top: -16px;
  z-index: 5;
  margin: -16px -16px 14px;
  padding: 12px 16px;
  background: var(--panel);
  border-bottom: 2px solid var(--line);
  font-weight: 700;
}
#selection-count.over { color: var(--danger); }

/* --- board preview inside setup --- */

#preview {
  display: grid;
  gap: 4px;
  border: 2px solid var(--line);
  border-radius: 10px;
  padding: 8px;
  background: var(--bg);
  aspect-ratio: 4 / 3;
  max-height: 260px;
}
#preview .pcell {
  border-radius: 4px;
  border: 1px solid rgba(0,0,0,.18);
  min-height: 0;
}
#preview .pcell.blank { border: none; background: transparent; }

/* --- edit mode --- */

body.edit-mode .cell { cursor: pointer; }
body.edit-mode .cell.blank { pointer-events: auto; }
body.edit-mode button.cell.blank {
  border: 2px dashed var(--line);
  background: transparent;
}
body.edit-mode button.cell.blank::after {
  content: "+";
  position: absolute;
  top: 2px; right: 6px;
  font-size: 14px;
  opacity: 0.5;
}

#edit-banner {
  display: none;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: #e85d04;
  color: #fff;
  font-weight: 700;
}
body.edit-mode #edit-banner { display: flex; }
#edit-banner .spacer { flex: 1; }
#edit-banner .btn { background: transparent; color: inherit; border-color: currentColor; min-height: 40px; padding: 8px 12px; }

/* --- recording indicator --- */

.rec-dot {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--danger);
  display: inline-block;
  margin-right: 8px;
  animation: ss-recblink 1s steps(2, start) infinite;
}
@keyframes ss-recblink { 0%, 100% { opacity: 1; } 50% { opacity: 0.15; } }

/* --- toast --- */

#toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%) translateY(20px);
  background: #1b1b1b;
  color: #fff;
  padding: 13px 20px;
  border-radius: 12px;
  font-weight: 600;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms, transform 180ms;
  z-index: 100;
  max-width: 90vw;
  text-align: center;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* --- setup steps --- */

.step { display: none; }
.step.active { display: block; }

#step-dots { display: flex; gap: 6px; }
#step-dots .dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--line);
}
#step-dots .dot.on { background: var(--accent); }

/* ---------- 9. Landscape phones ------------------------------ */
/* Short viewports are the normal case for a phone held sideways; give the grid
   every pixel we can. */
@media (max-height: 480px) {
  #message-bar { min-height: 48px; padding: 4px var(--gap); }
  .board-wrap { padding: 6px; }
}

@media print { #adult-btn, #hold-hint { display: none; } }

.pw-home {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 32px 20px calc(24px + env(safe-area-inset-bottom));
  text-align: center;
  overflow: auto;
}
.pw-home-patchy { border-radius: 24px; object-fit: cover; }
.pw-home-wordmark { height: 48px; width: auto; object-fit: contain; }
.pw-home-lede { margin: 0; font-size: 18px; color: var(--muted); max-width: 28rem; }
.pw-home-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.pw-home-boards { width: min(520px, 100%); text-align: left; display: flex; flex-direction: column; gap: 8px; }
.pw-home-boards-title { margin: 12px 0 0; font-size: 15px; }
.pw-home-board {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  width: 100%; min-height: 48px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: 14px; background: var(--panel);
  text-align: left;
}
.pw-home-board[aria-current="true"] { outline: 3px solid var(--accent); }
.pw-home-board small { color: var(--muted); }

#toast.error { background: #8b1a12; }
@keyframes refuse-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-8px); }
  40% { transform: translateX(8px); }
  60% { transform: translateX(-6px); }
  80% { transform: translateX(6px); }
}
.refuse-shake { animation: refuse-shake .45s ease; }

.action-list { display: flex; flex-direction: column; gap: 8px; }
.action-row {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  min-height: 48px; padding: 8px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--bg); touch-action: pan-y;
}
.action-row select { flex: 1; min-height: 44px; }
.action-remove { flex: none; }
.pic-emoji { font-size: 42px; line-height: 1; display: grid; place-items: center; min-height: 64px; }
