/* Happy Lucky Chacho — the board's design.
   Tokens, type and shapes come from happyluckychacho.org so the board and the
   project look like one thing. Two rules govern everything else:

   1. Contrast on the child's face. Light is the default because the screen is
      a fill light, and the iris is only tens of pixels across.
   2. The chosen tile must be unmistakable from across a room. A child with
      cortical visual impairment reads *one* strong, consistent highlight far
      better than subtle shading, so focus is a thick ink ring, a hard shadow,
      and everything else stepping back. */

/* Fonts are vendored (SIL OFL, see THIRD_PARTY_NOTICES.md) so the board makes
   no third-party requests and works offline. Each has a system fallback. */
@font-face { font-family: "Luckiest Guy"; font-weight: 400; font-display: swap; src: url(brand/fonts/LuckiestGuy.woff2) format("woff2"); }
@font-face { font-family: "Poppins"; font-weight: 400; font-display: swap; src: url(brand/fonts/Poppins-400.woff2) format("woff2"); }
@font-face { font-family: "Poppins"; font-weight: 600; font-display: swap; src: url(brand/fonts/Poppins-600.woff2) format("woff2"); }
@font-face { font-family: "Poppins"; font-weight: 700; font-display: swap; src: url(brand/fonts/Poppins-700.woff2) format("woff2"); }
@font-face { font-family: "Noto Sans Ethiopic"; font-weight: 400 700; font-display: swap; src: url(brand/fonts/NotoSansEthiopic.woff2) format("woff2"); }

:root {
  /* Brand */
  --ground: #f6fffa;
  --paper: #ffffff;
  --ink: #1f1a1c;
  --ink-2: #5a5254;
  --ink-3: #8b8386;
  --line: #e3ede8;
  --rose: #e47fc8;      --rose-deep: #c75aa8;    --rose-soft: #fbe9f5;
  --teal: #4b9b9d;      --teal-deep: #377f81;    --teal-soft: #e2f2f1;
  --gold: #f3bc29;      --gold-soft: #fff4d1;
  --spring: cubic-bezier(.34, 1.56, .64, 1);
  --shadow: 0 18px 40px -22px rgba(31, 26, 28, .28);

  /* The app's own names, mapped onto the brand */
  --bg: var(--ground);
  --panel: var(--paper);
  --muted: var(--ink-2);
  --edge: var(--paper);
  --edge-ink: var(--ink);
  --focus: var(--ink);           /* the highlight ring: darkest thing on screen */
  --focus-glow: rgba(31, 26, 28, .18);

  /* Tile colours. Each carries text that meets 4.5:1 on it, which is why the
     gold and rose tiles use ink text and the rest use white. */
  --aqua:   #377f81;  --aqua-ink:   #ffffff;
  --green:  #2f7d4f;  --green-ink:  #ffffff;
  --indigo: #3560c4;  --indigo-ink: #ffffff;
  --amber:  #f3bc29;  --amber-ink:  #1f1a1c;
  --red:    #c8432f;  --red-ink:    #ffffff;
  --pink:   #e47fc8;  --pink-ink:   #1f1a1c;
  --slate:  #5a5254;  --slate-ink:  #ffffff;

  --cal-bg: #ffffff;
  --cal-dot: var(--ink);
  --cal-ring: var(--teal);
  --cal-fill: var(--gold);

  --font-display: "Luckiest Guy", "Poppins", system-ui, sans-serif;
  --font-text: "Poppins", "Segoe UI", Nyala, Ebrima, "Noto Sans Ethiopic", system-ui, sans-serif;
  --font-ethiopic: "Noto Sans Ethiopic", Nyala, Ebrima, "Poppins", system-ui, sans-serif;
}

:root[data-theme="dark"] {
  /* For children who find a white screen uncomfortable. */
  --bg: #14181a;
  --panel: #1f2528;
  --ink: #f4f7f5;
  --ink-2: #b7c0bc;
  --ink-3: #8f9995;
  --line: #2f383b;
  --edge: #1f2528;
  --edge-ink: #f4f7f5;
  --focus: var(--gold);
  --focus-glow: rgba(243, 188, 41, .35);
  --aqua: #2f7476;  --green: #2a6f47;  --indigo: #3556ad;  --red: #b83d2b;  --slate: #4a5155;
  --amber: #e2ac1f; --pink: #d46fb8;
  --cal-bg: #0f1214;
  --cal-dot: var(--gold);
  --cal-ring: var(--teal);
  --cal-fill: var(--gold);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-text);
  overflow: hidden;
  /* A child leaning on the screen should not select text or trigger zoom. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}
.am, :lang(am) { font-family: var(--font-ethiopic); }

/* The brand button: pill, ink border, hard shadow, springy press. */
.btn, #start button, #settings-btn, #settings button, .cal-actions button {
  font-family: var(--font-text);
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--paper);
  border: 2.5px solid var(--ink);
  border-radius: 999px;
  padding: .8rem 1.4rem;
  cursor: pointer;
  box-shadow: 4px 4px 0 var(--ink);
  transition: transform .2s var(--spring), box-shadow .2s;
}
.btn:hover, #start button:hover, #settings-btn:hover, #settings button:hover,
.cal-actions button:hover { transform: translate(-1px, -1px); box-shadow: 6px 6px 0 var(--ink); }
.btn:active, #start button:active, #settings button:active,
.cal-actions button:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.btn.gold, #start button, .cal-actions button.primary { background: var(--gold); color: #1f1a1c; }
:root[data-theme="dark"] .btn, :root[data-theme="dark"] #settings button,
:root[data-theme="dark"] #settings-btn, :root[data-theme="dark"] .cal-actions button { box-shadow: 4px 4px 0 rgba(0,0,0,.6); }

