*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  min-height: 100dvh;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--ink);
  background: var(--paper);
  background-image:
    radial-gradient(circle at 12% 18%, rgba(255, 140, 66, 0.14), transparent 42%),
    radial-gradient(circle at 88% 8%, rgba(58, 160, 255, 0.16), transparent 40%),
    radial-gradient(circle at 78% 92%, rgba(56, 193, 114, 0.14), transparent 45%);
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.01em;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  touch-action: manipulation;
}

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

/* ─────────── Shell ─────────── */

/*
  The page column.

  .play-about sits under the game and has to start and end where the game
  does, so it shares the width rather than keeping its own copy of it. It
  had one, in games.css, which loads after this file — so it won the
  cascade, ignored the wide-screen rule below, and sat forty pixels
  inside the board on every large screen.
*/
.shell,
.play-about {
  max-width: 1100px;
  margin-inline: auto;
  padding-inline: clamp(1rem, 0.6rem + 2vw, 2rem);
}

.shell {
  padding-block: clamp(0.75rem, 0.4rem + 1.5vw, 1.5rem) calc(2rem + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: var(--space-block);
  min-height: 100dvh;
}

.topbar {
  display: flex;
  align-items: center;
  gap: var(--gap);
  flex-wrap: wrap;
}

.topbar__spacer {
  flex: 1;
}

/* ─────────── Buttons ─────────── */

.btn {
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 1.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 600;
  background: var(--card);
  border: var(--line);
  border-radius: var(--radius);
  box-shadow: var(--lift-sm);
  transition:
    transform var(--fast) var(--ease-pop),
    box-shadow var(--fast) ease;
}

.btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--ink);
}

.btn:active {
  transform: translateY(2px);
  box-shadow: var(--lift-press);
}

.btn--primary {
  background: var(--star);
}

.btn--icon {
  padding: 0;
  font-size: 1.6rem;
}

.btn--ghost {
  background: transparent;
  box-shadow: none;
  border-style: dashed;
}

/* ─────────── Home ─────────── */

.hero {
  display: grid;
  gap: 0.9rem;
  padding-top: clamp(0.75rem, 2vw, 1.5rem);
}

.hero h1 {
  font-size: var(--text-huge);
  line-height: 1.05;
  padding-bottom: 0.1em;
}

/* The greeting leads, the count follows it quietly. Same size for both made
   the block read as one grey paragraph with no way in. */
.hero p {
  font-size: var(--text-lg);
  color: var(--ink-soft);
}

.hero .hero__hello {
  color: var(--ink);
}

.hero__mark {
  display: inline-block;
  transform: rotate(-3deg);
  background: var(--star);
  border: var(--line);
  border-radius: var(--radius);
  box-shadow: var(--lift-sm);
  padding: 0.1em 0.45em;
}

.menu {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1rem, 0.5rem + 2vw, 1.75rem);
}

.tile {
  position: relative;
  min-height: 210px;
  padding: 1.5rem 1.5rem 1.35rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1.1rem;
  text-align: left;
  color: var(--ink);
  background: var(--card);
  border: var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--lift);
  overflow: hidden;
  transition:
    transform var(--normal) var(--ease-pop),
    box-shadow var(--normal) ease;
}

.tile:nth-child(odd) {
  transform: rotate(-1deg);
}

.tile:nth-child(even) {
  transform: rotate(1deg);
}

.tile:hover {
  transform: translateY(-6px) rotate(0deg);
  box-shadow: 0 12px 0 var(--ink);
}

.tile__icon {
  font-size: 3.5rem;
  line-height: 1;
}

.tile__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-lg);
}

.tile__note {
  display: inline-block;
  /* Without this the note lays itself out at its natural width inside a
     flex column that does not stop it, runs past the tile and is clipped.
     At the default size "Letters, keyboard, words, sentences" just fitted;
     in a wider face it lost its last word. */
  max-width: 100%;
  margin-top: 0.2rem;
  font-size: var(--text-sm);
  line-height: 1.35;
  color: var(--ink-soft);
}

.tile__blob {
  position: absolute;
  inset: auto -30px -50px auto;
  width: 150px;
  height: 150px;
  border-radius: 50%;
  opacity: 0.25;
}

