@charset "UTF-8";
/* ==========================================================================
   Bible Games — the game shell  (shared/shell.css)
   --------------------------------------------------------------------------
   The parts of a game page that are the same whatever the game is: the
   no-scroll flex column, the top bar with its Back link, the scoreboard, the
   ordinary buttons, the hint line, and the end-of-round card.

   WHY THIS EXISTS

   All of this used to live in quiz.css, which was correct while every rebuilt
   game was a four-choice quiz. It stopped being correct the moment a game that
   is NOT a quiz needed the same Back link and the same score card. A falling-
   word catcher, a card grid, a Lights Out puzzle and a whack-a-mole all want
   exactly this chrome and none of them want an answer grid.

   The test from Shared.md §5 is "could a page reasonably want it different?"

     * The topbar, the scoreboard, the score card — no. Every game wants the
       same ones, and six copies is how they drift apart.
     * The answer grid — yes, obviously; a card grid is not four buttons. That
       stays in quiz.css.

   So this file was SPLIT OUT of quiz.css rather than written fresh. Every rule
   below is the rule that was already there, in the order it was already in, so
   the five games that were on quiz.css get a byte-identical cascade.

   --------------------------------------------------------------------------
   HOW A PAGE LOADS IT

     <link rel="stylesheet" href="/biblegames/shared/theme.css?v=...">
     <link rel="stylesheet" href="/biblegames/shared/shell.css?v=...">
     <link rel="stylesheet" href="/biblegames/shared/quiz.css?v=...">   <- quizzes only
     <link rel="stylesheet" href="styles.css?v=...">

   In that order. Tokens, then the shell, then the game's shape, then the
   game's own identity — each free to override the one before it.

   --------------------------------------------------------------------------
   Contents
     1.  Tokens this shell adds
     2.  Base, background, the no-scroll column
     3.  Row 1 — top bar
     4.  Row 2 — scoreboard and progress
     5.  The stage and the standing line above it
     6.  Buttons, hint, noscript
     7.  End-of-round card
     8.  Responsive
     9.  Reduced motion, forced colours, more contrast, print
   --------------------------------------------------------------------------
   THE LAYOUT CONTRACT (Standards.md §5)

   The page must NEVER scroll. This is a fixed-height flex column; every row is
   `flex: 0 0 auto` and .stage alone is `flex: 1 1 auto` with `min-height: 0`.
   Adding something means adding a ROW, never putting it inside .stage.

   The single exception is @media (max-height: 299px) in section 8, which is
   kept in step with MIN_NO_SCROLL_HEIGHT in BibleGames_Preview.py.

   Colours, type stacks, --radius and --dur all come from theme.css, which is
   loaded before this file.
   ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 1. Tokens this shell adds                                                  */
/* -------------------------------------------------------------------------- */

:root {
  --gap-row: 10px;
  --shell-max: 900px;

  /* Right and wrong are ALIASES of the shared accents, never fresh values, so
     a game agrees with its own self-check report by construction. */
  --right: var(--ok);
  --wrong: var(--warn);
}

/* -------------------------------------------------------------------------- */
/* 2. Base, background, the no-scroll column                                  */
/* -------------------------------------------------------------------------- */

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 pushes the
     controls off the bottom of the screen. The vh line first is the fallback. */
  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;
}

.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. 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;
  min-height: 44px;          /* what a fingertip actually needs */
  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);
  font-size: clamp(1.05rem, 4vw, 1.8rem);
  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;
}

/* -------------------------------------------------------------------------- */
/* 4. Row 2 — scoreboard and progress                                         */
/* -------------------------------------------------------------------------- */

.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
   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;
}

/*
 * A scoreboard value that can go NEGATIVE.
 *
 * Only the arcade games need this — catching a distraction costs points, and a
 * score of "-30" printed in the same gold as "+30" reads as a fault rather
 * than as a penalty. Colour is not the only signal: the minus sign is right
 * there in the number, and this merely agrees with it.
 */
.hud__value.is-negative { color: var(--warn); }

.progress {
  flex: 0 0 auto;
  height: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
}

.progress__fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--gold-3), var(--gold-2));
  transition: width 320ms ease;
}

/*
 * The progress bar as a COUNTDOWN.
 *
 * A timed game runs the same bar the other way, and the last few seconds
 * should look like the last few seconds. Amber from about a third left, red
 * from about a tenth — set by the game as a class, never by reading the width
 * back out of the DOM.
 */
