/* Crosswords — minimal, mobile-first, themeable. */

:root {
    --bg: #f4f5f7;
    --surface: #ffffff;
    --surface-2: #eceef1;
    --text: #1c2024;
    --muted: #6b7280;
    --border: #d4d8dd;
    --accent: #2f6fed;
    --accent-soft: #d9e4fb;
    --inword: #fff4c2;
    --active: #ffd23f;
    --revealed: #d3f0dd;
    --locked: #b8e7c8;
    --block: #1c2024;
    /* Partner colours. Assigned server-side by index, so a player keeps the same one all game.
       Keep in step with PARTNER_COLOURS in crossword.js. */
    --partner-0: #e05d5d;
    --partner-1: #3fa7d6;
    --partner-2: #6cbf5a;
    --partner-3: #c874d9;
    --partner-4: #e2933a;
    --partner-5: #2fb8a6;
    --radius: 12px;
    --shadow: 0 1px 3px rgba(0,0,0,.08), 0 6px 20px rgba(0,0,0,.06);
}

:root[data-theme="dark"] {
    --bg: #0f1115;
    --surface: #191c22;
    --surface-2: #232833;
    --text: #e6e8eb;
    --muted: #9aa2ad;
    --border: #2c313b;
    --accent: #5b8def;
    --accent-soft: #24344f;
    --inword: #4a421d;
    --active: #b8901f;
    --revealed: #1f3b2c;
    --locked: #2c5d3f;
    --block: #05070a;
    /* Lifted for contrast against the dark grid. */
    --partner-0: #ff8080;
    --partner-1: #64c8f0;
    --partner-2: #8ed97c;
    --partner-3: #de95ec;
    --partner-4: #f2ad5c;
    --partner-5: #55d6c4;
    --shadow: 0 1px 3px rgba(0,0,0,.4), 0 6px 20px rgba(0,0,0,.35);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--text);
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    -webkit-text-size-adjust: 100%;
}

#screen { min-height: 100dvh; }

/* ---------------------------------------------------------------- theme btn */
.theme-toggle {
    position: fixed;
    top: 12px;
    right: 12px;
    z-index: 20;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    box-shadow: var(--shadow);
}
.theme-toggle:hover { border-color: var(--accent); }

/* ------------------------------------------------------------- start screen */
.start {
    max-width: 460px;
    margin: 0 auto;
    padding: 14vh 20px 40px;
    display: flex;
    flex-direction: column;
    gap: 22px;
}
.start__brand { text-align: center; }
.start__brand h1 { margin: 0; font-size: 2.6rem; letter-spacing: -.02em; }
.start__tagline { margin: 6px 0 0; color: var(--muted); }

.setup {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 20px;
}
.setup__group { border: 0; margin: 0; padding: 0; }
.setup__group legend {
    padding: 0 0 10px;
    font-weight: 600;
    font-size: .92rem;
    color: var(--muted);
}

