/* ============================================================
   Slušaj i napiši — visual system from the "direction 1a" handoff.

   Two rules drive everything here:

     Borders carry the structure, not fills. 3px on chrome, 4px on
     cards, 5px on letter boxes.

     Elevation is a hard offset shadow (0 8px 0 <colour>), never a
     blur. A blurred shadow reads as glass; a hard one reads as a
     thick paper card, which is what a 5-year-old can see across a
     room.
   ============================================================ */

/* ---------------------------------------------------------- fonts
   Self-hosted so the game keeps working with no network, same as the
   audio. Both families are variable, so one file per subset covers
   every weight; latin-ext is what carries č ć ž š đ. */
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/baloo2-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/baloo2-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
                 U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/nunito-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/nunito-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
                 U+FEFF, U+FFFD;
}

/* ---------------------------------------------------------- tokens */
:root {
  --canvas: #F1EAE0;
  --paper: #FBF7EF;
  --card: #FFFFFF;
  --line: #EADFC9;
  --line-strong: #E2D8C4;
  --line-dashed: #D9CDB6;
  --ink: #241F1B;
  --ink-soft: #4A4238;
  --muted: #6B6153;
  --muted-2: #8A8074;
  --dot-empty: #E7DFCF;
  --disabled-bg: #E4DCCC;
  --disabled-fg: #A79B88;

  --teal: oklch(0.68 0.16 190);
  --teal-deep: oklch(0.55 0.14 190);
  --coral: oklch(0.68 0.16 32);
  --coral-deep: oklch(0.55 0.14 32);
  --grape: oklch(0.68 0.16 300);
  --grape-deep: oklch(0.55 0.14 300);
  --grape-ink: oklch(0.55 0.16 300);

  --green: oklch(0.68 0.16 145);
  --green-deep: oklch(0.52 0.13 145);
  --green-ink: oklch(0.42 0.12 145);
  --green-paper: oklch(0.97 0.03 145);
  --green-line: oklch(0.85 0.08 145);

  --amber: oklch(0.72 0.17 60);
  --amber-fg: oklch(0.58 0.18 50);
  --amber-paper: oklch(0.94 0.06 85);
  --amber-line: oklch(0.85 0.10 80);
  --amber-ink: oklch(0.45 0.11 65);

  --star: oklch(0.82 0.16 85);

  --display: 'Baloo 2', system-ui, sans-serif;
  --ui: 'Nunito', system-ui, sans-serif;

  --star-shape: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%,
                        50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);

  /* Scale for the tall screens (gameplay and correct). 1 = exactly the
     handoff's sizes; the SHORT VIEWPORTS block at the end steps it down. */
  --u: 1;

  /* Ceiling for one letter box, unitless so game.js can read it back with
     parseFloat and size the row against the available width. The handoff draws
     126; the row of ten is the tallest single block on the screen, so a
     slightly smaller box is what buys the illustration its own row back on a
     laptop. Every proportion inside a box is still a ratio of 126, so the box
     only changes size — never shape. */
  --box-max: 108;
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; min-height: 100%; }

body {
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--ui);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

/* Every interactive element gets the same ring — the handoff asks for a
   visible one everywhere, and children tab around by accident. */
:focus-visible {
  outline: 3px solid var(--teal);
  outline-offset: 3px;
}

/* ---------------------------------------------------------- screens */
.screen {
  display: none;
  flex-direction: column;
  /* An exact height, not a minimum: it pins the top bar and makes the body
     below it the thing that scrolls when content does not fit. */
  height: 100vh;
  height: 100dvh;
  background: var(--paper);
}
.screen.active {
  display: flex;
  animation: screen-in 220ms ease both;
}

@keyframes screen-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The body of a screen is one centred column. `safe center` keeps the
   top of the content reachable when the viewport is shorter than the
   content instead of clipping it. */
.screen-body {
  flex: 1;
  /* min-height:0 is what lets this shrink below its content so its own
     overflow takes effect. Without it a flex item refuses to go under its
     content height and the whole page grows a scrollbar instead — which
     would scroll the top bar away. */
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: safe center;
  gap: 34px;
  padding: 0 56px 44px;
  overflow-y: auto;
}

.screen-correct { background: var(--green-paper); }

/* the gameplay rhythm follows the same scale as the elements in it */
.screen-game .screen-body { gap: calc(34px * var(--u)); }

/* ---------------------------------------------------------- shared shapes */
.star-token {
  background: var(--star);
  clip-path: var(--star-shape);
  flex: none;
}
/* Unearned markers (level-card dots) are a flat grey. Since the round's rating
   went away this is the only size a star token comes in, so it no longer needs
   the hollow treatment a 104px one did. */
.star-token.empty { background: var(--dot-empty); }

/* Every hard-shadow control presses the same way: it sinks into its own
   shadow. One rule, so the whole UI feels like one material. */
.pressable {
  cursor: pointer;
  transition: transform 120ms, box-shadow 120ms;
}
.pressable:hover { transform: translateY(-2px); }
.pressable:active { transform: translateY(4px); }

