@charset "UTF-8";
/* ==========================================================================
   Guess the Bible Word — styles.css
   --------------------------------------------------------------------------
   Same world as the Bible Games hub, True or False, Christian Reversi, Jesus
   Chess and Jesus Checkers: a deep indigo night with warm light falling from
   above, and everything of value framed in gold.

   The one idea driving the layout: THE WORD IS THE GAME. The tiles get the
   largest type on the page and the middle of the screen; the clue sits just
   above them because it is what makes the puzzle fair; the scoreboard, the
   lamps and the alphabet are deliberately quieter around them.
   --------------------------------------------------------------------------
   Contents
     1.  Design tokens
     2.  Base, background, and the no-scroll shell
     3.  Accessibility helpers
     4.  Row 1 — top bar
     5.  Row 2 — scoreboard
     6.  Row 3 — the lamps
     7.  The stage: clue, word, fact
     8.  The alphabet and the Next button
     9.  Buttons, hint, noscript
     10. End-of-round overlay
     11. Responsive
     12. Reduced motion, forced colours, print
   --------------------------------------------------------------------------
   The page must NEVER scroll. The shell is a fixed-height flex column and
   .stage is the only element allowed to grow or shrink. If something new is
   added, it goes in a row with `flex: 0 0 auto` — never into .stage.

   Re-theming the whole SITE is one edit in shared/theme.css.
   Re-theming just this GAME is one edit in section 1 below.
   ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 1. Design tokens                                                           */
/* -------------------------------------------------------------------------- */

:root {
  /* ------------------------------------------------------------------------
     Only what is UNIQUE to this game lives here. The night sky, the golds, the
     three levels of ink, the panels, the type stacks, --dur and --radius all
     come from /biblegames/shared/theme.css, which index.php loads immediately
     before this file.
     ------------------------------------------------------------------------ */

  /* A lit lamp and a spent one.

     The lamps are the only place on this page where a colour carries state, so
     they are aria-hidden and the count is written out in words beside them.
     Brightness, not hue, is what separates the two — they survive a greyscale
     print and colour blindness of every kind. */
  --lamp-lit: #ffd980;
  --lamp-out: rgba(255, 217, 128, 0.16);

  /* A key that found a letter, and one that did not.

     Deliberately NOT plain green and red. The hit is gold — the same gold as
     the letter it just uncovered, so the eye connects the two — and the miss
     simply goes dark and empty, which is what a used-up guess actually feels
     like. Both are also disabled, so shape and behaviour say it as well. */
  --key-hit-1: #ffd45c;
  --key-hit-2: #e0a114;
  --key-miss: rgba(255, 255, 255, 0.05);

  /* Right and wrong feedback. Aliases of the shared accents rather than fresh
     values, so the game agrees with its own self-check report by
     construction. */
  --right: var(--ok);
  --wrong: var(--warn);

  /* Rhythm particular to this layout. */
  --gap-row: 10px;
  --shell-max: 900px;

  /* One tile.

     Held as tokens rather than written into .tile, because the tiles are the
     one thing on this page that has to change size with the screen: at 320px
     a fourteen-letter word needs them small enough to wrap into two readable
     rows, and on a desktop the stage is over a thousand pixels tall and small
     tiles leave it looking empty. Section 11 raises these twice. */
  --tile-w: clamp(24px, 6.2vw, 46px);
  --tile-h: clamp(34px, 8vw, 58px);
  --tile-font: clamp(1.05rem, 3.8vw, 2rem);
}

/* -------------------------------------------------------------------------- */
/* 2. Base, background, and the no-scroll shell                               */
/* -------------------------------------------------------------------------- */