/* ---------- brand mark ---------- */

.mark { width: 1em; height: 1em; vertical-align: -0.15em; }
.brand {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--font-display);
  letter-spacing: .02em;
  color: var(--ink);
  text-decoration: none;
}
.brand .mark { width: 1.6em; height: 1.6em; }
.brand .w { color: var(--teal-deep); }
.brand .w2 { color: var(--rose-deep); }
.brand .w3 { color: var(--ink); }
:root[data-theme="dark"] .brand .w { color: var(--teal); }
:root[data-theme="dark"] .brand .w2 { color: var(--rose); }

/* "Powered by": on the board it is text and a mark, never a link — a gaze
   user must not be able to wander off the board by looking at a corner. */
.powered { display: flex; justify-content: center; flex: 0 0 auto; padding: 2px 0 0; }
.powered .mark { width: 1.6rem; height: 1.6rem; opacity: .9; }

/* ---------- start screen ---------- */

#start {
  position: fixed;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 14px;
  text-align: center;
  background: var(--bg);
  overflow: hidden;
  z-index: 60;
  padding: 24px;
}
#start > * { position: relative; z-index: 1; }
#start .sky { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
/* Big soft blobs of the brand colours, drifting. */
#start .blob { position: absolute; border-radius: 50%; animation: drift 14s ease-in-out infinite alternate; }
#start .b1 { width: 40vmax; height: 40vmax; left: -14vmax; top: -12vmax; background: var(--rose-soft); }
#start .b2 { width: 30vmax; height: 30vmax; right: -8vmax; top: -8vmax; background: var(--gold-soft); animation-delay: -4s; }
#start .b3 { width: 36vmax; height: 36vmax; right: -12vmax; bottom: -14vmax; background: var(--teal-soft); animation-delay: -8s; }
#start .b4 { width: 26vmax; height: 26vmax; left: -6vmax; bottom: -10vmax; background: #ebe7ff; animation-delay: -2s; }
#start .b5 { width: 18vmax; height: 18vmax; left: 44vw; bottom: -8vmax; background: #e0f7e8; animation-delay: -11s; }
:root[data-theme="dark"] #start .blob { opacity: .22; }
/* Things floating up, slowly, forever. */
#start .float {
  position: absolute; bottom: -10vh; font-size: clamp(1.4rem, 4.5vh, 2.8rem); opacity: 0;
  animation: rise 11s linear infinite;
  filter: drop-shadow(0 3px 0 rgba(0, 0, 0, .08));
}
#start .f1 { left: 6%; }  #start .f2 { left: 18%; animation-delay: -3s; }
#start .f3 { left: 30%; animation-delay: -7s; }  #start .f4 { left: 44%; animation-delay: -1.5s; }
#start .f5 { left: 58%; animation-delay: -9s; }  #start .f6 { left: 70%; animation-delay: -5s; }
#start .f7 { left: 82%; animation-delay: -2.5s; } #start .f8 { left: 92%; animation-delay: -8s; }

#start .hero-mark { width: clamp(96px, 18vh, 170px); height: auto; animation: bob 3.2s ease-in-out infinite; }
#start h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.6rem, 9vh, 4.4rem);
  line-height: 1;
  margin: 0;
  letter-spacing: .03em;
}
/* Every letter its own colour, popping in one after another. */
#start h1 .letters { display: inline-flex; letter-spacing: .01em; }
#start h1 .letters span {
  display: inline-block;
  animation: popin .6s var(--spring) backwards;
  animation-delay: calc(var(--i) * 90ms);
  text-shadow: 3px 3px 0 rgba(31, 26, 28, .12);
}
#start h1 .letters span:nth-child(1) { color: var(--teal-deep); }
#start h1 .letters span:nth-child(2) { color: var(--rose-deep); }
#start h1 .letters span:nth-child(3) { color: #d9a300; }
#start h1 .letters span:nth-child(4) { color: var(--indigo); }
#start h1 .letters span:nth-child(5) { color: var(--green); }
#start h1 .letters span:nth-child(6) { color: var(--red); }
#start h1 .am { display: block; font-size: .4em; color: var(--rose-deep); margin-top: 10px; letter-spacing: 0; }
#start p { color: var(--ink-2); max-width: 44ch; line-height: 1.55; margin: 0; }
#start button { margin-top: 6px; font-size: 1.4rem; padding: 18px 48px; animation: pulse 2.4s ease-in-out infinite; }
#start.hidden { display: none; }
#start-note { color: var(--muted); font-size: .9rem; min-height: 1.2em; margin: 0; }
#start-note.bad { color: #b4342a; max-width: 46ch; }
#ios-hint { font-size: .82rem; color: var(--ink-2); max-width: 40ch; background: var(--gold-soft); border-radius: 14px; padding: 8px 14px; }
:root[data-theme="dark"] #start-note.bad { color: #ffb08a; }

