/* Crossword mode styles. Loaded after style.css and built on its tokens
   (slot colours, radii, the pop and feedback keyframes), so the board
   inherits every theme and accessibility setting the main game has —
   including the over-a-scene readability backing, which arrives through
   the same --slot-bg/--panel-bg tokens the main board uses. Linked from
   index.html (the crossword screen) and from the crossword-preview
   harness. Sections are append-only, as in style.css. */

:root {
  /* Cells are sized to fit: never wider than the board's share of the
     viewport, never taller than its share of the height, and capped so a
     tiny grid does not become a wall of slabs. The board therefore never
     scrolls sideways, whatever the grid's shape. */
  --crossword-cell-max: 52px;
  --crossword-cell-gap: 4px;
  --crossword-board-max-width: min(100vw - 32px, 560px);
  --crossword-board-max-height: min(38vh, 380px);
}

.crossword-board {
  display: flex;
  justify-content: center;
  width: 100%;
  flex-shrink: 0;
}

.crossword-grid {
  --crossword-gap-width: calc((var(--crossword-columns) - 1) * var(--crossword-cell-gap));
  --crossword-gap-height: calc((var(--crossword-rows) - 1) * var(--crossword-cell-gap));
  --crossword-cell-size: min(
    var(--crossword-cell-max),
    calc((var(--crossword-board-max-width) - var(--crossword-gap-width)) / var(--crossword-columns)),
    calc((var(--crossword-board-max-height) - var(--crossword-gap-height)) / var(--crossword-rows))
  );

  display: grid;
  grid-template-columns: repeat(var(--crossword-columns), var(--crossword-cell-size));
  grid-auto-rows: var(--crossword-cell-size);
  gap: var(--crossword-cell-gap);
}

/* Cells a word runs through look like the main game's word slots; the rest
   of the grid stays invisible, so the crossword's shape reads at a glance. */
.crossword-cell.slot {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--slot-border);
  border-radius: calc(var(--crossword-cell-size) * 0.19);
  background: var(--slot-bg);
  font-size: calc(var(--crossword-cell-size) * 0.48);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--text);
}

.crossword-cell.slot.filled {
  background: var(--slot-found-bg);
  border-color: var(--slot-found-bd);
  animation: pop 0.22s ease forwards;
}

:root[data-reduced-animation] .crossword-cell.slot.filled {
  animation: none;
}

/* ─── The crossword screen's chrome (appended section) ────────
   The crossword screen in index.html mirrors the game screen's furniture
   — header, traced-word readout, wheel, win overlay — but the originals
   are styled by id in style.css and ids cannot repeat, so the crossword
   screen carries class twins of those rules. Each rule notes the
   style.css counterpart it mirrors; if one changes, change the twin. */

/* Mirrors #header. */
.crossword-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  flex-shrink: 0;
}

/* Mirrors #level-indicator. */
.crossword-level-indicator {
  margin-right: auto;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.04em;
}

/* Mirrors #bonus-count at rest — a plain readout, because the crossword
   mode has no found-word round-up for the counter to open. */
.crossword-bonus-count {
  min-width: 80px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--accent-gold);
  text-align: right;
}

/* Mirrors #current-word, feedback flashes included — the fixed height that
   keeps this screen's wheel still while a word is traced very much
   included. See the twin in style.css for why it is a height and not a
   min-height. */
.crossword-current-word {
  font-size: clamp(1.5rem, 4.4vmin, 2.4rem);
  font-weight: 800;
  letter-spacing: 0.14em;
  text-indent: 0.14em;
  line-height: 1.15;
  height: 1.3em;
  display: flex;
  align-items: center;
  justify-content: center;
  text-transform: uppercase;
  flex-shrink: 0;
  color: var(--text);
}

.crossword-current-word.feedback-valid {
  animation: flash-colour 0.4s ease;
  --flash: var(--valid);
}

.crossword-current-word.feedback-invalid {
  animation: flash-colour 0.4s ease;
  --flash: var(--invalid);
}

.crossword-current-word.feedback-duplicate {
  animation: flash-colour 0.4s ease;
  --flash: var(--duplicate);
}

:root[data-large-letters] .crossword-current-word {
  font-size: clamp(1.9rem, 5.6vmin, 3rem);
}

/* Mirror #wheel-wrap and #wheel. */
.crossword-wheel-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 0;
}

#crossword-wheel {
  display: block;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* ─── The crossword win overlay (appended section) ────────────
   Mirrors #win-overlay/#win-card/#win-icon/#win-btn, with the token
   fix-ups (--entry-rule border, --on-accent button text) folded in. */

.crossword-win-overlay {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  animation: fade-in 0.3s ease;
}

.crossword-win-overlay.hidden {
  display: none;
}