/*
  One colour per subject, all of them here.
  
  These were written one at a time, in three different files, as each
  subject was added — so Chess and Using a computer simply never got one
  and their circle was invisible. A subject added tomorrow needs a line in
  this block and nowhere else, and a test fails if it does not have one.
*/
.tile--write .tile__blob { background: var(--write); }
.tile--english .tile__blob { background: var(--english); }
.tile--maths .tile__blob { background: var(--maths); }
.tile--polish .tile__blob { background: var(--polish); }
.tile--world .tile__blob { background: var(--world); }
.tile--logic .tile__blob { background: var(--logic); }
.tile--chess .tile__blob { background: var(--chess); }
.tile--it .tile__blob { background: var(--it); }
.tile--science .tile__blob { background: var(--science); }
.tile--reading .tile__blob { background: var(--reading); }
.tile--arts .tile__blob { background: var(--arts); }

.tile__best {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--text-sm);
  background: var(--paper-deep);
  border: var(--line-thin);
  border-radius: 999px;
  padding: 0.25rem 0.8rem;
}

/* ─────────── Scoreboard ─────────── */

.hud {
  display: flex;
  align-items: center;
  gap: var(--gap);
  flex-wrap: wrap;
}

.chip {
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0 1rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-base);
  background: var(--card);
  border: var(--line-thin);
  border-radius: 999px;
}

.chip__value {
  font-variant-numeric: tabular-nums;
}

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

.dot {
  width: 20px;
  height: 20px;
  border: var(--line-thin);
  border-radius: 50%;
  background: var(--card);
  transition: background var(--normal) ease;
}

.dot--done {
  background: var(--good);
}

/* ─────────── Play board ─────────── */

.board {
  display: grid;
  gap: var(--space-block);
  align-content: start;
  flex: 1;
  /* Without this a wide child stretches the whole page past the screen. */
  min-width: 0;
}

.prompt {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-lg);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.card {
  background: var(--card);
  border: var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--lift-sm);
  padding: clamp(1rem, 0.6rem + 1.5vw, 1.75rem);
  min-width: 0;
}

/* Drag pieces */

.piece {
  min-width: var(--tap);
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.35rem 0.8rem;
  font-family: var(--font-display);
  font-weight: 600;
  background: var(--card);
  border: var(--line);
  border-radius: var(--radius);
  box-shadow: var(--lift-sm);
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  transition: box-shadow var(--fast) ease;
}

.piece--dragging {
  box-shadow: 0 14px 22px rgba(35, 32, 28, 0.28);
  z-index: 40;
  cursor: grabbing;
  /* Deliberately still hit-testable. This carried pointer-events: none so
     elementFromPoint could see the bucket underneath; the drop is pure
     geometry now and does not need it, and with it a tile left stuck in
     mid-air could never be picked up again. */
}

.piece--placed {
  opacity: 0;
  pointer-events: none;
}

.piece--wrong {
  animation: shake 320ms ease;
}

.slot {
  min-width: var(--tap);
  min-height: var(--tap);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 3px dashed rgba(35, 32, 28, 0.35);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.55);
  transition:
    border-color var(--fast) ease,
    background var(--fast) ease,
    transform var(--fast) var(--ease-pop);
}

/* Where the piece will land. Deliberately not green: green means correct
   everywhere else in this game, and a child dragging a wrong letter over a
   slot was being told it was the right one. */
.slot--target {
  border-color: var(--ink);
  border-style: solid;
  background: var(--paper-deep);
  transform: scale(1.06);
  box-shadow: var(--lift-sm);
}

/* This one really was the right answer, shown after a miss. */
.slot--show {
  border-color: var(--good);
  background: rgba(47, 182, 115, 0.16);
  transform: scale(1.05);
}

.slot--filled {
  border-style: solid;
  border-color: var(--ink);
  background: var(--card);
  box-shadow: var(--lift-sm);
}

@keyframes shake {
  0%,
  100% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(-8px);
  }
  75% {
    transform: translateX(8px);
  }
}

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

/* ─────────── Cheer and end screen ─────────── */

.cheer {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: clamp(4rem, 18vw, 10rem);
  pointer-events: none;
  z-index: 60;
  animation: pop-in 260ms var(--ease-pop);
}

.done {
  display: grid;
  gap: 1.25rem;
  justify-items: center;
  text-align: center;
  padding: clamp(1.5rem, 1rem + 3vw, 3rem);
}

.done h2 {
  font-size: var(--text-xl);
}