/* ---------- the teaching guide ---------- */

/* The way in, for the adult: a small pill in the corner of the start screen
   (out of the flow, so the screen is no taller for it) and at the top of
   Settings. Gold until the guide has been opened once on this device. */
.guide-link {
  display: inline-flex; align-items: center; gap: .4em;
  font: 600 .85rem/1.2 var(--font-text); color: var(--ink); background: var(--panel);
  border: 2px solid var(--ink); border-radius: 999px; padding: 6px 12px;
  box-shadow: 3px 3px 0 var(--ink); text-decoration: none;
}
.guide-link.new { background: var(--gold); color: #1f1a1c; }
.guide-link .short { display: none; }
#start > .guide-link {
  position: absolute; z-index: 2;
  top: max(10px, env(safe-area-inset-top)); right: max(12px, env(safe-area-inset-right));
}
#settings .guide-link { margin: 0 0 4px; }
.guide-more { color: inherit; font-weight: 600; text-decoration: underline; }
@media (max-width: 600px) {
  #start > .guide-link .long { display: none; }
  #start > .guide-link .short { display: inline; }
}

/* The guide itself: guide.html in a frame, over the board or the dashboard.
   Above the start screen and Settings, below calibration. */
#guide { position: fixed; inset: 0; z-index: 80; display: flex; flex-direction: column; background: var(--bg); }
#guide[hidden] { display: none; }
.guide-bar {
  display: flex; align-items: center; gap: 8px; padding: 6px 10px;
  padding-top: max(6px, env(safe-area-inset-top));
  padding-left: max(10px, env(safe-area-inset-left));
  padding-right: max(10px, env(safe-area-inset-right));
  background: var(--panel); border-bottom: 2.5px solid var(--ink);
}
.guide-bar .mark { width: 1.6rem; height: 1.6rem; flex: 0 0 auto; }
.guide-jump { display: flex; gap: 6px; flex: 1; min-width: 0; overflow-x: auto; padding: 2px 2px 4px; scrollbar-width: none; }
.guide-jump::-webkit-scrollbar { display: none; }
#guide button {
  flex: 0 0 auto; margin: 0; width: auto; white-space: nowrap; cursor: pointer; animation: none;
  font: 600 .85rem/1.2 var(--font-text); color: var(--ink); background: var(--panel);
  border: 2px solid var(--ink); border-radius: 999px; padding: 6px 12px; box-shadow: 2px 2px 0 var(--ink);
}
#guide #guide-close { background: var(--gold); color: #1f1a1c; }
#guide #guide-print { background: var(--bg); }
#guide-frame { flex: 1; min-height: 0; width: 100%; border: 0; background: var(--bg); }
.guide-miss { margin: auto; padding: 24px; text-align: center; max-width: 40ch; color: var(--ink); }
html.guide-open, html.guide-open body { overflow: hidden; }
@media (max-width: 600px) { .guide-bar .mark { display: none; } }