.progress__fill.is-low  { background: linear-gradient(90deg, #b7860b, #ffb03a); }
.progress__fill.is-out  { background: linear-gradient(90deg, #a8391a, #ff7a4d); }

/* -------------------------------------------------------------------------- */
/* 5. The stage and the standing line above it                                */
/* -------------------------------------------------------------------------- */

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

/* A standing line above the stage — "Who did this?" — which never changes and
   so is deliberately quiet. It exists so the stage itself does not have to
   carry the question, and so a screen reader meets the task before the text. */
.lede {
  flex: 0 0 auto;
  margin: 0;
  text-align: center;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* -------------------------------------------------------------------------- */
/* 5a. The answer panel — the verdict, the explanation and the verse          */
/* -------------------------------------------------------------------------- */

/*
 * "Here is what the answer was, here is why, and here is the verse."
 *
 * This lived in quiz.css until a card grid needed exactly the same panel after
 * a matched pair. The Shared.md §5 test — could a page reasonably want it
 * different? — answers no: every game on this site says the same three things
 * in the same order, and it is the whole reason for the rebuild. The old games
 * said "wrong" and moved on, and a child learnt nothing.
 *
 * Its POSITION is not shared. A quiz puts it inside .stage above the answer
 * buttons; a card grid puts it in its own row under the board. Each game's own
 * styles.css places it.
 *
 * role="status" belongs on the element, so the verdict, the explanation AND the
 * verse announce together as one message rather than three.
 */
.feedback {
  margin: 0 auto;
  max-width: 62ch;
  padding: 10px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--panel-edge);
  background: rgba(8, 6, 26, 0.55);
  text-align: center;
}

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

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

.feedback.is-right .feedback__verdict { color: var(--right); }
.feedback.is-wrong .feedback__verdict { color: var(--wrong); }

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

/*
 * The verse itself, KJV, with its reference. Set apart with rules above and
 * below so it reads as scripture and not as more of the explanation.
 *
 * Every one of these is checked against the KJV text on disk by
 * BibleGames_Scripture.py. Nothing anywhere on this site is quoted from memory.
 */
.feedback__scripture {
  margin: 10px 0 0;
  padding: 9px 12px 7px;
  border-top: 1px solid rgba(255, 214, 130, 0.28);
  border-bottom: 1px solid rgba(255, 214, 130, 0.28);
}

.feedback__verse {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.95rem;
  line-height: 1.45;
  color: #fdf6e3;
}

.feedback__ref {
  display: block;
  margin-top: 5px;
  font-size: 0.72rem;
  font-weight: 700;
  font-style: normal;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gold-2);
}

/* -------------------------------------------------------------------------- */
/* 6. 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 {
  flex: 0 0 auto;
  align-self: center;
  min-width: 200px;
}

/* --text-dim, not --text-faint. This line teaches the keyboard shortcuts, so
   it is instruction rather than decoration; --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); }

/* -------------------------------------------------------------------------- */
/* 7. End-of-round card                                                       */
/* -------------------------------------------------------------------------- */

.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 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;
}

/* -------------------------------------------------------------------------- */
/* 8. Responsive                                                              */
/* -------------------------------------------------------------------------- */

/* Hover only where a pointer can genuinely hover. On a touch screen the
   browser fakes a hover on tap and leaves the element stuck in its lit state
   until you tap elsewhere — so every control a child had tapped would glow. */
@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); }
}

/* Small phones: trim the chrome so the game keeps its 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 */
}

/* 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,
  .progress,
  .lede {
    display: none;
  }
}

/*
 * The one place a game page is allowed to scroll.  (Standards.md §5)
 *
 * Below about 300px of height — roughly 300–400% browser zoom, which WCAG
 * 1.4.10 asks a page to survive, or a very small window — no fixed-height
 * layout can hold a game. The controls alone have a ~44px floor and the text
 * still needs room to wrap. Holding the no-scroll contract here does not
 * produce a tidy page; it produces a sentence cropped in half.
 *
 * The exception permits SCROLLING, never CLIPPING.
 *
 * Kept in step with MIN_NO_SCROLL_HEIGHT in BibleGames_Preview.py, which
 * waives the no-scroll check at exactly the same height and says so in its
 * report rather than passing silently.
 */
@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;
  }
}

/* -------------------------------------------------------------------------- */
/* 9. Reduced motion, forced colours, more contrast, print                    */
/* -------------------------------------------------------------------------- */

/* The blanket "kill every transition" rule is in theme.css. Nothing in the
   shell moves by TRANSFORM, so there is nothing extra to switch off here; the
   games that do move add their own block. */

/*
 * Windows High Contrast. The system throws our palette away wholesale, which
 * would leave every panel as an invisible rectangle. Explicit borders bring
 * the difference back in a form the system cannot flatten.
 */
@media (forced-colors: active) {
  .btn,
  .hud,
  .overlay__card {
    border: 1px solid CanvasText;
  }

  .hud__value.is-negative { color: CanvasText; }

  a:focus,
  a:focus-visible,
  button:focus,
  button: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);
  }

  .beams { display: none; }
}

/*
 * Print. A game is not a printable thing, so this exists only so 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,
  .progress,
  .btn,
  .hint,
  .overlay,
  .report {
    display: none !important;
  }

  .app { height: auto; }

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