/* ===========================================================================
   Kidwings — the parts of the design system a games-only site still needs
   ---------------------------------------------------------------------------
   Lifted verbatim from ../../static/css/site.css. Not rewritten: every game
   on this site was built against these rules and tested against them, so the
   boards keep the CSS they know and the launch candidates differ in theme.css
   instead. Regenerate with scripts/extract_core_css.py.

   What was left behind, because the pages that used it are gone: the old
   header and footer, the hero, the homepage rail, the passport band, the
   lesson chrome, the explores, the Passport to Wildlife properties, the
   hand-drawn map and the drifting specks.

   The barn in the corner is kept. It is the one piece of chrome that is not
   chrome: four owls get out and wreck the page, and a child who has just
   been told what a pellet is made of should be allowed to break the screen.
   Its switch comes with it; each theme recolours that for its own bar.

   The contract a theme.css must honour: it may redefine any token in :root,
   and it owns the page shell (header, nav, footer, home, cards). It must not
   restyle .lab, .howto, .diet or any game's own block — those are the game.
   ========================================================================= */

/* ===========================================================================
   Kidwings design system
   ---------------------------------------------------------------------------
   Two surfaces, deliberately: "paper" for reading (lessons, prose, marketing)
   and "night" for the labs, because an owl lab should feel like dusk and a
   dark stage makes bone and feather photography read far better. Both are
   driven by the same token set, so a component can sit on either.
   ========================================================================= */

/* --- Tokens -------------------------------------------------------------- */
:root {
  /* Brand teal, sampled from the Kidwings wordmark. */
  --teal-900: #062f34;
  --teal-800: #0a464e;
  --teal-700: #0d6068;
  --teal-600: #10757f;
  --teal-500: #168d99;
  --teal-300: #6fc0c8;
  --teal-100: #d3ecef;

  /* Amber, sampled from Sherlock Bones' plumage. */
  --amber-600: #c96a1f;
  --amber-500: #e8883a;
  --amber-400: #f5a623;
  --amber-200: #fbd79a;
  --amber-100: #fdeed3;

  --paper: #fbf7f0;
  --paper-warm: #f5ede0;
  --white: #ffffff;
  --ink: #14241f;
  --ink-soft: #40544f;
  --ink-faint: #6e837d;
  --line: #e2dacb;

  --night-900: #05161a;
  --night-800: #0a2126;
  --night-700: #0f2f35;
  --night-600: #16424a;
  --night-line: #1d4e57;
  --night-text: #dcecee;
  --night-soft: #93b3b8;

  --good: #2f9e6b;
  --bad: #c4483f;

  /* Type */
  --display: "Fraunces", ui-serif, Georgia, serif;
  --sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;

  /* A 1.2 modular scale that shrinks gracefully on phones. */
  --step--1: clamp(0.83rem, 0.8rem + 0.15vw, 0.9rem);
  --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.08rem);
  --step-1: clamp(1.2rem, 1.12rem + 0.4vw, 1.4rem);
  --step-2: clamp(1.45rem, 1.3rem + 0.75vw, 1.85rem);
  --step-3: clamp(1.75rem, 1.5rem + 1.25vw, 2.45rem);
  --step-4: clamp(2.1rem, 1.65rem + 2.2vw, 3.25rem);
  --step-5: clamp(2.5rem, 1.7rem + 3.6vw, 4.35rem);

  --sp-1: 0.35rem;
  --sp-2: 0.7rem;
  --sp-3: 1.1rem;
  --sp-4: 1.6rem;
  --sp-5: 2.4rem;
  --sp-6: 3.4rem;
  --sp-7: 5rem;

  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 22px;
  --r-xl: 32px;
  --r-pill: 999px;

  --shadow-sm: 0 1px 2px rgb(6 47 52 / 0.07), 0 2px 8px rgb(6 47 52 / 0.05);
  --shadow-md: 0 3px 6px rgb(6 47 52 / 0.07), 0 12px 28px rgb(6 47 52 / 0.09);
  --shadow-lg: 0 8px 18px rgb(6 47 52 / 0.1), 0 28px 60px rgb(6 47 52 / 0.14);

  --wrap: 1180px;
  --wrap-narrow: 760px;
  --nav-h: 72px;
}

/* --- Reset --------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 1rem);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--step-0);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

/* The UA rule is only `[hidden] { display: none }`, which any explicit
   `display` in this file outranks — `.btn` is inline-flex, so hiding a button
   with `el.hidden = true` silently did nothing. */
[hidden] { display: none !important; }

img, svg, video { max-width: 100%; height: auto; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--teal-700); text-decoration-thickness: 1.5px; text-underline-offset: 2px; }
a:hover { color: var(--teal-600); }

h1, h2, h3, h4, h5 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.1;
  margin: 0 0 0.5em;
  letter-spacing: -0.015em;
  font-variation-settings: "SOFT" 40, "WONK" 1;
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

:focus-visible {
  outline: 3px solid var(--amber-400);
  outline-offset: 2px;
  border-radius: 4px;
}

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

.skip-link {
  position: absolute; left: 50%; top: 0;
  transform: translate(-50%, -120%);
  z-index: 200; padding: 0.7rem 1.2rem;
  background: var(--teal-700); color: var(--white);
  border-radius: 0 0 var(--r-md) var(--r-md);
  font-weight: 600; text-decoration: none;
}
.skip-link:focus { transform: translate(-50%, 0); color: var(--white); }

/* --- Layout -------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(1.1rem, 4vw, 2.2rem);
}
.wrap--narrow { max-width: var(--wrap-narrow); }

.section { padding-block: var(--sp-7); }
.section--tight { padding-block: var(--sp-6); }
.section--paper { background: var(--paper-warm); }
.section--white { background: var(--white); }
.section--night { background: var(--night-900); color: var(--night-text); }
.section--night h2, .section--night h3 { color: var(--white); }

.stack > * + * { margin-top: var(--sp-3); }
.grid { display: grid; gap: var(--sp-4); }

/* --- Eyebrow / section heads --------------------------------------------- */
.eyebrow {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--teal-600);
  margin: 0 0 var(--sp-2);
}
.eyebrow::before {
  content: ""; width: 1.6rem; height: 2px;
  background: var(--amber-500); border-radius: 2px;
}
.section--night .eyebrow { color: var(--amber-400); }

.section-head { max-width: 62ch; margin-bottom: var(--sp-5); }
.section-head p { color: var(--ink-soft); font-size: var(--step-1); line-height: 1.55; }
.section--night .section-head p { color: var(--night-soft); }

.lede { font-size: var(--step-1); line-height: 1.55; color: var(--ink-soft); }
/* On a dark band the default lede is a dark grey on near-black, which is about
   two to one and unreadable. Written here rather than per-section so a lede
   dropped onto a night surface anywhere is legible by default. */
.section--night .lede, .passport-band .lede { color: var(--night-soft); }

/* --- Buttons ------------------------------------------------------------- */
.btn {
  --btn-bg: var(--teal-700);
  --btn-fg: var(--white);
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0.55rem;
  padding: 0.85rem 1.5rem;
  background: var(--btn-bg); color: var(--btn-fg);
  border: 2px solid transparent;
  border-radius: var(--r-pill);
  font-weight: 650; font-size: var(--step-0);
  text-decoration: none; cursor: pointer;
  transition: transform 0.16s ease, box-shadow 0.16s ease, background 0.16s ease;
  box-shadow: var(--shadow-sm);
}
.btn:hover {
  color: var(--btn-fg);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}
.btn:active { transform: translateY(0); }
.btn--amber { --btn-bg: var(--amber-500); --btn-fg: #3a1c04; }
.btn--ghost {
  --btn-bg: transparent; --btn-fg: var(--teal-700);
  border-color: currentColor; box-shadow: none;
}
.btn--ghost:hover { background: var(--teal-100); }
.btn--on-night {
  --btn-bg: transparent; --btn-fg: var(--night-text);
  border-color: var(--night-line); box-shadow: none;
}
.btn--on-night:hover { background: var(--night-700); }
.btn--lg { padding: 1rem 2rem; font-size: var(--step-1); }
.btn--sm { padding: 0.5rem 1rem; font-size: var(--step--1); }
.btn[disabled] { opacity: 0.45; pointer-events: none; }


/* --- Cards --------------------------------------------------------------- */
.card {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.card--link:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--teal-300);
}
.card__media { position: relative; aspect-ratio: 16 / 10; background: var(--paper-warm); overflow: hidden; }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
/* Specimen and diagram art: photographed on white, or transparent line art
   from the old site's GIFs. Either way it needs a light ground — on a dark
   one the foot diagrams disappear completely. */