/* Who is using the board: big, few, named, springy, each a colour. */
.child-picker { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.child-picker:empty { display: none; }
.child-picker button {
  font: inherit; font-size: 1.15rem; font-weight: 600;
  padding: 12px 22px; border-radius: 999px; cursor: pointer;
  background: var(--paper); color: var(--ink); border: 2.5px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  transition: transform .2s var(--spring), background .2s;
}
.child-picker button:nth-child(4n+1) { background: var(--teal-soft); }
.child-picker button:nth-child(4n+2) { background: var(--rose-soft); }
.child-picker button:nth-child(4n+3) { background: #ebe7ff; }
.child-picker button:nth-child(4n+4) { background: #e0f7e8; }
.child-picker button.on { background: var(--gold); color: #1f1a1c; transform: translate(-1px, -1px); }

/* ---------- layout ---------- */

#app { display: flex; flex-direction: column; height: 100%; padding: 10px 12px; gap: 8px; }
#app.hidden { display: none; }

header {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 48px;
  flex: 0 0 auto;
}
header .brand { font-size: 1.05rem; flex: 0 0 auto; }
header .brand .mark { width: 2rem; height: 2rem; }
#title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.2rem, 3.4vh, 1.9rem);
  letter-spacing: .02em;
  margin: 0; flex: 1;
  line-height: 1.05;
}
#title .am { color: var(--ink-2); font-size: .62em; display: block; font-family: var(--font-ethiopic); letter-spacing: 0; }

#spoken {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--teal-deep);
  background: var(--teal-soft);
  border-radius: 999px;
  padding: 6px 14px;
  opacity: 0;
  transition: opacity .25s;
  max-width: 34ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#spoken.show { opacity: 1; animation: boing .45s var(--spring); }
:root[data-theme="dark"] #spoken { color: var(--gold); background: #2a2a1c; }

#status {
  font-size: .8rem;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}
#dot-status { width: 10px; height: 10px; border-radius: 50%; background: var(--ink-3); }
#dot-status.live { background: #2f9e63; box-shadow: 0 0 0 4px rgba(47,158,99,.2); }
#dot-status.lost { background: var(--gold); box-shadow: 0 0 0 4px var(--gold-soft); }

/* ---------- the row ---------- */

/* Equal full-height columns: page-turners at the edges, choices between.
   Equal widths keep every target the same size, and full height means a gaze
   that is too high or too low still lands on the right tile. */
#row {
  flex: 1 1 auto;
  display: grid;
  /* Page-turners on the outside, the level's grid between them. The script
     sets --cols per level; --turner shrinks on narrow screens. */
  --turner: 1fr;
  grid-template-columns: var(--turner) repeat(var(--cols, 3), 1.35fr) var(--turner);
  grid-template-rows: 1fr;
  gap: 14px;
  min-height: 0;
  padding: 8px 6px 10px;
}

.tile {
  position: relative;
  border-radius: 24px;
  background: var(--slate);
  color: var(--slate-ink);
  border: 4px solid transparent;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  overflow: hidden;
  cursor: pointer;
  min-height: 0;
  padding: 10px;
  box-shadow: var(--shadow);
  transition: transform .22s var(--spring), box-shadow .22s, filter .22s, opacity .22s;
  /* Each new page arrives with a small staggered pop, so a page turn is
     visibly a new set of things and not a silent swap. `backwards` so the
     animation lets go of transform once it ends and .active can take over. */
  animation: popin .5s var(--spring) backwards;
  animation-delay: calc(var(--i, 0) * 60ms);
}
.tile[data-colour="aqua"]   { background: var(--aqua);   color: var(--aqua-ink); }
.tile[data-colour="green"]  { background: var(--green);  color: var(--green-ink); }
.tile[data-colour="indigo"] { background: var(--indigo); color: var(--indigo-ink); }
.tile[data-colour="amber"]  { background: var(--amber);  color: var(--amber-ink); }
.tile[data-colour="red"]    { background: var(--red);    color: var(--red-ink); }
.tile[data-colour="pink"]   { background: var(--pink);   color: var(--pink-ink); }
.tile[data-colour="slate"]  { background: var(--slate);  color: var(--slate-ink); }
.tile.turner {
  background: var(--edge);
  color: var(--edge-ink);
  border-color: var(--line);
  box-shadow: none;
}

.tile .emoji {
  font-size: clamp(2.6rem, 13vh, 8rem);
  line-height: 1;
  position: relative;
  z-index: 2;
  filter: drop-shadow(0 4px 0 rgba(0,0,0,.12));
}
.tile img.photo {
  width: clamp(3.5rem, 22vh, 11rem); height: clamp(3.5rem, 22vh, 11rem);
  object-fit: cover; border-radius: 18px; position: relative; z-index: 2;
  border: 4px solid rgba(255,255,255,.85);
}
.tile .label {
  font-size: clamp(1rem, 3.4vh, 2.2rem);
  font-weight: 700;
  position: relative;
  z-index: 2;
  text-align: center;
  line-height: 1.15;
  letter-spacing: .01em;
}
.tile .label .am {
  display: block;
  font-size: .78em;
  font-weight: 600;
  opacity: .9;
  margin-top: 4px;
}
.tile.turner .emoji { font-size: clamp(2rem, 8vh, 4.5rem); filter: none; }
/* Colour-swatch tiles paint their own background (yellow, white, black...), so
   their text gets a dark halo rather than trusting any one text colour. */
.tile:not([data-colour]):not(.turner) .label {
  color: #fff;
  text-shadow: 0 2px 0 rgba(0,0,0,.45), 0 0 14px rgba(0,0,0,.55);
}
.tile.turner .label { font-size: clamp(.85rem, 2.2vh, 1.4rem); color: var(--ink-2); }

/* The dwell fill rises from the bottom with a dark edge so it reads on every
   tile colour, including the gold tile itself. */
.tile .fill {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 0%;
  background: rgba(255, 255, 255, .42);
  border-top: 4px solid var(--focus);
  z-index: 1;
  pointer-events: none;
}
.tile[data-colour="amber"] .fill, .tile[data-colour="pink"] .fill { background: rgba(31,26,28,.16); }
/* Only the active tile fills; hidden otherwise so the 0% fill's top edge does
   not draw a stray line along the bottom of every tile. */
.tile:not(.active) .fill { visibility: hidden; }
.tile.turner .fill { background: var(--gold-soft); }
:root[data-theme="dark"] .tile.turner .fill { background: #3a3620; }

/* FOCUS. This is the contrast the child depends on: the chosen tile lifts,
   gets the darkest ring on the screen and a hard shadow. One consistent
   signal, strong enough to read from across a room. The other tiles are left
   alone on purpose: with gaze there is nearly always an active tile, so dimming
   the rest would mean the board spends its life looking washed out. */
.tile.active {
  transform: scale(1.05) translateY(-4px);
  border-color: var(--focus);
  box-shadow: 8px 8px 0 var(--focus), 0 0 0 10px var(--focus-glow);
  z-index: 3;
}
.tile.chosen { animation: cheer .55s var(--spring); }

/* ---------- spotlight ---------- */

/* Colour only where the child is pointing. Every other tile drops to black
   and white, so the one tile in colour is the whole answer to "where am I?",
   and colour itself becomes the feedback for moving. */
body.spotlight #row .tile:not(.active):not(.chosen):not(.turner) { filter: grayscale(1) contrast(.95); }

/* ---------- follow-up question ---------- */

/* While a tile's own question is open the title turns rose and gets a mark,
   so "Where does it hurt?" reads as a question, not as another page. */
body.follow #title { color: var(--rose-deep); }
body.follow #title::before { content: "? "; color: var(--gold); }
:root[data-theme="dark"] body.follow #title { color: var(--rose); }

/* ---------- the AI is thinking ---------- */

#thinking {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  z-index: 56; background: var(--paper); color: var(--ink);
  border: 3px solid var(--ink); border-radius: 999px; box-shadow: 6px 6px 0 var(--ink);
  padding: 16px 30px; font-size: clamp(1.1rem, 3vh, 1.8rem); font-weight: 700;
  pointer-events: none; display: flex; align-items: center; gap: 12px;
}
#thinking[hidden] { display: none; }
#thinking .dots { display: inline-block; animation: bob .8s ease-in-out infinite; }
#thinking .am { font-size: .75em; color: var(--ink-2); font-weight: 600; }
body.thinking #row { filter: grayscale(1) opacity(.45); }

/* ---------- party ---------- */

#party { position: fixed; inset: 0; pointer-events: none; z-index: 59; overflow: hidden; }
#party i {
  position: absolute; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 3px;
  animation: burst .85s cubic-bezier(.2, .8, .3, 1) forwards;
}
#party i.round { border-radius: 50%; }
@keyframes burst {
  0%   { transform: translate(0, 0) rotate(0) scale(1); opacity: 1; }
  100% { transform: translate(var(--dx), calc(var(--dy) + 90px)) rotate(var(--rot)) scale(.6); opacity: 0; }
}