/* ---------------------------------------------------------- pills */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: calc(16px * var(--u)) 28px;
  border-radius: 999px;
  background: var(--card);
  border: 3px solid var(--line);
  font: 700 max(20px, calc(24px * var(--u))) var(--ui);
  color: var(--ink-soft);
  cursor: pointer;
  transition: transform 120ms, border-color 150ms;
}
.pill:hover { transform: translateY(-2px); }
.pill:active { transform: translateY(2px); }
.pill:disabled { opacity: 0.45; cursor: default; transform: none; }
.pill.points-here { border-color: var(--amber); }

/* ---------------------------------------------------------- key hints
   The key a button answers to, drawn on the button. Small and quiet: there to
   be discovered, not to compete with the label — and a symbol rather than a
   word, because the child this is for cannot read one yet.

   Drawn as SVG masks, not as glyphs: ⏎ ␣ ⌫ are outside both font subsets
   (neither latin nor latin-ext carries U+23CE, U+2423 or U+232B), so as text
   they fall through to whatever symbol font the device happens to have — which
   on a bare system is a tofu box. Same reasoning as the tick and the back
   arrow, which this file already draws from borders rather than from an icon
   font. `background-color: currentColor` through the mask means the symbol
   inherits the button's colour for free, white or amber alike.

   Each viewBox is trimmed to its own path, so the three symbols come out at
   the same visual weight instead of being letterboxed inside a shared square —
   hence one height and three widths. */
