/* Vega Bell - the Constellation Run playfield. */

[data-screen="game"] { max-width: 720px; }

.game-loading { text-align: center; padding: var(--sp-8) 0; color: var(--text-dim); }

/* ---------------------------------------------------------- countdown -- */

.countdown { display: grid; place-items: center; min-height: 50vh; }
.countdown__n {
  font-family: var(--font-display); font-weight: 700; font-size: clamp(4rem, 22vw, 9rem);
  background: var(--grad-hero); -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ---------------------------------------------------------------- hud -- */

.hud {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
  background: var(--surface); border: 1px solid var(--stroke); border-radius: var(--r-lg);
  /* Close to the constellation, because the two are one block of ambient
     status. The seam belongs below them, not between them. */
  margin-bottom: var(--sp-3);
}
.hud__cell { display: flex; align-items: center; gap: 6px; color: var(--text-dim); font-weight: 600; }
.hud__cell--score { flex-direction: column; gap: 0; }
.hud__cell--reson { flex-direction: column; gap: 4px; justify-self: end; }
.hud__score {
  font-family: var(--font-display); font-size: 1.7rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.hud__label { font-size: .68rem; text-transform: uppercase; letter-spacing: .1em; opacity: .7; }
.hud__clock { font-variant-numeric: tabular-nums; font-size: 1.05rem; }
.hud__clock.is-low { color: var(--accent-mag); }
.hud__reson { font-family: var(--font-display); color: var(--accent-cyan); font-size: 1.05rem; }

.pips { display: flex; gap: 3px; }
.pip { width: 9px; height: 9px; border-radius: 50%; background: var(--stroke); transition: background .2s ease, box-shadow .2s ease; }
.pip--on { background: var(--accent-cyan); box-shadow: 0 0 8px rgba(34,211,238,.8); }

/* ------------------------------------------------------ constellation -- */

.constellation {
  display: flex; justify-content: center; gap: 10px;
  /* The hard break between the chrome and the task. */
  margin-bottom: var(--sp-6); flex-wrap: wrap;
}
.star {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--stroke); background: transparent;
  transition: background .3s ease, box-shadow .3s ease, transform .3s ease;
}
.star--big { width: 18px; height: 18px; }
.star--bell { width: 22px; height: 22px; border-color: var(--gold); }
.star--lit { background: var(--gold); border-color: var(--gold); box-shadow: 0 0 12px rgba(251,191,36,.7); }
.star--salvage { background: var(--primary-lite); border-color: var(--primary-lite); }
.star--miss { background: transparent; border-color: var(--stroke); opacity: .5; }

/* ---------------------------------------------------------- playfield -- */

/* TWO spacing levels, not one.
 *
 * Every row of the playfield used to sit 16px from every other row: nine
 * unlike things at one distance, which is exactly the arrangement that reads
 * as cramped, because equidistant items form no groups and the eye gets no
 * seams to rest on. The gap here is now the seam BETWEEN groups; .prompt below
 * carries the tighter gap within one. The screen can well afford it: the whole
 * stack was using under half the height of the tablet it is played on.
 */
.playfield {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-6); width: 100%;
}

/* Everything the player is given before they act: the sound, its status, the
   meaning, and the small print about the word. One group, held together. */
.prompt {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-4); width: 100%;
}

.wordslot { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; }
.wordslot__count {
  font-size: .74rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-dim); font-weight: 700;
}

/* A column, so the note underneath reads as the speaker's own subtitle rather
   than as a caption to whatever else happens to be near it. */
.speakerrow {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
}

/* Why the sound did not play: no recording, a browser that will not play one
   without a tap, or a failure. Matches .tune__audio-note in auth.css, which
   says the same things in The Tuning. min-height reserves the line so the
   definition card below does not jump down when it fills in. */
.speakernote { min-height: 1.2rem; margin: 0; color: var(--text-dim); font-size: .875rem; }
.speaker {
  width: 84px; height: 84px; border-radius: 50%;
  border: 2px solid var(--accent-cyan); background: var(--surface);
  color: var(--accent-cyan); cursor: pointer;
  display: grid; place-items: center;
  transition: transform .15s cubic-bezier(.34,1.56,.64,1), box-shadow .2s ease;
}
.speaker:hover { transform: scale(1.06); box-shadow: var(--shadow-glow); }
.speaker.is-playing { box-shadow: 0 0 0 8px rgba(34,211,238,.15); }

/* The browser refused to play without a tap, so the speaker has to ask for
   one. Static rather than a pulse: reduced motion is a common setting here
   and a nudge that only exists as an animation would not reach those players
   at all. */
.speaker--locked {
  border-color: var(--gold); color: var(--gold);
  box-shadow: 0 0 0 6px rgba(251,191,36,.14);
}

/* ------------------------------------------------------- the definition -- */