/* ---------- page dots ---------- */

#pager {
  display: flex;
  justify-content: center;
  gap: 8px;
  flex: 0 0 auto;
  padding: 2px 0 2px;
}
#pager span { width: 10px; height: 10px; border-radius: 50%; background: var(--line); transition: transform .25s var(--spring), background .25s; }
#pager span.on { background: var(--rose-deep); transform: scale(1.5); }
:root[data-theme="dark"] #pager span.on { background: var(--rose); }

/* ---------- message bar (keyboard) ---------- */

/* What has been spelled so far, shown large because it is the whole point of
   the keyboard and a child needs to see it growing. */
#message {
  display: none;
  flex: 0 0 auto;
  font-size: clamp(1.2rem, 4vh, 2.4rem);
  font-weight: 700;
  letter-spacing: .06em;
  padding: 8px 16px;
  border-radius: 18px;
  background: var(--paper);
  border: 2.5px solid var(--ink);
  box-shadow: 4px 4px 0 var(--ink);
  min-height: 1.6em;
  text-align: center;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#message.show { display: block; }

/* ---------- colour flash ---------- */

/* Choosing a colour should *do* something: the whole screen becomes it. */
#flash {
  position: fixed;
  inset: 0;
  z-index: 58;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease;
  display: grid;
  place-content: center;
  font-family: var(--font-display);
  font-size: clamp(2rem, 10vh, 7rem);
  color: rgba(255, 255, 255, .95);
  text-shadow: 0 3px 0 rgba(0, 0, 0, .25);
  letter-spacing: .03em;
}
#flash.show { opacity: 1; }

/* ---------- story ---------- */

#story {
  position: fixed;
  inset: 0;
  z-index: 57;
  background: var(--bg);
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 6vh 8vw;
  text-align: center;
}
#story.show { display: flex; }
#story .art { font-size: clamp(3rem, 16vh, 9rem); line-height: 1; animation: bob 3s ease-in-out infinite; }
#story .line {
  font-size: clamp(1.2rem, 4.2vh, 2.6rem);
  font-weight: 600;
  line-height: 1.45;
  max-width: 22ch;
}
#story .line .am { display: block; color: var(--teal-deep); font-size: .85em; margin-top: 10px; }
:root[data-theme="dark"] #story .line .am { color: var(--teal); }
#story .hint { color: var(--muted); font-size: 1rem; }

/* ---------- gaze dot ---------- */

