/* ═══════════════════════════════════════════════════════════════════
   Crossword — the playable crossing grid (D78)
   /games/crossword · /chs/games/crossword

   Hero: bark → amber → gold (#7c2d12 → #b45309 → #f59e0b). Deliberately
   distinct from Word Search's forest-lime, Cloze's violet, Sentence
   Dictation's cyan, Grammar Gauntlet's cyan-slate, Look-Alike's teal, Tone
   Pair Arcade's rose and Character Builder's indigo — every game hero reads
   as its own place.

   ONE STYLESHEET FOR BOTH PAGES. The Chinese-UI twin is the same board with a
   different bank; a second stylesheet is how the two would start drifting.
   Everything colour-neutral uses Bootstrap's own CSS variables so the page
   follows the site's dark mode without a second palette to keep in step.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Hero ────────────────────────────────────────────────────────── */
.cw-hero {
    background: linear-gradient(135deg, #7c2d12 0%, #b45309 55%, #f59e0b 100%);
    border-radius: 0.5rem;
}

/* ── HUD ─────────────────────────────────────────────────────────── */
.cw-hud {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin-bottom: 0.5rem;
}

.cw-hud-item {
    font-weight: 700;
    font-size: 1.05rem;
    line-height: 1.2;
    white-space: nowrap;
}

.cw-hud-item small {
    display: block;
    font-weight: 400;
    font-size: 0.7rem;
    color: var(--bs-secondary-color);
}

.cw-combo-badge {
    display: inline-block;
    min-width: 2.2rem;
    padding: 0 0.35rem;
    border-radius: 0.4rem;
    background: #b45309;
    color: #fff;
}

.cw-combo-t2 { background: #d97706; }
.cw-combo-t3 { background: #ea580c; }
.cw-combo-t4 { background: #dc2626; }
.cw-combo-t5 { background: #a21caf; }

.cw-timer {
    height: 8px;
    border-radius: 4px;
    background: var(--bs-tertiary-bg);
    overflow: hidden;
    margin-bottom: 0.75rem;
}

.cw-timer-fill {
    height: 100%;
    width: 100%;
    background: #d97706;
    transition: width 0.2s linear;
}

.cw-timer-fill.cw-timer-low {
    background: #dc2626;
}

/* ── The stage ───────────────────────────────────────────────────── */
/* Tall enough that the start overlay is not cramped before the first grid exists — the board,
   the keypad and the clue columns are all inside the stage, so the overlay has to cover them. */
.cw-stage {
    position: relative;
    min-height: 30rem;
}

/* ── The current clue, above the grid ────────────────────────────── */
/* The one line a solver reads between every keystroke, so it is pinned above the board rather
   than left in the clue column where a phone would have it off-screen. */
.cw-current {
    min-height: 2.6rem;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 0.4rem;
    padding: 0.4rem 0.6rem;
    margin-bottom: 0.6rem;
    border-radius: 0.4rem;
    background: var(--bs-tertiary-bg);
    font-weight: 600;
    font-size: 0.95rem;
}

/* ── The board ───────────────────────────────────────────────────── */
.cw-board {
    display: grid;
    grid-template-columns: repeat(var(--cw-cols, 12), minmax(0, 1fr));
    gap: 2px;
    max-width: 34rem;
    margin: 0 auto;
    /* The grid is tapped, never dragged, but a long-press on a cell would otherwise raise the
       text-selection callout on touch. */
    -webkit-user-select: none;
    user-select: none;
}

/* A cell that is not part of any answer. Rendered rather than skipped so the grid keeps its
   shape; inert, unlabelled and invisible to assistive technology. */
.cw-void {
    aspect-ratio: 1 / 1;
}

.cw-cell {
    /* A real <button>, so the grid is reachable and operable from a keyboard. */
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.2rem;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-weight: 700;
    font-size: clamp(0.65rem, 2.5vw, 1.05rem);
    line-height: 1;
    text-transform: uppercase;
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.cw-num {
    position: absolute;
    top: 1px;
    left: 2px;
    font-size: clamp(0.35rem, 1.1vw, 0.55rem);
    font-weight: 600;
    line-height: 1;
    color: var(--bs-secondary-color);
    pointer-events: none;
}

.cw-letter {
    pointer-events: none;
}

/* The answer the caret is in, so a solver can see the run they are filling without counting
   squares. Lighter than the caret itself, which has to stay findable inside it. */
.cw-cell-active {
    background: rgba(245, 158, 11, 0.18);
    border-color: #d97706;
}

.cw-cell-focus {
    background: rgba(245, 158, 11, 0.45);
    border-color: #b45309;
    box-shadow: inset 0 0 0 2px #b45309;
}

.cw-cell-solved {
    background: #16a34a;
    border-color: #15803d;
    color: #fff;
}

.cw-cell-solved .cw-num {
    color: rgba(255, 255, 255, 0.85);
}

/* Filled in for the player at the end of a run, so the grid finishes on something readable
   rather than on squares they never get to see the answers in. */
.cw-cell-miss {
    background: #fef3c7;
    border-color: #f59e0b;
    color: #92400e;
}

/* ── The on-screen keypad ────────────────────────────────────────── */
/* Not a fallback. On a phone this is the only comfortable way to fill a crossword, and the
   alternative — a hidden input focused behind the grid to raise the soft keyboard — drags the
   viewport around on iOS every time the caret moves. */
.cw-keys {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    margin: 0.9rem auto 0;
    max-width: 34rem;
}

.cw-keys-row {
    display: flex;
    justify-content: center;
    gap: 4px;
    width: 100%;
}

.cw-key {
    appearance: none;
    -webkit-appearance: none;
    flex: 1 1 auto;
    min-width: 0;
    max-width: 2.6rem;
    padding: 0.5rem 0;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.3rem;
    background: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
    font-weight: 700;
    font-size: 0.85rem;
    line-height: 1;
    text-transform: uppercase;
    cursor: pointer;
}

.cw-key:active {
    background: #f59e0b;
    color: #fff;
}

.cw-key-wide {
    max-width: 4.2rem;
    font-size: 0.75rem;
}

/* ── The clue columns ────────────────────────────────────────────── */
.cw-clue-cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1rem;
    margin-top: 1.1rem;
}

.cw-clue-head {
    font-weight: 700;
    font-size: 0.85rem;
    margin-bottom: 0.35rem;
    padding-bottom: 0.2rem;
    border-bottom: 2px solid #f59e0b;
}

.cw-clues {
    list-style: none;
    padding: 0;
    margin: 0;
    max-height: 16rem;
    overflow-y: auto;
}

.cw-clue {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    padding: 0.3rem 0.4rem;
    border-radius: 0.3rem;
    cursor: pointer;
    font-size: 0.85rem;
}

.cw-clue:hover {
    background: var(--bs-tertiary-bg);
}

.cw-clue-current {
    background: rgba(245, 158, 11, 0.22);
}

.cw-clue-num {
    font-weight: 700;
    min-width: 1.3rem;
    text-align: right;
    color: var(--bs-secondary-color);
}

.cw-clue-body {
    flex: 1 1 auto;
    min-width: 0;
}

.cw-clue-main {
    display: block;
}

.cw-clue-sub {
    display: block;
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
}

.cw-clue-solved .cw-clue-main {
    text-decoration: line-through;
    color: var(--bs-secondary-color);
}

.cw-clue-solved .cw-clue-num {
    color: #16a34a;
}

.cw-clue-tools {
    display: flex;
    gap: 0.15rem;
    flex: 0 0 auto;
}

.cw-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.7rem;
    height: 1.7rem;
    border: 0;
    border-radius: 0.3rem;
    background: transparent;
    color: var(--bs-secondary-color);
    text-decoration: none;
}

.cw-icon-btn:hover {
    background: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
}

/* ── Pickers and overlays ────────────────────────────────────────── */
.cw-picker {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
}

.cw-picker-label {
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--bs-secondary-color);
    text-transform: uppercase;
    margin-right: 0.2rem;
}

.cw-pick-btn {
    appearance: none;
    -webkit-appearance: none;
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 2rem;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}

.cw-pick-btn.active {
    background: #b45309;
    border-color: #b45309;
    color: #fff;
}

.cw-btn-primary {
    background: #b45309;
    border-color: #b45309;
    color: #fff;
}

.cw-btn-primary:hover,
.cw-btn-primary:focus {
    background: #92400e;
    border-color: #92400e;
    color: #fff;
}

.cw-overlay {
    position: absolute;
    inset: 0;
    z-index: 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.7rem;
    text-align: center;
    padding: 1rem;
    background: var(--bs-body-bg);
    border-radius: 0.5rem;
}

.cw-big-icon {
    font-size: 2.4rem;
    color: #b45309;
}

.cw-final-score {
    font-size: 2.6rem;
    font-weight: 800;
    line-height: 1;
    color: #b45309;
}

.cw-final-stats {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.9rem;
    font-size: 0.85rem;
    color: var(--bs-secondary-color);
}

.cw-toast {
    position: absolute;
    top: 0.4rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    padding: 0.3rem 0.8rem;
    border-radius: 2rem;
    font-size: 0.85rem;
    font-weight: 600;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
}

.cw-toast-show {
    opacity: 1;
}

.cw-toast-good { background: #16a34a; color: #fff; }
.cw-toast-bad { background: #dc2626; color: #fff; }
.cw-toast-info { background: var(--bs-secondary-bg); color: var(--bs-body-color); }

.cw-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── The worked example, which is the JavaScript-off half of the page ── */
.cw-example-grid {
    display: grid;
    gap: 2px;
    max-width: 26rem;
}

.cw-example-cell {
    position: relative;
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.15rem;
    background: var(--bs-body-bg);
    font-size: clamp(0.5rem, 1.6vw, 0.8rem);
    font-weight: 700;
    text-transform: uppercase;
}

.cw-example-cell.void {
    border-color: transparent;
    background: transparent;
}

.cw-example-num {
    position: absolute;
    top: 0;
    left: 1px;
    font-size: 0.4rem;
    font-weight: 600;
    color: var(--bs-secondary-color);
}

/* ═══════════════════════════════════════════════════════════════════
   EFFECTS

   Every animation below is decoration over a state change the HUD, the clue
   columns and the #cwAnnounce live region have already reported in text. That
   is what makes silencing the whole block under prefers-reduced-motion at the
   bottom of the file correct rather than lossy: nothing here is the only way
   to learn anything about the game.
   ═══════════════════════════════════════════════════════════════════ */

/* The layer the throwaway nodes live on. Inside .cw-stage, which is position:relative, and inert
   to the pointer so it can never swallow a tap on the grid underneath it. */
.cw-effects {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: 3;
}

/* A letter landing. Small on purpose — this fires on every keystroke, so anything larger reads
   as the grid twitching rather than as the square accepting the letter. */
.cw-cell-typed {
    animation: cw-cell-typed 0.16s ease-out;
}

@keyframes cw-cell-typed {
    0% { transform: scale(1); }
    50% { transform: scale(1.16); }
    100% { transform: scale(1); }
}

/* A solved answer lights up one cell at a time along its run. The cells keep their green
   underneath, so it reads as the word being written in rather than as a colour change. */
.cw-cell-pop {
    animation: cw-cell-pop 0.42s ease-out both;
    animation-delay: var(--cw-pop-delay, 0ms);
    z-index: 2;
}

@keyframes cw-cell-pop {
    0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.55); }
    45% { transform: scale(1.3); box-shadow: 0 0 0 0.5rem rgba(22, 163, 74, 0); }
    100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(22, 163, 74, 0); }
}

/* The points, floating off the middle of the answer that earned them. */
.cw-pop {
    position: absolute;
    transform: translate(-50%, -50%);
    padding: 0.1rem 0.45rem;
    border-radius: 2rem;
    background: #16a34a;
    color: #fff;
    font-weight: 800;
    font-size: 0.9rem;
    white-space: nowrap;
    animation: cw-pop-rise 1.2s ease-out forwards;
}

.cw-pop-combo {
    background: #ea580c;
}

/* One keystroke that finished an across AND a down. The moment a crossword is better than a
   word list, so it gets its own colour and its own bonus. */
.cw-pop-cross {
    background: #a21caf;
    font-size: 1.05rem;
}

@keyframes cw-pop-rise {
    0% { opacity: 0; transform: translate(-50%, -30%) scale(0.7); }
    20% { opacity: 1; transform: translate(-50%, -60%) scale(1.1); }
    100% { opacity: 0; transform: translate(-50%, -160%) scale(1); }
}

/* The combo badge stepping up a tier. */
.cw-combo-step {
    animation: cw-combo-step 0.45s ease-out;
}

@keyframes cw-combo-step {
    0% { transform: scale(1); }
    40% { transform: scale(1.35) rotate(-4deg); }
    100% { transform: scale(1); }
}

/* A completed answer that is wrong: the board recoils, so the rejection is felt at the grid
   rather than only read in the toast. */
.cw-board-shake {
    animation: cw-shake 0.3s ease-in-out;
}

@keyframes cw-shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-6px); }
    50% { transform: translateX(6px); }
    75% { transform: translateX(-3px); }
}