/* The definition: free, always on screen, directly under the sound, and now
 * the only thing on the playfield inside a card.
 *
 * Sized like something meant to be read rather than glanced at, for the same
 * reason the bought-clue text was enlarged: this is a listening game played on
 * tablets at arm's length, by children, and the meaning is the thing they
 * reason from when they have only heard a word. It scales with the viewport and
 * settles a step larger, so it is comfortable on a tablet without shouting on a
 * phone.
 *
 * The card's shape follows .verdict and .hud on this same screen: --surface on
 * a stroke at --r-lg. The violet border is --primary-lite at half strength,
 * because cyan is already spoken for here by the speaker, the origin chip and
 * the bought-clue rail, and violet is the meaning colour in The Tuning. The
 * halo is a plain box-shadow in the manner of .board-row--me, NOT the
 * .pulse-glow class: under prefers-reduced-motion that class becomes a four
 * second opacity breathe, which would set the definition quietly throbbing on
 * every word of every run for the players who asked for less movement. A
 * static shadow is fully present for everyone and moves for nobody.
 */
.defcard {
  /* The full column, deliberately: the same left and right edge as .hud above
     and .hintrail below, so the three boxes on this screen line up instead of
     each floating at its own width. No max-width of its own is needed, because
     [data-screen="game"] already caps the column at 720px, which keeps the
     measure around sixty characters. It is also what the card already did at
     phone widths, so the shape no longer changes across breakpoints. */
  width: 100%;
  margin: 0;
  /* Shorter at the top: the label's own line box adds weight there, so equal
     padding would sit the block visually low. */
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
  background: var(--surface);
  border: 1px solid rgba(167, 139, 250, .5);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1), 0 0 24px rgba(167, 139, 250, .28);
  text-align: left;
}

.defcard__label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: clamp(.72rem, .66rem + .28vw, .82rem);
  letter-spacing: .16em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--primary-lite);
}

.worddef {
  margin: 0;
  /* The measure moved to the card. Left-aligned now: centred lines hanging
     under a left-set label read as a mistake, and a ragged right edge is
     easier for a child to track back to the start of the next line. */
  text-align: left;
  color: var(--text);
  line-height: 1.55;
  font-size: clamp(1rem, .93rem + .5vw, 1.25rem);
  text-wrap: pretty;
}

.wordcard { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; }
.wordcard__ipa { font-family: var(--font-mono); color: var(--text-dim); font-size: 1rem; }
.wordcard__len { font-size: .8rem; color: var(--text-dim); opacity: .7; }

/* -------------------------------------------------------------- tiles -- */

.tiles {
  display: flex; gap: 6px; flex-wrap: wrap; justify-content: center;
  cursor: text; min-height: 60px; transition: opacity .25s ease, filter .25s ease;
}
.tiles.is-over { outline: 2px dashed var(--danger); outline-offset: 6px; border-radius: var(--r-sm); }
.tiles.is-dimmed { opacity: .4; filter: saturate(.3); }

.tile {
  width: 44px; height: 56px; border-radius: var(--r-sm);
  border: 2px solid var(--stroke); background: var(--surface);
  display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 1.5rem; font-weight: 700;
  text-transform: uppercase;
  transition: border-color .18s ease, background .18s ease, color .18s ease;
}
.tile.is-filled { border-color: var(--primary-lite); }
.tile--ok { border-color: var(--success); background: rgba(52,211,153,.16); color: var(--success); }
.tile--near { border-color: var(--primary-lite); background: rgba(167,139,250,.16); color: var(--primary-lite); }
.tile--miss { border-color: var(--stroke); color: var(--text-dim); }

/* The input is the real control; the tiles are its costume.
 *
 * It used to be a 1px by 1px transparent box, absolutely positioned with no
 * positioned ancestor, so it sat somewhere off in the layout with no real
 * dimensions. Chrome copes. Samsung Internet does not: on an Android tablet it
 * parked the caret at 0 with a word already typed, so every new letter went to
 * the FRONT and reordered everything the child had written. A probe on the
 * device read `field: "trade" caret: 0`, and the same word typed fine in Chrome
 * on the same tablet.
 *
 * This is the shape The Tuning has always used (auth.css, `.tune
 * .tiles__input`) and never had the bug: a real, full-size, on-screen element
 * that happens to be invisible, rather than a degenerate one. Invisibility
 * comes from transparent colour, not zero size, so the browser still has a
 * proper box to put a caret and its predictions in. 1rem stops iOS zooming.
 */
.tiles-host { position: relative; width: 100%; display: flex; justify-content: center; }
.tiles__input {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  color: transparent;
  caret-color: transparent;
  font-size: 1rem;
  text-align: center;
  cursor: text;
}
.tiles__input:focus { outline: none; }

/* -------------------------------------------------------------- hints -- */

.hintrail { width: 100%; }
.hintrail__title {
  font-size: .74rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-dim); text-align: center; margin: 0 0 var(--sp-2);
}
/* Equal tracks, so four clue buttons read as one tidy block instead of the
   centred 2 + 1 + 1 ladder a wrapping flex row produces. */