.crossword-win-card {
  background: var(--surface);
  border: 1px solid var(--entry-rule);
  border-radius: 24px;
  padding: 44px 48px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  animation: slide-up 0.35s ease;
}

.crossword-win-icon {
  font-size: 2.4rem;
  color: var(--accent-gold);
  margin-bottom: 4px;
}

.crossword-win-card h2 {
  font-size: 1.8rem;
  font-weight: 800;
  color: var(--accent);
}

.crossword-win-card p {
  font-size: 0.95rem;
  color: var(--text-muted);
  min-height: 1.2em;
}

.crossword-win-btn {
  margin-top: 12px;
  padding: 14px 44px;
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: 50px;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  transition: opacity 0.15s, transform 0.1s;
}

.crossword-win-btn:active {
  opacity: 0.85;
  transform: scale(0.97);
}

:root[data-reduced-animation] .crossword-win-overlay,
:root[data-reduced-animation] .crossword-win-card {
  animation: none;
}

/* Mirrors #win-card #round-up-hint / #win-round-up — the crossword win
   card's found-word round-up (built by definitions.js's buildRoundUp,
   the same helper the classic card uses). .word-chip itself is a shared
   class in style.css, so it needs no crossword twin. */
.crossword-win-card #crossword-round-up-hint {
  margin-top: var(--gap-tight);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

#crossword-win-round-up {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--gap-tight);
  max-width: 320px;
  max-height: 30vh;
  overflow-y: auto;
}

/* ─── Pack background: the garden (appended section) ──────────
   The Gardening pack's animated scene (packBackground.js) renders on a
   canvas in #garden-background, a fixed layer behind the app content —
   the same layering as #pond-background, and #app's z-index in style.css
   already lifts every screen above it. A whisper of a dark scrim settles
   the lawn behind the board; readability comes from the same near-solid
   slot and panel backings the pond relies on. */

#garden-background {
  position: fixed;
  inset: 0;
  z-index: 0;
}

#garden-background canvas {
  display: block;
}

#garden-background::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--garden-scrim, rgba(15, 25, 35, 0.14));
}

/* ─── Pack background: the study (appended section) ──────────
   The Detective pack's animated scene (studyBackground.js) renders on a
   canvas in #study-background, layered exactly like #garden-background.
   The desk is already dark, so its scrim is a whisper lighter than the
   garden's — readability comes from the same near-solid slot and panel
   backings the other scenes rely on. */

#study-background {
  position: fixed;
  inset: 0;
  z-index: 0;
}

#study-background canvas {
  display: block;
}

#study-background::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--study-scrim, rgba(10, 12, 18, 0.10));
}

/* ─── Pack background: the sewing table (appended section) ────
   The Embroidery pack's scene (sewing.js via packBackground.js), on the
   same fixed layer as the garden. The linen tabletop is brighter than the
   lawn, so its scrim is a touch lighter still. */

#sewing-background {
  position: fixed;
  inset: 0;
  z-index: 0;
}

#sewing-background canvas {
  display: block;
}

#sewing-background::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--sewing-scrim, rgba(35, 28, 18, 0.10));
}

/* ─── Pack background: the castle (appended section) ──────────
   The Medieval pack's scene (castle.js via packBackground.js), on the
   same fixed layer as the garden. The wall is already dusk-lit and the
   torches are the brightest thing on it, so its scrim is the lightest of
   the five — enough to settle the flagstones without dulling the flames. */

#castle-background {
  position: fixed;
  inset: 0;
  z-index: 0;
}

#castle-background canvas {
  display: block;
}

#castle-background::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--castle-scrim, rgba(12, 16, 26, 0.08));
}

/* ─── Pack background: the temple courtyard (appended section) ──
   The Classical pack's scene (temple.js via packBackground.js), on the
   same fixed layer as the garden. Sunlit plaster is the brightest ground
   of the five, so its scrim is the warmest, and as heavy as the
   garden's. */

#temple-background {
  position: fixed;
  inset: 0;
  z-index: 0;
}

#temple-background canvas {
  display: block;
}

#temple-background::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--temple-scrim, rgba(46, 33, 14, 0.14));
}

/* ─── Feature parity with the main mode (appended section) ────
   Hints, the definitions panel behind found words, and the bonus-word
   list arrived on the crossword screen. As with the chrome section
   above, these are class/id twins of style.css rules — each notes its
   counterpart; if one changes, change the twin. The earlier
   .crossword-bonus-count comment ("a plain readout") predates this
   section: the counter is a button now and opens the bonus list, so it
   takes the same control states as the main counter. */

/* Mirrors #hint-btn. */
#crossword-hint-btn {
  padding: 4px 12px;
  border: 1px solid var(--chip-border);
  border-radius: var(--radius-pill);
  background: var(--chip-bg);
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-soft);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s, opacity 0.15s;
}