.key-hint {
  --key-h: max(15px, calc(18px * var(--u)));
  display: block;
  height: var(--key-h);
  background-color: currentColor;
  opacity: 0.85;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* ⏎ — down, then left into an arrowhead */
.key-hint.enter {
  width: calc(var(--key-h) * 1.29);
  --key-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='2.7 2.7 17.6 13.6' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 4v7H4'/%3E%3Cpath d='M8 7 4 11l4 4'/%3E%3C/svg%3E");
}

/* ␣ — a long bar with a tick at each end: the width is the point, since the
   real key is the widest one on the board and carries no label at all. */
.key-hint.space {
  width: calc(var(--key-h) * 2.4);
  --key-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='1.7 8.7 20.6 8.6' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 10v6h18v-6'/%3E%3C/svg%3E");
}

/* ⌫ — a plain left arrow, which is what the key itself is printed with on the
   keyboards this game is played on. */
.key-hint.backspace {
  width: calc(var(--key-h) * 1.34);
  --key-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='2.7 4.7 19.6 14.6' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12H4'/%3E%3Cpath d='M10 6l-6 6 6 6'/%3E%3C/svg%3E");
}

.key-hint {
  -webkit-mask-image: var(--key-mask);
  mask-image: var(--key-mask);
}

/* ---------------------------------------------------------- primary button */
.primary {
  /* a column so the key hint can sit under the label */
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(3px * var(--u));
  padding: calc(20px * var(--u)) 64px;
  border: none;
  border-radius: 999px;
  background: var(--green);
  color: #fff;
  font: 800 max(22px, calc(30px * var(--u))) var(--display);
  cursor: pointer;
  box-shadow: 0 10px 0 var(--green-deep);
  transition: transform 120ms, box-shadow 120ms;
}
.primary:hover { transform: translateY(-2px); box-shadow: 0 12px 0 var(--green-deep); }
.primary:active { transform: translateY(4px); box-shadow: 0 6px 0 var(--green-deep); }
.primary:disabled {
  background: var(--disabled-bg);
  color: var(--disabled-fg);
  box-shadow: none;
  cursor: default;
  transform: none;
}
.primary .key-hint { color: #fff; }
.primary:disabled .key-hint { color: var(--disabled-fg); opacity: 1; }

.primary.teal { background: var(--teal); box-shadow: 0 10px 0 var(--teal-deep); }
.primary.teal:hover { box-shadow: 0 12px 0 var(--teal-deep); }
.primary.teal:active { box-shadow: 0 6px 0 var(--teal-deep); }

.secondary {
  padding: calc(24px * var(--u)) 56px;
  border-radius: 999px;
  background: var(--card);
  border: 3px solid var(--line);
  color: var(--ink-soft);
  font: 800 max(22px, calc(30px * var(--u))) var(--display);
  cursor: pointer;
  transition: transform 120ms;
}
.secondary:hover { transform: translateY(-2px); }
.secondary:active { transform: translateY(2px); }

/* A parent-facing escape hatch, deliberately the quietest thing on the
   screen so it does not read as part of the game. */
.quiet-button {
  background: none;
  border: none;
  padding: 8px 12px;
  font: 600 20px var(--ui);
  color: var(--muted-2);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* ============================================================
   1 · LEVEL SELECT
   ============================================================ */
.title {
  margin: 0;
  font: 800 72px var(--display);
  color: var(--ink);
  letter-spacing: -0.01em;
  text-align: center;
}
.subtitle {
  margin: 0;
  font: 600 26px var(--ui);
  color: var(--muted-2);
}

.start-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.screen-start .screen-body { gap: 44px; padding: 56px; }

.levels {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  width: 100%;
  max-width: 1000px;
}

.level-card {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 34px;
  border-radius: 32px;
  background: var(--card);
  border: 4px solid var(--level);
  box-shadow: 0 10px 0 var(--level-shadow);
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: transform 120ms, box-shadow 120ms;
}
.level-card:hover { transform: translateY(-2px); box-shadow: 0 12px 0 var(--level-shadow); }
.level-card:active { transform: translateY(4px); box-shadow: 0 6px 0 var(--level-shadow); }

.level-card[data-level="1"] { --level: var(--teal);  --level-shadow: oklch(0.68 0.16 190 / 0.28); }
.level-card[data-level="2"] { --level: var(--coral); --level-shadow: oklch(0.68 0.16 32 / 0.28); }
.level-card[data-level="3"] { --level: var(--grape); --level-shadow: oklch(0.68 0.16 300 / 0.28); }

.level-badge {
  width: 74px;
  height: 74px;
  border-radius: 24px;
  background: var(--level);
  color: #fff;
  font: 800 44px var(--display);
  display: grid;
  place-items: center;
}
.level-name { font: 800 34px var(--display); color: var(--ink); line-height: 1.1; }
.level-meta { font: 600 22px var(--ui); color: var(--muted); text-wrap: pretty; }

/* pinned to the bottom so the dots line up across cards whose meta text
   wraps to a different number of lines */
.level-progress { display: flex; gap: 8px; margin-top: auto; padding-top: 6px; }
.level-progress span {
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: var(--dot-empty);
  flex: none;
}
.level-progress span.done { background: var(--star); }

/* a ten-word round needs smaller markers to stay on one line inside the card */
.level-progress.compact { gap: 6px; }
.level-progress.compact span { width: 14px; height: 14px; }

/* --- setting toggles --- */
.settings { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; justify-content: center; }

.toggle {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 16px 26px;
  border-radius: 999px;
  background: var(--card);
  border: 3px solid var(--line);
  font: 700 22px var(--ui);
  color: var(--ink-soft);
  cursor: pointer;
  transition: transform 120ms;
}
.toggle:hover { transform: translateY(-2px); }
.toggle:active { transform: translateY(2px); }
.toggle input { position: absolute; opacity: 0; pointer-events: none; }

.toggle .box-mark {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 12px;
  background: var(--dot-empty);
  display: grid;
  place-items: center;
  transition: background 150ms;
}
/* The tick is two borders rotated 45°, not a glyph — it stays crisp at
   any size and needs no icon font. */
.toggle .box-mark::after {
  content: '';
  width: 14px;
  height: 8px;
  border-left: 4px solid #fff;
  border-bottom: 4px solid #fff;
  transform: rotate(-45deg) translateY(-2px);
  opacity: 0;
  transition: opacity 150ms;
}
.toggle input:checked + .box-mark { background: var(--green); }
.toggle input:checked + .box-mark::after { opacity: 1; }
.toggle input:focus-visible + .box-mark { outline: 3px solid var(--teal); outline-offset: 3px; }

/* ============================================================
   2 · GAMEPLAY

   Sizes below are the handoff's values multiplied by --u, a single scale
   for the gameplay column. --u is 1 at and above the design height, and
   steps down on shorter windows (see SHORT VIEWPORTS at the end) so that
   "Provjeri" stays on screen without hand-tuning every rule twice.
   `max()` guards keep interactive targets at 56px and text at 20px, the
   two floors the handoff sets.
   ============================================================ */
/*
 * Three columns for two children. With the star count gone the bar holds only
 * the back button and the dots, and `space-between` would push the dots hard
 * against the right edge; an empty third column of the same 1fr keeps them
 * centred on the bar itself rather than in whatever space the level label
 * happens to leave.
 */
.topbar {
  flex: none;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 20px;
  padding: calc(28px * var(--u)) 40px;
}

.back {
  width: max(52px, calc(64px * var(--u)));
  height: max(52px, calc(64px * var(--u)));
  flex: none;
  border-radius: 999px;
  background: var(--card);
  border: 3px solid var(--line);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: transform 120ms;
}
.back:hover { transform: translateY(-2px); }
.back:active { transform: translateY(2px); }
.back::before {
  content: '';
  width: 16px;
  height: 16px;
  border-left: 5px solid var(--ink-soft);
  border-bottom: 5px solid var(--ink-soft);
  transform: rotate(45deg);
}

.topbar-left { display: flex; align-items: center; gap: 16px; }
.level-label { font: 800 26px var(--display); color: var(--ink-soft); }

.dots { display: flex; align-items: center; gap: 12px; min-width: 0; }
.dots span {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--dot-empty);
  transition: background 200ms;
  flex: none;
}
.dots span.done { background: var(--star); }
.dots span.current { background: var(--teal); }

/* ten dots at the designed size would not fit the bar */
.dots.compact { gap: 8px; }
.dots.compact span { width: 16px; height: 16px; }


/* --- listen --- */
.listen-row { display: flex; align-items: center; gap: 34px; }

.listen {
  width: calc(190px * var(--u));
  height: calc(190px * var(--u));
  flex: none;
  border: none;
  border-radius: 999px;
  background: var(--teal);
  box-shadow: 0 12px 0 var(--teal-deep), 0 0 0 14px oklch(0.68 0.16 190 / 0.18);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  cursor: pointer;
  transition: transform 120ms, box-shadow 120ms;
}
.listen:hover { transform: translateY(-2px); }
.listen:active,
.listen.playing {
  transform: translateY(4px);
  box-shadow: 0 6px 0 var(--teal-deep), 0 0 0 18px oklch(0.68 0.16 190 / 0.18);
}
/* Play triangle from a single border — nudged right because an optical
   centre and a geometric centre are not the same for a triangle. */
.listen::before {
  content: '';
  width: 0;
  height: 0;
  border-left: calc(44px * var(--u)) solid #fff;
  border-top: calc(28px * var(--u)) solid transparent;
  border-bottom: calc(28px * var(--u)) solid transparent;
  margin-left: calc(10px * var(--u));
}
.listen span {
  font: 800 max(20px, calc(24px * var(--u))) var(--display);
  color: #fff;
  letter-spacing: 0.04em;
}
/* The button and the key do the same thing, so the key is drawn on it. */
.listen .key-hint { color: #fff; margin-top: calc(-4px * var(--u)); }

.helpers { display: flex; flex-direction: column; gap: calc(14px * var(--u)); }

/* --- illustration --- */
/* Its own full row under the listen button, at every size that shows it. */
.picture {
  width: calc(220px * var(--u));
  height: calc(150px * var(--u));
  flex: none;
  border-radius: 24px;
  border: 3px solid var(--line);
  background: var(--card);
  display: grid;
  place-items: center;
  font-size: calc(92px * var(--u));
  line-height: 1;
  user-select: none;
}
.picture[hidden] { display: none; }

/* --- message + reveal pills, directly above the boxes --- */
.notes { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.notes:empty { display: none; }

.message {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  padding: calc(16px * var(--u)) 30px;
  border-radius: 24px;
  background: var(--amber-paper);
  border: 3px solid var(--amber-line);
  font: 700 max(20px, calc(28px * var(--u))) var(--ui);
  color: var(--amber-ink);
  text-align: center;
  text-wrap: pretty;
  animation: pop-in 220ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}
/* Backspace is the one key with no button anywhere on the screen, and the
   moment a letter is flagged is exactly when it is needed — so it is shown
   here rather than in a legend nobody reads. The symbol alone would float
   without a label, so it keeps the verb: "Briši ⌫". */
.key-note {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-top: 6px;
  font: 700 max(15px, calc(19px * var(--u))) var(--ui);
  color: var(--amber-fg);
}

.reveal {
  display: inline-flex;
  align-items: center;
  padding: calc(14px * var(--u)) 30px;
  border-radius: 24px;
  background: var(--card);
  border: 3px solid var(--line);
  font: 800 max(22px, calc(34px * var(--u))) var(--display);
  color: var(--ink-soft);
  letter-spacing: 0.22em;
  animation: pop-in 220ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}

@keyframes pop-in {
  from { opacity: 0; transform: scale(0.8); }
  to   { opacity: 1; transform: scale(1); }
}

/* --- letter boxes --- */
/* The row never wraps. A word broken across two lines of boxes stops looking
   like a word, and the second row pushes "Provjeri" off screen — so the boxes
   shrink to fit the width instead, keeping 126×150 as their ceiling.

   One number drives all of it: --slot, the box width, which game.js works out
   once per word. Height and glyph are plain multiples of it, so nothing here
   depends on the layout resolving first. The earlier version used
   `aspect-ratio` plus container-query units and flickered on every keystroke,
   because both are computed from a width that is briefly wrong. */
.boxes {
  /* The same ceiling game.js reads, so a hidden screen (which it cannot
     measure) and a visible one agree on the size. */
  --slot: calc(var(--box-max) * 1px * var(--u));
  display: flex;
  gap: calc(20px * var(--u));
  /* One row whenever the boxes clear their 30px floor — js/game.js sizes
     --slot so they do, and only a word that cannot fit at that size (ten
     letters on a phone) reaches this wrap. */
  flex-wrap: wrap;
  justify-content: center;
  width: 100%;
}

.box {
  flex: none;
  width: var(--slot);
  height: calc(var(--slot) * 150 / 126);
  border-radius: calc(26px * var(--u));
  background: var(--card);
  border: 5px dashed var(--line-dashed);
  display: grid;
  place-items: center;
  color: var(--ink);
  transition: border-color 150ms, background 150ms, box-shadow 150ms;
}

/* the handoff's 76px glyph in its 126px box, proportional below that */
.box span { font: 800 calc(var(--slot) * 76 / 126) var(--display); line-height: 1; }

/* settled: the child typed it and it is not being questioned */
.box.filled {
  border-style: solid;
  border-color: var(--line-strong);
}

/* next up: solid teal with a blinking caret */
.box.current {
  border-style: solid;
  border-color: var(--teal);
  box-shadow: 0 8px 0 oklch(0.68 0.16 190 / 0.22);
}
.box.current::after {
  content: '';
  width: max(4px, calc(var(--slot) * 6 / 126));
  height: calc(var(--slot) * 70 / 126);
  border-radius: 4px;
  background: var(--teal);
  animation: blink 1s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* the one letter that differs from the target — the only failure signal
   in the whole game. Never red, never a reset. */
.box.mismatch {
  border-style: solid;
  border-color: var(--amber);
  background: oklch(0.72 0.17 60 / 0.10);
  color: var(--amber-fg);
  box-shadow: 0 0 0 12px oklch(0.72 0.17 60 / 0.16);
  animation: nudge 320ms ease 2;
}
@keyframes nudge {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-6px); }
  75%      { transform: translateX(6px); }
}

/* The pop belongs to the letter, not to the box. Animating the box meant
   fading its border and background in from nothing on every keystroke, which
   reads as the whole row blinking. */
.box.just-typed span { animation: pop-in 180ms ease both; }

.uppercase .box,
.uppercase .reveal,
.uppercase .chip { text-transform: uppercase; }

/* Full width on purpose. The screen body centres its children rather than
   stretching them, so without this the form sizes to its own content — and
   then `.boxes { width: 100% }` measures that content instead of the space
   available, game.js sees room that is not there, and the row overflows the
   screen. */
#form {
  position: relative;
  width: 100%;
  display: flex;
  justify-content: center;
}

/* The real input is invisible; it exists to catch the physical keyboard
   and to open the system keyboard on a phone. The boxes are the render
   of its value. */
#input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  border: none;
  padding: 0;
  font-size: 16px; /* anything smaller and mobile Safari zooms the page */
  color: transparent;
  caret-color: transparent;
}

/* --- diacritic keys --- */
/* On screen in every level: words with kvačice are mixed into all three. */
.diacritics { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }

.diacritic {
  width: max(56px, calc(78px * var(--u)));
  height: max(56px, calc(78px * var(--u)));
  border-radius: 20px;
  background: var(--card);
  border: 3px solid var(--line);
  font: 700 max(20px, calc(38px * var(--u))) var(--display);
  color: var(--grape-ink);
  cursor: pointer;
  transition: transform 120ms;
}
.diacritic:hover { transform: translateY(-2px); }
.diacritic:active { transform: translateY(3px); }

/* ============================================================
   3 · CORRECT
   ============================================================ */
.screen-correct .screen-body { gap: calc(40px * var(--u)); padding: 56px; }

.tick-circle {
  width: calc(150px * var(--u));
  height: calc(150px * var(--u));
  flex: none;
  border-radius: 999px;
  background: var(--green);
  display: grid;
  place-items: center;
  box-shadow: 0 0 0 18px oklch(0.68 0.16 145 / 0.16);
  animation: grow 260ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}
.tick-circle::after {
  content: '';
  width: 56px;
  height: 30px;
  border-left: 12px solid #fff;
  border-bottom: 12px solid #fff;
  transform: rotate(-45deg) translateY(-6px);
}
@keyframes grow { from { transform: scale(0); } to { transform: scale(1); } }

/*
 * Holding the illustration, the disc turns to paper with a thick green ring
 * and the tick steps aside. An emoji on saturated green loses half the word
 * list — 🌳 and 🍃 simply vanish into it — and the ring carries the "correct"
 * signal on its own, next to a green heading, a gold star and confetti.
 */
.tick-circle.with-picture {
  background: var(--card);
  border: calc(9px * var(--u)) solid var(--green);
}
.tick-circle.with-picture::after { content: none; }
.tick-circle span { font-size: calc(84px * var(--u)); line-height: 1; user-select: none; }

.bravo {
  margin: 0;
  font: 800 calc(84px * var(--u)) var(--display);
  color: var(--green-ink);
  text-align: center;
  text-wrap: balance;
}
/* "Sporije je pomoglo!" is a sentence where "Bravo!" was one word. At 84px it
   runs off both edges, so the tool-naming lines get their own size — the
   heading is the same thing either way, just no longer a shout. */
.bravo.long { font-size: calc(46px * var(--u)); }

.boxes.solved .box {
  border-style: solid;
  border-color: var(--green);
  background: var(--card);
  color: var(--ink);
  box-shadow: 0 8px 0 oklch(0.68 0.16 145 / 0.25);
  animation: pop-in 220ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}
.boxes.solved .box::after { content: none; }


.primary.wide { padding: calc(24px * var(--u)) 76px; font-size: max(22px, calc(32px * var(--u))); }

/* ============================================================
   4 · ROUND SUMMARY
   ============================================================ */
.screen-summary .screen-body { gap: calc(38px * var(--u)); padding: 56px; }

.summary-title { margin: 0; font: 800 calc(96px * var(--u)) var(--display); color: var(--ink); }
/*
 * The round's three kinds of success, side by side. Only the ones that
 * happened are drawn: a "0" next to a label reads as a shortfall, and there
 * is no shortfall here — every word ended in a star either way.
 *
 * Each one gets its own colour so the child can tell them apart before being
 * able to read the labels: cream for the clean ones, amber for the ones that
 * took another go, teal for the ones a tool solved. Amber is the same hue the
 * flagged letter uses, which is the point — that colour means "keep going",
 * not "wrong", and here it is attached to an outright win.
 */
.tally { display: flex; gap: 18px; flex-wrap: wrap; justify-content: center; }

/* Bigger than they were as a footnote under the rating. They are what lands
   now, and what lands is what the eye should go to — the stars they replaced
   were 104px and owned the screen. Not star-sized, though: a tile carries a
   word, so it earns its weight from the count instead of from area. */
.tally-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: calc(172px * var(--u));
  padding: calc(20px * var(--u)) 26px;
  border-radius: 28px;
  background: var(--card);
  border: 3px solid var(--tally-line);
}
.tally-count { font: 800 max(32px, calc(56px * var(--u))) var(--display); color: var(--tally-ink); }
.tally-label {
  font: 600 max(15px, calc(20px * var(--u))) var(--ui);
  color: var(--muted);
  text-align: center;
  text-wrap: balance;
}