/* segmented radio control */
.segmented { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px; }
.segmented label { position: relative; display: block; }
.segmented input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.segmented span {
    display: block;
    text-align: center;
    padding: 11px 8px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--surface-2);
    font-weight: 600;
    font-size: .95rem;
    transition: background .12s, border-color .12s, color .12s;
}
.segmented input:checked + span {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.segmented input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
/* No file for this pairing (e.g. Large exists, but not Large + Hard). */
.segmented input:disabled + span {
    opacity: .38;
    cursor: not-allowed;
    text-decoration: line-through;
}
.segmented input:disabled { cursor: not-allowed; }

/* Only one puzzle kind installed — nothing to pick, so just name it. */
.setup__only {
    margin: 0;
    text-align: center;
    font-weight: 600;
    color: var(--muted);
}
.start__empty {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 18px 20px;
    margin: 0;
    line-height: 1.5;
    color: var(--muted);
}
.start__empty code {
    background: var(--surface-2);
    border-radius: 5px;
    padding: 1px 5px;
    font-size: .9em;
    color: var(--text);
}

/* ------------------------------------------------------------------ buttons */
.btn {
    display: inline-block;
    text-align: center;
    text-decoration: none;
    font: inherit;
    font-weight: 600;
    padding: 12px 16px;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text);
    cursor: pointer;
}
.btn:hover { border-color: var(--accent); }
.btn--primary { background: var(--accent); border-color: var(--accent); color: #fff; font-size: 1.05rem; }
.btn--resume { background: var(--accent-soft); border-color: var(--accent); color: var(--text); }
.btn--ghost { background: transparent; padding: 8px 12px; }

/* --------------------------------------------------------------------- game */
.game {
    max-width: 1000px;
    margin: 0 auto;
    /* Bottom padding clears the sticky clue bar so the last clue stays reachable. */
    padding: 12px 14px calc(84px + env(safe-area-inset-bottom));
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.game__toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: nowrap;
    gap: 10px;
    padding-top: 4px;
    padding-right: 52px; /* clear the fixed theme toggle */
}
.game__meta { color: var(--muted); font-weight: 600; font-size: .9rem; }
@media (max-width: 480px) { .game__meta { display: none; } }
.game__actions { display: flex; gap: 6px; }
.game__status { min-height: 1.4em; font-weight: 600; color: var(--accent); }

.game__body { display: flex; flex-direction: column; gap: 18px; min-height: 0; }
.game__main { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

/* Short viewports (phone landscape) have vertical space to spare nowhere: tighten the
   chrome and drop the status line's reserved height until it has something to say. */
@media (max-height: 619px) {
    .game { gap: 6px; padding-top: 6px; }
    .game__toolbar { padding-top: 0; }
    .game__status:empty { display: none; }
    .game__body { gap: 12px; }
}

/* Wide enough for two columns: grid on the left, clue list alongside. Phone landscape also
   lands here (iPhone 16 is ~874px wide on its side), which is why the clue bar below keys off
   height as well as width — sideways there is plenty of width but very little height. */
@media (min-width: 820px) {
    .game__body { flex-direction: row; align-items: flex-start; }
    .clues { flex: 1 1 auto; }
    .game__main {
        flex: 0 0 auto;
        position: sticky;
        top: 12px;
        /* A definite width is required: the grid inside is width:100%, which resolves to
           nothing against a shrink-to-fit column. The grid's own cap then centres it here. */
        width: min(620px, 48vw);
    }
}

/* grid */
.grid-wrap { position: relative; display: flex; justify-content: center; }
.grid { width: 100%; display: flex; justify-content: center; }
.grid__svg {
    width: 100%;
    height: auto;
    background: var(--surface);
    border: 2px solid var(--block);
    border-radius: 6px;
    box-shadow: var(--shadow);
    touch-action: manipulation;
}
.grid__input {
    position: absolute;
    width: 1px; height: 1px;
    opacity: 0;
    border: 0; padding: 0;
    pointer-events: none;
}

/* cells */
.cell { stroke: var(--border); stroke-width: 1; }
.cell--block { fill: var(--block); stroke: var(--block); }
.cell--white { fill: var(--surface); cursor: pointer; }
.cell--white.is-inword { fill: var(--inword); }
.cell--white.is-active { fill: var(--active); }
.cell--white.is-revealed { fill: var(--revealed); }
.cell--white.is-locked { fill: var(--locked); }
.cell__num { font-size: 9px; fill: var(--muted); pointer-events: none; user-select: none; }
.cell__letter {
    font-size: 20px;
    font-weight: 700;
    fill: var(--text);
    pointer-events: none;
    user-select: none;
    text-transform: uppercase;
}

/* --------------------------------------------------------------- current clue
   Pinned to the bottom of the viewport whenever the full list can't comfortably sit
   beside the grid, so the clue you're answering is never the thing that scrolls away. */
.cluebar {
    /* Fixed rather than sticky: sticky only pins while its container overflows, so in
       landscape — where the clue list scrolls internally and the page barely scrolls at
       all — a sticky bar would drift up into the content. */
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 15;
    display: flex;
    align-items: stretch;
    gap: 4px;
    padding: 8px 10px calc(8px + env(safe-area-inset-bottom));
    background: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: 0 -2px 14px rgba(0,0,0,.10);
}
:root[data-theme="dark"] .cluebar { box-shadow: 0 -2px 14px rgba(0,0,0,.45); }

.cluebar__nav {
    flex: 0 0 auto;
    width: 40px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--surface-2);
    color: var(--text);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}
.cluebar__nav:hover { border-color: var(--accent); }
.cluebar__text {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 2px;
    font-size: .95rem;
    line-height: 1.3;
    /* Two lines then ellipsis — long clues must not push the grid off screen. */
    overflow: hidden;
}
.cluebar__label { flex: 0 0 auto; font-weight: 700; color: var(--accent); }
.cluebar__clue {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Enough width AND height for the full list beside the grid: the bar is redundant. */
@media (min-width: 820px) and (min-height: 620px) {
    .cluebar { display: none; }
    .game { padding-bottom: 40px; }
}

/* clues */
.clues { display: grid; gap: 18px; }
@media (min-width: 520px) { .clues { grid-template-columns: 1fr 1fr; } }

/* Short viewports (phone landscape): the clue list gets its own scroll area so the grid
   and the pinned clue stay on screen instead of being pushed off by the list. */
@media (min-width: 820px) and (max-height: 619px) {
    .clues {
        grid-template-columns: 1fr;
        max-height: calc(100dvh - 150px);
        overflow-y: auto;
        overscroll-behavior: contain;
        /* The list scrolls inside itself here, so the page's bottom padding can't keep the
           last clue clear of the pinned bar — the padding has to live inside the scroller. */
        padding-bottom: calc(84px + env(safe-area-inset-bottom));
    }
}
.clues__heading { margin: 0 0 8px; font-size: 1rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.clues__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.clue {
    display: flex;
    gap: 8px;
    padding: 7px 9px;
    border-radius: 8px;
    cursor: pointer;
    line-height: 1.35;
    font-size: .95rem;
}
.clue:hover { background: var(--surface-2); }
.clue.is-active { background: var(--accent-soft); }
.clue.is-solved { opacity: .72; }
.clue__num { font-weight: 700; min-width: 1.4em; color: var(--accent); }

/* solved */
.game.is-solved .grid__svg { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow); }

/* ----------------------------------------------------------------- join a game
   Below the picker, and quiet: co-op is opt-in, so it must not compete with just playing. */
.join {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 16px 20px 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.join__label { font-weight: 600; font-size: .92rem; color: var(--muted); }
.join__row { display: flex; gap: 8px; }
.join__code {
    flex: 1 1 auto;
    min-width: 0;
    font: inherit;
    font-weight: 700;
    /* Codes are 5 unambiguous characters; spacing them out makes a misread obvious. */
    letter-spacing: .22em;
    text-transform: uppercase;
    text-align: center;
    padding: 11px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    color: var(--text);
}
.join__code:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.join__help { margin: 0; font-size: .85rem; color: var(--muted); }

/* ------------------------------------------------------------------- presence
   One dot per person in the game, in their partner colour. Empty and invisible while solo. */
.presence { display: flex; align-items: center; gap: 4px; padding-right: 2px; }
.presence__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    box-shadow: 0 0 0 1.5px var(--surface);
}
/* Your own dot is ringed, so "which one is me" needs no legend. */
.presence__dot--me { box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 3px var(--border); }

/* ------------------------------------------------------- partner indicators
   Drawn in a group above the cells: the word tint is translucent so the letters underneath stay
   readable, and the cursor is an outline rather than a fill so it can't hide one. */
.partners { pointer-events: none; }
.partner-word { fill-opacity: .26; }
.partner-cursor { fill: none; stroke-width: 3; rx: 2; }

/* ---------------------------------------------------------------- share panel
   Fallback for anything without an OS share sheet — and the place to read the code aloud from. */
.share {
    position: fixed;
    inset: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(0,0,0,.45);
}
.share__panel {
    width: 100%;
    max-width: 380px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.share__title { margin: 0; font-size: 1.2rem; }
.share__hint { margin: 0; color: var(--muted); font-size: .92rem; line-height: 1.45; }
.share__code {
    margin: 0;
    text-align: center;
    font-size: 2rem;
    font-weight: 800;
    letter-spacing: .18em;
    /* letter-spacing adds a trailing gap, which shifts centred text left; indent it back. */
    text-indent: .18em;
    color: var(--accent);
    background: var(--surface-2);
    border-radius: 10px;
    padding: 12px 8px;
}
.share__link {
    font: inherit;
    font-size: .85rem;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
    color: var(--muted);
    text-overflow: ellipsis;
}
.share__actions { display: flex; gap: 8px; }
.share__actions .btn { flex: 1 1 auto; }

/* ------------------------------------------------------------------- finished
   The puzzle is over, so the things you'd use to play it recede — the grid stays where it is,
   because that's the bit you want to look at. */
.game.is-complete .clues,
.game.is-complete .cluebar {
    pointer-events: none;
    /* Faded, not hidden: removing the clue bar would change the grid's available height and
       resize it out from under the moment. */
    transition: opacity .5s ease .15s;
}
/* The clue bar is an input control and now useless, so it goes entirely. The list stays faintly
   legible — a finished puzzle is worth reading back, and blanking it would leave a void. */
.game.is-complete .cluebar { opacity: 0; }
.game.is-complete .clues { opacity: .38; }
.game.is-complete .grid__svg { transition: border-color .4s ease, box-shadow .4s ease; }

.done {
    position: fixed;
    inset: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(0,0,0,.45);
    animation: done-in .45s ease both;
}
/* A 45% black scrim barely registers over an already-dark page; lean on it harder there so the
   backdrop still reads as "the game has stepped back". */
:root[data-theme="dark"] .done,
:root[data-theme="dark"] .share { background: rgba(0,0,0,.66); }
.done__panel {
    width: 100%;
    max-width: 340px;
    text-align: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 24px 20px 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    animation: done-rise .45s cubic-bezier(.2,.9,.3,1) both .08s;
}
.done__badge { margin: 0; font-size: 2.6rem; line-height: 1; }
.done__title { margin: 0; font-size: 1.5rem; letter-spacing: -.01em; }
.done__note { margin: 0; color: var(--muted); font-size: .95rem; }
.done__actions { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.done__dismiss {
    font: inherit;
    font-size: .88rem;
    border: 0;
    background: none;
    color: var(--muted);
    padding: 4px;
    cursor: pointer;
    text-decoration: underline;
}
.done__dismiss:hover { color: var(--text); }

@keyframes done-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes done-rise {
    from { opacity: 0; transform: translateY(14px) scale(.97); }
    to { opacity: 1; transform: none; }
}

/* Confetti canvas: above the panel so pieces fall in front of it, but never in the way. */
.confetti {
    position: fixed;
    inset: 0;
    z-index: 50;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

/* Respect the OS setting: the fade stays (it carries meaning), the movement goes. */
@media (prefers-reduced-motion: reduce) {
    .done, .done__panel { animation-duration: .01ms; }
    .done__panel { transform: none; }
}