html {
  height: 100%;
  background-color: var(--bg-deep);
  background-image:
    radial-gradient(ellipse 130% 60% at 50% -10%, var(--bg-glow) 0%, rgba(255, 214, 138, 0) 60%),
    linear-gradient(168deg, var(--bg-mid) 0%, var(--bg-deep) 58%, #04030d 100%);
  background-repeat: no-repeat;
  background-attachment: fixed;
  background-size: cover;
  /* Stop iOS rubber-banding the page while a child taps quickly. */
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  /* 100dvh where it exists: on mobile Safari and Chrome, 100vh is taller than
     the visible area while the address bar is showing, which would push the
     alphabet off the bottom of the screen. The vh line first is the fallback
     for browsers that do not know dvh. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
}

.beams {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(96deg, transparent 30%, var(--bg-beam) 42%, transparent 54%),
    linear-gradient(84deg, transparent 46%, var(--bg-beam) 58%, transparent 70%);
  background-repeat: no-repeat;
}

/* The fixed-height flex column. Everything is a row with a fixed height
   except .stage, which absorbs whatever is left. */
.app {
  position: relative;
  z-index: 1;
  height: 100%;
  max-width: var(--shell-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--gap-row);
  padding:
    max(10px, env(safe-area-inset-top))
    max(14px, env(safe-area-inset-right))
    max(12px, env(safe-area-inset-bottom))
    max(14px, env(safe-area-inset-left));
}

/* -------------------------------------------------------------------------- */
/* 3. Accessibility helpers                                                   */
/* -------------------------------------------------------------------------- */

/* .visually-hidden, the three-step focus ring and ::selection are all in
   /biblegames/shared/theme.css. The one rule this game adds is for the word
   panel, which Scripts.js moves focus to as each puzzle appears. It is not a
   link or a button, so it needs saying explicitly — and the ring is drawn
   around the whole row of tiles, which is the thing being announced. */
.word:focus {
  outline: 3px solid var(--gold-1);
  outline-offset: 6px;
}

.word:focus:not(:focus-visible) {
  outline: none;
}

.word:focus-visible {
  outline: 3px solid var(--gold-1);
  outline-offset: 6px;
}

/* -------------------------------------------------------------------------- */
/* 4. Row 1 — top bar                                                         */
/* -------------------------------------------------------------------------- */

.topbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

.back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* 44px tall: the size a fingertip actually needs. */
  min-height: 44px;
  padding: 0 10px;
  border-radius: 10px;
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.9rem;
  white-space: nowrap;
  transition: color var(--dur) ease, background-color var(--dur) ease;
}

.back__arrow {
  font-size: 1.1em;
}

.title {
  flex: 1 1 auto;
  margin: 0;
  text-align: center;
  font-family: var(--font-display);
  /* Three words, so it needs a smaller ceiling than a two-word title would.
     `white-space: nowrap` is deliberately NOT set: on a narrow phone this is
     allowed to wrap to two lines rather than shrink to nothing. */
  font-size: clamp(1rem, 3.6vw, 1.7rem);
  font-weight: 700;
  line-height: 1.15;
  color: var(--gold-1);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
}

.tools {
  display: flex;
  gap: 6px;
}

/* -------------------------------------------------------------------------- */
/* 5. Row 2 — scoreboard                                                      */
/* -------------------------------------------------------------------------- */

.hud {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  gap: 6px;
  padding: 8px 12px;
  background: linear-gradient(180deg, rgba(255, 232, 176, 0.07), rgba(255, 232, 176, 0.02));
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
}

.hud__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

/* --text-dim, not --text-faint. The scoreboard sits directly under the warm
   glow at the top of the page, which lifts the background there and drops the
   contrast of small text on it to about 3:1 — measured, and below AA. */
.hud__label {
  font-size: 0.66rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.hud__value {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--gold-1);
  /* Tabular figures stop the scoreboard jiggling as the numbers change. */
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------- */
/* 6. Row 3 — the lamps                                                       */
/* -------------------------------------------------------------------------- */

.lamps {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.lamps__row {
  display: flex;
  gap: 6px;
}

.lamp {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--lamp-lit);
  box-shadow: 0 0 8px var(--gold-glow);
  transition: background-color var(--dur) ease, box-shadow var(--dur) ease;
}

.lamp.is-out {
  background: var(--lamp-out);
  box-shadow: none;
}

/* The count in words. This is the accessible name for the whole row — the
   lamps themselves are aria-hidden — so it is never allowed to become
   decorative. --text-dim at 0.8rem clears AA on the lit background here. */
.lamps__count {
  margin: 0;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.lamps__count.is-low {
  color: var(--warn);
  font-weight: 700;
}

/* -------------------------------------------------------------------------- */
/* 7. The stage: clue, word, fact                                             */
/* -------------------------------------------------------------------------- */

/* The only flexible row. `min-height: 0` is what actually allows a flex child
   to shrink below its content size — without it a long fact pushes the
   alphabet off the bottom of the screen. */
.stage {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
  padding: 4px;
  overflow: hidden;
}

/*
 * The clue.
 *
 * Set in the display serif and given a lit panel, because it is not chrome —
 * it is half the puzzle. Eight wrong guesses is not many, and without the clue
 * this game would be a spelling test with no way in.
 */
.clue {
  margin: 0 auto;
  width: 100%;
  max-width: 40rem;
  padding: clamp(10px, 2.4vw, 16px) clamp(14px, 3.5vw, 24px);
  text-align: center;
  font-family: var(--font-display);
  font-size: clamp(0.95rem, 3vw, 1.35rem);
  line-height: 1.35;
  color: #fffaf0;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  background: linear-gradient(160deg, rgba(46, 34, 92, 0.55), rgba(26, 18, 58, 0.7));
  border: 1px solid var(--panel-edge);
  border-radius: 16px;
  box-shadow:
    0 10px 30px rgba(3, 2, 12, 0.45),
    inset 0 1px 0 rgba(255, 232, 176, 0.13);
}

/*
 * The word.
 *
 * Wrapping is allowed on purpose. Fourteen tiles cannot fit across a 320px
 * screen at a size a child can read, and two readable rows beat one row of
 * seventeen-pixel letters every time.
 */
.word {
  /* `align-self: center` matters more than it looks.
     .stage is a column flex container, so its children stretch to full width
     by default — and Scripts.js focuses this element on every new word, which
     drew the focus ring as two full-width rules across an apparently empty
     page. Shrinking the box to its tiles makes the ring hug the puzzle, which
     is what it is actually announcing. */
  align-self: center;
  max-width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-content: center;
  justify-content: center;
  gap: 5px;
  min-height: 0;
}

.tile {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--tile-w);
  min-height: var(--tile-h);
  border-bottom: 3px solid var(--frame-3);
  border-radius: 4px 4px 8px 8px;
  background: rgba(255, 255, 255, 0.05);
  font-family: var(--font-display);
  /* The largest type on the page, because it is the game. */
  font-size: var(--tile-font);
  font-weight: 700;
  line-height: 1;
  color: var(--gold-1);
}

/* A letter the player found. The lit panel is what makes uncovering one feel
   like a small win rather than a text change. */
.tile.is-shown {
  background: linear-gradient(180deg, rgba(255, 214, 130, 0.18), rgba(255, 214, 130, 0.06));
  border-bottom-color: var(--gold-2);
  box-shadow: inset 0 1px 0 rgba(255, 232, 176, 0.2);
}

/*
 * A letter revealed only because the guesses ran out.
 *
 * Marked differently from a letter the player earned, so that the answer at
 * the end of a lost word is honest about which parts were theirs. Colour is
 * not doing this alone: the italic and the dotted underline say it too.
 */
.tile.is-missed {
  background: rgba(255, 156, 110, 0.1);
  border-bottom: 3px dotted var(--warn);
  color: var(--warn);
  font-style: italic;
}

.fact {
  margin: 0 auto;
  max-width: 60ch;
  padding: 10px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--panel-edge);
  background: rgba(8, 6, 26, 0.55);
  text-align: center;
}

.fact.is-right { border-color: rgba(134, 230, 168, 0.55); }
.fact.is-wrong { border-color: rgba(255, 156, 110, 0.55); }

.fact__verdict {
  margin: 0 0 4px;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

.fact.is-right .fact__verdict { color: var(--right); }
.fact.is-wrong .fact__verdict { color: var(--wrong); }

.fact__text {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--text);
}

/* -------------------------------------------------------------------------- */
/* 8. The alphabet and the Next button                                        */
/* -------------------------------------------------------------------------- */

/*
 * One row holds both. They are never on screen together: the alphabet is of no
 * use once the word is finished, and stacking them would cost about fifty
 * pixels of height that a phone in landscape does not have.
 */
.playrow {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
}

/*
 * Seven columns at every width.
 *
 * A full A-Z keyboard cannot give every key a 44px target on a 320px screen:
 * twenty-six keys across four rows works out at about 36px wide there, and
 * fewer columns would mean five or six rows, which costs more height than the
 * screen has. The keys are 42px TALL to claw back most of the target area, and
 * from 480px up they clear 44px in both directions. This is a deliberate,
 * measured exception, recorded in docs/README.md.
 */
.keys {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
  max-width: 27rem;
}

.key {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  padding: 0;
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  background: linear-gradient(160deg, var(--panel-1), var(--panel-2));
  color: var(--text);
  font-family: var(--font-ui);
  font-size: clamp(0.95rem, 3.2vw, 1.15rem);
  font-weight: 700;
  cursor: pointer;
  transition: border-color var(--dur) ease, transform var(--dur) ease,
              background-color var(--dur) ease, color var(--dur) ease;
  -webkit-tap-highlight-color: transparent;
}

.key:active:not(:disabled) {
  transform: translateY(2px);
}

/* Found a letter. Gold, the same gold as the tile it just lit, so the eye
   joins the two. Dark ink on a bright key, for the same reason the answer
   buttons in True or False carry dark ink: white on a mid-tone gold is the one
   thing that cannot be read on it. */
.key.is-hit {
  background: linear-gradient(180deg, var(--key-hit-1), var(--key-hit-2));
  border-color: transparent;
  color: #241703;
  opacity: 1;
}

/* Missed. It simply goes dark and empty — what a spent guess feels like.

   --text-dim, NOT --text-faint. The faint tone is reserved for decoration, and
   a missed letter is not decoration: the player reads these to remember what
   they have already tried, which is most of the skill in the game. Losing the
   border and the panel gradient is what says "spent"; dimming the letter until
   it cannot be read would say "irrelevant". */
.key.is-miss {
  background: var(--key-miss);
  border-color: rgba(255, 255, 255, 0.08);
  color: var(--text-dim);
}

/* Disabled at the end of a word, when every key is off. Kept legible: the
   player should still be able to read what they tried. */
.key:disabled {
  cursor: default;
}

.key.is-hit:disabled,
.key.is-miss:disabled {
  /* The two marked states already say everything; do not grey them as well. */
  opacity: 1;
}

.key:disabled:not(.is-hit):not(.is-miss) {
  opacity: 0.4;
}

/* -------------------------------------------------------------------------- */
/* 9. Buttons, hint, noscript                                                 */
/* -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  min-width: 44px;
  padding: 0 16px;
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  background: linear-gradient(160deg, var(--panel-1), var(--panel-2));
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--dur) ease, transform var(--dur) ease;
  -webkit-tap-highlight-color: transparent;
}

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

.btn--primary {
  border-color: transparent;
  background: linear-gradient(180deg, #ffd45c, #e08c14);
  color: #241703;
  font-weight: 800;
}

.btn--big {
  min-height: 54px;
  padding: 0 28px;
  font-size: 1.1rem;
}

.btn--next {
  align-self: center;
  min-height: 52px;
  min-width: 200px;
}

/* --text-dim, not --text-faint. This line teaches the keyboard shortcuts, so
   it is instruction rather than decoration, and --text-faint is reserved for
   text no player ever needs to read. */
.hint {
  flex: 0 0 auto;
  margin: 0;
  text-align: center;
  font-size: 0.78rem;
  color: var(--text-dim);
}

kbd {
  display: inline-block;
  padding: 1px 6px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.07);
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 0.9em;
  color: var(--gold-1);
}