.tally-item[data-kind="clean"]     { --tally-line: var(--line-strong); --tally-ink: var(--ink); }
.tally-item[data-kind="persisted"] { --tally-line: var(--amber-line); --tally-ink: var(--amber-fg); }
.tally-item[data-kind="helped"]    { --tally-line: oklch(0.85 0.08 190); --tally-ink: var(--teal-deep); }

/*
 * The tiles arrive one at a time, each on its own note and its own burst of
 * confetti — the cascade the rating stars used to carry. The script sets the
 * delay so only the tiles that were actually earned are in the sequence.
 *
 * A tile does not spin in the way a star did: a star is a token and a half turn
 * reads as play, but a rotating panel of text reads as a glitch. It rises and
 * settles instead, on the same overshoot curve.
 */
.tally-item {
  animation: tally-land 380ms cubic-bezier(0.2, 0.9, 0.3, 1.6) both;
}

@keyframes tally-land {
  from { opacity: 0; transform: scale(0.72) translateY(16px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* The setting means "do not throw things across my screen", not "do not
   congratulate me" — same reading the confetti gets. The tiles still arrive in
   sequence, they just do not travel. */
@media (prefers-reduced-motion: reduce) {
  @keyframes tally-land {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}

/*
 * Day is full: "Dosta za danas" becomes the primary action and "Igraj još"
 * is demoted to a quiet secondary. Demoted, not removed — a hard lock turns
 * the ending into a fight with whoever is in the room and teaches the child
 * nothing about stopping. The choice stays theirs; only the default moves.
 */
#btn-enough { display: none; }
.screen-summary.day-full #btn-enough { display: inline-flex; }
.screen-summary.day-full #btn-again {
  background: var(--card);
  border: 3px solid var(--line);
  color: var(--ink-soft);
  box-shadow: none;
  font-size: max(20px, calc(26px * var(--u)));
}
.screen-summary.day-full #btn-again:hover,
.screen-summary.day-full #btn-again:active { box-shadow: none; }
/* Enter now belongs to "Dosta za danas", so it stops being advertised here. */
.screen-summary.day-full #btn-again .key-hint { display: none; }

.chips {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 820px;
}
.chip {
  padding: calc(14px * var(--u)) 28px;
  border-radius: 999px;
  background: var(--card);
  border: 3px solid var(--line);
  font: 700 max(20px, calc(26px * var(--u))) var(--display);
  color: var(--ink-soft);
}

.buttons-row { display: flex; gap: 20px; flex-wrap: wrap; justify-content: center; }

/* ============================================================
   5 · DOSTA ZA DANAS

   Deliberately the calmest screen in the game: no green, no gold, no
   movement beyond the screen transition itself. Everything the celebration
   screens do, this one does not do — that contrast is the whole message.
   ============================================================ */
.screen-done .screen-body { gap: calc(30px * var(--u)); padding: 56px; }
.done-title {
  margin: 0;
  font: 800 calc(64px * var(--u)) var(--display);
  color: var(--ink-soft);
  text-align: center;
  text-wrap: balance;
}
.done-count { margin: 0; font: 600 max(20px, calc(28px * var(--u))) var(--ui); color: var(--muted); }

/* Level select, once the day is full. The cards still work — they open the
   parent gate instead of a round — but they should stop looking like the fresh
   start they were an hour ago. Faded, not greyed out and not disabled: the
   answer is still yes, it just has to come from an adult now. */
.day-note { margin: 0; font: 600 20px var(--ui); color: var(--muted-2); }
.day-note[hidden] { display: none; }

.levels.day-full .level-card { opacity: 0.62; }
.levels.day-full .level-card:hover { opacity: 0.8; }

/* ---------------------------------------------------------- parent gate
   Everything here is deliberately outside the game's visual language: no
   card fill, no hard shadow, no big type. It should read as a piece of the
   machine rather than as something to press for fun.

   An overlay, because the same gate now opens over the round summary as well
   as the level screen. Above the confetti (z-index 50), or a round that ends
   in celebration would rain over the sum. */
.gate {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* Dark and thin rather than mid-grey and thick: a mid-lightness veil over
     cream paper comes out muddy brown and the card stops standing out of it. */
  background: oklch(0.22 0.02 80 / 0.45);
}
.gate[hidden] { display: none; }

.gate-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 22px 26px;
  background: var(--paper);
  border: 3px dashed var(--line-dashed);
  border-radius: 22px;
  max-width: 460px;
}

.gate-text {
  margin: 0;
  font: 600 17px/1.45 var(--ui);
  color: var(--muted);
  text-align: center;
  text-wrap: balance;
}
.gate-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center; }
.gate-sum { font: 700 20px var(--ui); color: var(--ink-soft); }

.gate-answer {
  width: 76px;
  padding: 10px 12px;
  border: 2px solid var(--line-strong);
  border-radius: 12px;
  background: var(--card);
  font: 700 20px var(--ui);
  color: var(--ink);
  text-align: center;
}
/* The only red in the whole game, and it is pointed at an adult. */
.gate-answer.wrong { border-color: oklch(0.6 0.19 25); }

.gate-confirm {
  padding: 10px 20px;
  border: 2px solid var(--line-strong);
  border-radius: 12px;
  background: var(--card);
  font: 700 17px var(--ui);
  color: var(--ink-soft);
  cursor: pointer;
}
.gate-confirm:hover { border-color: var(--muted-2); }

/* ---------------------------------------------------------- confetti */
/* `inset: 0` alone sizes this to the viewport. A width/height of 100% resolves
   against the containing block instead and comes out a scrollbar narrower than
   the bitmap, which quietly stretches every piece of confetti sideways. */
#confetti {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 50;
}