.card__media--contain { background: var(--white); }
.card__media--contain img { object-fit: contain; padding: 0.9rem; }
.card__body { padding: var(--sp-3) var(--sp-3) var(--sp-4); display: flex; flex-direction: column; flex: 1; gap: 0.5rem; }
.card__body h3 { margin: 0; font-size: var(--step-1); }
.card__body p { color: var(--ink-soft); font-size: 0.95rem; margin: 0; }
.card__foot { margin-top: auto; padding-top: var(--sp-2); }
.card a.card__hit { position: absolute; inset: 0; z-index: 2; }
.card a.card__hit span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* Numbered lesson cards */
.lesson-card__num {
  position: absolute; top: 0.8rem; left: 0.8rem; z-index: 1;
  display: grid; place-items: center;
  width: 2.3rem; height: 2.3rem;
  background: var(--amber-400); color: #3a1c04;
  border-radius: var(--r-pill);
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.25);
}

/* --- Badges -------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.25rem 0.65rem;
  border-radius: var(--r-pill);
  font-size: 0.74rem; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase;
  background: var(--teal-100); color: var(--teal-800);
  white-space: nowrap;
}
.badge--play { background: var(--amber-100); color: var(--amber-600); }
.badge--read { background: var(--teal-100); color: var(--teal-800); }
.badge--night { background: var(--night-700); color: var(--teal-300); }
.badge-row { display: flex; flex-wrap: wrap; gap: 0.4rem; }

/* --- Grid helpers -------------------------------------------------------- */
.cards-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }
.cards-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); }
.cards-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr)); }


/* --- Night Lines ----------------------------------------------------------
   A tangle of same-coloured lines between an owl and its prey. The drawing is
   SVG; the owl and the animals are HTML on top of it, so they are real images
   with real alt text rather than SVG nobody can select. The two stay in
   register because everything is positioned as a percentage of the same
   1000x560 box. */
.lab--maze { padding: 0; background: none; border: 0; }
.maze {
  display: flex; flex-direction: column; gap: var(--sp-2); align-items: center;
  padding: var(--sp-2) clamp(0.5rem, 2vw, 1.5rem) var(--sp-3);
}
/* The animal it is after, beside the question rather than only named in it.
   A child who cannot yet read "Small Songbird" can still see which one to aim
   for, and the art is already drawn. */
.maze__brief {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(0.8rem, 2vw, 1.4rem); text-align: left;
  /* Air around it. Hard against the panel edge it read as something that had
     overflowed rather than something placed. */
  padding: var(--sp-3) var(--sp-4) 0;
  transition: opacity 0.4s ease;
}
.maze__brief .maze__pic--want {
  flex: none; width: clamp(54px, 7vw, 84px);
  max-width: clamp(54px, 7vw, 84px);
}
.maze__ask {
  margin: 0; font-size: var(--step-1); color: var(--night-text);
  /* Wide enough that the question does not break after "Which line", and
     balanced so the two lines come out near enough the same length. */
  max-width: 34ch; text-wrap: balance; line-height: 1.35;
}
.maze__ask strong { color: var(--amber-400); }
/* The second sentence onto its own line, without a gap that reads as a new
   paragraph. */
.maze__ask2 { display: block; }
.maze.is-caught .maze__brief { opacity: 0; }

.maze__board { position: relative; width: 100%; max-width: 62rem; }
.maze__svg {
  width: 100%; height: auto; display: block; overflow: hidden;
  border-radius: var(--r-lg);
}

.maze__line { transition: opacity 0.45s ease; }
.maze__line.is-dim { opacity: 0.16; }
/* A line already flown, struck off so it is not tried twice by accident. */
.maze__line.is-spent { opacity: 0.1; }
.maze__tab.is-spent { opacity: 0.32; pointer-events: none; }

/* Said over the board while the owl comes back, not under it: the board is
   where the player is looking. */
.maze__miss {
  position: absolute; left: 50%; top: 8%; translate: -50% 0; margin: 0;
  padding: 0.6rem 1.1rem; border-radius: var(--r-pill);
  background: rgb(6 24 28 / 0.92); border: 1px solid var(--night-line);
  color: var(--night-text); font-size: var(--step--1); text-align: center;
  max-width: 88%; animation: maze-note 0.35s ease-out both;
}
.maze__miss strong { color: var(--amber-400); }
.maze__note {
  margin: 0; font-size: 0.8rem; color: var(--night-soft); opacity: 0.85;
  max-width: 44ch; font-style: italic;
}
/* The sky sits behind everything and never wants a click. */
.maze__sky { pointer-events: none; }
.maze__ground { pointer-events: none; }

/* Which owl is hunting. The picture on the button is the same bird that will
   fly the line, so the choice is made by looking rather than by reading. */