.noscript {
  position: relative;
  z-index: 2;
  margin: 24px auto;
  max-width: 40ch;
  padding: 16px;
  text-align: center;
  color: var(--text);
}

.noscript a { color: var(--gold-2); }

/* -------------------------------------------------------------------------- */
/* 10. End-of-round overlay                                                   */
/* -------------------------------------------------------------------------- */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: 18px;
  background: rgba(4, 3, 13, 0.82);
  /* Progressive: a plain dim is the fallback where backdrop-filter is absent. */
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

/* The global [hidden] rule in shared/theme.css covers this element too. */

.overlay__card {
  width: min(30rem, 100%);
  max-height: 100%;
  overflow-y: auto;
  padding: 22px;
  text-align: center;
  background: linear-gradient(160deg, #241a4e, #150e33);
  border: 1px solid rgba(255, 214, 130, 0.45);
  border-radius: 18px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

.overlay__icon {
  font-size: 2.6rem;
  line-height: 1;
}

.overlay__title {
  margin: 8px 0 4px;
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--gold-1);
}

.overlay__score {
  margin: 0;
  font-size: 1.35rem;
  font-weight: 800;
  color: #fffaf0;
  font-variant-numeric: tabular-nums;
}

.overlay__msg {
  margin: 8px 0 0;
  color: var(--text-dim);
}

.overlay__verse {
  margin: 16px 0;
  padding: 12px 14px;
  border-top: 1px solid rgba(255, 214, 130, 0.25);
  border-bottom: 1px solid rgba(255, 214, 130, 0.25);
}

.overlay__verse-text {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  color: #fdf6e3;
}

.overlay__verse-ref {
  display: block;
  margin-top: 6px;
  font-size: 0.78rem;
  font-weight: 600;
  font-style: normal;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gold-2);
}