/* ============================================================
   PHONE — the sizes shrink, the hierarchy does not.
   The illustration is the first thing dropped when vertical space runs
   short; the diacritic row is never dropped.
   ============================================================ */
@media (max-width: 900px) {
  .levels { grid-template-columns: 1fr; max-width: 520px; }
  .title { font-size: 56px; }
}

@media (max-width: 560px) {
  .topbar { padding: 24px 26px; gap: 12px; }
  .back { width: 52px; height: 52px; }
  .back::before { width: 13px; height: 13px; }
  .level-label { display: none; }

  .dots span { width: 18px; height: 18px; }
  .dots { gap: 9px; }
  .dots.compact { gap: 4px; }
  .dots.compact span { width: 10px; height: 10px; }

  .screen-body { padding: 0 26px 30px; gap: 26px; }
  .screen-start .screen-body,
  .screen-correct .screen-body,
  .screen-summary .screen-body { padding: 30px 26px; gap: 26px; }

  .title { font-size: 40px; }
  .subtitle { font-size: 20px; }

  .listen { width: 168px; height: 168px; }
  .listen { box-shadow: 0 10px 0 var(--teal-deep), 0 0 0 12px oklch(0.68 0.16 190 / 0.18); }
  .listen::before { border-left-width: 38px; border-top-width: 24px; border-bottom-width: 24px; margin-left: 9px; }
  .listen span { font-size: 21px; }
  .listen-row { flex-direction: column; gap: 20px; }

  /* on a phone the helper pills sit in a row, not a column — the padding is
     trimmed so all three still fit on one line at 420px */
  .helpers { flex-direction: row; gap: 12px; flex-wrap: wrap; justify-content: center; }
  .pill { padding: 14px 16px; font-size: 20px; gap: 8px; }

  .picture { width: 100%; max-width: 340px; height: 150px; font-size: 84px; }

  .message { font-size: 21px; padding: 14px 22px; }
  .reveal { font-size: 26px; padding: 12px 22px; }

  /* 92px wide is the phone ceiling; longer words shrink below it rather than
     wrapping. Height and glyph follow from the box, as on desktop. */
  :root { --box-max: 92; }
  .boxes { --slot: 92px; gap: 14px; }
  .box { border-radius: 22px; }

  .diacritics { gap: 10px; }
  .diacritic { width: 58px; height: 58px; border-radius: 16px; font-size: 28px; }

  .primary { width: 100%; padding: 22px 0; font-size: 26px; }
  .primary.wide { padding: 22px 0; font-size: 26px; }
  .secondary { padding: 20px 36px; font-size: 24px; }

  .level-card { padding: 26px; gap: 14px; }
  .level-badge { width: 62px; height: 62px; border-radius: 20px; font-size: 36px; }
  .level-name { font-size: 28px; }
  .level-meta { font-size: 20px; }

  .tick-circle { width: 110px; height: 110px; box-shadow: 0 0 0 14px oklch(0.68 0.16 145 / 0.16); }
  .tick-circle::after { width: 42px; height: 22px; border-left-width: 10px; border-bottom-width: 10px; }
  .tick-circle.with-picture { border-width: 7px; }
  .tick-circle span { font-size: 62px; }
  .bravo { font-size: 56px; }
  .bravo.long { font-size: 32px; }

  .summary-title { font-size: 54px; }
  .chip { font-size: 20px; padding: 10px 20px; }
  .buttons-row { width: 100%; flex-direction: column; }
  .buttons-row > * { width: 100%; }

  /* Three tiles do not fit side by side at phone width, so they go narrow and
     wrap into a grid rather than shrinking the count out of legibility. */
  .tally { gap: 10px; }
  .tally-item { min-width: 0; flex: 1 1 100px; padding: 12px 10px; }
  .tally-count { font-size: 28px; }
  .tally-label { font-size: 15px; }

  .done-title { font-size: 40px; }
  .done-count { font-size: 20px; }
}