#gaze-dot {
  position: fixed;
  width: 22px; height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: var(--rose-deep);
  box-shadow: 0 0 0 3px #fff, 0 0 0 5px rgba(31,26,28,.35);
  pointer-events: none;
  z-index: 50;
  display: none;
}
#gaze-dot.show { display: block; }
#gaze-dot.untrusted { background: var(--gold); }
:root[data-theme="dark"] #gaze-dot { background: var(--rose); box-shadow: 0 0 0 3px rgba(0,0,0,.6); }

/* ---------- coaching banner ---------- */

/* Large and central: the person who must act on this is a carer across the
   room, not someone reading a status line. */
#coach {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  background: var(--paper);
  border: 3px solid var(--ink);
  box-shadow: 6px 6px 0 var(--ink);
  color: var(--ink);
  font-size: clamp(1.1rem, 3.2vh, 2rem);
  font-weight: 700;
  padding: 22px 38px;
  border-radius: 22px;
  z-index: 55;
  pointer-events: none;
  opacity: 0;
  transition: opacity .18s ease, transform .18s var(--spring);
  text-align: center;
  max-width: 80vw;
}
#coach.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
:root[data-theme="dark"] #coach { box-shadow: 6px 6px 0 rgba(0,0,0,.6); }

/* ---------- camera self-view ---------- */

/* In the header flow, never floating: a box that overlaps a tile hides the very
   thing the child is trying to look at. */
#camera {
  position: relative;
  flex: 0 0 auto;
  width: 140px;
  border-radius: 14px;
  overflow: hidden;
  background: var(--panel);
  border: 3px solid var(--line);
  pointer-events: none;
}
#camera.hidden { display: none; }
#camera.face { border-color: #2f9e63; }
#camera.noface { border-color: var(--rose-deep); }
/* Mirrored: a self-view that does not mirror feels wrong to move in front of. */
#camera video, #camera img { display: block; width: 100%; transform: scaleX(-1); }
#camera:not(.local) video { display: none; }
#camera.local img { display: none; }
#camera img:not([src]) { display: none; }
#camera-faces {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  transform: scaleX(-1);         /* mirrored like the picture under it */
  pointer-events: auto; cursor: pointer; display: none;
}
#camera.many #camera-faces { display: block; }
#camera-hint { display: block; padding: 6px 4px; font-size: .7rem; color: var(--muted); text-align: center; }
#camera.live #camera-hint { display: none; }

/* ---------- settings ---------- */

#settings-btn { padding: 8px 16px; font-size: .9rem; display: inline-flex; align-items: center; gap: 6px; }
#settings-btn .ico { display: none; font-size: 1.1rem; line-height: 1; }
#fullscreen-btn { padding: 6px 12px; font-size: 1.1rem; line-height: 1; }
#settings {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(380px, 92vw);
  background: var(--panel);
  border-left: 3px solid var(--ink);
  padding: 22px;
  z-index: 70;
  overflow-y: auto;
  transform: translateX(105%);
  transition: transform .28s var(--spring);
  font-family: var(--font-text);
}
#settings.open { transform: translateX(0); }
#settings h2 { margin-top: 0; font-family: var(--font-display); font-weight: 400; font-size: 1.5rem; letter-spacing: .02em; }
#settings label { display: block; margin: 18px 0 6px; font-size: .88rem; color: var(--muted); font-weight: 600; }
#settings input[type="range"] { width: 100%; accent-color: var(--rose-deep); }
#settings input[type="checkbox"] { accent-color: var(--teal-deep); width: 18px; height: 18px; }
#settings .row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 14px 0;
}
#settings button { width: 100%; margin-top: 12px; padding: 10px 14px; font-size: .95rem; }

#lang-row, #control-row, #engine-row, #theme-row, #pointing-row, #select-row, #scan-row {
  display: flex; gap: 8px; margin-top: 6px;
}
#lang-row button, #control-row button, #engine-row button,
#theme-row button, #pointing-row button, #select-row button, #scan-row button {
  margin: 0; flex: 1; padding: 9px 6px; font-size: .9rem;
  box-shadow: 3px 3px 0 var(--ink);
}
#lang-row button.on, #control-row button.on, #engine-row button.on,
#theme-row button.on, #pointing-row button.on, #select-row button.on,
#scan-row button.on { background: var(--gold); color: #1f1a1c; }
/* Six movements: two to a line, so each name fits on a phone. */
#faceswitch-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 6px; }
#faceswitch-row button { margin: 0; width: 100%; padding: 9px 6px; font-size: .88rem; box-shadow: 3px 3px 0 var(--ink); }
#faceswitch-row button.on { background: var(--gold); color: #1f1a1c; }
#scan-block { display: none; }
#scan-block.show { display: block; }
#select-row { flex-wrap: wrap; }
#select-row button { flex: 1 1 45%; font-size: .85rem; padding: 9px 6px; }
.hint-note { color: var(--muted); font-size: .78rem; margin: 8px 0 0; line-height: 1.45; }