.maze__pick {
  display: flex; align-items: center; justify-content: center;
  gap: 0.6rem; flex-wrap: wrap; padding-inline: var(--sp-3);
}
.maze__owlbtn {
  display: flex; align-items: center; gap: 0.55rem; cursor: pointer;
  padding: 0.45rem 0.9rem 0.45rem 0.5rem; border-radius: var(--r-pill);
  background: rgb(255 255 255 / 0.05); border: 1.5px solid rgb(255 255 255 / 0.16);
  color: rgb(255 255 255 / 0.72); font: 600 var(--step--1)/1 var(--sans);
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.maze__owlbtn img { width: 52px; height: auto; display: block; border-radius: 10px; }
.maze__owlbtn:hover:not([disabled]) { background: rgb(255 255 255 / 0.1); color: var(--white); }
.maze__owlbtn.is-on {
  background: rgb(232 136 58 / 0.16); border-color: var(--amber-400);
  color: var(--white);
}
.maze__owlbtn[disabled] { opacity: 0.45; cursor: default; }
.maze__owlname { display: flex; flex-direction: column; gap: 0.1rem; text-align: left; }
.maze__owlname em { font-style: normal; font-weight: 400; font-size: 0.78em; opacity: 0.75; }
.maze__soon {
  font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.1em;
  padding: 0.15em 0.5em; border-radius: var(--r-pill);
  background: rgb(255 255 255 / 0.12); color: rgb(255 255 255 / 0.7);
}
.maze__where {
  flex-basis: 100%; margin: 0.1rem 0 0; text-align: center;
  font-size: var(--step--1); color: var(--night-soft);
}
.maze.is-caught .maze__sky { opacity: 0.45; transition: opacity 0.5s ease; }
@keyframes maze-note { from { opacity: 0; translate: -50% -8px; } to { opacity: 1; translate: -50% 0; } }
/* Everything clears before the card arrives. */
.maze.is-caught .maze__svg,
.maze.is-caught .maze__prey { opacity: 0; transition: opacity 0.4s ease; }
.maze.is-caught .maze__owl { opacity: 0; transition: opacity 0.35s ease 0.1s; }

.maze__tab { cursor: pointer; transition: r 0.15s ease; }
.maze__tab:hover, .maze__tab:focus-visible { r: 21; outline: none; }
.maze__tab:focus-visible { stroke: var(--amber-400); stroke-width: 4; }
.maze.is-flying .maze__tab { cursor: default; pointer-events: none; }

/* Five lanes over the height means each animal gets a fifth of the board. */
.maze__prey { position: absolute; inset: 0; pointer-events: none; }
.maze__animal {
  position: absolute; right: 0; translate: 0 -50%;
  display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
  width: 12%;
}
.maze__pic { width: 100%; max-width: 66px; height: auto; display: block; margin-inline: auto; }
.maze__tag {
  font-size: 0.64rem; font-weight: 650; color: var(--night-text);
  text-align: center; line-height: 1.15; text-wrap: balance;
}
.maze__stand {
  aspect-ratio: 1; width: 100%; max-width: 66px; border-radius: var(--r-pill);
  background: rgb(255 255 255 / 0.08); border: 1.5px solid rgb(255 255 255 / 0.3);
  display: block;
}

/* `translate` rather than a transform: the flight sets left/top, and a
   transform here would be one more thing able to fight it. */
.maze__owl { position: absolute; width: 12%; translate: -50% -50%; pointer-events: none; }
.maze__owl img, .maze__owl .maze__stand { width: 100%; height: auto; display: block; }

/* The catch. Sits over the cleared board so the page does not jump. */
.maze__catch {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 0.5rem; text-align: center;
  padding: var(--sp-3); opacity: 0; translate: 0 14px;
  transition: opacity 0.5s ease, translate 0.5s cubic-bezier(0.22, 0.9, 0.3, 1.1);
}
.maze__catch.is-in { opacity: 1; translate: 0 0; }
.maze__pair { display: flex; align-items: center; gap: clamp(1rem, 5vw, 3.5rem); }
.maze__pic--owl { max-width: 190px; }
.maze__pic--got { max-width: 150px; }
/* The placeholder is an empty span, so unlike an image it has no size of its
   own to flex from and collapses to nothing in the card. */
.maze__catch .maze__stand { width: 130px; max-width: 130px; }
.maze__catch.is-in .maze__pic--owl { animation: maze-swoop 0.7s ease-out both; }
.maze__catch.is-in .maze__pic--got { animation: maze-pop 0.6s 0.15s cubic-bezier(0.22,0.9,0.3,1.3) both; }
@keyframes maze-swoop { from { opacity: 0; translate: -28px 0; } to { opacity: 1; translate: 0 0; } }
@keyframes maze-pop { from { opacity: 0; scale: 0.7; } to { opacity: 1; scale: 1; } }

.maze__verdict {
  margin: 0.4rem 0 0; font-family: var(--display); font-size: var(--step-2);
  color: var(--white);
}
.maze__catch.is-right .maze__verdict { color: var(--good); }
.maze__name { margin: 0; color: var(--night-text); font-weight: 650; }
.maze__name em { color: var(--night-soft); font-weight: 400; }
.maze__fact { margin: 0; color: var(--night-soft); line-height: 1.55; max-width: 48ch; }
.maze__pellet { margin: 0; color: var(--amber-400); font-size: var(--step--1); }
.maze__catch .btn { margin-top: 0.4rem; }
.maze__empty { color: var(--night-soft); }

@media (max-width: 700px) {
  .maze__ask { font-size: var(--step-0); max-width: 26ch; }
  .maze__brief { gap: 0.7rem; padding-inline: var(--sp-2); }
  /* On a phone there is no room beside the board for "Broad-footed Mole",
     and it was breaking into "Broad-" over the next animal's head. It is
     taken out of sight rather than shrunk further: the question already shows
     the animal it wants as a picture, so the match is picture to picture.
     The name stays in the markup for anything reading the page aloud. */
  .maze__tag {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  .maze__animal { width: 15%; }
  .maze__animal .maze__pic { max-width: none; }
  .maze__owl { width: 14%; }
  .maze__verdict { font-size: var(--step-1); }
  .maze__pic--owl { max-width: 110px; }
  .maze__pic--got { max-width: 92px; }
  .maze__owlbtn { padding: 0.4rem 0.7rem 0.4rem 0.4rem; font-size: 0.78rem; }
  .maze__owlbtn img { width: 40px; }

  /* The catch card is laid over the board on a wide screen, where the board is
     tall enough to hold it. On a phone the board is about 270px and the card
     needs 340, so centring it inside meant it spilled sixty pixels out of the
     top and sat on the owl switch. Here it takes normal flow instead: the
     board's own contents go, and the card decides the height. */
  .maze.is-caught .maze__svg,
  .maze.is-caught .maze__prey,
  .maze.is-caught .maze__owl { display: none; }
  .maze.is-caught .maze__catch { position: static; padding: var(--sp-3) 0; }
  .maze__where { font-size: 0.78rem; }
}

@media (prefers-reduced-motion: reduce) {
  .maze__line, .maze__catch { transition: none; }
  .maze__catch.is-in .maze__pic--owl,
  .maze__catch.is-in .maze__pic--got { animation: none; }
}

/* --- Match the Sounds ------------------------------------------------------
   Sits on the night surface with the other games. The play button is the
   biggest thing on the screen on purpose: the call is the question, and
   everything else is just the answer sheet. */
.lab--sounds { padding: 0; background: none; border: 0; }
.snd { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
  text-align: center; padding-block: var(--sp-5); min-height: 31rem;
  justify-content: center; }
.snd__count {
  margin: 0; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--night-soft);
}
.snd__play {
  display: flex; align-items: center; gap: 0.9rem; cursor: pointer;
  font: 650 var(--step-0)/1 var(--sans);
  padding: 1.1rem 2rem; border-radius: var(--r-pill);
  background: var(--amber-500); color: #3a1c04; border: 0;
  box-shadow: 0 12px 30px rgb(232 136 58 / 0.3);
  transition: background 0.18s ease;
}
.snd__play:hover { background: var(--amber-400); }
.snd__play.is-stuck { background: var(--night-600); color: var(--night-text); cursor: default; }

/* Five bars that only move while something is actually playing, so the button
   never claims to be making a sound it is not. */
.snd__wave { display: flex; align-items: center; gap: 3px; height: 22px; }
.snd__wave i { width: 3px; height: 7px; border-radius: 2px; background: currentColor; }
.snd__play.is-playing .snd__wave i { animation: snd-bar 0.9s ease-in-out infinite; }
.snd__play.is-playing .snd__wave i:nth-child(2) { animation-delay: 0.12s; }
.snd__play.is-playing .snd__wave i:nth-child(3) { animation-delay: 0.24s; }
.snd__play.is-playing .snd__wave i:nth-child(4) { animation-delay: 0.36s; }
.snd__play.is-playing .snd__wave i:nth-child(5) { animation-delay: 0.48s; }
@keyframes snd-bar { 0%, 100% { height: 7px; } 50% { height: 22px; } }

/* Two by two, always. There are four options by construction, and auto-fit
   left the fourth stranded on a row of its own at most widths — which reads as
   a mistake rather than as a choice. */
.snd__options {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  width: 100%; max-width: 40rem;
}
@media (max-width: 520px) { .snd__options { grid-template-columns: 1fr; } }
.snd__option {
  width: 100%; height: 100%; cursor: pointer;
  font: 600 var(--step-0)/1.3 var(--sans);
  padding: 1.1rem 1rem; border-radius: var(--r-md);
  background: rgb(255 255 255 / 0.06); color: var(--night-text);
  border: 1.5px solid var(--night-line);
  transition: background 0.16s ease, border-color 0.16s ease;
}
.snd__option:hover:not([disabled]) { background: rgb(255 255 255 / 0.13); }
.snd__option[disabled] { cursor: default; }
.snd__option.is-right { background: var(--good); border-color: var(--good); color: #04181c; }
.snd__option.is-wrong { background: var(--bad); border-color: var(--bad); color: #fff; }

.snd__said { display: flex; flex-direction: column; gap: 0.5rem; align-items: center;
  max-width: 48ch; }
.snd__verdict { margin: 0; font-family: var(--display); font-size: var(--step-1);
  color: var(--white); }
.snd__tell { margin: 0; color: var(--night-soft); line-height: 1.55; }
.snd__credit { margin: 0; font-size: 0.75rem; color: var(--night-soft); opacity: 0.75; }
.snd__score { margin: 0; font-family: var(--display); font-size: var(--step-4);
  color: var(--amber-400); line-height: 1; }
.snd__empty { color: var(--night-soft); max-width: 44ch; }

/* The game's own opener: the logo carries the title, so there is no dark page
   head above it. It sits on white because the artwork has a white ground. */
/* Centred, because the logo is a symmetrical badge and hanging it off the left
   margin fought its own composition. */
.game-open { padding-block: var(--sp-4) var(--sp-5); text-align: center; }
.game-open .crumbs--mid { justify-content: center; }
.game-open__mark { margin: var(--sp-2) auto var(--sp-3); }
.game-open__mark img {
  width: 100%; max-width: 34rem; height: auto; display: block; margin-inline: auto;
}
.game-open .lede--wide { margin-inline: auto; }

/* The sticker, landing with the answer. */
.snd__owl {
  width: 190px; height: auto; margin-bottom: 0.2rem;
  animation: snd-land 420ms cubic-bezier(0.22, 0.9, 0.3, 1.2) both;
}
@keyframes snd-land {
  from { opacity: 0; scale: 0.82; }
  to   { opacity: 1; scale: 1; }
}

/* Field notes as a set of cards rather than six tall rows. As rows they ran to
   three times the height of the game itself, which put the reference material
   above the thing it refers to in every sense. */
.notes {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.note {
  display: block; padding: 1rem 0.9rem; border: 1px solid var(--line);
  border-radius: var(--r-md); background: var(--white); text-align: center;
}
.note__owl { width: 92px; height: auto; display: block; margin: 0 auto 0.4rem; }
.note__name { display: block; font-size: var(--step-0); }
.note__latin {
  display: block; color: var(--ink-faint); font-style: italic;
  font-size: 0.82rem; margin-bottom: 0.4rem;
}
.credit-line {
  margin: var(--sp-4) 0 0; font-size: 0.82rem; line-height: 1.6;
  color: var(--ink-faint);
}
.credit-line a { color: var(--ink-soft); text-decoration: underline; }
.credit-line a:hover { color: var(--teal-700); }

@media (max-width: 640px) {
  .notes { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.7rem; }
  .note { padding: 0.9rem 0.7rem; }
  .note__owl { width: 78px; }
  .snd__owl { width: 150px; }
}

@media (prefers-reduced-motion: reduce) { .snd__owl { animation: none; } }

/* Field notes: the calls written down, under the game. A list to read after
   losing a round, not a table to study before one. The cards themselves are
   laid out further up; only the shared bits live here. */
.lede--wide { max-width: 62ch; font-size: var(--step-1); line-height: 1.6; }
.notes { list-style: none; margin: var(--sp-4) 0 0; padding: 0; }
.note__tell { color: var(--ink-soft); line-height: 1.5; font-size: 0.84rem; }

/* --- Sound credits --------------------------------------------------------
   On the page, because the licences ask for it and because a child should see
   that real people went out at night and recorded these. */
.credits__head { margin-bottom: 0.3em; }
.credits { list-style: none; margin: var(--sp-4) 0 0; padding: 0; display: grid; gap: 0; }
.credit {
  display: grid; gap: 0.15rem 1rem; padding: 0.8rem 0;
  border-bottom: 1px solid var(--line);
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) auto auto;
  align-items: baseline; font-size: var(--step--1);
}
.credit__what em { color: var(--ink-faint); font-size: 0.85em; font-style: italic; }
.credit__who { color: var(--ink-soft); }
.credit__lic a, .credit__src a { color: var(--teal-700); text-decoration: none; }
.credit__lic a:hover, .credit__src a:hover { text-decoration: underline; }
.credit__lic { font-size: 0.8rem; color: var(--ink-faint); }
.credit__src { font-size: 0.8rem; }

@media (max-width: 640px) {
  .credit { grid-template-columns: 1fr auto; }
  .credit__what { grid-column: 1 / -1; }
}

@media (prefers-reduced-motion: reduce) {
  .snd__play.is-playing .snd__wave i { animation: none; height: 14px; }
}

/* --- The atlas ------------------------------------------------------------
   A map of a made-up country, one territory per property. Every animation here
   changes opacity or a dash offset — nothing sets a CSS `transform` on an
   element the SVG has already positioned with a transform attribute, because
   that overrides the attribute and piles the whole map onto the origin. It has
   happened twice on this site. The owl is SMIL, so it needs no script. */
.jmap { width: 100%; max-width: 580px; margin-inline: auto; }
.jmap svg { width: 100%; height: auto; display: block; overflow: visible; }

.jmap__label {
  font-family: var(--sans); font-size: 13px; font-weight: 650;
  fill: var(--ink); text-anchor: middle;
}
.jmap__kind {
  font-family: var(--sans); font-size: 9.5px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; text-anchor: middle;
}
.jmap__mono {
  font-family: var(--sans); font-size: 13px; font-weight: 700; text-anchor: middle;
}

/* Coastlines draw themselves, then settle. */
.jmap__home-shore, .jmap__shore {
  stroke-dasharray: 900; stroke-dashoffset: 900;
  animation: jmap-coast 2s ease-out forwards;
}
.jmap__shore2 { opacity: 0; animation: jmap-fade 900ms 1.3s ease-out forwards; }
.jmap__land { opacity: 0; animation: jmap-fade 800ms ease-out forwards; }
.jmap__city { opacity: 0; animation: jmap-fade 650ms ease-out forwards; }
.jmap__sea { animation: jmap-tide 9s ease-in-out infinite; }

/* The road is dashed, and its dashes crawl the way a route on a map should. */
.jmap__road {
  stroke-dasharray: 6 10; opacity: 0;
  animation: jmap-road 1.1s 1.1s ease-out forwards,
             jmap-crawl 2.2s 2.2s linear infinite;
}
.jmap__compass { opacity: 0; animation: jmap-fade 900ms 1.8s ease-out forwards; }
.jmap__owl { opacity: 0; animation: jmap-fade 900ms 2.2s ease-out forwards; }

@keyframes jmap-coast { to { stroke-dashoffset: 0; } }
@keyframes jmap-fade { to { opacity: 1; } }
@keyframes jmap-road { to { opacity: 0.8; } }
@keyframes jmap-crawl { to { stroke-dashoffset: -32; } }
@keyframes jmap-tide { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .jmap__home-shore, .jmap__shore, .jmap__shore2, .jmap__land, .jmap__city,
  .jmap__compass, .jmap__owl, .jmap__road {
    animation: none; opacity: 1; stroke-dashoffset: 0;
  }
  .jmap__road { opacity: 0.8; }
  .jmap__sea { animation: none; }
  .jmap animateMotion { display: none; }
}

@media (max-width: 900px) { .jmap { max-width: 460px; } }

/* --- Page header (interior pages) --------------------------------------- */
.page-head {
  background: linear-gradient(168deg, var(--night-800), var(--teal-900));
  color: var(--night-text);
  padding-block: var(--sp-6) var(--sp-6);
}
.page-head h1 { color: var(--white); margin-bottom: var(--sp-2); }
.page-head p { color: #b6d2d5; max-width: 60ch; font-size: var(--step-1); }

.crumbs {
  display: flex; flex-wrap: wrap; gap: 0.4rem;
  list-style: none; margin: 0 0 var(--sp-3); padding: 0;
  font-size: var(--step--1); color: var(--teal-300);
}
.crumbs a { color: var(--teal-300); text-decoration: none; }
.crumbs a:hover { color: var(--white); text-decoration: underline; }
.crumbs li + li::before { content: "/"; margin-right: 0.4rem; opacity: 0.5; }


/* --- Generic gallery ----------------------------------------------------- */
.tiles { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(min(100%, 190px), 1fr)); }
.tile {
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-md); overflow: hidden; text-align: center;
  box-shadow: var(--shadow-sm);
}
.tile img { width: 100%; aspect-ratio: 4/3; object-fit: contain; background: var(--paper-warm); padding: 0.5rem; }
.tile figcaption { padding: 0.6rem; font-size: 0.88rem; font-weight: 600; }

/* --- Download strip ------------------------------------------------------ */
.downloads { display: grid; gap: var(--sp-2); grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.download {
  display: flex; align-items: center; gap: 0.8rem;
  padding: 0.9rem 1.1rem;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-md); text-decoration: none;
  box-shadow: var(--shadow-sm);
}
.download:hover { border-color: var(--amber-400); }
.download svg { flex: none; color: var(--amber-600); }
.download b { display: block; font-size: 0.95rem; color: var(--ink); }
.download span { font-size: var(--step--1); color: var(--ink-faint); }

.kit-credit { margin-top: var(--sp-3); font-size: var(--step--1); color: var(--ink-faint); text-align: center; }

/* =========================================================================
   Kit pages (Salamander Lab): hero, fact strip, field guide
   ========================================================================= */
.kit-hero {
  position: relative; overflow: hidden;
  background:
    radial-gradient(90% 80% at 80% 30%, #1d5560 0%, transparent 60%),
    linear-gradient(165deg, var(--night-800), var(--teal-900) 60%, #041f24);
  color: var(--night-text);
  padding-block: var(--sp-6);
}
.kit-hero__inner {
  display: grid; gap: var(--sp-5); align-items: center;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
}
.kit-hero h1 { color: var(--white); font-size: var(--step-5); margin-bottom: var(--sp-3); }
.kit-hero__lede { color: #bcd6d9; font-size: var(--step-1); line-height: 1.55; max-width: 52ch; }
.kit-hero__art { margin: 0; }
.kit-hero__art img {
  width: 100%; max-width: 520px; margin-inline: auto;
  filter: drop-shadow(0 26px 40px rgb(0 0 0 / 0.55));
}
@media (max-width: 860px) {
  .kit-hero__inner { grid-template-columns: 1fr; }
  .kit-hero__art img { max-width: 300px; }
}

.kit-facts { background: var(--paper-warm); border-bottom: 1px solid var(--line); }
.kit-facts__row {
  display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr));
  padding-block: var(--sp-4);
}
.kit-fact { border-left: 3px solid var(--amber-400); padding-left: 0.8rem; }
.kit-fact strong {
  display: block; font-family: var(--display); font-weight: 700;
  font-size: var(--step-2); color: var(--teal-800); line-height: 1.1;
}
.kit-fact span { font-size: 0.9rem; color: var(--ink-soft); }

.guide-nav { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: var(--sp-5); }
.guide-nav a {
  padding: 0.45rem 0.95rem; border-radius: var(--r-pill);
  background: var(--white); border: 1px solid var(--line);
  color: var(--teal-800); font-weight: 600; font-size: 0.92rem; text-decoration: none;
}
.guide-nav a:hover { border-color: var(--teal-300); background: var(--teal-100); }

.guide-chapter { margin-top: var(--sp-6); scroll-margin-top: calc(var(--nav-h) + 1.5rem); }
.guide-chapter:first-of-type { margin-top: 0; }
.guide-chapter > h3 {
  font-size: var(--step-3); padding-bottom: 0.4em; margin-bottom: var(--sp-4);
  border-bottom: 2px solid var(--line);
}
.guide-chapter h4 { font-size: var(--step-2); margin-bottom: 0.45em; }
.guide-chapter p { color: var(--ink-soft); max-width: 62ch; }

.guide-feature {
  display: grid; gap: var(--sp-5); align-items: center;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  margin-bottom: var(--sp-5);
}
.guide-feature:nth-of-type(even) .guide-feature__figure { order: -1; }
.guide-feature__figure {
  margin: 0; padding: var(--sp-3);
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
}
.guide-feature__figure img { width: 100%; height: auto; border-radius: var(--r-md); }
@media (max-width: 820px) {
  .guide-feature { grid-template-columns: 1fr; gap: var(--sp-3); }
  .guide-feature:nth-of-type(even) .guide-feature__figure { order: 0; }
}

.guide-cards { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.guide-card {
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: var(--sp-4); box-shadow: var(--shadow-sm);
}
.guide-card h4 { font-size: var(--step-1); }
.guide-card p { font-size: 0.97rem; }

.guide-source { margin-top: var(--sp-6); }
.kit-hero .eyebrow { color: var(--amber-400); }
.kit-hero .crumbs { margin-bottom: var(--sp-2); }
.guide-intro {
  font-family: var(--display); font-size: var(--step-1); line-height: 1.5;
  color: var(--ink); max-width: 60ch; margin-bottom: var(--sp-6);
}


/* --- The barn in the corner ---------------------------------------------- */
/*
   An easter egg: click the barn, four owls get out and crack the glass.
   `static/js/owls.js` builds all of this; nothing here exists without it.

   The whole flying layer is `pointer-events: none`. Four owls hurtling over
   the page must not swallow a click meant for a link underneath — only the
   barn itself takes one.
*/
html.owls-loose #main { overflow: clip; }

.owl-layer {
  position: fixed; inset: 0; z-index: 400;
  pointer-events: none; overflow: hidden;
}
.owl-glass { position: absolute; inset: 0; width: 100%; height: 100%; }

.owl-bird {
  position: absolute; top: 0; left: 0;
  width: 112px; height: auto;
  will-change: transform;
  filter: drop-shadow(0 10px 18px rgb(6 24 28 / 0.3));
}
/* transform is written every frame by the script, so it must not be
   transitioned here — the two would fight and the birds would lag the maths. */
.owl-bird.is-flying { animation: owl-burst 0.4s ease-out; }
@keyframes owl-burst { from { opacity: 0; } to { opacity: 1; } }

.owl-barn {
  position: fixed; right: var(--sp-4); bottom: var(--sp-4); z-index: 420;
  width: 96px; padding: 0; border: 0; background: none; cursor: pointer;
  line-height: 0; border-radius: 12px;
  transition: transform 0.25s cubic-bezier(0.34, 1.4, 0.64, 1), filter 0.25s ease;
  filter: drop-shadow(0 6px 14px rgb(6 24 28 / 0.26));
}
.owl-barn img { width: 100%; height: auto; }
.owl-barn:hover { transform: translateY(-4px) scale(1.06) rotate(-2deg); }
.owl-barn:active { transform: translateY(0) scale(0.97); }
.owl-barn:focus-visible { outline: 3px solid var(--amber-500); outline-offset: 4px; }
/* Doors open: the barn leans and warms up while the owls are out, so the
   thing you click to undo it is visibly the thing that is going on. */
.owl-barn.is-open {
  transform: rotate(-3deg) scale(1.04);
  filter: drop-shadow(0 0 16px rgb(242 169 58 / 0.75)) drop-shadow(0 6px 14px rgb(6 24 28 / 0.3));
}

/* What the barn says. It is a speech bubble on the barn, not a banner across
   the top: the barn is the thing you clicked and the thing that is shouting,
   and a message that appears somewhere else is a message from the website.
   Anchored to the same corner, with the tail pointing down at the roof. */
.owl-shout {
  position: fixed; z-index: 430;
  right: var(--sp-4); bottom: calc(96px + var(--sp-4) + 16px);
  margin: 0; padding: 0.7rem 1.1rem;
  width: max-content; max-width: min(72vw, 21rem); text-align: left;
  font-family: var(--display); font-weight: 700; font-size: var(--step-0);
  line-height: 1.3; color: var(--night-text); background: var(--night-900);
  border: 2px solid var(--amber-500); border-radius: 16px;
  box-shadow: 0 16px 36px rgb(6 24 28 / 0.42);
  opacity: 0; pointer-events: none;
  /* Grown from the corner nearest the barn, so it reads as coming out of it
     rather than as appearing over it. */
  transform-origin: calc(100% - 26px) 100%;
  transform: scale(0.6) translateY(10px);
  transition: opacity 0.26s ease, transform 0.36s cubic-bezier(0.34, 1.5, 0.6, 1);
}
.owl-shout::after {
  content: ""; position: absolute; right: 20px; bottom: -9px;
  width: 14px; height: 14px; background: var(--night-900);
  border-right: 2px solid var(--amber-500); border-bottom: 2px solid var(--amber-500);
  transform: rotate(45deg); border-bottom-right-radius: 3px;
}
.owl-shout.is-in { opacity: 1; transform: scale(1) translateY(0); }

/* Cracked glass. Every line is drawn twice — a pale wide stroke with a dark
   hairline over it — so a crack reads on the cream page, on the night band
   and over a photograph without being coloured per background. */
.owl-crack { opacity: 0; transition: opacity 0.18s ease; }
.owl-crack.is-in { opacity: 1; }
.owl-crack__glow {
  fill: none; stroke: rgb(255 255 255 / 0.75);
  stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round;
}
.owl-crack__line {
  fill: none; stroke: rgb(10 24 28 / 0.38);
  stroke-width: 0.8; stroke-linejoin: round; stroke-linecap: round;
}
.owl-crack__hit { fill: rgb(255 255 255 / 0.9); stroke: rgb(10 24 28 / 0.4); stroke-width: 1; }
/* A split running the whole way across the window. Thinner than a chip, or it
   reads as a drawn line rather than as glass that has gone. */
.owl-crack--long .owl-crack__glow { stroke-width: 1.8; stroke: rgb(255 255 255 / 0.6); }
.owl-crack--long .owl-crack__line { stroke-width: 0.7; }

/* What the owls have got to.

   `owl-askew` is something standing next to a hit and knocked off true, and
   `owl-hit` is something on its way off the bottom of the window. Both are
   written as transforms by the script; what is here is only the colouring, so
   the two never fight over the same property. */
.owl-askew { filter: drop-shadow(0 6px 12px rgb(6 24 28 / 0.22)); }
.owl-hit { filter: drop-shadow(0 14px 26px rgb(6 24 28 / 0.4)) saturate(0.85); }

/* The shake is on <main> and nowhere above it: transforming an ancestor of a
   fixed element makes that element fixed to the ancestor instead of the
   viewport, which would drag the header, the barn and the owls into it. */
#main.is-struck { animation: owl-struck 0.3s ease-in-out; }
@keyframes owl-struck {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-5px, 3px); }
  45% { transform: translate(4px, -3px); }
  70% { transform: translate(-3px, -2px); }
}

/* The switch in the top bar. `hidden` in the markup and revealed by the
   script, so a browser that never ran it is not left with a dead control. */
.owl-switch {
  order: 2; flex: none;
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.4rem 0.7rem 0.4rem 0.5rem;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--paper); color: var(--ink-soft); cursor: pointer;
  font: inherit; font-size: 0.82rem; font-weight: 600; white-space: nowrap;
}
.owl-switch:hover { border-color: var(--teal-700); color: var(--teal-800); }
.owl-switch:focus-visible { outline: 3px solid var(--amber-500); outline-offset: 2px; }
.owl-switch img { width: 22px; height: auto; }
.owl-switch__track {
  width: 30px; height: 17px; border-radius: 9px; flex: none;
  background: var(--line); position: relative; transition: background 0.2s ease;
}
.owl-switch__track::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 13px; height: 13px; border-radius: 50%; background: var(--white);
  box-shadow: 0 1px 3px rgb(6 24 28 / 0.35);
  transition: transform 0.2s cubic-bezier(0.34, 1.4, 0.64, 1);
}
.owl-switch[aria-checked="true"] .owl-switch__track { background: var(--teal-700); }
.owl-switch[aria-checked="true"] .owl-switch__track::after { transform: translateX(13px); }