/* ============================================================
   SHORT VIEWPORTS

   The designed gameplay frame is 1040px tall, 1120px with the hint pill —
   taller than the viewport of an ordinary laptop. "Provjeri" is the primary
   action and a 5-year-old will not scroll to find it, so on a short window
   the column compacts: the illustration moves up beside the listen button
   and the big blocks step down one size. Proportions and the hierarchy are
   kept; nothing interactive drops under the handoff's 56px floor.

   At the reference height (1040px and up) every value is exactly as
   specified.
   ============================================================ */
/* 1120px is the tallest designed frame (gameplay plus the hint pill). Above
   it nothing scales; below it --u steps down. */
@media (min-width: 561px) and (max-height: 1119px) {
  :root { --u: 0.92; }
  /* 1120px is the designed frame *with* the hint pill, so at 1119 the pill is
     already a few pixels over budget once the browser's own chrome is counted.
     The rhythm gives them back before anything else has to shrink. */
  .screen-game .screen-body { gap: calc(28px * var(--u)); }
}

@media (min-width: 561px) and (max-height: 1039px) {
  :root { --u: 0.82; }
  /* The three helper pills go side by side instead of stacked: a column of
     three is taller than the scaled-down listen button next to it, so turning
     it sideways costs nothing and the row stops setting the height.
     The illustration keeps its own row — pulling it up beside the buttons
     reclaimed more space than the column ever needed and read as a broken
     layout on any ordinary laptop, which is most of them. */
  .helpers { flex-direction: row; flex-wrap: wrap; justify-content: center; }
  /* Tighter rhythm, same rows. Six blocks means five gaps, so this is worth
     more than shrinking any single one of them — and it is what keeps
     "Provjeri" above the fold on the worst case the game can build: hint pill
     and revealed letters both showing, on a level with the diacritic row. */
  .screen-game .screen-body { gap: calc(22px * var(--u)); padding-bottom: 28px; }
}