.overlay__back {
  display: inline-block;
  margin-top: 12px;
  min-height: 44px;
  line-height: 44px;
  padding: 0 10px;
  color: var(--text-dim);
  font-size: 0.9rem;
}

/* -------------------------------------------------------------------------- */
/* 11. Responsive                                                             */
/* -------------------------------------------------------------------------- */

/* Hover only where a pointer can genuinely hover. On a touch screen the
   browser fakes a hover on tap and leaves it stuck there. */
@media (hover: hover) {
  .back:hover {
    color: var(--gold-1);
    background: rgba(255, 255, 255, 0.06);
  }

  .btn:hover {
    border-color: rgba(255, 214, 130, 0.7);
  }

  .key:hover:not(:disabled) {
    border-color: rgba(255, 214, 130, 0.7);
    transform: translateY(-2px);
  }
}

/* Small phones: trim the chrome so the word and the alphabet keep their share
   of a short screen. */
@media (max-width: 420px) {
  :root { --gap-row: 8px; }

  .back__label { display: none; }   /* the arrow alone is enough */

  .hud { padding: 6px 8px; }

  .hud__item--best { display: none; }  /* least important of the four */

  .lamps { gap: 8px; }
}

/* Nine columns once there is room, so the alphabet is three rows rather than
   four and the word gets the height back. */