/* The level chooser stacks, because the names matter more than compactness
   and a carer picks this once rather than often. */
#level-row { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
#level-row button {
  margin: 0; text-align: left; padding: 10px 14px; border-radius: 16px; font-size: .95rem;
  box-shadow: 3px 3px 0 var(--ink);
}
#level-row button small { display: block; color: var(--muted); font-size: .76rem; font-weight: 500; }
#level-row button.on { background: var(--teal-soft); border-color: var(--teal-deep); }
:root[data-theme="dark"] #level-row button.on { background: #1d2c2c; border-color: var(--teal); }

#camera-list { display: flex; flex-direction: column; gap: 8px; }
#camera-list button { margin: 0; text-align: left; padding: 10px 12px; border-radius: 16px; font-size: .9rem; box-shadow: 3px 3px 0 var(--ink); }
#camera-list button.current { background: var(--teal-soft); border-color: var(--teal-deep); }
:root[data-theme="dark"] #camera-list button.current { background: #1d2c2c; border-color: var(--teal); }
#camera-list button small { display: block; color: var(--muted); font-size: .76rem; font-weight: 500; }
#camera-note { font-size: .8rem; color: var(--rose-deep); margin: 10px 0 0; min-height: 1em; }

#log {
  margin-top: 20px; font-size: .85rem; color: var(--muted);
  border-top: 2px solid var(--line); padding-top: 12px;
  max-height: 30vh; overflow-y: auto;
}
#log div { padding: 4px 0; border-bottom: 1px solid var(--line); }

/* ---------- calibration overlay ---------- */

.cal-root { position: fixed; inset: 0; z-index: 90; background: var(--cal-bg); color: var(--ink); font-family: var(--font-text); }

/* The dot is the only thing on screen during a target: anything else would
   pull the eye away from the point being measured. */