/* Under roughly 700px of viewport the column stops fitting once help is on
   screen: a hint pill and revealed letters are two extra rows the designed
   frame does not have. The illustration goes first, exactly as it does on a
   phone — it is decoration, and the pills are the actual help. It comes
   straight back for the next word. */
@media (min-width: 561px) and (max-height: 700px) {
  .screen-game.has-note .picture { display: none; }
}

@media (min-width: 561px) and (max-height: 900px) { :root { --u: 0.7; } }
@media (min-width: 561px) and (max-height: 740px) { :root { --u: 0.58; } }
@media (min-width: 561px) and (max-height: 640px) { :root { --u: 0.5; } }

/* Phones get the layout the handoff draws for them, at its own sizes, so
   --u goes back to 1 and the explicit phone rules below take over. */
@media (max-width: 560px) {
  :root { --u: 1; }
  .screen-game .screen-body { gap: min(26px, 2.2vh); }

  /* The illustration keeps the full-width row the handoff draws for it, just
     a shorter one so the column still lands above the fold. */
  .picture { height: min(150px, 13vh); font-size: 64px; }

  /* A hint pill wraps to two lines at phone width and needs the row the
     illustration was using. */
  .screen-game.has-note .picture { display: none; }
}

/* Below roughly 780px of phone viewport the illustration cannot coexist with
   a reachable "Provjeri" — this is the drop the handoff names, and the
   diacritic row stays either way. Real phones in portrait mostly land here
   once browser chrome is counted, so "Sličica uz riječ" is in practice a
   tablet and desktop setting. */
@media (max-width: 560px) and (max-height: 780px) {
  .picture { display: none; }
}

/* Last resort, as the handoff asks: the illustration is dropped first, and
   the diacritic row is never dropped. */
@media (max-height: 560px) {
  .picture { display: none; }
}

/* ============================================================
   Keep every state change, drop the movement.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .pressable:hover,
  .pill:hover,
  .level-card:hover,
  .primary:hover,
  .secondary:hover,
  .toggle:hover,
  .back:hover,
  .listen:hover,
  .diacritic:hover { transform: none; }
  .box.current::after { animation: none; }
}