@media (max-width: 900px) {
  /* The word "on"/"off" goes and the track carries the state on its own. The
     name stays: a bare toggle in a top bar, next to a burger, is a control
     nobody can identify. The top bar on a phone is already carrying a logo,
     the passport and a burger, so the switch gives up its padding too. */
  .owl-switch__state { display: none; }
  .owl-switch { padding: 0.35rem 0.5rem 0.35rem 0.55rem; gap: 0.35rem; }
  .owl-barn { width: 72px; right: var(--sp-2); bottom: var(--sp-2); }
  .owl-bird { width: 84px; }
  .owl-shout {
    font-size: var(--step--1); padding: 0.55rem 0.85rem;
    right: var(--sp-2); bottom: calc(72px + var(--sp-2) + 14px);
    max-width: min(76vw, 17rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* The owls still get out — switching the joke off for somebody who gets
     motion sick is not an accessibility feature. They drift instead of
     hurtling (the script halves their speed), and nothing shakes. */
  #main.is-struck { animation: none; }
  .owl-barn, .owl-bird.is-flying { transition: none; animation: none; }
  .owl-barn:hover { transform: none; }
}


/* --- Hungry Owlets ------------------------------------------------------- */
/*
   A night's hunting and a nest to feed. The board is one canvas; everything
   round it is ordinary markup, so the meters and the brood work with a screen
   reader and the canvas carries only the picture.
*/
/* The standard lab shell — a bar and a body, both with the padding every
   other lab uses. Built with its own wrapper to begin with, which had no
   padding at all, so "Night 1" sat under the panel's rounded corner. */
.owlets__body { display: flex; flex-direction: column; gap: var(--sp-3); }
.owlets__bar { color: var(--night-text); font-size: var(--step--1); }
.owlets__night { font-family: var(--display); font-weight: 700; font-size: var(--step-0); }
/* A label and a track of its own, rather than a bar faked with ::before and a
   calc() that assumed how wide the word "Energy" is. On a phone that guess was
   wrong and the bar ran off both ends of the row. */
.owlets__meter { display: inline-flex; align-items: center; gap: 0.45rem; flex: none; }
.owlets__meter b { font-weight: 600; color: var(--night-soft); }
.owlets__track {
  width: 5rem; height: 9px; border-radius: 5px; flex: none; overflow: hidden;
  background: rgb(255 255 255 / 0.16);
}
.owlets__track i { display: block; height: 100%; width: 100%; transition: width 0.2s linear; }
.owlets__meter--moon i { background: linear-gradient(90deg, #4d6f9a, #fdf6e0); }
.owlets__meter--stam i { background: linear-gradient(90deg, var(--amber-500), #ffd79a); }
.owlets__food { margin-left: auto; color: var(--amber-400); font-weight: 650; }

.owlets__stage { position: relative; }
.owlets__canvas {
  display: block; width: 100%; border-radius: var(--r-md);
  background: #0b1b33; cursor: crosshair; touch-action: none;
}

/* What the game has just taught, over the picture rather than beside it. */
/* At the top, not the bottom. It used to sit over the lower third of the
   board, which on the nest screen is exactly where the owlets stand — the
   message about who just ate covered the one who had eaten. */
.owlets__say {
  /* Top right, because top left is where the barn stands on the meadow and
     the way back out sits on the nest screen. Centred, it covered both. */
  position: absolute; right: 3%; top: 3%;
  width: min(72%, 30rem); padding: 0.7rem 1rem;
  display: flex; flex-direction: column; gap: 0.25rem;
  background: rgb(5 22 26 / 0.92); border: 1px solid var(--night-line);
  border-radius: var(--r-md); color: var(--night-text);
  font-size: var(--step--1); line-height: 1.45;
}
.owlets__say strong { font-family: var(--display); font-size: var(--step-0); }
.owlets__say span { color: var(--night-soft); }

.owlets__brood {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: var(--sp-2);
}
.owlet {
  display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
  padding: 0.6rem 0.5rem; cursor: pointer;
  background: rgb(255 255 255 / 0.05); border: 1px solid var(--night-line);
  border-radius: var(--r-md); color: var(--night-text); font: inherit;
}
.owlet:not([disabled]):hover { border-color: var(--amber-500); background: rgb(242 169 58 / 0.12); }
.owlet[disabled] { cursor: default; opacity: 0.72; }
.owlet:focus-visible { outline: 3px solid var(--amber-500); outline-offset: 2px; }
.owlet img { width: 58px; height: auto; }
.owlet__name { font-family: var(--display); font-weight: 700; }
.owlet__stage { color: var(--night-soft); font-size: var(--step--1); }
.owlet__bar {
  width: 100%; height: 7px; border-radius: 4px;
  background: rgb(255 255 255 / 0.14); overflow: hidden;
}
.owlet__bar i { display: block; height: 100%; background: var(--good); transition: width 0.3s ease; }

.owlets__foot { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.owlets__line { margin: 0; color: var(--night-soft); }
.owlets__empty { margin: 0; color: var(--night-soft); }
.owlets__end h3 { margin: 0 0 0.3rem; font-size: var(--step-2); color: var(--white); }
.owlets__end p { margin: 0 0 var(--sp-3); color: var(--night-soft); max-width: 44rem; }

@media (max-width: 700px) {
  .owlets__bar { gap: var(--sp-2); font-size: var(--step--1); }
  .owlets__track { width: 4rem; }
  .owlets__food { margin-left: 0; width: 100%; }
  .owlet img { width: 46px; }
  .owlets__say { font-size: var(--step--1); padding: 0.55rem 0.8rem; }
}

@media (prefers-reduced-motion: reduce) {
  .owlets__track i, .owlet__bar i { transition: none; }
}

/* --- How a game works, in pictures from the game ------------------------- */
.howto {
  list-style: none; margin: var(--sp-5) 0 0; padding: 0;
  display: grid; gap: var(--sp-5);
}
.howto__step {
  display: grid; gap: var(--sp-4); align-items: center;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
}
/* Alternating, so the eye goes down the page in a zigzag rather than reading
   four identical rows and stopping at two. */
.howto__step:nth-child(even) > img { order: 2; }
.howto__step img {
  width: 100%; height: auto; border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}
.howto__step h3 {
  display: flex; align-items: baseline; gap: 0.7rem;
  margin: 0 0 0.4rem; font-size: var(--step-2);
}
.howto__step h3 span {
  display: grid; place-items: center; flex: none;
  width: 2rem; height: 2rem; border-radius: 50%;
  background: var(--amber-500); color: var(--night-900);
  font-family: var(--display); font-size: var(--step--1); font-weight: 700;
}
.howto__step p { margin: 0; color: var(--ink-soft); max-width: 34rem; }

@media (max-width: 760px) {
  .howto { gap: var(--sp-4); }
  .howto__step { grid-template-columns: 1fr; gap: var(--sp-3); }
  .howto__step:nth-child(even) > img { order: 0; }
  .howto__step h3 { font-size: var(--step-1); }
}

/* --- A lab filling the window -------------------------------------------- */
/*
   Added to every lab by mount.js. The point is that a game should be visible
   all at once: the owlets panel was below the fold, so feeding meant scrolling
   away from the meadow and back.
*/
.lab-full {
  position: absolute; top: var(--sp-2); right: var(--sp-2); z-index: 6;
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.4rem 0.8rem;
  background: rgb(5 22 26 / 0.74); color: var(--night-text);
  border: 1px solid var(--night-line); border-radius: var(--r-pill);
  font: inherit; font-size: var(--step--1); font-weight: 600; cursor: pointer;
  backdrop-filter: blur(6px);
}
.lab-full:hover { background: rgb(5 22 26 / 0.92); border-color: var(--amber-500); }
.lab-full:focus-visible { outline: 3px solid var(--amber-500); outline-offset: 2px; }
.lab-full__icon {
  width: 14px; height: 14px; flex: none;
  border: 2px solid currentColor; border-radius: 2px;
  /* Corners only, so it reads as "expand" rather than as an empty box. */
  clip-path: polygon(0 0, 42% 0, 42% 16%, 16% 16%, 16% 42%, 0 42%,
                     0 100%, 42% 100%, 42% 84%, 16% 84%, 16% 58%, 0 58%,
                     100% 0, 58% 0, 58% 16%, 84% 16%, 84% 42%, 100% 42%,
                     100% 100%, 58% 100%, 58% 84%, 84% 84%, 84% 58%, 100% 58%);
}
.lab { position: relative; }
/* The button sits over the lab's top right corner, which on most labs is the
   bar — so the bar gives up that much of its own width rather than having the
   button land on the meters. */
.lab .lab__bar { padding-right: 9.5rem; }

@media (max-width: 520px) {
  /* Just the icon down here: the bar is already wrapping and the word costs
     more than it explains next to a symbol everyone knows. */
  .lab-full__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .lab-full { padding: 0.4rem 0.5rem; }
  .lab .lab__bar { padding-right: 3.4rem; }
}

/* The page behind must not scroll under it, or a stray swipe moves the
   article while the child is trying to hunt. */
html.has-full-lab { overflow: clip; }

.lab.is-full {
  position: fixed; inset: 0; z-index: 400;
  margin: 0; border-radius: 0; max-width: none;
  display: flex; flex-direction: column;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
/* Most labs wrap everything in one div of their own, and a plain block in the
   middle of the chain stops the height getting through: the lab was 959 tall,
   the wrapper 622, and the game sat in the top two thirds with dead space
   under it. Anything directly inside a full-screen lab becomes a flex column
   that fills — except the button, which is positioned and not a flex item. */
.lab.is-full > :not(.lab-full) {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
}
.lab.is-full .lab__bar { flex: none; }
.lab.is-full .lab__body,
.lab.is-full .owlets__body {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  overflow: auto;
  /* On a tall phone the board is limited by the width of the screen, so there
     is height left over. Centred, that space falls above and below the game
     instead of all of it under the footer. */
  justify-content: center;
}
/* The board takes whatever height is left once the panels have had theirs,
   and the aspect-ratio turns that into a width — which is what stops the
   canvas growing past the bottom of the window. */
.lab.is-full .owlets__stage {
  flex: 1 1 auto; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  /* Read by fitCanvas: fill this box, both ways, keeping the board's shape. */
  --fit: box;
}
.lab.is-full .owlets__brood { flex: none; }
.lab.is-full .owlets__foot { flex: none; }

@media (max-width: 700px) {
  .lab.is-full .lab__body,
  .lab.is-full .owlets__body { padding: var(--sp-2); }
  .lab.is-full .owlet img { width: 34px; }
  .lab.is-full .owlet { padding: 0.35rem 0.3rem; gap: 0.15rem; }
}

/* --- Bear Feast ---------------------------------------------------------- */
.bears__bar { color: var(--night-text); font-size: var(--step--1); }
.bears__level { font-family: var(--display); font-weight: 700; font-size: var(--step-0); }
.bears__moves b { color: var(--amber-400); font-size: var(--step-0); }
/* A tight level has to feel tight. The count used to sit in the bar in the
   same colour whether there were twenty swaps left or one, which is how a
   level built around its move limit got played without the limit being
   noticed at all. */
.bears__moves { transition: color 180ms ease; }
.bears__moves.is-low { color: var(--amber-400); }
.bears__moves.is-low b { animation: moves-tick 900ms ease-in-out infinite; }
.bears__moves.is-last { color: var(--bad); }
.bears__moves.is-last b { color: var(--bad); animation-duration: 520ms; }
@keyframes moves-tick {
  0%, 100% { transform: none; }
  50%      { transform: scale(1.22); }
}
@media (prefers-reduced-motion: reduce) {
  /* The colour still changes; only the beat goes. */
  .bears__moves.is-low b, .bears__moves.is-last b { animation: none; }
}
.bears__aim { margin-left: auto; display: inline-flex; align-items: center; gap: 0.6rem;
  font-weight: 650; color: var(--amber-400); }
.bears__want { display: inline-flex; align-items: center; gap: 0.3rem; }
.bears__want img { width: 26px; height: auto; }

.bears__body { display: flex; flex-direction: column; gap: var(--sp-3); }
/* Board and bear side by side where there is room; the bear goes under it on
   a phone, which is the only place it fits without shrinking the board. */
.bears__row { display: grid; grid-template-columns: minmax(0, 1fr) 13rem; gap: var(--sp-3); align-items: start; }
.bears__stage { position: relative; --fit: box; }
/* Out of the board. "That does not line anything up" is a thing to read, not
   a thing to play around, and centred it sat on the pieces it was about. */
.bears__stage .lab-toast { bottom: auto; top: -0.6rem; }
.bears__canvas {
  display: block; width: 100%; border-radius: var(--r-md);
  /* Translucent, so the bear's country shows through its own board rather
     than only around the edges of it. */
  background: rgb(5 22 26 / 0.42);
  cursor: pointer; touch-action: none;
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.08);
}

.bears__over {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgb(5 22 26 / 0.82); border-radius: var(--r-md);
}
.bears__card { text-align: center; max-width: 26rem; padding: var(--sp-4); }
.bears__card h3 { margin: 0 0 0.5rem; font-size: var(--step-2); color: var(--white); }
.bears__card p { margin: 0 0 var(--sp-3); color: var(--night-soft); line-height: 1.5; }

.bears__side { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; text-align: center; }
.bears__bear { width: 100%; max-width: 9rem; height: auto; }
/* The bear lunges when the board cascades. The weather over the board is drawn
   on the canvas; this is the one part of a combo that happens outside it, so
   the animal gets credit for the meal rather than just the scenery. */
.bears__bear.is-lunging { animation: bear-lunge 620ms cubic-bezier(.3, 1.5, .4, 1); }
@keyframes bear-lunge {
  0%   { transform: none; }
  30%  { transform: translateY(-9%) scale(1.09); }
  60%  { transform: translateY(1%) scale(0.99); }
  100% { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .bears__bear.is-lunging { animation: none; }
}
.bears__name { margin: 0; font-size: var(--step--1); color: var(--night-soft); }
.bears__fat {
  width: 100%; height: 12px; border-radius: 6px; overflow: hidden;
  background: rgb(255 255 255 / 0.16);
}
.bears__fat i { display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, var(--amber-500), #ffd79a); transition: width 0.3s ease; }
.bears__fatline { margin: 0; font-weight: 650; color: var(--night-text); font-size: var(--step--1); }

.bears__scores { list-style: none; margin: var(--sp-2) 0 0; padding: 0; width: 100%;
  display: flex; flex-direction: column; gap: 0.25rem; }
.bears__scores li { display: flex; align-items: center; gap: 0.45rem;
  font-size: var(--step--1); color: var(--night-soft); }
.bears__scores img { width: 26px; height: auto; flex: none; }
.bears__scores span { flex: 1; text-align: left; }
.bears__scores b { color: var(--amber-400); }

.bears__levels { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.bears__pick {
  display: flex; align-items: center; justify-content: center; gap: 0.35rem;
  padding: 0.5rem 0.9rem; cursor: pointer; font: inherit;
  background: rgb(255 255 255 / 0.05); border: 1px solid var(--night-line);
  border-radius: var(--r-md); color: var(--night-text); font-weight: 650;
}
.bears__pick:hover { border-color: var(--amber-500); }
.bears__pick.is-on { border-color: var(--amber-500); background: rgb(242 169 58 / 0.14); }
/* The tick for a level already beaten. It sits beside the name rather than on
   a row of its own: that row was reserved whether there was a tick in it or
   not, and left every unbeaten level's name sitting low in its button with a
   band of nothing above it. Empty, it takes no room at all — reserving the
   space instead made the three buttons wide enough to wrap onto two lines,
   which is a worse thing to look at than a button growing by one glyph on the
   day a child earns it. */
.bears__picktop { color: var(--good); font-size: var(--step--1); }
.bears__picktop:empty { display: none; }
.bears__note { margin: 0; color: var(--night-soft); max-width: 46rem; line-height: 1.5; }
.bears__empty { margin: 0; color: var(--night-soft); }

@media (max-width: 760px) {
  .bears__row { grid-template-columns: 1fr; }
  .bears__side { flex-direction: row; flex-wrap: wrap; justify-content: center; }
  .bears__bear { max-width: 5.5rem; }
  .bears__fat, .bears__scores { width: 100%; }
  .bears__scores { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 0.6rem; }
  .bears__scores li { gap: 0.25rem; }
  .bears__scores span { display: none; }
  .bears__aim { margin-left: 0; }
}

/* The bear's own country, behind the board. Dimmed hard: it is a place, not
   a picture to look at, and the food has to stay readable on top of it. */
.bears {
  position: relative;
  background-image: linear-gradient(rgb(5 22 26 / 0.62), rgb(5 22 26 / 0.74)), var(--scene, none);
  background-size: cover; background-position: center;
}
.bears__who {
  display: flex; align-items: center; gap: 0.6rem; text-align: left;
  padding: 0.45rem 0.8rem 0.45rem 0.45rem; cursor: pointer; font: inherit;
  background: rgb(255 255 255 / 0.05); border: 1px solid var(--night-line);
  border-radius: var(--r-pill); color: var(--night-text);
}
.bears__who:hover { border-color: var(--amber-500); }
.bears__who.is-on { border-color: var(--amber-500); background: rgb(242 169 58 / 0.14); }
.bears__who img { width: 40px; height: auto; flex: none; }
.bears__who strong { display: block; font-family: var(--display); font-size: var(--step--1); }
.bears__who em { display: block; font-style: normal; font-size: 0.78rem; color: var(--night-soft); }
.bears__bears { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

@media (max-width: 760px) {
  .bears__who em { display: none; }
}

/* --- What each bear eats -------------------------------------------------- */
.diets {
  margin-top: var(--sp-5);
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.diet {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-4); border-radius: var(--r-lg);
  border: 1px solid var(--night-line);
  /* Its own country behind it, the same picture the board sits on. */
  background-image: linear-gradient(rgb(5 22 26 / 0.84), rgb(5 22 26 / 0.9)), var(--habitat);
  background-size: cover; background-position: center;
}
.diet__who { display: flex; align-items: center; gap: var(--sp-3); }
.diet__who img { width: 78px; height: auto; flex: none; }
.diet__who h3 { margin: 0; font-size: var(--step-1); color: var(--white); }
.diet__latin { margin: 0; font-style: italic; color: var(--night-soft); font-size: var(--step--1); }
.diet__where { margin: 0.15rem 0 0; color: var(--night-soft); font-size: var(--step--1); }
.diet__goal {
  margin: 0; align-self: flex-start;
  padding: 0.3rem 0.8rem; border-radius: var(--r-pill);
  background: rgb(242 169 58 / 0.18); color: var(--amber-400);
  font-weight: 650; font-size: var(--step--1);
}
.diet__foods {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.4rem 0.8rem;
}
.diet__foods li { display: flex; align-items: center; gap: 0.45rem; font-size: var(--step--1); }
.diet__foods img { width: 32px; height: auto; flex: none; }
.diet__foods span { flex: 1; color: var(--night-text); }
.diet__foods b { color: var(--amber-400); }
.diet__fact { margin: 0; color: var(--night-soft); line-height: 1.5; font-size: var(--step--1); }

@media (max-width: 520px) {
  .diet__foods { grid-template-columns: 1fr; }
}

/* --- Name That Part ------------------------------------------------------ */
/* The sticker asks and the bird answers, so the two sit side by side: the
   drawing on the left with the dusk wood behind it, the question and its three
   names on the right. Below about 900px they stack, question first, because on
   a phone the thing you are about to tap should not be under the fold. */

.parts__body {
  display: grid; gap: var(--sp-4);
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: start;
}
/* The spread wing is landscape and its lesson is the rows of feathers, which
   need length to read as rows. It gets the wider share — but only where there
   are two columns to share out. Unscoped, this beat the one-column phone rule
   on specificity and split a 375px screen in two, which collapsed the board to
   ninety pixels. */
@media (min-width: 901px) {
  .parts__body.is-wide { grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr); }
}
/* A fixed box, with the canvas fitted inside it. The board changes shape
   between rounds — a standing bird is portrait, a spread wing is landscape —
   and if the stage took its height from the canvas the whole page would jump
   on every round change. So the stage holds still and the canvas moves. */
.parts__stage {
  position: relative; display: grid; place-items: center;
  height: 38rem; min-width: 0;
}
.parts__canvas {
  display: block; border-radius: var(--r-md);
  background: #07252b;
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.08);
}

.parts__bar { align-items: center; }
/* The game's own sign, in the bar where a lab puts its title. The page's H1
   already says the name, so this is the mark and not the heading — hidden from
   a screen reader's second telling by the H1 above it. */
.parts__logo {
  display: block; height: 2.6rem; width: auto; flex: none;
  filter: drop-shadow(0 2px 6px rgb(0 0 0 / 0.5));
}
.parts__round { font-family: var(--display); font-weight: 700; font-size: var(--step-0); }
.parts__score b { color: var(--amber-400); }

/* --- the tray ------------------------------------------------------------ */

.parts__quiz { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }
.parts__lede { margin: 0; color: var(--night-soft); font-size: var(--step--1); }

/* Every part still to go on. A grid rather than a list: these are objects to
   pick up, and a child scans a tray of shapes far faster than a column of
   names. */
.parts__tray {
  display: grid; gap: var(--sp-2);
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  align-content: start;
  max-height: 30rem; overflow-y: auto; padding-right: 0.25rem;
}
.parts__chip {
  display: flex; flex-direction: column; align-items: center; gap: 0.35rem;
  padding: 0.6rem 0.4rem; cursor: pointer; font: inherit; text-align: center;
  background: rgb(255 255 255 / 0.05); border: 1px solid var(--night-line);
  border-radius: var(--r-md); color: var(--night-text); overflow: hidden;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.12s ease;
}
.parts__chip:hover { border-color: var(--amber-500); background: rgb(242 169 58 / 0.1); }
.parts__chip:active { transform: scale(0.96); }
.parts__chip b { font-size: var(--step--1); line-height: 1.25; font-weight: 650; }
/* The picture box is a constant height so the names line up, while the part
   inside it is sized by how big it really is on the bird.
   The part is placed absolutely and scaled by transform rather than sized in
   percentages: a percentage height against a grid area that sizes itself is
   ignored, so tall tracts — the nape, the forehead — grew to their natural
   height, burst out of the card and sat over their neighbours' names. */
.parts__chip-pic { position: relative; display: block; width: 100%; height: 5rem; }
.parts__chip-pic img {
  position: absolute; inset: 0; margin: auto;
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  transform: scale(var(--pic, 1));
  filter: drop-shadow(0 3px 6px rgb(0 0 0 / 0.45));
}

/* What the part is, arriving with it. */
.parts__fact {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  margin: 0; padding: 0.75rem 0.95rem; border-radius: var(--r-md);
  color: var(--night-soft); line-height: 1.5; font-size: var(--step--1);
  border: 1px solid var(--night-line); background: rgb(255 255 255 / 0.04);
}
.parts__fact b {
  display: block; width: 100%; color: var(--amber-400);
  font-size: var(--step-0); font-weight: 700;
}
.parts__fact[data-kind="done"] b { width: auto; color: var(--white); }

/* --- the shelf ----------------------------------------------------------- */
/* What has been built so far, standing under the game waiting for the rest.
   Three slots from the start, so the shape of the whole thing is visible on
   the first screen rather than arriving as a surprise at the end. */

.parts__shelf {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-3); margin: var(--sp-4) 0 0; padding: var(--sp-3) 0 0; list-style: none;
  border-top: 1px solid var(--night-line);
}
.parts__slot {
  display: flex; flex-direction: column; align-items: center; gap: 0.4rem;
  padding: var(--sp-2); border-radius: var(--r-md);
  border: 1px dashed var(--night-line);
  color: var(--night-soft); font-size: var(--step--1); text-align: center;
  min-height: 7rem; justify-content: center;
}
.parts__slot img { display: block; height: 7rem; width: auto; max-width: 100%; }
.parts__slot.is-done {
  border-style: solid; border-color: rgb(242 169 58 / 0.4);
  background: rgb(242 169 58 / 0.07); color: var(--amber-200);
}
.parts__slot.is-waiting { opacity: 0.55; }
.parts__slot-mark { font-size: 1.3rem; opacity: 0.7; }

/* The arrival: it drops in from where the board is, overshoots and settles,
   so the bird reads as having walked down off the board rather than appeared. */
@keyframes parts-land {
  0%   { transform: translateY(-90%) scale(1.5); opacity: 0; }
  55%  { transform: translateY(6%) scale(0.96); opacity: 1; }
  100% { transform: translateY(0) scale(1); opacity: 1; }
}
.parts__slot.is-landing img { animation: parts-land 0.9s cubic-bezier(.2,.8,.3,1) both; }
@media (prefers-reduced-motion: reduce) {
  .parts__slot.is-landing img { animation: none; }
}

/* --- rounds -------------------------------------------------------------- */

.parts__rounds { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-3); }
.parts__pick {
  display: flex; align-items: center; gap: 0.4rem;
  padding: 0.5rem 0.9rem; cursor: pointer; font: inherit; font-weight: 650;
  background: rgb(255 255 255 / 0.05); border: 1px solid var(--night-line);
  border-radius: var(--r-md); color: var(--night-text);
}
.parts__pick span { color: var(--night-soft); font-weight: 500; }
.parts__pick:hover:not(:disabled) { border-color: var(--amber-500); }
/* Shut until the one before it is done — visible, so the progression reads as
   a path rather than as buttons that happen not to work. */
.parts__pick:disabled { opacity: 0.45; cursor: not-allowed; }
.parts__pick.is-on { border-color: var(--amber-500); background: rgb(242 169 58 / 0.14); }

.parts__empty { margin: 0; color: var(--night-soft); }

@media (max-width: 900px) {
  .parts__body { grid-template-columns: minmax(0, 1fr); }
  /* Question above the bird on a phone: the buttons should not be under the
     fold, and the bird is the feedback, which can be scrolled to. */
  .parts__quiz { order: -1; }
  .parts__stage { height: 26rem; max-width: 32rem; margin-inline: auto; width: 100%; }
}
@media (max-width: 520px) {
  .parts__chip-pic { height: 4rem; }
  .parts__slot img { height: 4.6rem; }
  .parts__slot { min-height: 5.6rem; font-size: 0.78rem; }
  .parts__stage { height: 22rem; }
}