.cal-dot { position: absolute; width: 0; height: 0; display: none; }
.cal-dot::after {
  content: ""; position: absolute; left: -10px; top: -10px;
  width: 20px; height: 20px; border-radius: 50%; background: var(--cal-dot);
}
.cal-dot.recording::after { background: var(--cal-fill); box-shadow: 0 0 0 3px var(--ink); }
.cal-ring {
  position: absolute; left: -36px; top: -36px; width: 72px; height: 72px; border-radius: 50%;
  border: 3px solid var(--cal-ring);
  transition: transform .08s linear;
  background: conic-gradient(var(--cal-fill) var(--p, 0%), transparent 0);
  -webkit-mask: radial-gradient(circle, transparent 27px, #000 28px);
  mask: radial-gradient(circle, transparent 27px, #000 28px);
}
.cal-dot.recording .cal-ring { border-color: var(--cal-fill); }
.cal-face {
  position: absolute; left: -26px; top: -26px; width: 52px; height: 52px;
  font-size: 40px; line-height: 52px; text-align: center; pointer-events: none;
}
.cal-face.pop { animation: popin .45s var(--spring) backwards; }
.cal-dot.recording .cal-face { animation: bob .5s ease-in-out infinite; }
.cal-caption { position: absolute; left: 0; right: 0; bottom: 8vh; text-align: center; font-size: clamp(1rem, 2.6vh, 1.6rem); color: var(--muted); font-weight: 600; }
.cal-panel { position: absolute; inset: 0; display: none; flex-direction: column; align-items: center; justify-content: center; gap: 14px; text-align: center; padding: 6vh 8vw; }
.cal-panel h2 { font-family: var(--font-display); font-weight: 400; font-size: clamp(1.6rem, 5vh, 2.8rem); margin: 0 0 6px; letter-spacing: .02em; }
.cal-panel p { color: var(--muted); max-width: 46ch; line-height: 1.55; margin: 0; }
.cal-actions { display: flex; gap: 12px; margin-top: 18px; flex-wrap: wrap; justify-content: center; }
.cal-actions button { font-size: 1.05rem; padding: 14px 28px; }

/* ---------- phones and small tablets ---------- */

/* The tiles are the whole point. On a small screen the header becomes one
   slim line (mark, title, two icon buttons), the status line, subtitle,
   page-dot padding and footer go, and the camera view only appears when it
   is needed: when more than one face is seen, or the face is lost. */
@media (max-width: 900px), (max-height: 520px) {
  #app { padding: 4px 6px 2px; gap: 4px; }
  header { min-height: 34px; gap: 6px; }
  header .brand .mark { width: 1.3rem; height: 1.3rem; }
  #title { font-size: .95rem; line-height: 1.05; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  #title .am { display: none; }
  #status { display: none; }
  #settings-btn .txt { display: none; }
  #settings-btn .ico { display: inline; }
  #settings-btn, #fullscreen-btn { padding: 5px 9px; font-size: 1.05rem; box-shadow: 2px 2px 0 var(--ink); }
  #spoken {
    position: fixed; left: 50%; top: 6px; transform: translateX(-50%);
    z-index: 56; max-width: 70vw; font-size: .9rem; padding: 4px 10px;
  }
  #camera { width: 72px; border-width: 2px; border-radius: 8px; }
  #camera-hint { display: none; }
  #row { padding: 2px 0 4px; gap: 8px; }
  .tile { border-radius: 14px; padding: 6px 4px; gap: 4px; box-shadow: none; }
  .tile.active { transform: scale(1.03) translateY(-2px); box-shadow: 4px 4px 0 var(--focus), 0 0 0 5px var(--focus-glow); }
  #pager { padding: 0; }
  #pager span { width: 6px; height: 6px; }
  .powered { display: none; }
  #rotate-hint { font-size: .74rem; padding: 3px 10px; }
}

/* ---------- small screens: a phone upright or sideways ---------- */

/* The start screen must fit a phone without scrolling the button off the
   bottom, upright (narrow) or sideways (short). Centre it, but never let the
   centring push the top out of reach. */
#start { place-content: safe center; overflow-y: auto; }
@media (max-width: 600px), (max-height: 520px) {
  #start { gap: 10px; padding: 16px 16px 20px; }
  #start .hero-mark { width: clamp(64px, 12vh, 110px); }
  #start h1 { font-size: clamp(2rem, 11vh, 3rem); }
  #start h1 .am { margin-top: 6px; }
  #start p { font-size: .92rem; max-width: 40ch; }
  #start button { font-size: 1.15rem; padding: 13px 36px; }
  #start .float { font-size: clamp(1.1rem, 3.5vh, 1.8rem); }
  .child-picker button { font-size: 1rem; padding: 10px 18px; }
}
@media (max-height: 520px) {
  #start .intro { display: none; }
  #start .hero-mark { width: clamp(48px, 16vh, 72px); }
  #start h1 { font-size: clamp(1.7rem, 13vh, 2.4rem); }
  #start h1 .am { font-size: .38em; margin-top: 4px; }
  #start button { margin-top: 0; }
}

/* Sideways and short: the tiles' contents scale with the height they have. */
@media (max-height: 480px) {
  .tile .emoji { font-size: clamp(1.8rem, 22vh, 5rem); }
  .tile .label { font-size: clamp(.9rem, 6vh, 1.6rem); }
  .tile .label .am { font-size: .72em; margin-top: 2px; }
  .tile img.photo { width: clamp(2.6rem, 24vh, 6rem); height: clamp(2.6rem, 24vh, 6rem); }
}

/* ---------- narrow screens ---------- */

/* A phone. The board is meant to be used sideways, and says so, but it must
   still be usable upright: slimmer page-turners, labels sized to the width,
   and a header that stays one line. */
#rotate-hint { display: none; }
@media (max-width: 600px) {
  header .brand { display: none; }
  #row { --turner: .55fr; gap: 8px; padding: 4px 0 6px; }
  .tile { border-radius: 18px; padding: 6px 4px; gap: 6px; }
  .tile .emoji { font-size: clamp(1.8rem, 12vw, 8rem); }
  .tile .label { font-size: clamp(.8rem, 4.6vw, 2.2rem); overflow-wrap: anywhere; }
  .tile.turner .emoji { font-size: clamp(1.4rem, 7vw, 4.5rem); }
  .tile.turner .label { display: none; }   /* the arrow is the word here */
  .tile img.photo { width: clamp(2.6rem, 18vw, 11rem); height: clamp(2.6rem, 18vw, 11rem); }
  .tile.active { transform: scale(1.03) translateY(-2px); box-shadow: 5px 5px 0 var(--focus), 0 0 0 6px var(--focus-glow); }
  .powered { font-size: .7rem; }
}
@media (max-width: 600px) and (orientation: portrait) {
  #rotate-hint {
    display: block; flex: 0 0 auto; text-align: center;
    font-size: .8rem; font-weight: 600; color: var(--ink-2);
    background: var(--gold-soft); border-radius: 999px; padding: 5px 12px;
  }
  :root[data-theme="dark"] #rotate-hint { background: #3a3620; }
}

/* ---------- motion ---------- */

@keyframes popin { from { opacity: 0; transform: scale(.86) translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes boing { 0% { transform: scale(.9); } 60% { transform: scale(1.06); } 100% { transform: scale(1); } }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
@keyframes drift { from { transform: translate(0, 0) scale(1); } to { transform: translate(4vmax, -3vmax) scale(1.08); } }
@keyframes rise {
  0%   { transform: translateY(0) rotate(0); opacity: 0; }
  8%   { opacity: .95; }
  92%  { opacity: .95; }
  100% { transform: translateY(-120vh) rotate(20deg); opacity: 0; }
}
@keyframes cheer {
  0%   { transform: scale(1.05) translateY(-4px) rotate(0); }
  30%  { transform: scale(1.12) translateY(-10px) rotate(-2deg); }
  60%  { transform: scale(1.08) translateY(-6px) rotate(2deg); }
  100% { transform: scale(1) translateY(0) rotate(0); }
}

/* Motion is decoration here, never information. Anyone who asks for less
   gets none, and loses nothing. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
