/* ═══════════════════════════════════════════════════════════════════
   Word Search — the playable grid (D77)
   /games/word-search · /chs/games/word-search

   Hero: forest → emerald → lime (#052e16 → #15803d → #84cc16). Deliberately
   distinct from 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 ────────────────────────────────────────────────────────── */
.ws-hero {
    background: linear-gradient(135deg, #052e16 0%, #15803d 55%, #84cc16 100%);
    border-radius: 0.5rem;
}

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

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

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

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

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

.ws-timer-fill {
    height: 100%;
    width: 100%;
    background: #16a34a;
    transition: width 0.2s linear;
}

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

/* ── The stage ───────────────────────────────────────────────────── */
/* Tall enough that the start overlay is not cramped before the first board exists — the board
   and the clue list are both inside the stage, so the overlay has to cover both. */
.ws-stage {
    position: relative;
    min-height: 27rem;
}

/* ── The board ───────────────────────────────────────────────────── */
.ws-board {
    display: grid;
    grid-template-columns: repeat(var(--ws-size, 12), minmax(0, 1fr));
    gap: 2px;
    max-width: 34rem;
    margin: 0 auto;
    /* The whole point of the page is dragging across it: without this the drag
       scrolls the page on touch and selects text on desktop. */
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
}

.ws-cell {
    /* A real <button>, so the grid is reachable by keyboard — Enter anchors a
       cell and a second Enter completes the run (see word-search.js). */
    appearance: none;
    -webkit-appearance: none;
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.25rem;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-weight: 600;
    font-size: clamp(0.7rem, 2.6vw, 1.05rem);
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease;
}

.ws-board-hanzi .ws-cell {
    font-family: "Noto Sans SC", "Microsoft YaHei", "PingFang SC", sans-serif;
    font-size: clamp(0.8rem, 3vw, 1.2rem);
}

.ws-cell:focus-visible {
    outline: 2px solid #15803d;
    outline-offset: 1px;
    z-index: 1;
}

.ws-cell-sel {
    background: #fde68a;
    border-color: #f59e0b;
    color: #78350f;
}

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

/* A word the clock ran out on, shown in the reveal. Amber rather than green so
   "you found this" and "here is what you missed" never read the same. */
.ws-cell-miss {
    background: #fca5a5;
    border-color: #ef4444;
    color: #450a0a;
}

.ws-cell-hint {
    animation: ws-hint-pulse 0.6s ease-in-out 4;
}

@keyframes ws-hint-pulse {
    0%, 100% { background: var(--bs-body-bg); }
    50% { background: #60a5fa; color: #fff; }
}

/* Larger grids need smaller type before they need to shrink the board. The 16 and 18 rungs are
   reached by the fifth round and by the Expert band, which widens every grid by two cells. */
.ws-board-14 .ws-cell {
    font-size: clamp(0.62rem, 2.2vw, 0.95rem);
}

.ws-board-16 .ws-cell {
    font-size: clamp(0.55rem, 1.9vw, 0.85rem);
}

.ws-board-18 .ws-cell {
    font-size: clamp(0.5rem, 1.7vw, 0.78rem);
    border-radius: 0.15rem;
}

/* ── The clue list ───────────────────────────────────────────────── */
.ws-clues {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: 0.35rem;
}

.ws-clue {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.35rem;
    background: var(--bs-body-bg);
    font-size: 0.85rem;
}

.ws-clue-found {
    background: rgba(22, 163, 74, 0.12);
    border-color: #16a34a;
}

.ws-clue-body {
    min-width: 0;
}

.ws-clue-main {
    display: block;
    font-weight: 700;
    overflow-wrap: anywhere;
}

.ws-clue-found .ws-clue-main {
    text-decoration: line-through;
    text-decoration-color: rgba(22, 163, 74, 0.6);
}

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

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

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

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

/* ── Feedback toast over the board ───────────────────────────────── */
.ws-toast {
    position: absolute;
    top: 0.5rem;
    left: 50%;
    transform: translateX(-50%) translateY(-0.4rem);
    padding: 0.25rem 0.75rem;
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 700;
    color: #fff;
    background: #334155;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease, transform 0.15s ease;
    z-index: 3;
}

.ws-toast-show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

.ws-toast-good { background: #16a34a; }
.ws-toast-bad { background: #dc2626; }
.ws-toast-info { background: #334155; }

/* ── Overlays ────────────────────────────────────────────────────── */
.ws-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    text-align: center;
    padding: 1rem;
    background: var(--bs-body-bg);
    border-radius: 0.5rem;
    z-index: 4;
    /* The start screen carries two pickers and a best-score line; on a short phone in landscape
       that is taller than the stage, and a clipped Start button is an unplayable page. */
    overflow-y: auto;
}

.ws-big-icon {
    font-size: 2.5rem;
    color: #15803d;
}

.ws-picker {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
}

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

.ws-pick-btn {
    padding: 0.25rem 0.7rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

.ws-pick-btn.active {
    background: #15803d;
    border-color: #15803d;
    color: #fff;
}

.ws-btn-primary {
    background: #15803d;
    border-color: #15803d;
    color: #fff;
}

.ws-btn-primary:hover,
.ws-btn-primary:focus {
    background: #166534;
    border-color: #166534;
    color: #fff;
}

.ws-final-score {
    font-size: 2.5rem;
    font-weight: 800;
    color: #15803d;
    line-height: 1;
}

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

/* ── Worked example (the JavaScript-off half of the page) ─────────── */
.ws-example-grid {
    display: grid;
    grid-template-columns: repeat(8, minmax(0, 1fr));
    gap: 2px;
    max-width: 20rem;
}

.ws-example-cell {
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.2rem;
    font-size: 0.8rem;
    font-weight: 600;
}

.ws-example-cell.hit {
    background: #16a34a;
    border-color: #15803d;
    color: #fff;
}

.ws-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* ── Effects ─────────────────────────────────────────────────────── */
/* Everything in this block is decoration over a state change the HUD, the clue list and the
   #wsAnnounce live region have already reported, which is why the whole block is switched off
   under prefers-reduced-motion at the bottom of the file rather than being given a quieter
   variant. Nothing here is the only way to learn anything about the game. */

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

/* A found word lights up one cell at a time along the run the player dragged. The delay is set
   per cell by the script; the cell keeps its .ws-cell-hit green underneath, so the animation
   reads as the word being struck through rather than as a colour change. */
.ws-cell-pop {
    animation: ws-cell-pop 0.42s ease-out both;
    animation-delay: var(--ws-pop-delay, 0ms);
    z-index: 2;
}

@keyframes ws-cell-pop {
    0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.55); }
    45% { transform: scale(1.28); 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 word that earned them. */
.ws-pop {
    position: absolute;
    transform: translate(-50%, -50%);
    font-weight: 800;
    font-size: 1.1rem;
    color: #15803d;
    text-shadow: 0 1px 0 #fff, 0 0 0.5rem rgba(255, 255, 255, 0.9);
    white-space: nowrap;
    animation: ws-pop-rise 1.1s ease-out forwards;
}

/* A multiplied score is worth reading differently from a flat one — it is the thing the combo
   rule exists to teach. */
.ws-pop-combo {
    color: #b45309;
    font-size: 1.35rem;
}

@keyframes ws-pop-rise {
    0% { opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
    25% { opacity: 1; transform: translate(-50%, -85%) scale(1.12); }
    100% { opacity: 0; transform: translate(-50%, -190%) scale(1); }
}

/* The combo badge climbs through five tiers, so a run is visible without reading the number. */
.ws-combo-badge {
    transition: background-color 0.2s ease, transform 0.2s ease;
}

.ws-combo-t1 { background: #15803d; }
.ws-combo-t2 { background: #0d9488; }
.ws-combo-t3 { background: #2563eb; }
.ws-combo-t4 { background: #c026d3; }
.ws-combo-t5 { background: #ea580c; box-shadow: 0 0 0.75rem rgba(234, 88, 12, 0.7); }

.ws-combo-step {
    animation: ws-combo-step 0.5s ease-out;
}

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

/* A run that spelled nothing. Small enough not to move the board's layout — a translate on the
   grid container repaints the cells, it does not reflow them. */
.ws-board-shake {
    animation: ws-shake 0.3s ease-in-out;
}

@keyframes ws-shake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-6px); }
    45% { transform: translateX(5px); }
    70% { transform: translateX(-3px); }
}

/* The last ten seconds. The clock in the HUD is easy to miss while staring at the grid. */
.ws-board-urgent {
    animation: ws-urgent 1s ease-in-out infinite;
    border-radius: 0.5rem;
}

@keyframes ws-urgent {
    0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0); }
    50% { box-shadow: 0 0 0 0.4rem rgba(220, 38, 38, 0.28); }
}

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

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

/* Grid cleared. 28 spans for about two seconds, once per round. */
.ws-confetti {
    position: absolute;
    top: -1.5rem;
    width: 0.5rem;
    height: 0.75rem;
    border-radius: 1px;
    opacity: 0;
    animation: ws-confetti-fall 1.9s ease-in forwards;
}

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

/* Study first. Every rule above is decoration over something already reported in text, so the
   whole block is simply turned off rather than softened. */
@media (prefers-reduced-motion: reduce) {
    .ws-cell-pop,
    .ws-pop,
    .ws-combo-step,
    .ws-board-shake,
    .ws-board-urgent,
    .ws-time-urgent,
    .ws-confetti,
    .ws-cell-hint {
        animation: none;
    }

    .ws-combo-badge {
        transition: none;
    }

    .ws-confetti,
    .ws-pop {
        display: none;
    }
}