.stars {
  display: flex;
  gap: 0.5rem;
  font-size: clamp(2.5rem, 10vw, 4.5rem);
}

.star--off {
  filter: grayscale(1);
  opacity: 0.3;
}

.skip-link {
  position: absolute;
  left: -9999px;
}

/* ─────────── iPad behaviour ─────────── */

html,
body {
  overscroll-behavior: none;
  touch-action: manipulation;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* The settings page is the one place where text must be selectable. */
.field {
  -webkit-user-select: text;
  user-select: text;
}

/* Hover styles must not stick on a touch screen. */
@media (hover: none) {
  .btn:hover,
  .tile:hover {
    transform: none;
    box-shadow: var(--lift-sm);
  }
}

/* Screen-reader announcements for feedback that is otherwise only colour. */
.live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════
   Screen sizes
   The game is played on an iPad first, a phone second, a Mac third.
   Small screens get tighter spacing so the play area, not the chrome,
   fills the screen. The header always stays at the top, where a child
   looks for the way back.
   ═══════════════════════════════════════════════════════════ */

/* Phones and small tablets: less air, smaller chrome, same touch targets. */
@media (max-width: 680px) {
  :root {
    --gap: 10px;
    --space-block: clamp(0.75rem, 0.5rem + 1.5vw, 1.25rem);
    --radius-lg: 22px;
  }

  .shell {
    padding-inline: 12px;
    gap: 0.85rem;
  }

  .topbar {
    gap: 8px;
    flex-wrap: nowrap;
  }

  .topbar .prompt {
    font-size: 1.15rem;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .topbar .btn {
    min-height: 52px;
    min-width: 52px;
    padding: 0 0.8rem;
    font-size: 1rem;
  }

  .hud {
    gap: 8px;
  }

  .chip {
    min-height: 40px;
    padding: 0 0.7rem;
    font-size: 1rem;
  }

  .buddy__face {
    font-size: 1.5rem;
  }

  .dot {
    width: 14px;
    height: 14px;
  }

  .card {
    padding: 0.9rem;
  }

  .hint {
    font-size: 0.95rem;
  }
}

/* Landscape on a phone, or a short window: strip the vertical padding. */
@media (orientation: landscape) and (max-height: 560px) {
  .shell {
    padding-block: 8px;
    gap: 0.6rem;
  }

  .hint {
    display: none;
  }

  .card {
    padding: 0.7rem;
  }

  .board {
    gap: 0.6rem;
  }
}

/* A big tablet in landscape has room to breathe: use it.

   .play-about rides along because it sits under the game and has to
   start where the game starts. It lived in games.css with its own copy
   of 1100px and did not know about this rule, so on a wide screen its
   left edge was forty pixels adrift of the board above it. */
@media (min-width: 1000px) and (min-height: 700px) {
  .shell,
  .play-about {
    max-width: 1180px;
  }
}

/*
  Easier to read, switched on in the Parents Area.

  This used to name OpenDyslexic, then Comic Sans, then Andika, then
  Verdana, and ship none of them. On a Mac that meant it quietly turned the
  whole game into Comic Sans and added 0.8px of letter spacing — which is
  not a reading intervention, it is a joke about one.

  So it does what the evidence actually supports instead:

  Letter spacing is the one intervention with a clear result. Zorzi and
  colleagues (PNAS, 2012) widened it for dyslexic children and their
  reading speed and accuracy improved immediately, with no training at all.
  It is by far the largest effect here, and 0.04em was too small to be one.

  Special dyslexia typefaces are not. Kuster (2018) and Wery and Diliberto
  (2017) both tested OpenDyslexic against an ordinary sans-serif and found
  no advantage. Shipping a 70 KB font to deliver nothing would have been
  the wrong answer to the same question.

  What is left is a plain, wide, open sans-serif that every device already
  has, set larger, with room around every letter, every word and every
  line, and a short line to come back to.
*/
/*
  Both typefaces are redefined at the root rather than listed selector by
  selector. An earlier attempt named eight selectors and missed the story
  problems, the tiles and half the buttons, because a component that sets
  its own font wins. There is one name for each face on this site; change
  those two and everything follows.
*/
.easy-font {
  --font-display: Verdana, Tahoma, 'DejaVu Sans', system-ui, sans-serif;
  --font-body: Verdana, Tahoma, 'DejaVu Sans', system-ui, sans-serif;

  /* Both inherit, so this reaches every word without naming anything —
     including the words in a game that does not exist yet. */
  letter-spacing: 0.1em;
  word-spacing: 0.28em;
}

/*
  And then it is taken back off the furniture.
  
  A button, a chip, a level number and a keyboard key are things a child
  aims at, not things they read, and they sit in rows that were measured
  for them. Widening those pushed the keypad off the maths card, stretched
  the four strictness boxes past their row and clipped the wordmark. The
  reading is in the prose, the question and the answers; the spacing goes
  there and stops there.
*/
.easy-font .btn,
.easy-font .chip,
.easy-font .tag,
.easy-font .level,
.easy-font .levels__label,
.easy-font .levels__name,
.easy-font .pick,
.easy-font .key,
.easy-font .pad button,
.easy-font .topbar,
.easy-font .hero__brand,
.easy-font .site-link,
.easy-font .tile__best,
.easy-font .fact,
.easy-font .goal,
.easy-font .lock-badge,
.easy-font .setup__label,
.easy-font .readout__name,
.easy-font .age-slider__tick,
.easy-font .dots,
/* The text under the game is about the game rather than part of it: a
   parent deciding, or a crawler reading. The wide spacing exists to help
   a child read the words they are being tested on, and spreading a
   footnote across twice the width only makes it louder than the game. */
.easy-font .play-about,
.easy-font table {
  letter-spacing: normal;
  word-spacing: normal;
}

/*
  And put it back on the answers.
  
  A button does not inherit letter-spacing at all — the browser's own
  stylesheet sets it to normal on every form control — so the answers to a
  reading question were the one place on the screen still set tight. They
  are exactly what a child reads hardest, weighing four of them against
  each other. They wrap, so they grow downwards rather than sideways.
*/
.easy-font .choice,
.easy-font .choice--text,
.easy-font .duel__option {
  letter-spacing: 0.1em;
  word-spacing: 0.28em;
}

/*
  Room between the lines, where the lines are sentences.
  
  No max-width here. Setting one turned every hint in the Parents Area into
  a narrow ribbon down the left of a card twice its width, which looks
  broken rather than readable — the card is already the measure.
*/
.easy-font p,
.easy-font li,
.easy-font .hint,
.easy-font .story__text,
.easy-font .quiz__prompt {
  line-height: 1.85;
}

/*
  The wordmark keeps its own type.
  
  It is drawn as SVG text in a fixed 500-unit box, so a wider face simply
  runs off the end of it — Verdana rendered "smarties" at 433 units from
  x=140 and the page showed "smartie". A logo is not reading material
  anyway: this setting is for the words a child has to work out, and the
  name of the site is not one of them.
*/
.easy-font .logo__word,
.easy-font .logo__line {
  font-family: 'Fredoka', 'Trebuchet MS', Verdana, system-ui, sans-serif;
}

/* Never italic: a slanted letter is one more shape to work out. */
.easy-font em,
.easy-font i {
  font-style: normal;
  font-weight: 700;
}

/* ─────────── Whiteboard mode ─────────── */

/*
  The same game on a screen at the front of a room, read from the back of
  it. Turned on with ?board=1 or from the Parents Area, and never guessed
  at: a projector looks exactly like a laptop to a browser.

  The type is already half again larger, because the root font size is
  multiplied when the mode is on. What is left to do here is take away
  everything that is company rather than content. A child playing alone
  wants a butterfly to wander across the screen; thirty children watching
  one board do not, and the teacher holding their attention wants it even
  less.
*/
.board-mode .tile--discover,
.board-mode .discover,
.board-mode .discover__review,
.board-mode .home-foot--quiet,
.board-mode .critter,
.board-mode .install {
  display: none !important;
}

/* A wide screen at four metres: fewer things, further apart. */
.board-mode .shell {
  max-width: min(94vw, 96rem);
}

.board-mode .menu {
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: clamp(1.2rem, 1rem + 1.4vw, 2.2rem);
}

/* Anything a child has to hit from the front of the class is a big target
   for a finger on a board, not a mouse pointer. */
.board-mode .btn,
.board-mode .choice,
.board-mode .level,
.board-mode .pad button {
  min-height: 4rem;
}

.board-mode .topbar h1,
.board-mode .top-bar h1 {
  letter-spacing: 0.01em;
}

/* A projector loses thin lines and pale greys before anything else. */
.board-mode .hint {
  color: var(--ink);
}
