@charset "UTF-8";
/* ==========================================================================
   Fruit of the Spirit — what belongs to this game alone
   --------------------------------------------------------------------------
   Everything structural is in /biblegames/shared/shell.css: the no-scroll flex
   column, the top bar, the scoreboard, the buttons, the score card. Loaded
   before this file, so anything here overrides it.

   What is genuinely this game's own is three things nothing else has:

     the BASKET   nine slots that fill up — the lesson, kept on screen
     the FIELD    where words fall, and the tiles that fall through it
     the FLASH    one line saying what the word you just caught means

   --------------------------------------------------------------------------
   THE ONE RULE THIS FILE MUST NOT BREAK

   A falling tile MUST NOT look different depending on whether it is a fruit or
   a work of the flesh.

   Green fruits and red works would turn this into a reflex test that a child
   could win at speed without reading a single word, and reading the word is
   the entire game. The tiles are identical until the moment one is caught, and
   only then does it show what it was.

   That is also why the basket exists: the nine names have to be somewhere a
   child can look, because the game will not colour them in for you.
   --------------------------------------------------------------------------
   Contents
     1. Tokens
     2. The basket
     3. The field and the falling tiles
     4. The flash line
     5. The tally on the score card
     6. Responsive
     7. Reduced motion, forced colours, more contrast, print
   ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 1. Tokens                                                                  */
/* -------------------------------------------------------------------------- */

:root {
  /* One height for a tile, used by the stylesheet AND read by Scripts.js
     through getComputedStyle, so the fall distance and the tile can never
     disagree about how tall a tile is. Change it here only. */
  --tile-h: 40px;
  --tile-font: clamp(0.82rem, 3.2vw, 1.05rem);

  /* The basket chip when it is still empty, and when it has been filled.
     Both are ordinary panel colours; the difference the eye uses is the
     brightness and the ring, and the difference a screen reader uses is the
     word in .basket__state. */
  --slot-empty: rgba(46, 34, 92, 0.55);
  --slot-full: linear-gradient(160deg, #2f6d4b, #1b4630);
}

/* -------------------------------------------------------------------------- */
/* 2. The basket                                                              */
/* -------------------------------------------------------------------------- */

.basket {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.basket__slot {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border: 1px solid var(--panel-edge);
  border-radius: 999px;
  background: var(--slot-empty);
  color: var(--text-dim);
  font-size: clamp(0.62rem, 2.4vw, 0.78rem);
  line-height: 1.2;
  white-space: nowrap;
  transition: background var(--dur) ease, color var(--dur) ease,
              border-color var(--dur) ease;
}

/*
 * Gathered.
 *
 * Three signals, not one: the ring goes gold, the ink goes bright, and the
 * open circle becomes a tick. The visually-hidden .basket__state text changes
 * from "not yet gathered" to "gathered" in the same moment, so a screen reader
 * is told in words what the colour says to everyone else.
 */
.basket__slot.is-gathered {
  background: var(--slot-full);
  border-color: var(--ok);
  color: #eafff1;
  font-weight: 700;
}

/*
 * The open circle on a slot that is still empty.
 *
 * --text-dim, NOT --text-faint, and theme.css says why in its own comment:
 * --text-faint is decorative only, and this circle is not decoration. It is
 * one of the three signals that say "not gathered yet", so a visitor has to be
 * able to see it. It measured 4.37:1 as --text-faint, against AA's 4.5:1.
 */
.basket__mark {
  font-size: 0.9em;
  line-height: 1;
  color: var(--text-dim);
}

.basket__slot.is-gathered .basket__mark { color: var(--ok); }

/* -------------------------------------------------------------------------- */
/* 3. The field and the falling tiles                                         */
/* -------------------------------------------------------------------------- */

/*
 * The field fills the one flexible row and clips whatever leaves it.
 *
 * position: relative because every tile is absolutely positioned inside it,
 * and overflow: hidden because a tile that has fallen past the bottom must not
 * be able to lengthen the page — the layout contract says this page never
 * scrolls, and a stray tile at y = 900 would break that silently.
 */
.field {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  height: 100%;
  overflow: hidden;
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  background:
    linear-gradient(180deg, rgba(255, 232, 176, 0.05), rgba(8, 6, 26, 0.35));
}

/*
 * A falling word.
 *
 * Identical for a fruit and for a work of the flesh. See the rule at the top
 * of this file — this is the line that keeps the game about reading.
 */
.tile {
  position: absolute;
  top: 0;
  left: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: var(--tile-h);
  padding: 0 10px;
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  background: linear-gradient(160deg, var(--panel-1), var(--panel-2));
  color: var(--gold-1);
  font-family: var(--font-display);
  font-size: var(--tile-font);
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(0, 0, 0, 0.3), 0 6px 14px rgba(3, 2, 12, 0.4);
  -webkit-tap-highlight-color: transparent;
  /* Movement is set from script, frame by frame. Transitions here would fight
     it and make the fall stutter. */
  transition: none;
}

/*
 * The keyboard number.
 *
 * Visible, not a hidden shortcut. A child playing on a keyboard needs to know
 * WHICH key catches which word, and a game that requires you to guess is not
 * keyboard-accessible just because the keys are bound.
 */
.tile__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25em;
  height: 1.25em;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.14);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.72em;
  font-weight: 700;
}

