/* ===========================================================================
   Candidate 6 — "Sticker Book"
   ---------------------------------------------------------------------------
   Candidate 5's structure and candidate 5's painted habitats behind the
   games; neither was the problem. The note was "fresher… make it look less
   like a Claude website", and it is worth naming the tell: cream paper, a
   tasteful serif, a lot of white space, thin rules, letterspaced small-cap
   labels, muted earth tones. That is the house style of every generated
   marketing page on the internet this year. It is not ugly. It is anonymous,
   and anonymous is the worse failure for a site that has twelve hand-drawn
   animal stickers sitting unused in a folder.

   So: no parchment, no serif, no hairline rules, no pill-shaped eyebrow
   badges. Flat saturated colour fields, a grotesk with corners on it, type
   set large and tight, and the stickers drifting across the top where the
   first thing you meet is an animal.

   Owns the shell. Leaves the games and the activities app alone, except to
   hand the app the stickers in place of the silhouettes I drew for it.
   ========================================================================= */

:root {
  --display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --sans: "DM Sans", ui-sans-serif, system-ui, -apple-system, sans-serif;

  --paper: #ffffff;
  --paper-2: #f2f4f1;
  --paper-3: #e6eae4;
  --ink: #121a16;
  --ink-soft: #4a564f;
  --ink-faint: #78857d;
  --line: #dfe4dd;

  --night-900: #111821;
  --night-800: #19232e;
  --night-700: #22303d;
  --night-line: #31414f;
  --night-text: #eef3f4;
  --night-soft: #9fb1b8;

  /* Pulled off the stickers themselves: the fox and the monarch give the
     orange, the owls' eyes the amber, the stream the teal. */
  --teal: #0f8f88;
  --teal-dark: #0a6460;
  --teal-pale: #ddf0ee;
  --flame: #e4561f;
  --flame-dark: #ab3b12;
  --amber: #f4a623;
  --ink-green: #15322a;

  --shadow-sm: 0 1px 2px rgb(18 26 22 / 0.06);
  --shadow-md: 0 6px 20px rgb(18 26 22 / 0.09);
  --shadow-lg: 0 16px 44px rgb(18 26 22 / 0.14);

  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;

  --wrap: 1200px;
  --nav-h: 68px;
}

h1, h2, h3, h4, h5 {
  font-family: var(--display);
  font-variation-settings: normal;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.04;
}

body {
  background: var(--paper);
  color: var(--ink);
  font-size: var(--step-0);
  line-height: 1.6;
}
body::before { content: none; }       /* no paper texture; this one is not paper */

a { color: var(--teal-dark); text-underline-offset: 3px; }
a:hover { color: var(--flame); }
.lede { color: var(--ink-soft); }

/* Labels are set in the display face at a readable size rather than as
   letterspaced micro-caps, which is half the tasteful-template look. */
.head { margin-bottom: var(--sp-4); max-width: 60ch; }
.head__eyebrow {
  display: block; margin: 0 0 0.4rem;
  color: var(--flame); font-family: var(--display);
  font-size: 0.95rem; font-weight: 700; letter-spacing: -0.01em;
}
.head h2 { margin: 0 0 0.6rem; font-size: var(--step-3); }
.head p { margin: 0; color: var(--ink-soft); }