.hintrail__row {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  max-width: 460px;
  margin: 0 auto;
}
.hintbtn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-width: 0;
  min-height: var(--tap); padding: 10px 14px; border-radius: var(--r-pill);
  background: var(--surface); border: 1px solid var(--stroke); color: var(--text-dim);
  cursor: pointer; font-size: .86rem; font-weight: 600;
}
.hintbtn:hover:not(:disabled) { border-color: var(--primary-lite); color: var(--text); }
.hintbtn__cost { font-size: .72rem; opacity: .75; color: var(--accent-mag); }
.hintbtn.is-bought { opacity: .45; cursor: default; }

.hintreveal { width: 100%; display: flex; flex-direction: column; gap: var(--sp-2); }
/* It lives in the DOM from the start and holds nothing until a clue is bought,
   so as a flex item it was burning a gap above AND below itself for an empty
   box. At the wider playfield gap that is a hole in the middle of the screen. */
.hintreveal:empty { display: none; }
.hintreveal__item {
  background: var(--surface); border: 1px solid var(--stroke);
  border-left: 3px solid var(--accent-cyan);
  border-radius: var(--r-sm); padding: var(--sp-3);
}
/* A bought clue is read, not glanced at, and it is read at tablet arm's length
   rather than phone distance. The label was .68rem and the text had no size at
   all, so it inherited the 1rem base and stayed there at every width: the
   layout grew with the viewport and the copy did not. Both now scale with the
   viewport and settle a step larger, so a definition is comfortable to read on
   a tablet without shouting on a phone. */
.hintreveal__label {
  display: block; font-size: clamp(.72rem, .66rem + .28vw, .82rem);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 5px;
}
.hintreveal__text {
  color: var(--text); line-height: 1.55;
  font-size: clamp(1rem, .93rem + .5vw, 1.25rem);
}

/* ------------------------------------------------------------ verdict -- */

.verdict {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%;
  background: var(--surface); border: 1px solid var(--stroke);
  border-radius: var(--r-lg); padding: var(--sp-4);
}
.verdict--correct { border-color: var(--success); }
.verdict--salvage { border-color: var(--primary-lite); }
.verdict--miss { border-color: var(--stroke); }
.verdict__body { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.verdict__head { font-family: var(--font-display); font-size: 1.15rem; }
.verdict__spelling {
  font-family: var(--font-mono); font-size: 1.25rem; letter-spacing: .06em;
  color: var(--gold); text-transform: lowercase;
}
.verdict__points {
  font-family: var(--font-display); font-size: 1.6rem; font-weight: 700;
  color: var(--gold); font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------- actions -- */

.actions { display: flex; flex-direction: column; gap: var(--sp-2); width: 100%; max-width: 420px; }

/* ---------------------------------------------------------- bell word -- */

[data-screen="game"].is-bellword .hud,
[data-screen="game"].is-bellword .constellation { opacity: .35; transition: opacity .7s ease; }
[data-screen="game"].is-bellword .tile { border-color: var(--gold); }
[data-screen="game"].is-bellword { background: radial-gradient(ellipse at 50% 30%, rgba(251,191,36,.10), transparent 60%); }

/* ------------------------------------------------------------- the ring -- */

.ring {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-4);
  padding: var(--sp-6) 0; text-align: center;
}
.ring__reson {
  font-family: var(--font-display); letter-spacing: .1em; font-size: .9rem;
  color: var(--accent-cyan);
}
.ring__bell { filter: drop-shadow(0 0 28px rgba(251,191,36,.45)); }
.ring__bell.is-cracked { filter: grayscale(.55) drop-shadow(0 0 10px rgba(59,52,128,.5)); }
.ring.is-ringing .ring__bell { animation: bell-sway 900ms cubic-bezier(.3,0,.2,1); }
.ring__bonus {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(2.4rem, 12vw, 4.2rem);
  background: var(--grad-gold); -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums; min-height: 1.2em;
}

@media (max-width: 640px) {
  .tile { width: 36px; height: 48px; font-size: 1.2rem; }
  .speaker { width: 72px; height: 72px; }
  .hud { padding: var(--sp-2) var(--sp-3); }
  .hud__score { font-size: 1.4rem; }
  .defcard { padding: var(--sp-3); border-radius: var(--r-md); }
}

/* SHORT window, which is a different question from a narrow one.
 *
 * The seams above are paid for out of vertical room, and a laptop window or a
 * phone in portrait does not have it: at 900px tall the whole stack ends 37px
 * past the bottom, which puts SPELL IT off screen for the sake of whitespace.
 * So the rhythm is kept in proportion but tightened wherever the room is not
 * there. The tablet this is played on is 2000px tall and never sees this rule.
 *
 * Deliberately a height query rather than more width breakpoints: what runs
 * out here is height, and a wide-but-short laptop window needs the tightening
 * exactly as much as a phone does.
 */
@media (max-height: 1000px) {
  .hud { margin-bottom: var(--sp-2); }
  .constellation { margin-bottom: var(--sp-5); }
  .playfield { gap: var(--sp-5); }
  .prompt { gap: var(--sp-3); }
  .speakerrow { gap: var(--sp-1); }
}
@media (max-width: 380px) {
  .tile { width: 30px; height: 42px; font-size: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .ring.is-ringing .ring__bell { animation: none; }
  .tiles, .tile, .star, .pip { transition: none; }
}