@media (min-width: 560px) {
  .keys {
    grid-template-columns: repeat(9, minmax(0, 1fr));
    max-width: 34rem;
  }
}

/*
 * Tall screens: grow the tiles.
 *
 * On a desktop the stage is over a thousand pixels tall, and phone-sized tiles
 * centred in it read as a mistake rather than as breathing room — the puzzle
 * stops being the biggest thing on the page. Both steps are keyed on HEIGHT,
 * not width, because height is what is actually going spare. The clue grows
 * with them so the pair still reads as one unit.
 */
@media (min-height: 700px) and (min-width: 560px) {
  :root {
    --tile-w: clamp(40px, 5vw, 60px);
    --tile-h: clamp(56px, 7vw, 78px);
    --tile-font: clamp(1.7rem, 3vw, 2.6rem);
  }

  .word { gap: 8px; }

  .clue { font-size: clamp(1.2rem, 1.9vw, 1.6rem); }
}

@media (min-height: 1000px) and (min-width: 900px) {
  :root {
    --tile-w: 68px;
    --tile-h: 90px;
    --tile-font: 3rem;
  }
}

/* Landscape on a phone: very little height, so the scoreboard and the hint go
   and everything else tightens. */
@media (max-height: 460px) and (orientation: landscape) {
  .hud,
  .hint {
    display: none;
  }

  .stage {
    gap: 8px;
  }

  .clue {
    padding: 8px 14px;
    font-size: clamp(0.85rem, 2.6vh, 1.1rem);
  }

  .tile {
    min-height: clamp(28px, 9vh, 44px);
    font-size: clamp(0.95rem, 4vh, 1.5rem);
  }

  .key {
    min-height: 34px;
  }

  .fact__text {
    font-size: 0.85rem;
  }
}