/* ---- The moment after a catch ------------------------------------------- */

/*
 * Now — and only now — the tile says what it was. The player has already
 * decided, so this teaches rather than gives away.
 *
 * A ring AND a glyph, never a colour on its own.
 */
.tile.is-right {
  border-color: var(--ok);
  background: linear-gradient(160deg, #2f6d4b, #1b4630);
  color: #eafff1;
  box-shadow: 0 0 0 3px var(--ok);
}

.tile.is-wrong {
  border-color: var(--warn);
  background: linear-gradient(160deg, #7a3520, #4a1d10);
  color: #ffe6da;
  box-shadow: 0 0 0 3px var(--warn);
}

/* Already in the basket: not a mistake, so it is neither green nor orange. */
.tile.is-again {
  border-color: var(--gold-2);
  box-shadow: 0 0 0 3px var(--gold-glow);
}

.tile:active { transform: scale(0.94); }

/* -------------------------------------------------------------------------- */
/* 4. The flash line                                                          */
/* -------------------------------------------------------------------------- */

/*
 * What the word you just caught means.
 *
 * A FIXED height, and that is the important part. This line changes several
 * times a round, and if it grew from one line to two the field above it would
 * jump upwards mid-fall and move every tile a child was aiming at. Two lines'
 * worth of room, always, whether or not there is anything in it.
 */
.flash {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 3.1em;
  margin: 0;
  padding: 0 10px;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--panel-edge);
  background: rgba(8, 6, 26, 0.55);
  text-align: center;
  font-size: clamp(0.76rem, 2.9vw, 0.92rem);
  line-height: 1.35;
  color: var(--text);
  transition: border-color var(--dur) ease;
}

.flash.is-right { border-color: rgba(134, 230, 168, 0.55); }
.flash.is-wrong { border-color: rgba(255, 156, 110, 0.55); }
.flash.is-note  { border-color: rgba(255, 204, 51, 0.45); }

.flash__mark {
  flex: 0 0 auto;
  font-size: 1.15em;
  font-weight: 800;
  line-height: 1;
}

.flash.is-right .flash__mark { color: var(--ok); }
.flash.is-wrong .flash__mark { color: var(--warn); }
.flash.is-note  .flash__mark { color: var(--gold-2); }

/* The word itself, picked out of the sentence so the eye finds it first. */
.flash__word {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--gold-1);
}

/* -------------------------------------------------------------------------- */
/* 5. The tally on the score card                                             */
/* -------------------------------------------------------------------------- */

/*
 * The nine, with what each one means, at the end of every round.
 *
 * This is the only moment in the game when the player is not busy, so it is
 * where the teaching actually lands. A catcher cannot stop to explain the way
 * a quiz can, so it explains here instead.
 */
.tally {
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
  text-align: left;
}

.tally__item {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 4px 0;
  border-top: 1px solid rgba(255, 214, 130, 0.14);
  font-size: 0.84rem;
  line-height: 1.35;
}

.tally__item:first-child { border-top: 0; }