/* The last ten seconds. A number counting down in the corner of a grid you are staring at is
   easy to miss entirely, so the board says it too. */
.cw-board-urgent {
    animation: cw-urgent 1s ease-in-out infinite;
}

@keyframes cw-urgent {
    0%, 100% { filter: none; }
    50% { filter: drop-shadow(0 0 0.5rem rgba(220, 38, 38, 0.55)); }
}

.cw-time-urgent {
    color: #dc2626;
    animation: cw-time-urgent 1s ease-in-out infinite;
}

@keyframes cw-time-urgent {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.12); }
}

/* One revealed letter, pulsing so the player can see which square the hint paid for. */
.cw-cell-hint {
    animation: cw-hint-pulse 0.8s ease-in-out 3;
}

@keyframes cw-hint-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(162, 28, 175, 0); }
    50% { box-shadow: 0 0 0 0.35rem rgba(162, 28, 175, 0.5); }
}

/* Grid-completed confetti. DOM spans on the effects layer rather than a canvas: this fires at
   most once per round, the layer is already there, and a canvas would be a second rendering
   surface to size, clear and tear down on a single-core host. */
.cw-confetti {
    position: absolute;
    top: -1rem;
    width: 0.5rem;
    height: 0.8rem;
    border-radius: 1px;
    animation: cw-confetti-fall 2.2s ease-in forwards;
}

@keyframes cw-confetti-fall {
    0% { opacity: 1; transform: translate(0, 0) rotate(0deg); }
    100% { opacity: 0; transform: translate(var(--cw-drift, 0), 26rem) rotate(540deg); }
}

/* ── Reduced motion ──────────────────────────────────────────────── */
/* Everything above is decoration over something already reported in text, so it all goes. A new
   effect must be added to this list; CrosswordTests fails the build if one is missing. */
@media (prefers-reduced-motion: reduce) {
    .cw-cell-typed,
    .cw-cell-pop,
    .cw-pop,
    .cw-combo-step,
    .cw-board-shake,
    .cw-board-urgent,
    .cw-time-urgent,
    .cw-cell-hint,
    .cw-confetti {
        animation: none;
    }

    .cw-confetti {
        display: none;
    }

    .cw-timer-fill {
        transition: none;
    }
}