/*
 * The one place a game page is allowed to scroll.
 *
 * Below about 300px of height — roughly 300–400% browser zoom, or a very small
 * window — no fixed-height layout can hold this game. The alphabet alone needs
 * four rows and the word needs room to wrap. Holding the no-scroll contract
 * here does not produce a tidy page; it produces a puzzle cropped by .stage
 * with the keyboard sitting on top of where the rest of it should be.
 *
 * So below the floor the shell stops being a fixed-height column and becomes
 * an ordinary scrolling page. Everything is reachable, nothing is cropped, and
 * nobody at a normal magnification ever sees this block.
 *
 * Kept in step with MIN_NO_SCROLL_HEIGHT in BibleGames_Preview.py, which
 * waives the no-scroll check at exactly the same height.
 */
@media (max-height: 299px) {
  body {
    height: auto;
    min-height: 100vh;
    min-height: 100dvh;
    overflow-y: auto;
  }

  .app {
    height: auto;
    min-height: 100%;
  }

  /* No longer the elastic row — it takes the height its content needs, and the
     page grows instead of cropping it. */
  .stage {
    flex: 0 0 auto;
    overflow: visible;
  }
}

/* -------------------------------------------------------------------------- */
/* 12. Reduced motion, forced colours, print                                  */
/* -------------------------------------------------------------------------- */

/* The blanket "kill every transition" rule is in shared/theme.css. What is
   left here is the part specific to this game: the keys move by TRANSFORM on
   press and hover, and a transform with no duration still jumps. Duration
   alone does not switch that off; the movement itself has to go. */
@media (prefers-reduced-motion: reduce) {
  .key:hover:not(:disabled),
  .key:active:not(:disabled) {
    transform: none;
  }
}

/*
 * Windows High Contrast. The system throws our palette away, which would leave
 * a lit lamp and a spent one identical, and a hit key indistinguishable from a
 * miss. Borders and outlines bring the difference back in a form the system
 * cannot flatten.
 */
@media (forced-colors: active) {
  .btn,
  .key,
  .hud,
  .clue,
  .fact,
  .overlay__card {
    border: 1px solid CanvasText;
  }

  .lamp {
    border: 2px solid CanvasText;
    background: CanvasText;
  }

  .lamp.is-out {
    background: Canvas;
  }

  .key.is-hit { outline: 3px solid Highlight; outline-offset: -3px; }
  .key.is-miss { opacity: 0.5; }

  .tile { border-bottom: 3px solid CanvasText; }
  .tile.is-missed { border-bottom-style: dotted; }

  a:focus,
  a:focus-visible,
  button:focus,
  button:focus-visible,
  .word:focus-visible {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }

  .beams { display: none; }
}

/* "Increase contrast": keep the palette, firm up the edges. */
@media (prefers-contrast: more) {
  :root {
    --text-dim: #ddd4f2;
    --text-faint: #c3b9dd;
    --panel-edge: rgba(255, 214, 130, 0.75);
    --lamp-out: rgba(255, 217, 128, 0.3);
  }

  .beams { display: none; }
}

/*
 * Print. A game is not a printable thing, so this exists only so that an
 * accidental Ctrl+P produces one tidy page instead of a black rectangle.
 */
@media print {
  html, body {
    height: auto;
    overflow: visible;
    background: #fff;
    color: #000;
  }

  .beams,
  .tools,
  .lamps,
  .playrow,
  .btn,
  .hint,
  .overlay,
  .report {
    display: none !important;
  }

  .app { height: auto; }

  .title,
  .clue,
  .tile { color: #000; text-shadow: none; }
}