:where(:root:not([data-pointer-idle])) #crossword-hint-btn:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}

#crossword-hint-btn:active:not(:disabled) {
  transform: scale(0.96);
}

#crossword-hint-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Button-element resets for the counter (it was a span when the rule
   above was written), mirroring #bonus-count at rest. */
#crossword-bonus-count {
  padding: 0;
  border: 1px solid transparent;
  border-radius: 50px;
  background: none;
  font-family: inherit;
}

/* Mirrors #bonus-count.tappable and its states. */
#crossword-bonus-count.tappable {
  padding: 4px 11px;
  border-color: var(--bonus-border);
  background: var(--bonus-bg);
  text-align: center;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

#crossword-bonus-count.tappable::after {
  content: ' ›';
  font-weight: 700;
}

:where(:root:not([data-pointer-idle])) #crossword-bonus-count.tappable:hover {
  border-color: var(--accent-gold);
  background: var(--accent-gold);
  color: var(--bg);
}

#crossword-bonus-count.tappable:active {
  transform: scale(0.96);
}

/* Mirrors #bonus-count.just-found (keyframes live in style.css). */
#crossword-bonus-count.just-found {
  animation: bonus-arrive 0.85s ease;
}

/* Mirrors .letter-box.hinted: a hint-revealed letter takes the gold
   "given" palette, unmistakably not an earned fill. */
.crossword-cell.slot.hinted {
  background: var(--bonus-bg);
  border-color: var(--bonus-border);
  color: var(--accent-gold);
  animation: hint-appear 0.35s ease;
}

/* A found word is a tap target for its dictionary entry; tapping any of
   its cells flashes the whole word as the panel opens. */
.crossword-cell.slot.has-entry {
  cursor: pointer;
}

.crossword-cell.slot.word-highlight {
  border-color: var(--accent);
}

:root[data-reduced-animation] .crossword-cell.slot.hinted,
:root[data-reduced-animation] #crossword-bonus-count.just-found {
  animation: none;
}

/* Mirrors the over-a-scene washes style.css lays over #level-indicator,
   #hint-btn, #bonus-count, #current-word and .letter-box — the crossword
   screen's furniture is a set of class/id twins, so its readability has to
   be a twin too. One signal for every kind of background art: data-scene,
   stamped by pondBackground.js whether the art is a global scene the
   player chose or the pack's own (see the panel-backing section comment in
   style.css). --bg-backing and the halo come from there, per theme, so the
   light themes back these in light and the dark ones in dark.

   :not(:empty) matches its twin: the indicator carries no text until a
   level opens, and an empty chip would be a bare pill floating on the
   desk. */
:root[data-scene] .crossword-level-indicator:not(:empty) {
  padding: 4px 11px;
  border: 1px solid var(--chip-border);
  border-radius: var(--radius-pill);
  background: var(--bg-backing);
  color: var(--text-soft);
}

:root[data-scene] #crossword-hint-btn {
  background-color: var(--bg-backing);
  background-image: linear-gradient(var(--chip-bg), var(--chip-bg));
}

/* The counter carries no text until the first bonus word lands, and
   landing one is what makes it .tappable (definitions.js), so the pour on
   the control is the whole of its readability — there is no un-backed
   state with anything in it to read. */
:root[data-scene] #crossword-bonus-count.tappable {
  background-color: var(--bg-backing);
  background-image: linear-gradient(var(--bonus-bg), var(--bonus-bg));
}

/* Mirrors #current-word:not(:empty) — a pill that hugs the traced word,
   and nothing at all while the readout is empty. */
:root[data-scene] .crossword-current-word:not(:empty) {
  align-self: center;
  width: fit-content;
  min-width: 3.4em;
  padding-inline: 0.7em;
  border-radius: var(--radius-pill);
  background: var(--bg-backing);
}

/* A cell only needs to be opaque once it carries a letter; an empty cell
   keeps its translucent ground so the scene still shows through the board's
   shape. Both filled states replace the idle ground outright, so each
   re-lays its own tint over the pour. */
:root[data-scene] .crossword-cell.slot.filled {
  background-color: var(--bg-backing);
  background-image: linear-gradient(var(--slot-found-bg), var(--slot-found-bg));
}

:root[data-scene] .crossword-cell.slot.hinted {
  background-color: var(--bg-backing);
  background-image: linear-gradient(var(--bonus-bg), var(--bonus-bg));
}