/* --- Buttons ------------------------------------------------------------- */
.btn {
  --btn-bg: var(--ink);
  --btn-fg: #fff;
  border: 2px solid var(--ink);
  border-radius: 99px;
  box-shadow: none;
  font-family: var(--display); font-weight: 700;
  letter-spacing: -0.01em;
  padding: 0.8rem 1.5rem;
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.btn--lg { padding: 1rem 2rem; font-size: 1.05rem; }
.btn--amber { --btn-bg: var(--flame); --btn-fg: #fff; border-color: var(--flame); }
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--ink); border-color: var(--ink); }
.btn--ghost:hover { background: var(--ink); color: #fff; }
.btn--on-night {
  --btn-bg: transparent; --btn-fg: #fff; border-color: #fff;
}
.btn--on-night:hover { background: #fff; color: var(--ink); }

/* --- Masthead ------------------------------------------------------------ */
.mast {
  position: sticky; top: 0; z-index: 90;
  background: var(--ink);
  border-bottom: 0;
}
.mast__in { display: flex; align-items: center; gap: var(--sp-3); min-height: var(--nav-h); }
.mast__brand { display: flex; align-items: center; flex: none; }
.mast__brand img { width: 116px; height: auto; }
.mast__slogan {
  display: none; margin: 0 auto 0 0;
  color: rgb(238 243 244 / 0.62);
  font-family: var(--display); font-size: 0.92rem;
  font-weight: 500; letter-spacing: -0.01em; white-space: nowrap;
}
@media (min-width: 900px) { .mast__slogan { display: block; } }
@media (max-width: 899px) { .mast__brand { margin-right: auto; } }

.mast nav { display: flex; flex-wrap: wrap; gap: 0.2rem; }
.mast nav a {
  padding: 0.45rem 0.9rem; border-radius: 99px;
  color: #fff; text-decoration: none;
  font-family: var(--display); font-weight: 500;
  font-size: 0.96rem; white-space: nowrap;
}
.mast nav a:hover { background: rgb(255 255 255 / 0.12); }
.mast nav a[aria-current="page"] { background: var(--flame); }
@media (max-width: 780px) {
  .mast__brand img { width: 96px; }
  .mast nav a { padding: 0.4rem 0.6rem; font-size: 0.92rem; }
}
@media (max-width: 440px) {
  .mast__in { flex-wrap: wrap; gap: var(--sp-2); padding-bottom: 0.4rem; }
  .mast nav { order: 3; flex-basis: 100%; justify-content: space-between; gap: 0; }
}

/* --- The hands ------------------------------------------------------------ */
/* Two families, supplied by Stefano, and three faces between them. A board
   is not written by one person in one size, and the note was to play with
   it — so a word is in one of three hands and at one of eight sizes.

   All self-hosted, and asked for by *relative* urls so they resolve against
   the stylesheet's own folder: one file serves the site root and
   /versions/v7/ with nothing to rewrite.

   LICENCES, and they are not the same:
     Scribble Markers — 1001Fonts "Free For Personal Use". A company's
       website is explicitly commercial use, so this one has to be bought
       from the author or dropped before launch. See launch/README.md.
     Eraser (Regular and Dust) — 1001Fonts "Free For Commercial Use".
       Nothing to buy. If the marker has to go, the board still has two
       hands and loses nothing structural. */
@font-face {
  font-family: "Scribble Markers";
  src: url("../fonts/scribble-markers.otf") format("opentype");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Eraser";
  src: url("../fonts/eraser-regular.ttf") format("truetype");
  font-weight: 400; font-style: normal; font-display: swap;
}
/* Eraser Dust — the dry cut, where the strokes are broken up — came in with
   it and is not loaded: the words it was for are gone, and a 180KB face
   that nothing on the page sets is 180KB. The file is still in
   static/fonts if a use turns up. */

/* --- The banner, and the words on it ------------------------------------ */
/* The ground is candidate 6's: a colour rather than a photograph, drifting
   slowly so a flat field is never quite flat, with a fine grain over it so
   a gradient that wide does not band.

   It was briefly an actual chalkboard — green slate, wooden frame, a tray
   with chalk in it — and that was the note: *too obvious*. A literal
   chalkboard explains the joke before anybody has read a word, and it
   turns the banner into a picture of a classroom rather than a banner with
   writing on it. The writing is the idea; the ground should stay out of
   its way. So this is back, and the writing does the work. */
.open {
  position: relative; isolation: isolate;
  color: #fff;
  overflow: hidden;
  background:
    radial-gradient(60rem 36rem at 12% 0%, #1d4f52 0%, transparent 60%),
    radial-gradient(52rem 34rem at 92% 100%, #3a2a52 0%, transparent 62%),
    linear-gradient(165deg, #16222b 0%, #101a21 55%, #0d161c 100%);
}
/* Two minutes a cycle: you do not watch it happen, you notice the banner is
   not the same one you looked at a moment ago. */
.open::before {
  content: ""; position: absolute; inset: -20%; z-index: -2;
  background:
    radial-gradient(38rem 26rem at 30% 40%, rgb(228 86 31 / 0.22), transparent 66%),
    radial-gradient(34rem 24rem at 70% 60%, rgb(15 143 136 / 0.26), transparent 66%);
  animation: wash 120s ease-in-out infinite alternate;
}
@keyframes wash {
  from { transform: translate3d(-3%, -2%, 0) scale(1); }
  to   { transform: translate3d(4%, 3%, 0) scale(1.1); }
}
.open::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  opacity: 0.4;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
}

/* The board: the wall ruled into squares, and wiped.

   It was eight squares floating about the banner, which was the wrong
   reading of it — a board is not decorated with squares, it is *ruled*
   into them, edge to edge, and the squares are what is left between the
   lines. This is a real grid: seventeen verticals and ten horizontals,
   drawn one after another the way somebody rules a board, held, and
   wiped off together.

   Everything about it is imperfect on purpose. No two lines are the same
   brightness or the same width, none of them is straight, and the whole
   thing is a fifth of full strength and slightly out of focus — a crisp
   grid of 1px rules is a spreadsheet, not a blackboard.

   The class is `ruled`, not `grid`: core.css has a `.grid { display: grid }`
   utility, and an <svg> made into a grid container paints nothing at all. */
.board { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.ruled {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  /* Soft, not sharp: chalk has no edges. A third of a pixel is enough —
     any more and the lines stop being lines. */
  filter: blur(0.35px);
  /* Faint where the words are, full where the mark is. A board is ruled
     all the way across, but a headline reading across a grid of lines is
     a headline with a grid of lines through it, and the headline is the
     thing that has to land. */
  /* Only the logo's side of the banner. Ruled right across, the lines run
     behind the headline and the hook and read as something wrong with the
     screen; the mark is what they belong behind. White, not black: a CSS
     mask can be read by alpha or by luminance, and black-with-alpha is
     only correct under the first — read as luminance, black means *hide*,
     which erased the whole grid on WebKit while every computed style in
     the console looked perfectly right. */
  -webkit-mask-image: linear-gradient(95deg,
    transparent 0 42%, rgb(255 255 255 / 0.5) 54%, #fff 66%);
  mask-image: linear-gradient(95deg,
    transparent 0 42%, rgb(255 255 255 / 0.5) 54%, #fff 66%);
}
.ruled path {
  fill: none; stroke: #e8eef0;
  stroke-width: var(--w); stroke-linecap: round;
  opacity: 0;
  /* Drawn, not revealed: dashed to its own length — pathLength="100", so
     one number fits every line — and the offset animated to nothing. */
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: rule 34s cubic-bezier(0.3, 0.6, 0.4, 1) infinite;
  animation-delay: var(--d);
}
/* Thirty-four seconds: about four to rule the board, twenty-seven with the
   grid up, two to wipe it, and under two blank before it starts again.
   The brightness each line settles at is its own, set in the markup. */
@keyframes rule {
  0%        { stroke-dashoffset: 100; opacity: 0; }
  1%        { opacity: var(--o); }
  7%        { stroke-dashoffset: 0; opacity: var(--o); }
  88%       { stroke-dashoffset: 0; opacity: var(--o); }
  95%       { stroke-dashoffset: 0; opacity: 0; }
  /* back to the start while it is invisible, ready for the next ruling.
     Under two seconds of blank wall, and every line starts on a negative
     delay so nobody arrives during it. */
  96%, 100% { stroke-dashoffset: 100; opacity: 0; }
}

/* The dirt. A board that has been wiped a thousand times is never clean:
   there are arcs where the cloth went and a haze where the chalk did not
   come off. Four soft sweeps, drifting very slowly, over the grid rather
   than under it — dust sits on top of the lines. */
.dust {
  position: absolute; inset: -10%; z-index: 1; pointer-events: none;
  opacity: 0.5;
  background:
    radial-gradient(28rem 9rem at 24% 32%, rgb(226 240 232 / 0.07), transparent 70%),
    radial-gradient(34rem 7rem at 72% 58%, rgb(226 240 232 / 0.055), transparent 72%),
    radial-gradient(20rem 12rem at 52% 84%, rgb(226 240 232 / 0.05), transparent 70%),
    radial-gradient(24rem 8rem at 88% 18%, rgb(226 240 232 / 0.045), transparent 70%);
  animation: wash 150s ease-in-out infinite alternate;
}

@media (max-width: 899px) {
  /* The mask fades the grid where the words are, and on a phone the words
     are everywhere — which left the board looking unruled. Even and faint
     instead. */
  .ruled {
    /* One column down here, with the mark at the top — so the board is
       ruled behind the mark and fades out before the words start. */
    -webkit-mask-image: linear-gradient(180deg,
      #fff 0 22%, rgb(255 255 255 / 0.45) 34%, transparent 46%);
    mask-image: linear-gradient(180deg,
      #fff 0 22%, rgb(255 255 255 / 0.45) 34%, transparent 46%);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* A board nobody has cleaned: the grid is simply there. */
  .open::before, .dust { animation: none; }
  .ruled path { animation: none; stroke-dashoffset: 0; opacity: var(--o); }
}

.open__in {
  position: relative; z-index: 1;
  padding-block: var(--sp-6) var(--sp-7);
  display: grid; gap: var(--sp-5);
  align-content: center; justify-items: start;
}
/* The mark stands on the right at this width, which is what the banner is
   for: the words on one side, the thing itself on the other, and the
   squares ruled in the space between them. */
@media (min-width: 900px) {
  .open__in {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    align-items: center; gap: var(--sp-6);
  }
  .open__mark { justify-self: center; }
}
.open__words { display: grid; justify-items: start; gap: 0; }
.open__mark {
  /* It is half the composition, so it is sized like half of it. */
  width: clamp(210px, 32vw, 480px); height: auto;
  filter: drop-shadow(0 16px 34px rgb(0 0 0 / 0.5));
}
.open h1 {
  margin: 0 0 var(--sp-3); max-width: 14ch;
  color: #fff; font-size: clamp(2.3rem, 1.3rem + 4.6vw, 4.2rem);
  letter-spacing: -0.04em;
  text-shadow: 0 2px 22px rgb(0 0 0 / 0.55);
}
.open__hook {
  margin: 0 0 var(--sp-4); max-width: 44ch;
  font-size: var(--step-1); line-height: 1.5;
  color: rgb(238 243 244 / 0.88);
  text-shadow: 0 1px 14px rgb(0 0 0 / 0.6);
}
.open__go { display: flex; flex-wrap: wrap; gap: 0.8rem; }
.open__go .btn--ghost { --btn-fg: #fff; border-color: rgb(255 255 255 / 0.55); }
.open__go .btn--ghost:hover { background: #fff; color: var(--ink); border-color: #fff; }
.open__facts {
  display: flex; flex-wrap: wrap; gap: 0.5rem 2rem;
  margin: var(--sp-5) 0 0;
  color: rgb(238 243 244 / 0.66); font-size: 0.92rem;
}
.open__facts b { color: var(--amber); font-family: var(--display); font-weight: 700; }

@media (max-width: 899px) {
  /* One column, and the mark goes first. It is after the words in the
     markup because that is the order on a wide screen — words left, mark
     right — and in a single column that put it under the facts, off the
     bottom of the first screen on a phone. The eight-and-a-half-rem top
     padding that used to be here went with the words it was reserving
     room for. */
  .open__mark {
    order: -1; justify-self: center;
    width: clamp(200px, 62vw, 330px);
    margin-bottom: var(--sp-2);
  }
  .open__in { padding-block: var(--sp-5) var(--sp-6); }
}

/* --- A game, standing in its own habitat --------------------------------- */
.worlds {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
}
.worlds--pair { grid-template-columns: repeat(auto-fill, minmax(min(100%, 430px), 1fr)); }

.world {
  position: relative;
  display: flex; flex-direction: column;
  border: 0; border-radius: var(--r-lg);
  background: var(--ink);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: box-shadow 0.18s ease, transform 0.18s ease;
}
.world:hover { box-shadow: var(--shadow-lg); transform: translateY(-4px); }
.world__scene {
  position: relative; isolation: isolate;
  aspect-ratio: 16 / 10;
  display: grid; place-items: center;
  padding: var(--sp-3);
  overflow: hidden;
}
.world__scene > img.world__bg {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover;
}
.world__scene::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(76% 68% at 50% 50%, rgb(17 24 33 / 0.06), rgb(17 24 33 / 0.55) 100%),
    linear-gradient(180deg, rgb(17 24 33 / 0.22), rgb(17 24 33 / 0) 45%, rgb(17 24 33 / 0.45));
}
.world__logo {
  max-height: 86%; max-width: 92%; width: auto; object-fit: contain;
  filter: drop-shadow(0 8px 20px rgb(0 0 0 / 0.5));
}
.world__plaque {
  display: flex; flex-direction: column; gap: 0.3rem;
  flex: 1; padding: var(--sp-3) var(--sp-4) var(--sp-4);
  background: var(--ink); color: #fff;
}
.world__plaque h3 { margin: 0; font-size: var(--step-1); color: #fff; }
.world__plaque p { margin: 0; color: rgb(238 243 244 / 0.7); font-size: 0.94rem; }
.world__meta {
  margin-top: auto; padding-top: 0.8rem;
  display: flex; flex-wrap: wrap; gap: 0.2rem 0.5rem;
  color: var(--amber); font-family: var(--display);
  font-size: 0.78rem; font-weight: 500;
}
.world__meta span + span::before { content: "·"; margin-right: 0.5rem; color: rgb(238 243 244 / 0.4); }
.world__hit { position: absolute; inset: 0; z-index: 3; }
.world__hit span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.world__flag {
  position: absolute; top: 0.9rem; left: 0.9rem; z-index: 2;
  padding: 0.3rem 0.8rem; border-radius: 99px;
  background: var(--flame); color: #fff;
  font-family: var(--display); font-size: 0.76rem; font-weight: 700;
}

/* --- The passport, drawn to OBDK's measurements -------------------------- */
/* Candidate 6 built this in the spirit of the OBDK passport. The note was
   that it should *be* that passport — same fonts, same everything — so this
   one is transcribed from their stylesheet rather than remembered from it:
   the forest cover and its 14px binding, the guilloche of two repeating
   radial gradients over a turbulence grain, the dashed rules under the
   fields, the mono labels at ten pixels with .14em of tracking, the
   signature in Permanent Marker at 34px rotated four degrees, the stamps at
   40% of the page with their hover tags, and the six positions they are
   pinned at. Only the words are ours. */
.pass { background: #15322a; color: #fff; }
.pass .head { max-width: 58ch; margin-inline: auto; text-align: center; }
.pass .head__eyebrow { color: var(--amber); }
.pass .head h2 { color: #fff; font-size: clamp(1.9rem, 1.2rem + 2.8vw, 3.2rem); }
.pass__brush { color: #7fd4a8; }
.pass .head p { color: rgb(255 255 255 / 0.68); margin-inline: auto; }

/* OBDK's three faces, and their two inks. Scoped to the book: the rest of
   this site is Space Grotesk and DM Sans, and a passport is the one place
   where wearing somebody else's typography is the correct answer. */
.ppbook {
  --pp-head: "Montserrat", ui-sans-serif, system-ui, sans-serif;
  --pp-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, monospace;
  /* A real stack, not a bare `cursive`: on a Mac the generic cursive
     is Apple Chancery, so the one moment Google Fonts is slow or
     blocked the signature comes out as wedding calligraphy. */
  --pp-brush: "Permanent Marker", "Marker Felt", "Comic Sans MS", cursive;
  --pp-navy: #141726;
  --pp-mute: #5f6475;
  --pp-forest: #2d5940;

  position: relative;
  display: grid; grid-template-columns: 1fr 1fr;
  max-width: 1080px; margin: var(--sp-5) auto 0;
  padding: 14px;
  background: var(--pp-forest);
  border-radius: 24px;
  box-shadow:
    0 40px 70px -40px rgb(20 23 38 / 0.7),
    inset 0 0 0 2px rgb(255 255 255 / 0.06);
}
/* The gutter where the two pages meet. */
.ppbook::after {
  content: ""; position: absolute; left: 50%; top: 14px; bottom: 14px;
  width: 2px; background: rgb(0 0 0 / 0.18); transform: translateX(-1px);
}

.pg {
  position: relative; overflow: hidden;
  min-height: 560px; padding: 28px 30px 34px;
  color: var(--pp-navy);
  background-color: #fbf6ea;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .35 0 0 0 0 .3 0 0 0 0 .2 0 0 0 .07 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    repeating-radial-gradient(circle at 18% 22%, rgb(45 89 64 / 0.07) 0 1px, transparent 1px 10px),
    repeating-radial-gradient(circle at 82% 78%, rgb(181 86 45 / 0.06) 0 1px, transparent 1px 12px);
}
.pg--left { border-radius: 14px 4px 4px 14px; box-shadow: inset -22px 0 26px -22px rgb(0 0 0 / 0.28); }
/* Taller than OBDK's 560. At their width a stamp is about 210px across and
   the bottom row starts at 68%, so on their own page the last two run past
   the edge and `overflow: hidden` cuts them off. It was noticed here, so
   the page gets the height the arithmetic needs and the six positions stay
   exactly where they were drawn. */
.pg--right { border-radius: 4px 14px 14px 4px; box-shadow: inset 22px 0 26px -22px rgb(0 0 0 / 0.28); min-height: 690px; }

.pg__top {
  display: flex; justify-content: space-between; align-items: center;
  border-bottom: 1.5px solid rgb(20 23 38 / 0.18);
  padding-bottom: 12px; margin-bottom: 22px;
}
.pg .mono {
  font-family: var(--pp-mono); font-size: 12px;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.pg__top .mono { color: var(--pp-forest); }
/* No filter on it any more: the Kidwings wordmark that was here is white
   artwork and had to be inverted to sit on paper. The passport wordmark is
   drawn in burgundy and gold, which is what a passport is drawn in. */
/* Taller than OBDK's 30px, because this is a long wordmark rather than a
   compact mark: at thirty pixels the letters are six and the owl in the P
   is a smudge. */
.pg__mark { height: clamp(46px, 5.4vw, 72px); width: auto; }
.pgno {
  position: absolute; bottom: 12px; left: 50%;
  font: 600 11px var(--pp-mono); color: var(--pp-mute);
}

.idrow { display: grid; grid-template-columns: 150px 1fr; gap: 22px; align-items: start; }
.idphoto {
  display: block; aspect-ratio: 3 / 4; border-radius: 10px; overflow: hidden;
  border: 3px solid #fff; box-shadow: 0 6px 18px -8px rgb(0 0 0 / 0.4);
  background: #e9eef0;
}
.idphoto img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 25%; filter: saturate(0.9); }
.idrow dl { display: grid; gap: 12px; margin: 0; }
.idrow dt {
  font: 600 10px var(--pp-mono); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--pp-mute);
}
.idrow dd {
  font: 800 17px var(--pp-head); text-transform: uppercase;
  margin: -10px 0 0; padding-bottom: 6px;
  border-bottom: 1px dashed rgb(20 23 38 / 0.2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sig { margin: 26px 0 0; }
.sig .mono { display: block; color: var(--pp-mute); font-size: 10px; }
.sig b {
  display: inline-block; margin-top: 6px; max-width: 100%;
  font: 400 34px/1.2 var(--pp-brush); color: #2a3f8f;
  transform: rotate(-4deg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mrz {
  position: absolute; left: 30px; right: 30px; bottom: 34px; margin: 0;
  font: 600 13px/1.6 var(--pp-mono); letter-spacing: 0.06em;
  color: #3b3f4f; word-break: break-all;
  background: rgb(255 255 255 / 0.55); padding: 8px 10px; border-radius: 6px;
}

/* --- The stamps --------------------------------------------------------- */
/* `mix-blend-mode: multiply` is the whole difference between a stamp and a
   sticker: the ink takes the guilloche underneath it instead of sitting on
   top of the page. What the stamp is for is a tag on hover rather than a
   caption — printed under all six at once, the captions ran into the stamp
   next door. */
.st {
  position: absolute; width: 40%; display: block;
  transform: rotate(var(--r));
  text-decoration: none; mix-blend-mode: multiply;
  transition: transform 0.25s;
}
.st img { width: 100%; height: auto; display: block; opacity: 0.93; }
.st em {
  position: absolute; left: 50%; bottom: -6px;
  transform: translateX(-50%) rotate(calc(var(--r) * -1));
  white-space: nowrap;
  font: 800 11px var(--pp-head); font-style: normal;
  letter-spacing: 0.06em; text-transform: uppercase;
  background: var(--pp-navy); color: #fff;
  padding: 6px 10px; border-radius: 999px;
  opacity: 0; transition: opacity 0.2s;
  mix-blend-mode: normal; pointer-events: none;
}
a.st:hover { transform: rotate(0deg) scale(1.06); z-index: 3; }
a.st:hover em, a.st:focus-visible em { opacity: 1; }

/* Nothing here is live yet, and a passport has an obvious idiom for "not
   valid": something inked across it at an angle. A quiet chip beside the
   heading would be missed at the exact moment somebody is deciding whether
   this is real. */
.pass__soon {
  position: absolute; z-index: 5;
  top: clamp(-0.6rem, -1vw, -0.2rem); right: clamp(0.6rem, 4vw, 3rem);
  margin: 0;
  padding: 0.5rem 1.4rem;
  transform: rotate(-9deg);
  border: 3px double var(--flame);
  border-radius: 6px;
  background: rgb(246 241 226 / 0.92);
  color: var(--flame);
  font-family: var(--pp-head); font-size: clamp(0.8rem, 2.2vw, 1.15rem);
  font-weight: 900; letter-spacing: 0.12em; text-transform: uppercase;
  box-shadow: 0 6px 18px rgb(20 23 38 / 0.35);
}
@media (max-width: 899px) { .pass__soon { top: -0.5rem; right: 1rem; } }

/* --- Stamping ------------------------------------------------------------ */
/* OBDK's own animation, down to the curve: in from scale 1.8 on a spring,
   0.55s, one after another. Visible by default here, because a browser that
   never runs shell.js should show a finished passport rather than an empty
   page waiting for an observer — the script hides them itself the moment it
   is running, and the observer brings them down when the book is in view. */
.ppbook.is-armed .st { opacity: 0; }
.ppbook.go .st {
  animation: stampIn 0.55s cubic-bezier(0.2, 1.5, 0.4, 1) both;
  animation-delay: var(--d);
}
@keyframes stampIn {
  0%   { opacity: 0; transform: rotate(var(--r)) scale(1.8); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: rotate(var(--r)) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .ppbook .st { animation: none !important; opacity: 1 !important; }
}

@media (max-width: 899px) {
  /* One page above the other, as on OBDK, and the stamps keep their
     scattered placement: the page is given the height that placement needs
     rather than the stamps being rearranged into a grid. */
  .ppbook { grid-template-columns: 1fr; }
  .ppbook::after { display: none; }
  .pg--left { border-radius: 14px 14px 4px 4px; min-height: 0; }
  .pg--right { border-radius: 4px 4px 14px 14px; min-height: 640px; }
}
@media (max-width: 640px) {
  .pg { padding: 22px 18px 30px; }
  .pg--right { min-height: 155vw; }
  .idrow { grid-template-columns: 112px 1fr; gap: 16px; }
  .idrow dd { font-size: 15px; }
  .mrz { position: static; margin-top: 20px; font-size: 11px; }
  .sig b { font-size: 28px; }
}

/* The loop under the passport. It was set in the body grotesk at 500,
   which made it read as a caption under the book; it is the one line that
   says what the whole family is for, so it is written in the marker like
   everything else on this site that is addressed to a child. */
.loopline {
  display: flex; flex-wrap: wrap; justify-content: center;
  align-items: center; gap: 0.4rem 1.1rem;
  margin: var(--sp-5) 0 0;
  font-family: "Scribble Markers", var(--display), sans-serif;
  font-weight: 400; letter-spacing: 0.02em; line-height: 1.1;
  font-size: clamp(1.15rem, 0.9rem + 1.1vw, 1.8rem);
  color: rgb(255 255 255 / 0.72);
}
.loopline i {
  font-style: normal; color: rgb(255 255 255 / 0.38);
  font-family: var(--display); font-size: 0.8em;
}
.loopline__end { color: var(--amber); }

/* --- A game's stage ------------------------------------------------------ */
.stage {
  position: relative; isolation: isolate;
  margin-block: var(--sp-3) var(--sp-5);
  padding: var(--sp-3);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.stage > img.stage__bg {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover;
}
.stage::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgb(17 24 33 / 0.66), rgb(17 24 33 / 0.95) 36%);
}
.stage__mark { margin: var(--sp-4) 0 var(--sp-2); text-align: center; }
.stage__mark img {
  display: inline-block; max-height: 12.5rem; width: auto;
  filter: drop-shadow(0 10px 26px rgb(0 0 0 / 0.55));
}
.stage__title { text-align: center; color: #fff; margin: var(--sp-3) 0 0.4rem; }
.stage__says {
  margin: 0 auto var(--sp-4); max-width: 46ch;
  text-align: center; color: rgb(238 243 244 / 0.72); font-size: 1.04rem;
}

/* --- Interior page heads ------------------------------------------------- */
.page-head {
  background: var(--ink); border-bottom: 0;
  padding-block: var(--sp-6);
}
.page-head h1 { color: #fff; font-size: var(--step-4); }
.page-head p { color: rgb(238 243 244 / 0.7); max-width: 56ch; }

/* --- Shelf heads --------------------------------------------------------- */
/* The head over a group of cards. It was a bold sans heading with "2 of 6"
   in grey at the other end and a 2px rule under it — a table header, which
   is what it looked like. It is written now, in the marker from the banner,
   with the count in the dry hand beside it and a rule that is drawn rather
   than ruled. */
.group__head {
  display: flex; align-items: baseline; gap: 0.3rem 0.9rem; flex-wrap: wrap;
  padding-bottom: 0.45rem;
  margin-bottom: var(--sp-4);
  border-bottom: 2px solid rgb(18 26 22 / 0.12);
}
.group__head h2 {
  margin: 0;
  font-family: "Scribble Markers", var(--display), sans-serif;
  font-weight: 400; letter-spacing: 0.01em;
  font-size: clamp(1.6rem, 1.1rem + 1.6vw, 2.5rem);
  line-height: 1;
}
.group__head span {
  margin-left: auto;
  font-family: "Eraser", var(--display), sans-serif;
  font-size: 0.98rem; letter-spacing: 0.05em;
  color: var(--ink-faint);
}
.group__head span a { color: var(--flame); text-decoration: none; }
.group__head span a:hover { text-decoration: underline; }
/* On the dark pages the same head has to sit on night rather than paper. */
.band--night .group__head,
.films .group__head { border-bottom-color: rgb(255 255 255 / 0.16); }

/* --- Prose --------------------------------------------------------------- */
.plain { max-width: 66ch; }
.plain h2 { margin-top: var(--sp-5); font-size: var(--step-2); }
.plain h2:first-child { margin-top: 0; }
.plain ul { padding-left: 1.2rem; }
.plain li { margin-bottom: 0.4rem; }
.pull {
  margin: var(--sp-5) 0; padding: var(--sp-4);
  border: 0; border-radius: var(--r-lg);
  background: var(--teal-pale); color: var(--teal-dark);
  font-family: var(--display); font-weight: 700;
  font-size: var(--step-2); line-height: 1.15; letter-spacing: -0.03em;
}
.sheet { width: 100%; border-collapse: collapse; margin-block: var(--sp-3); }
.sheet th, .sheet td {
  text-align: left; padding: 0.8rem; border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.sheet thead tr { border-bottom: 2px solid var(--ink); }
.sheet th { color: var(--ink); font-family: var(--display); font-size: 0.86rem; font-weight: 700; }
.sheet small { color: var(--ink-soft); }
.sheet-scroll { overflow-x: auto; }

/* --- Sections ------------------------------------------------------------ */
.section--paper { background: var(--paper-2); }
.section--white { background: var(--paper); }

/* --- How a game works ---------------------------------------------------- */
.howto__step img { border: 0; border-radius: var(--r-lg); box-shadow: var(--shadow-md); }
.howto__step h3 { font-family: var(--display); font-weight: 700; letter-spacing: -0.03em; }
.howto__step h3 span {
  background: var(--flame); color: #fff;
  font-family: var(--display); font-weight: 700;
}

/* --- The colophon -------------------------------------------------------- */
.colophon {
  background: var(--ink); color: rgb(238 243 244 / 0.6);
  border-top: 0; padding-block: var(--sp-6); font-size: 0.93rem;
}
.colophon__in { display: grid; gap: var(--sp-4); }
/* Four columns now that the films have one of their own: the brand, then
   Play, Watch and Teach. At three the fourth wrapped under the brand and
   read as part of its paragraph. */
@media (min-width: 900px) { .colophon__in { grid-template-columns: 1.7fr 1fr 1fr 1fr; } }
@media (min-width: 620px) and (max-width: 899px) { .colophon__in { grid-template-columns: 1fr 1fr; } }
.colophon img { width: 160px; height: auto; margin-bottom: var(--sp-3); }
.colophon > .wrap > .colophon__in > div:first-child p {
  font-family: var(--display); font-weight: 500; max-width: 32ch; color: rgb(238 243 244 / 0.72);
}
.colophon h4 {
  color: #fff; margin: 0 0 0.7rem;
  font-family: var(--display); font-size: 0.9rem; font-weight: 700; letter-spacing: -0.01em;
}
.colophon ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.colophon a { color: rgb(238 243 244 / 0.6); text-decoration: none; }
.colophon a:hover { color: var(--amber); text-decoration: underline; }
.colophon__note {
  margin-top: var(--sp-5); padding-top: var(--sp-3);
  border-top: 1px solid rgb(255 255 255 / 0.12); font-size: 0.85rem; max-width: 70ch;
}

/* --- The barn's switch --------------------------------------------------- */
.owl-switch {
  order: 0; flex: none;
  background: rgb(255 255 255 / 0.08); border: 0;
  color: #fff; font-family: var(--display); font-weight: 500;
}
.owl-switch:hover { background: rgb(255 255 255 / 0.18); color: #fff; }
.owl-switch__track { background: rgb(255 255 255 / 0.22); }
.owl-switch[aria-checked="true"] .owl-switch__track { background: var(--flame); }
.owl-switch__track::after { background: #fff; }

/* --- The activities app, wearing the stickers ---------------------------- */
/* activities.css drew its species marks as `currentColor` SVGs sized by the
   `color` of the tile. They are photographs now, so the two places that
   assume a glyph need the image treatment instead: fit inside the box, keep
   the alpha, and stop inheriting a colour that no longer does anything. */
img.act-species__mark { object-fit: contain; }
img.act-animal__mark {
  width: 1.5em; height: 1.5em; object-fit: contain;
  margin-left: -0.1em;
}
/* Bigger than the glyphs were. A 42px silhouette was an icon; a drawn
   sticker is the reason to tap the tile, so it gets the room. */
.act-species { border-radius: var(--r-md); padding-block: 0.9rem 0.8rem; }
.act-species__mark { width: 70px; height: 70px; }
.act-species:hover .act-species__mark { transform: scale(1.1) rotate(-4deg); }
@media (max-width: 700px) { .act-species__mark { width: 54px; height: 54px; } }
.act-top .act-animal__mark { filter: none; }
.act-card__title, .act-hero h1, .run-card__title, .act-top h1 { font-family: var(--display); }

/* --- The activities app's own logo --------------------------------------- */
.act-hero__mark { margin: 0.2rem 0 0.6rem; }
.act-hero__mark img {
  width: clamp(200px, 30vw, 330px); height: auto;
  filter: drop-shadow(0 12px 28px rgb(0 0 0 / 0.45));
}
/* One picture rather than three overlapped: the games wing shows three game
   logos, the classroom wing shows the app's mark, and they read as a pair. */
.wing__art--one img { max-width: 62%; max-height: 100%; }

/* --- This week, as two bands: one floating, one turning ------------------ */
/* Candidate 6 made these two bands instead of two more boxes, which was the
   right move and not far enough — the game still stood in a photograph and
   the activity was still a page of type. These two do the thing each half
   is actually about: a game is an object, so it floats in the dark with the
   pieces of two other games falling past it; a lesson is a sequence, so it
   plays as one. */

/* The game of the week, floating. ----------------------------------------- */
.float {
  position: relative; isolation: isolate; overflow: hidden;
  color: #fff;
  padding-block: var(--sp-7);
  min-height: 32rem;
  background:
    radial-gradient(44rem 30rem at 32% 42%, #1b3a44 0%, transparent 62%),
    radial-gradient(38rem 26rem at 88% 86%, #2d2140 0%, transparent 60%),
    linear-gradient(180deg, #0a1016 0%, #0e1a21 55%, #080e13 100%);
}

/* The shower. Eighteen cut-outs falling in eighteen lanes, each at its own
   speed, every one of them starting at a negative delay so the band is
   already raining when the page arrives rather than beginning to.
   The fall is a fixed distance in rem rather than a percentage, because a
   percentage translate is a percentage of the image, not of the band. It
   is set long enough to clear the tallest the band ever gets, and the band
   clips what is left. */
.shower { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.drop {
  position: absolute; top: 0; left: var(--x);
  width: calc(var(--s) * clamp(58px, 7.5vw, 118px)); height: auto;
  opacity: 0;
  /* A black bear on a near-black band is a hole in the picture, so every
     cut-out is lifted a little and given a rim of light to sit against. */
  filter: brightness(1.12) drop-shadow(0 12px 18px rgb(0 0 0 / 0.55))
          drop-shadow(0 0 1px rgb(255 255 255 / 0.35));
  animation: drop var(--t) linear infinite;
  animation-delay: var(--d);
}
@keyframes drop {
  0%   { opacity: 0; transform: translate(-50%, -8rem) rotate(0deg); }
  6%   { opacity: var(--peak, 0.9); }
  88%  { opacity: var(--peak, 0.9); }
  100% { opacity: 0; transform: translate(-50%, 46rem) rotate(var(--spin)); }
}

.float__in { position: relative; z-index: 1; display: grid; gap: var(--sp-5); align-items: center; }
@media (min-width: 880px) {
  .float__in { grid-template-columns: 1.05fr 1fr; gap: var(--sp-6); }
}

/* The logo rides on its own shadow. The two animations are the same length
   and deliberately out of step — the shadow spreads and fades as the logo
   rises — because a picture bobbing over a shadow that never moves reads as
   a thing on a spring rather than a thing in the air. */
.float__stage { position: relative; display: grid; place-items: center; padding-block: var(--sp-4); }
.float__glow {
  position: absolute; z-index: 0;
  width: min(34rem, 120%); aspect-ratio: 1;
  background: radial-gradient(circle at 50% 50%,
    rgb(244 166 35 / 0.20) 0%, rgb(15 143 136 / 0.12) 38%, transparent 66%);
}
.float__art {
  position: relative; z-index: 2;
  max-width: 100%; max-height: 23rem; width: auto; object-fit: contain;
  filter: drop-shadow(0 30px 44px rgb(0 0 0 / 0.65));
  animation: bob 6.5s ease-in-out infinite;
}
.float__shadow {
  position: absolute; z-index: 1; bottom: 0.4rem;
  width: 48%; height: 1.5rem; border-radius: 50%;
  background: radial-gradient(ellipse at 50% 50%, rgb(0 0 0 / 0.6), transparent 70%);
  animation: bob-shade 6.5s ease-in-out infinite;
}
@keyframes bob {
  0%, 100% { transform: translateY(-8px); }
  50%      { transform: translateY(10px); }
}
@keyframes bob-shade {
  0%, 100% { transform: scale(0.9); opacity: 0.45; }
  50%      { transform: scale(1.08); opacity: 0.75; }
}

.float__kicker {
  margin: 0 0 0.6rem; color: var(--amber);
  font-family: var(--display); font-weight: 700; font-size: 0.95rem;
}
.float h2 {
  margin: 0 0 0.6rem; color: #fff;
  font-size: clamp(2rem, 1.3rem + 3vw, 3.2rem);
}
.float__says {
  margin: 0 0 var(--sp-3); max-width: 40ch;
  font-size: var(--step-1); color: rgb(238 243 244 / 0.86);
}
.float__meta {
  display: flex; flex-wrap: wrap; gap: 0.2rem 0.5rem; margin: 0 0 var(--sp-4);
  color: rgb(238 243 244 / 0.6); font-family: var(--display); font-size: 0.86rem;
}
.float__meta span + span::before { content: "·"; margin-right: 0.5rem; }

@media (max-width: 860px) {
  /* The words fill the width down here, so only the lanes near an edge
     stay — a bear through the middle of a sentence is funny once. What is
     left falls further back, behind the words rather than across them. */
  .drop { display: none; }
  .drop--edge { display: block; --peak: 0.5; }
  .float__words { position: relative; text-shadow: 0 1px 12px rgb(0 0 0 / 0.75); }
}

@media (prefers-reduced-motion: reduce) {
  /* Still weather. The cut-outs stay where their delay left them and the
     logo stops bobbing; the band is a photograph of a shower, which is the
     right fallback for somebody who asked for no movement. */
  .drop { animation: none; opacity: var(--peak, 0.9); transform: translate(-50%, 14rem) rotate(var(--spin)); }
  .float__art, .float__shadow { animation: none; }
}

/* --- The films ----------------------------------------------------------- */
/* The third section. The live site puts these in a column of wide rows with
   the questions folded away inside a <details>, which reads as small print
   under a video. These are cards: the still is the whole left half and the
   questions are the right half, on paper, open — because the questions are
   the half that makes a film a lesson, and a thing worth doing should not
   need opening first. */
.films { display: grid; gap: var(--sp-4); }
.films__line { max-width: 60ch; margin: -0.4rem 0 var(--sp-4); color: var(--ink-soft); }

.film {
  display: grid; gap: var(--sp-4);
  padding: var(--sp-4);
  border-radius: var(--r-xl);
  background: var(--ink);
  box-shadow: var(--shadow-md);
}
@media (min-width: 900px) { .film { grid-template-columns: 1.1fr 1fr; align-items: start; } }

/* The poster is a button, and the player takes its place. Nothing has been
   asked of YouTube until somebody presses it. */
.film__screen { position: relative; }
.film__play,
.film__screen iframe {
  display: block; width: 100%; aspect-ratio: 16 / 9;
  border: 0; border-radius: var(--r-lg); overflow: hidden;
  background: #000;
}
.film__play { position: relative; padding: 0; cursor: pointer; }
.film__play img { width: 100%; height: 100%; object-fit: cover; display: block; }
.film__play::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(60% 60% at 50% 46%, rgb(0 0 0 / 0.1), rgb(0 0 0 / 0.5));
  transition: background 0.2s ease;
}
.film__play:hover::after { background: radial-gradient(60% 60% at 50% 46%, transparent, rgb(0 0 0 / 0.35)); }
/* The button itself: a disc with a triangle cut into it, drawn rather than
   set, because a glyph in a text font sits off-centre at every size. */
.film__go {
  position: absolute; left: 50%; top: 46%; z-index: 1;
  width: clamp(54px, 7vw, 78px); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--flame);
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.5);
  transition: transform 0.2s ease, background 0.2s ease;
}
.film__go::before {
  content: ""; position: absolute; left: 56%; top: 50%;
  transform: translate(-50%, -50%);
  border-style: solid; border-width: 0.72rem 0 0.72rem 1.15rem;
  border-color: transparent transparent transparent #fff;
}
.film__play:hover .film__go { transform: translate(-50%, -50%) scale(1.08); background: #f26a2f; }
.film__label {
  position: absolute; left: 0.9rem; bottom: 0.8rem; z-index: 1;
  padding: 0.3rem 0.7rem; border-radius: 99px;
  background: rgb(0 0 0 / 0.55); color: #fff;
  font-family: var(--display); font-weight: 700; font-size: 0.78rem;
  letter-spacing: 0.02em;
}
.film.is-started .film__label { display: none; }

.film__words { display: grid; gap: 0.6rem; align-content: start; padding: 0.2rem 0.2rem 0; }
.film__n {
  margin: 0; color: var(--amber);
  font-family: var(--display); font-weight: 700; font-size: 0.82rem;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.film__words h3 { margin: 0; font-size: var(--step-1); color: #fff; }
.film__blurb { margin: 0; color: rgb(238 243 244 / 0.74); font-size: 0.97rem; }

/* The questions, on paper, inside the dark card — the same ruled sheet the
   activity of the week is on, so "something to write on" looks the same
   everywhere on the site. */
.ask {
  margin-top: 0.4rem;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-lg);
  background:
    repeating-linear-gradient(to bottom, transparent 0 30px,
      rgb(15 143 136 / 0.16) 30px 31px),
    #fffdf7;
  color: var(--ink);
}
.ask__head {
  margin: 0 0 0.6rem; color: var(--flame);
  font-family: var(--display); font-weight: 700; font-size: 0.86rem;
}
.ask__list { margin: 0; padding-left: 1.1rem; display: grid; gap: 0.7rem; }
.ask__list li::marker { color: var(--teal-dark); font-family: var(--display); font-weight: 700; }
.ask__q { display: block; margin-bottom: 0.25rem; font-size: 0.95rem; }
.ask textarea {
  width: 100%; resize: vertical;
  padding: 0.4rem 0.5rem;
  border: 0; border-bottom: 2px solid rgb(15 143 136 / 0.35);
  border-radius: 4px 4px 0 0;
  background: rgb(15 143 136 / 0.06);
  font: inherit; font-size: 0.95rem; color: var(--ink);
}
.ask textarea:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.ask__foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.9rem;
  margin: 0.9rem 0 0;
}
.ask__foot span { color: var(--ink-faint); font-size: 0.82rem; max-width: 34ch; }
.btn--sm { padding: 0.45rem 0.9rem; font-size: 0.88rem; }
.film.is-answered .ask { box-shadow: inset 0 0 0 2px var(--teal); }

/* The wing on the homepage: a still, not a logo — the only photograph on
   that page, which is what makes it read as film. */
.wing--film .wing__kicker { color: #ffd9a1; }
.wing--film .wing__list li::before { color: var(--amber); }
.wing--film .wing__pieces img {
  /* Flat, not tilted. The overlap rules give a single piece of artwork the
     first-child angle, which on a sticker reads as pinned and on a frame
     from a film reads as a crooked screenshot. */
  transform: none; margin: 0;
  max-width: 86%; max-height: 100%;
  border-radius: var(--r-md);
  box-shadow: 0 18px 30px rgb(0 0 0 / 0.55);
}
.wing__art--film .wing__art::before,
.wing--film .wing__art::before {
  background: linear-gradient(170deg, rgb(20 36 48 / 0.5) 0%, rgb(8 16 22 / 0.78) 100%);
}

/* --- Two panels, and a band of films under them -------------------------- */
/* Idea D out of ideas.html. The count is the headline, the games take two
   thirds of the width and the classroom is a tall sheet of exercise paper
   beside it: the asymmetry says which one is the front door without a word.
   No bullet lists — what each one is gets a single line. */
.split { display: grid; gap: var(--sp-4); }
@media (min-width: 900px) { .split { grid-template-columns: 2fr 1fr; } }

.panel {
  position: relative; isolation: isolate; overflow: hidden;
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-md);
}
.panel__in { position: relative; z-index: 2; padding: clamp(1.3rem, 3vw, 2.2rem); }
.panel__hit { position: absolute; inset: 0; z-index: 3; }
.panel__hit span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* The count, in the marker from the banner, so the top of the page and this
   are written by the same hand. */
.panel__count {
  margin: 0; color: #fff;
  font-family: "Scribble Markers", var(--display), sans-serif;
  font-weight: 400; line-height: 0.92; letter-spacing: 0.01em;
  font-size: clamp(2.1rem, 1rem + 3.6vw, 4.2rem);
  text-shadow: 0 2px 18px rgb(0 0 0 / 0.5);
}
.panel__count em { font-style: normal; color: var(--amber); }
.panel__sub {
  margin: 0.5rem 0 var(--sp-4); max-width: 46ch;
  font-family: "Eraser", var(--display), sans-serif;
  letter-spacing: 0.04em; font-size: 1.02rem;
  color: rgb(238 243 244 / 0.76);
}
.panel__go {
  margin: var(--sp-4) 0 0; color: #fff;
  font-family: var(--display); font-weight: 700;
}

/* The games: the painted habitat pushed back, and the six logos on it. */
.panel--games { background: #0d1a20; color: #fff; }
.panel__bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.06);
  filter: blur(3px) saturate(0.85) brightness(0.42);
}
.panel--games::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(165deg, rgb(10 24 28 / 0.55), rgb(8 14 18 / 0.86));
}
.panel__grid {
  display: grid; gap: clamp(0.5rem, 1.4vw, 1rem);
  /* Three to a row rather than four: the logos are die-cut with a thick
     cream edge, and below about 200px that edge is most of what you see —
     six white rectangles instead of six paintings. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
}
@media (max-width: 560px) {
  /* Two to a row on a phone. One per row is six screens of scrolling to
     find out that there are six games. */
  .panel__grid { grid-template-columns: 1fr 1fr; }
  .filmstrip { grid-template-columns: 1fr 1fr; }
}
.tile {
  display: block; border-radius: var(--r-md);
  transition: transform 0.22s ease;
}
.tile img {
  width: 100%; height: auto; display: block;
  filter: drop-shadow(0 12px 16px rgb(0 0 0 / 0.55));
}
/* Breathing, not bobbing: a third of a pixel a second, each one on its own
   clock, so the shelf is alive without anything on it moving enough to
   catch the eye on purpose. */
.tile { animation: tile-float 7s ease-in-out infinite; animation-delay: calc(var(--i) * -1.3s); }
@keyframes tile-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}
.tile:hover { transform: translateY(-8px) scale(1.04); animation-play-state: paused; }

/* The classroom: a sheet of exercise paper, red margin rule and all. */
.panel--class {
  background:
    repeating-linear-gradient(to bottom, transparent 0 30px,
      rgb(15 143 136 / 0.15) 30px 31px),
    #fffdf7;
  color: var(--ink);
}
.panel--class::before {
  content: ""; position: absolute; z-index: 1;
  left: clamp(0.8rem, 2vw, 1.4rem); top: 0; bottom: 0; width: 2px;
  background: rgb(228 86 31 / 0.35);
}
/* A column, not a grid: `margin-top: auto` on a grid item does not eat the
   free space when the rows are packed to the start, so the link sat in the
   middle of the sheet with a hand's width of empty paper under it. In a
   flex column it goes to the bottom, which is where the last line on a
   page belongs. */
.panel--class .panel__in {
  display: flex; flex-direction: column; gap: 0.6rem; height: 100%;
  padding-left: clamp(1.8rem, 4vw, 2.8rem);
}
.panel__mark { width: min(62%, 230px); height: auto; margin-bottom: 0.4rem; }
.panel--class h2 { margin: 0; font-size: var(--step-2); }
.panel--class p { margin: 0; color: var(--ink-soft); }
.panel--class .panel__go { color: var(--flame); margin-top: auto; padding-top: var(--sp-4); }
.panel--class:hover { box-shadow: var(--shadow-lg); }

/* The films, across the bottom: the stills do the talking. */
.filmband {
  background: linear-gradient(170deg, #11202a, #0b151b);
  padding-block: var(--sp-6);
}
.filmband__in { display: grid; gap: var(--sp-5); align-items: center; }
@media (min-width: 980px) { .filmband__in { grid-template-columns: 0.85fr 1.15fr; gap: var(--sp-6); } }
.filmstrip {
  display: grid; gap: clamp(0.5rem, 1.2vw, 0.9rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 170px), 1fr));
}
.still {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 16 / 9; border-radius: var(--r-md);
  background: #000;
  transition: transform 0.22s ease, box-shadow 0.22s ease;
}
.still img { width: 100%; height: 100%; object-fit: cover; opacity: 0.78; transition: opacity 0.22s ease, transform 0.4s ease; }
.still:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); }
.still:hover img { opacity: 1; transform: scale(1.05); }
.still__go {
  position: absolute; left: 50%; top: 50%; z-index: 1;
  width: 2.3rem; aspect-ratio: 1; transform: translate(-50%, -50%);
  border-radius: 50%; background: rgb(228 86 31 / 0.92);
  box-shadow: 0 6px 18px rgb(0 0 0 / 0.5);
}
.still__go::before {
  content: ""; position: absolute; left: 56%; top: 50%;
  transform: translate(-50%, -50%);
  border-style: solid; border-width: 0.4rem 0 0.4rem 0.62rem;
  border-color: transparent transparent transparent #fff;
}
.still__t {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  padding: 1.4rem 0.6rem 0.5rem;
  background: linear-gradient(transparent, rgb(0 0 0 / 0.8));
  color: #fff; font-size: 0.78rem; line-height: 1.25;
  font-family: var(--display); font-weight: 700;
}

/* --- Arriving ------------------------------------------------------------ */
/* Each panel's parts come up one after another when the panel reaches the
   screen. Visible by default and armed by shell.js, the same way the
   passport stamps are: a browser that never runs it shows the finished
   thing rather than an empty box waiting for an observer. */
.rise.is-armed > *,
.rise.is-armed .panel__in > *,
.rise.is-armed .filmband__words > * {
  opacity: 0;
  transform: translateY(16px);
}
.rise.is-in > *,
.rise.is-in .panel__in > *,
.rise.is-in .filmband__words > * {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s ease, transform 0.55s cubic-bezier(0.2, 0.8, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 110ms);
}
.rise.is-armed .panel__bg { opacity: 0; }
.rise.is-in .panel__bg { opacity: 1; transition: opacity 0.8s ease; }

@media (prefers-reduced-motion: reduce) {
  .rise.is-armed > *, .rise.is-armed .panel__in > *,
  .rise.is-armed .filmband__words > *, .rise.is-armed .panel__bg {
    opacity: 1; transform: none;
  }
  .tile { animation: none; }
  .still, .tile, .panel { transition: none; }
}

/* --- The quiz under a film ----------------------------------------------- */
/* The card is a row across the top — poster, then what the film is — with
   the questions as a band under both. It was two columns with the quiz in
   the right-hand one, which left a hole under the poster the size of the
   poster. */
.film { display: block; padding: var(--sp-4); }
.film__top { display: grid; gap: var(--sp-4); align-items: start; }
@media (min-width: 820px) { .film__top { grid-template-columns: 1fr 1fr; } }
.film__words { display: grid; gap: 0.45rem; align-content: start; padding: 0.1rem 0.2rem 0; }

.quiz {
  margin-top: var(--sp-4);
  padding: var(--sp-4) clamp(1rem, 2.4vw, 1.6rem);
  border-radius: var(--r-lg);
  background:
    repeating-linear-gradient(to bottom, transparent 0 30px,
      rgb(15 143 136 / 0.14) 30px 31px),
    #fffdf7;
  color: var(--ink);
}
.quiz__head {
  margin: 0 0 0.9rem; color: var(--flame);
  font-family: "Scribble Markers", var(--display), sans-serif;
  font-weight: 400; font-size: 1.25rem; letter-spacing: 0.02em;
}
.quiz__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--sp-4);
}
@media (min-width: 820px) { .quiz__list { grid-template-columns: 1fr 1fr; gap: var(--sp-4) var(--sp-5); } }

.q__ask {
  display: grid; grid-template-columns: 1.6rem 1fr; gap: 0.55rem;
  align-items: baseline; margin: 0 0 0.6rem;
  font-weight: 500; line-height: 1.35;
}
.q__ask b {
  display: grid; place-items: center;
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  background: var(--teal-dark); color: #fff;
  font-family: var(--display); font-size: 0.8rem;
}
.q__opts { display: grid; gap: 0.4rem; }
/* An answer is a button, not a radio: a radio asks to be filled in and then
   submitted somewhere, and nothing here is submitted anywhere. */
.opt {
  text-align: left; cursor: pointer;
  padding: 0.5rem 0.7rem;
  border: 2px solid rgb(18 26 22 / 0.14); border-radius: var(--r-sm);
  background: #fff; color: var(--ink);
  font: inherit; font-size: 0.95rem; line-height: 1.3;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}
.opt:hover { border-color: var(--teal); transform: translateX(2px); }
/* Marked. The right answer is always shown once a question is done, even
   when the wrong one was pressed — being told "no" and not told what the
   answer was is the worst of both. */
.opt.is-right {
  border-color: var(--teal); background: var(--teal-pale);
  font-weight: 500;
}
.opt.is-right::after { content: " ✓"; color: var(--teal-dark); font-weight: 700; }
.opt.is-wrong { border-color: var(--flame); background: rgb(228 86 31 / 0.08); }
.opt.is-wrong::after { content: " ✕"; color: var(--flame); font-weight: 700; }
/* The ones nobody pressed step back, but they are still buttons: every
   answer stays pressable, so a question can be gone at again. */
.q.is-done .opt:not(.is-right):not(.is-wrong) { opacity: 0.5; }
.q.is-done .opt:not(.is-right):not(.is-wrong):hover { opacity: 0.8; }

.q__why {
  margin: 0.55rem 0 0; padding-left: 2.15rem;
  color: var(--ink-soft); font-size: 0.9rem; line-height: 1.45;
}
.quiz__score {
  margin: var(--sp-3) 0 0;
  font-family: "Eraser", var(--display), sans-serif;
  letter-spacing: 0.04em; color: var(--ink-faint);
}
.film.is-answered .quiz { box-shadow: inset 0 0 0 2px var(--teal); }

/* --- The eraser ---------------------------------------------------------- */
/* The banner is a chalkboard, so the pointer over it is the thing you hold
   against a chalkboard: a wooden block with felt on the bottom, drawn as an
   SVG data URI rather than an image file — twenty-six lines of markup
   against a request, and it stays sharp on any screen.
   The hotspot is the middle of the felt, which is the part that does the
   work; a cursor whose hotspot is its top-left corner feels like it is
   dragging behind the mouse.

   Only here. An eraser over the whole site would be a joke that outstays
   its welcome by about four seconds, and it would be an unreadable pointer
   over a page of links. */
.open {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='42' height='28'%3E%3Cg transform='rotate(-9 21 14)'%3E%3Crect x='4' y='5' width='34' height='10' rx='2' fill='%234a4f52'/%3E%3Crect x='4' y='4' width='34' height='3' rx='1.5' fill='%23717880'/%3E%3Crect x='4' y='14' width='34' height='9' rx='2' fill='%23ddd3c2'/%3E%3Crect x='4' y='14' width='34' height='2' fill='%23b9ad99'/%3E%3C/g%3E%3C/svg%3E") 21 20, crosshair;
}
/* The buttons keep a hand. A control that does not look clickable is worse
   than a joke that does not land. */
.open .btn { cursor: pointer; }

/* The dust it knocks off. One of these is dropped every so many pixels of
   travel, blown up and faded out in half a second, and removed the moment
   its animation ends — the banner never holds more than a dozen.
   Pinned to the layer above the grid and below the words, so the dust goes
   over the lines and under the headline, which is the way round it would
   happen on a wall. */
.puff {
  position: absolute; z-index: 1; pointer-events: none;
  width: var(--r); height: var(--r);
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgb(255 255 255 / 0.5), rgb(232 238 240 / 0.22) 45%, transparent 70%);
  filter: blur(2px);
  animation: puff 0.62s cubic-bezier(0.2, 0.7, 0.4, 1) forwards;
}
@keyframes puff {
  from { opacity: 0.6; transform: translate(-50%, -50%) scale(0.35); }
  to   { opacity: 0; transform: translate(-50%, -140%) scale(1.9); }
}

@media (prefers-reduced-motion: reduce) {
  /* The eraser stays — it is a cursor, not a movement — but it stops
     making a mess. */
  .puff { display: none; }
}

/* --- The lines that are not body text ------------------------------------ */
/* Three things on the page were still set in the body grotesk at 700 — the
   two "Open the…" links on the panels and the sign-off at the bottom — and
   they are not body text: they are the page talking to you. The rest of
   this candidate is written by hand, so these are too.

   The sentence above the sign-off is *not*: it is twenty words, and both
   supplied faces are caps-only, so twenty words of either is shouting. It
   gets size and air instead. */
.panel__go {
  font-family: "Scribble Markers", var(--display), sans-serif;
  font-weight: 400; letter-spacing: 0.02em;
  font-size: clamp(1.2rem, 1rem + 0.9vw, 1.7rem);
  line-height: 1.1;
}
/* A third of the width carries a shorter line: OPEN THE CLASSROOM in caps
   at the games panel's size breaks across two. */
.panel--class .panel__go {
  color: var(--flame);
  font-size: clamp(1.05rem, 0.9rem + 0.5vw, 1.35rem);
}

.closing { text-align: center; }
.closing__line {
  margin: 0 auto; max-width: 46ch;
  font-size: clamp(1.15rem, 1rem + 0.7vw, 1.5rem);
  line-height: 1.5; color: var(--ink-soft);
}
.closing__sign {
  margin: var(--sp-4) auto 0;
  font-family: "Scribble Markers", var(--display), sans-serif;
  font-weight: 400; letter-spacing: 0.02em; line-height: 1.05;
  font-size: clamp(1.5rem, 1.1rem + 2vw, 2.9rem);
  color: var(--ink);
}
.closing__sign em { font-style: normal; color: var(--flame); }

/* --- The ground under a film --------------------------------------------- */
/* Eight cards of the same flat near-black is eight times nothing, and the
   note was fair.

   Not a picture behind them, though — that was the first answer and the
   note on it was shorter than the answer: *don't put images behind, some
   animations*. So the ground is light, moving: three soft washes drifting
   across each other on a three-quarter-minute cycle, and a slow bar of
   light that crosses the card every twenty seconds the way a projector
   beam crosses a wall. All gradients, nothing to download, and each card
   on its own clock so eight of them never breathe together. */
.film {
  position: relative; isolation: isolate; overflow: hidden;
  background: #0d1419;
  box-shadow:
    var(--shadow-md),
    inset 0 1px 0 0 rgb(255 255 255 / 0.07),
    inset 0 -60px 60px -50px rgb(0 0 0 / 0.9);
}
.film::before {
  content: ""; position: absolute; inset: -18%; z-index: -2;
  background:
    radial-gradient(24rem 16rem at 24% 28%, rgb(15 143 136 / 0.55), transparent 68%),
    radial-gradient(22rem 14rem at 76% 66%, rgb(228 86 31 / 0.38), transparent 70%),
    radial-gradient(20rem 13rem at 54% 92%, rgb(104 78 170 / 0.42), transparent 72%);
  animation: film-drift 46s ease-in-out infinite alternate;
  animation-delay: calc(var(--i, 0) * -5s);
}
@keyframes film-drift {
  from { transform: translate3d(-3%, -2%, 0) scale(1); }
  to   { transform: translate3d(4%, 3%, 0) scale(1.12); }
}
/* The beam. Wide, faint, and gone for most of the cycle — a light that
   sweeps every two seconds is a notification, not a room. */
.film::after {
  content: ""; position: absolute; inset: -40% -60%; z-index: -1;
  background: linear-gradient(104deg,
    transparent 38%, rgb(255 255 255 / 0.07) 47%,
    rgb(255 255 255 / 0.03) 52%, transparent 62%);
  animation: film-beam 21s linear infinite;
  animation-delay: calc(var(--i, 0) * -3.4s);
}
@keyframes film-beam {
  0%        { transform: translateX(-38%); }
  55%, 100% { transform: translateX(42%); }
}
/* A little light thrown behind the poster, which is what stops the
   thumbnail looking stuck on. */
.film__screen::before {
  content: ""; position: absolute; inset: -8% -6%; z-index: -1;
  background: radial-gradient(60% 60% at 50% 50%, rgb(255 255 255 / 0.1), transparent 70%);
}

@media (prefers-reduced-motion: reduce) {
  /* The washes stay — they are what the card is made of — and they stop
     moving. The beam goes entirely: a bar of light parked across a card
     reads as a rendering fault. */
  .film::before { animation: none; }
  .film::after { display: none; }
}

/* --- The headline's own two words ---------------------------------------- */
/* Science, and All Ages: the two halves of what the site is for, written by
   hand in chalk while the rest of the line stays in the grotesk. The marker
   is a caps face, so they come out shouted, which is the point — and it
   sits a little low against a grotesk cap height, hence the nudge. */
.open h1 em {
  font-family: "Scribble Markers", var(--display), sans-serif;
  font-style: normal; font-weight: 400;
  font-size: 1.14em; line-height: 0.9;
  letter-spacing: 0.01em;
  color: var(--amber);
  display: inline-block;
  transform: translateY(0.035em) rotate(-1.1deg);
}
.open h1 em + em, .open h1 em:nth-of-type(2) { transform: translateY(0.035em) rotate(0.9deg); }

/* --- Buttons with a bottom edge ------------------------------------------ */
/* Chunky rather than flat: a solid edge under the button instead of a blur,
   which is what makes a thing look pressable rather than printed. Pressing
   it moves the face down onto the edge and the edge shrinks by the same
   amount, so the bottom of the button never moves — that is the whole
   trick, and it is why this feels like a key going down rather than a card
   sliding about. */
.btn--3d {
  position: relative;
  box-shadow: 0 5px 0 0 var(--btn-edge, rgb(0 0 0 / 0.45));
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.2s ease;
}
.btn--3d:hover {
  transform: translateY(-2px);
  box-shadow: 0 7px 0 0 var(--btn-edge, rgb(0 0 0 / 0.45));
}
.btn--3d:active {
  transform: translateY(3px);
  box-shadow: 0 2px 0 0 var(--btn-edge, rgb(0 0 0 / 0.45));
}
.btn--amber.btn--3d { --btn-edge: #a8380f; }
.open__go .btn--ghost.btn--3d { --btn-edge: rgb(255 255 255 / 0.3); }

/* The slight animation: the one that matters leans a degree and comes back,
   about three times a minute. Enough to catch an eye that has stopped
   moving, not enough to be a thing flashing on a page a child is reading. */
.open__go .btn--amber.btn--3d {
  animation: nudge 9s ease-in-out infinite;
}
@keyframes nudge {
  0%, 86%, 100% { rotate: 0deg; }
  90%           { rotate: -1.4deg; }
  94%           { rotate: 1deg; }
  97%           { rotate: -0.4deg; }
}
.open__go .btn--amber.btn--3d:hover { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .open__go .btn--amber.btn--3d { animation: none; }
  .btn--3d { transition: none; }
}