.tally__mark {
  flex: 0 0 auto;
  width: 1em;
  font-weight: 800;
}

.tally__item.is-got  .tally__mark { color: var(--ok); }

/* --text-dim for the same reason as .basket__mark above: this circle says
   "you did not get this one", which is the row most worth reading. */
.tally__item.is-lost .tally__mark { color: var(--text-dim); }

.tally__word {
  flex: 0 0 auto;
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--gold-1);
}

/* Missed ones are quieter, but never hidden — the one you did not get is the
   one worth reading. */
.tally__item.is-lost .tally__word { color: var(--text-dim); }

.tally__means {
  flex: 1 1 auto;
  color: var(--text-dim);
}

/* -------------------------------------------------------------------------- */
/* 6. Responsive                                                              */
/* -------------------------------------------------------------------------- */

@media (hover: hover) {
  .tile:hover {
    border-color: rgba(255, 214, 130, 0.8);
    box-shadow: 0 3px 0 rgba(0, 0, 0, 0.3), 0 8px 18px rgba(3, 2, 12, 0.5);
  }
}

@media (max-width: 420px) {
  :root {
    --tile-h: 36px;
  }

  .basket { gap: 3px; }

  .basket__slot { padding: 2px 6px; }

  .flash { height: 3.4em; padding: 0 8px; }
}

/*
 * Phone landscape.
 *
 * shell.css hides .hud, .hint and .progress here, because most games do not
 * need them and height is what there is least of. This game DOES need the
 * clock — it is a timed round, and a countdown a player cannot see is not a
 * countdown — so the progress bar comes back. It is six pixels tall.
 *
 * The basket goes instead: the nine names are also on the score card, and
 * losing them for sixty seconds costs less than losing the clock.
 */
@media (max-height: 460px) and (orientation: landscape) {
  .progress { display: block; }

  .basket { display: none; }

  .flash { height: 2.4em; font-size: 0.76rem; }

  :root { --tile-h: 32px; }
}

/*
 * Below 300px the shell stops being a fixed-height column and the page
 * scrolls (Standards.md §5). A field with no height cannot have anything fall
 * through it, so it is given a floor here rather than collapsing to nothing.
 */
@media (max-height: 299px) {
  .field { height: 220px; min-height: 220px; }

  .flash { height: auto; min-height: 3.1em; }
}

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

/*
 * Reduced motion.
 *
 * Falling IS the motion here, so there is no transition to switch off — the
 * whole mechanic is movement. Removing the feature would be a different kind
 * of harm, so Scripts.js plays a STILL round instead: the same words from the
 * same two verses appear in fixed slots and are replaced every couple of
 * seconds, and everything else about the game is identical.
 *
 * BG.prefersReducedMotion() is what chooses, and it is re-read at the start of
 * every round, so switching the setting on mid-game takes effect on the next
 * one.
 */
@media (prefers-reduced-motion: reduce) {
  .tile:active { transform: none; }
}

/* The still layout. Scripts.js puts this class on the field. */
.field.is-still {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px;
}

.field.is-still .tile {
  position: static;
  width: 100%;
  max-width: 22rem;
  justify-content: center;
}

/*
 * Windows High Contrast. The system throws the palette away, which would make
 * a gathered basket slot identical to an empty one and a caught tile identical
 * to an uncaught one. The glyphs survive — they are text — and these bring the
 * borders back.
 */
@media (forced-colors: active) {
  .field,
  .tile,
  .flash,
  .basket__slot {
    border: 1px solid CanvasText;
  }

  .basket__slot.is-gathered { outline: 3px solid Highlight; outline-offset: 1px; }

  .tile.is-right { outline: 4px solid Highlight; outline-offset: 2px; }
  .tile.is-wrong { outline: 4px dotted CanvasText; outline-offset: 2px; }

  .tally__item.is-got .tally__mark,
  .tally__item.is-lost .tally__mark { color: CanvasText; }
}

@media (prefers-contrast: more) {
  .basket__slot { color: var(--text); }

  .tally__means { color: var(--text); }
}

@media print {
  .field,
  .flash {
    display: none !important;
  }

  .basket__slot { color: #000; background: none; }
}