/* ─── Pack-completion celebration (appended section) ────────────
   Shown as a step after the ordinary Level Complete card, only on the
   transition into a pack being fully done (see isPackNewlyCompletedBy in
   packs.js and the win flow in crosswordMode.js). Deliberately calmer than
   the win card it follows: one soft radial bloom behind the card, and a
   gentle glow breathing on the card's own icon — no confetti, no motion
   the reduced-animation setting can't switch off. */

.pack-complete-overlay {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 110;
  animation: fade-in 0.4s ease;
}

.pack-complete-overlay.hidden {
  display: none;
}

.pack-complete-glow {
  position: absolute;
  inset: 0;
  margin: auto;
  width: min(90vw, 560px);
  height: min(90vw, 560px);
  background: radial-gradient(circle, var(--bonus-bg) 0%, transparent 70%);
  pointer-events: none;
  animation: pack-complete-bloom 3.6s ease-in-out infinite alternate;
}

.pack-complete-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--accent-gold);
  border-radius: 24px;
  padding: 44px 48px;
  max-width: min(90vw, 420px);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  animation: slide-up 0.4s ease;
}

.pack-complete-icon {
  font-size: 2.6rem;
  color: var(--accent-gold);
  margin-bottom: 4px;
  animation: pack-complete-glow-pulse 2.4s ease-in-out infinite alternate;
}

.pack-complete-card h2 {
  font-size: 1.6rem;
  font-weight: 800;
  color: var(--accent-gold);
}

.pack-complete-message {
  font-size: 0.95rem;
  color: var(--text-soft);
  min-height: 1.2em;
}

.pack-complete-btn {
  margin-top: 12px;
  padding: 14px 44px;
  background: var(--accent-gold);
  color: var(--on-accent);
  border: none;
  border-radius: 50px;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  transition: opacity 0.15s, transform 0.1s;
}

.pack-complete-btn:active {
  opacity: 0.85;
  transform: scale(0.97);
}

@keyframes pack-complete-bloom {
  from { opacity: 0.45; transform: scale(0.92); }
  to   { opacity: 0.85; transform: scale(1.04); }
}

@keyframes pack-complete-glow-pulse {
  from { text-shadow: 0 0 6px transparent; }
  to   { text-shadow: 0 0 18px var(--accent-gold); }
}

:root[data-reduced-animation] .pack-complete-overlay,
:root[data-reduced-animation] .pack-complete-card,
:root[data-reduced-animation] .pack-complete-glow,
:root[data-reduced-animation] .pack-complete-icon {
  animation: none;
}

/* The glow is a purely decorative bloom; keep it visible but settled when
   reduced animation is on, rather than mid-pulse. */
:root[data-reduced-animation] .pack-complete-glow {
  opacity: 0.6;
}

/* ─── Higher contrast over a scene (appended section) ──────────
   Twin of the section of the same name at the end of style.css, which
   carries the reasoning: under higher contrast the scene keeps running
   and every surface on it goes fully opaque instead of being veiled.

   Almost all of it reaches this file for free, because the crossword
   screen's furniture draws on the same tokens — an empty cell takes the
   opaque --slot-bg, a filled or hinted one re-lays its tint over the now
   opaque --bg-backing, the level indicator and hint chip take the same
   pour, and the win card was already --surface. What is left is the two
   places the crossword screen states a value of its own. */

/* Mirrors #current-word:not(:empty): the one backed element in the header
   row without an outline of its own takes the chip border. */
:root[data-scene][data-high-contrast] .crossword-current-word:not(:empty) {
  border: 1px solid var(--chip-border);
}

/* Mirrors #hint-btn:disabled — dimming the whole control would fade its
   backing and let the scene back through it. */
:root[data-scene][data-high-contrast] #crossword-hint-btn:disabled {
  opacity: 1;
  color: var(--text-muted);
}


/* ─── The level number is a label (appended section) ───────────
   Mirrors the section of the same name in style.css. The number sat in the
   header wearing the same pill outline as the hint button beside it and
   read as a control that did nothing when tapped; here it becomes a quiet
   uppercase caption on a plate. This file is loaded after style.css, so
   these twins are what actually decide it for the crossword screen — the
   rules above that they replace stay where they are, in the sections they
   belong to. */

.crossword-level-indicator {
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

:root[data-scene] .crossword-level-indicator:not(:empty) {
  padding: 3px 8px;
  border: 0;
  border-radius: var(--radius-xs);
  background: var(--bg-backing);
  color: var(--text-soft);
}

/* ─── The bonus counter's hover fill over a scene (appended section) ──
   Twin of the section of the same name at the end of style.css, which
   carries the reasoning: the scene pour out-specifies the counter's own
   :hover rule and leaves the hover state as dark text on a dark ground. */
:root[data-scene]:not([data-pointer-idle]) #crossword-bonus-count.tappable:hover {
  background-color: var(--accent-gold);
  background-image: none;
}
