/* Qwertiva owns its own visual world. Named for the interaction rather than the
   app: nginx routes /qwertiva as a prefix, so any root asset whose name starts
   with an app slug is swallowed by that app's alias and 404s. Every other product page on this site is
   cream paper with a serif display face; a typing game is a dark keycap surface,
   so the page borrows the app's real palette from Theme.swift instead. Scoped to
   .product-page--qwertiva so nothing here leaks into the rest of the site. */

body.product-page--qwertiva {
    --product-accent: #ffd60a;
    --product-accent-strong: #ffe14d;
    --product-accent-soft: rgba(255, 214, 10, 0.11);
    --product-accent-border: rgba(255, 214, 10, 0.32);

    --bg: #0b0b0e;
    --bg-elevated: #14141a;
    --bg-muted: #1b1b22;
    --bg-dark: #000;
    --line: #26262f;
    --line-strong: #33333f;
    --text: #f2efe8;
    --text-muted: #9a9aa4;
    --text-soft: #6b6b76;
    --text-inverse: #0b0b0e;
    --focus: #ffd60a;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px rgba(0, 0, 0, .38);
    --shadow-md: 0 2px 4px rgba(0, 0, 0, .5), 0 20px 54px rgba(0, 0, 0, .5);
    --shadow-lg: 0 28px 80px rgba(0, 0, 0, .62);

    /* The shared stylesheet's translucent surfaces are cream; on a black ground
       they have to become dark or the nav and cards float as light slabs. */
    --nav-surface: rgba(11, 11, 14, .88);
    --surface-translucent: rgba(20, 20, 26, .82);
    --surface-translucent-soft: rgba(20, 20, 26, .55);
    --surface-translucent-strong: rgba(14, 14, 19, .95);
    --surface-translucent-card: rgba(20, 20, 26, .85);

    color-scheme: dark;
    background: radial-gradient(circle at 78% -4%, rgba(255, 214, 10, .07), transparent 30rem), var(--bg);
}

/* The app renders words and metrics in a monospace face, so the page does too.
   Headings stay in a grotesque: a mono display face would be a costume. */
body.product-page--qwertiva h1,
body.product-page--qwertiva h2,
body.product-page--qwertiva h3 {
    font-family: var(--font-sans);
    font-weight: 700;
    letter-spacing: -0.028em;
}

body.product-page--qwertiva .product-hero h1 { letter-spacing: -0.04em; }

body.product-page--qwertiva .product-button--primary {
    color: #0b0b0e;
    background: var(--product-accent);
    border-color: transparent;
}

body.product-page--qwertiva .product-button--primary:hover { background: var(--product-accent-strong); }

body.product-page--qwertiva .product-trust span {
    color: var(--text-muted);
    border-color: var(--line);
    background: var(--bg-elevated);
}

body.product-page--qwertiva .brand-mark { background: var(--product-accent); color: #0b0b0e; }
body.product-page--qwertiva .product-icon { border-radius: 22.37%; }

/* ---- the playable strip ----
   The run screen mirrors the app's: title bar, progress rail, word field, stat
   line. Every colour below is the app's own "Keycap" theme from Theme.swift, so
   the page and a screenshot of the app agree.

     background #0a0a0c   surface #121215   elevated #1c1c20
     primary    #f5f2eb   secondary #8f8c85
     accent     #ffd60a   success #5cd675   warning #f5b338   danger #ff5959   */
body.product-page--qwertiva {
    --run-surface: #121215;
    --run-elevated: #1c1c20;
    --run-primary: #f5f2eb;
    --run-secondary: #8f8c85;
    --run-success: #5cd675;
    --run-warning: #f5b338;
    --run-danger: #ff5959;
    --run-mono: var(--mono, ui-monospace, "SF Mono", Menlo, monospace);
}

.typing-demo {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: #0a0a0c;
    padding: clamp(1.1rem, 3vw, 1.75rem);
    display: grid;
    gap: 1rem;
}

.js-ready .typing-demo__live { display: grid; gap: .85rem; }

/* Title bar — TypingGameTopBar. */
.run-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.run-bar__title {
    display: grid;
    gap: .12rem;
    color: var(--run-primary);
    font-size: .96rem;
    font-weight: 700;
}

.run-bar__title small {
    color: var(--run-secondary);
    font-size: .74rem;
    font-weight: 500;
}

.run-bar__clock {
    color: var(--product-accent);
    font-family: var(--run-mono);
    font-size: 1.45rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    transition: color 200ms ease;
}

/* The last five seconds turn the clock and the rail red — GhostRace isUrgent. */
.run-bar__clock.is-urgent { color: var(--run-danger); }

/* Progress rail — TypingGameProgressBar: a 4px capsule, 0.25s linear. */
.run-rail {
    height: 4px;
    border-radius: 999px;
    background: var(--run-elevated);
    overflow: hidden;
}

.run-rail__fill {
    display: block;
    width: 0;
    height: 100%;
    border-radius: 999px;
    background: var(--product-accent);
    transition: width 250ms linear, background-color 200ms ease;
}

.run-rail__fill.is-urgent { background: var(--run-danger); }

/* Word field. */
.run-field {
    position: relative;
    padding: .35rem 0;
    cursor: text;
}

/* A fixed four-line window, like the app's WordFieldView (which shows four lines,
   or two when the screen is short). Fixed rather than min-height so the strip
   never changes height mid-run and shoves the input out from under the cursor. */
.run-field__words {
    --run-line: 2.05;
    --run-lines: 4;
    font-family: var(--run-mono);
    font-size: clamp(1.02rem, 2.2vw, 1.28rem);
    line-height: var(--run-line);
    color: var(--run-secondary);
    height: calc(var(--run-line) * var(--run-lines) * 1em);
    overflow: hidden;
}

.run-field__words .word { display: inline-block; margin-right: .55ch; white-space: pre; }

/* Committed words: the app flashes green or red on commit, then settles into a
   muted history colour, with a red underline kept on the ones that were wrong. */
.run-field__words .is-past { color: rgba(143, 140, 133, .38); }
.run-field__words .is-past-wrong {
    color: rgba(255, 89, 89, .55);
    text-decoration: underline;
    text-decoration-color: rgba(255, 89, 89, .6);
    text-underline-offset: 4px;
}
.run-field__words .is-flash-ok { color: var(--run-success); }
.run-field__words .is-flash-bad { color: var(--run-danger); }

/* Active word: per character, like WordFieldView.color(for:). */
.run-field__words .is-active { position: relative; }
.run-field__words .ch.is-pending { color: var(--run-secondary); }
.run-field__words .ch.is-correct { color: var(--run-primary); }
.run-field__words .ch.is-incorrect { color: var(--run-danger); }
.run-field__words .ch.is-extra { color: var(--run-warning); background: rgba(255, 89, 89, .18); }

/* Caret, sitting before the next character to type. --caret-at is the count of
   characters already typed, and the field is monospace, so one ch per character
   places it without measuring anything. */
.run-field__words .is-active::after {
    content: "";
    position: absolute;
    top: .18em;
    left: calc(var(--caret-at, 0) * 1ch);
    width: 2px;
    height: 1.15em;
    border-radius: 1px;
    background: var(--product-accent);
    animation: run-caret 1.05s steps(2, jump-none) infinite;
}

@keyframes run-caret { 50% { opacity: .15; } }

/* Error shake — ShakeEffect: 4px of travel, three shakes, 0.25s linear. */
.run-field__words .word.is-shaking { animation: run-shake 250ms linear; }

@keyframes run-shake {
    0%, 100% { transform: translateX(0); }
    16.6% { transform: translateX(4px); }
    33.3% { transform: translateX(-4px); }
    50% { transform: translateX(4px); }
    66.6% { transform: translateX(-4px); }
    83.3% { transform: translateX(4px); }
}

/* Time's up. The old version only disabled the input, which read as a broken
   page; this covers the words it no longer makes sense to type. */
/* A `display` of our own beats the UA rule behind the hidden attribute, so the
   attribute has to be honoured explicitly or the overlay shows mid-run. */
.run-over[hidden] { display: none; }

.run-over {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: .55rem;
    padding: 1rem;
    text-align: center;
    border-radius: var(--radius-md);
    background: rgba(10, 10, 12, .93);
    backdrop-filter: blur(3px);
}

.run-over__flag {
    margin: 0;
    color: var(--run-danger);
    font-family: var(--run-mono);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
}

.run-over__score {
    margin: 0;
    color: var(--run-primary);
    font-family: var(--run-mono);
    font-size: clamp(.95rem, 2.2vw, 1.15rem);
    font-variant-numeric: tabular-nums;
}

/* Stat line — ComboMeter's lowercase mono row, wpm in the accent. */
.run-stats {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem 1.4rem;
    margin: 0;
    color: var(--run-secondary);
    font-family: var(--run-mono);
    font-size: .92rem;
    font-variant-numeric: tabular-nums;
}

.run-stats b { color: var(--run-primary); font-weight: 700; }
.run-stats__wpm { color: var(--product-accent); }
.run-stats__wpm b { color: var(--product-accent); }
.run-stats.is-final b { color: var(--run-primary); }

/* The input is the real keyboard target but not the thing to look at: the word
   field above is. Kept on-screen and full-size so a tap focuses it on a phone
   and the browser does not scroll it out of view. */
.run-input { display: grid; gap: .4rem; }

.run-input__hint {
    color: var(--text-soft);
    font-family: var(--run-mono);
    font-size: .68rem;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.run-input input {
    width: 100%;
    min-height: 48px;
    padding: .7rem .9rem;
    font-family: var(--run-mono);
    font-size: 1rem;
    color: var(--run-primary);
    background: var(--run-surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    transition: border-color 180ms ease, opacity 180ms ease;
}

.run-input input:focus { outline: none; border-color: var(--product-accent); }
.run-input input:disabled { opacity: .45; cursor: not-allowed; }

.typing-demo.is-finished .run-input__hint { color: var(--run-danger); }

.typing-demo__foot {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem 1rem;
    align-items: center;
}

.typing-demo__note {
    color: var(--text-muted);
    font-size: .84rem;
    line-height: 1.5;
    flex: 1 1 16rem;
    min-width: 0;
}

.typing-demo__note strong { color: var(--text); }

.typing-demo__button {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    padding: .55rem 1.1rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
    font-family: var(--font-sans);
    font-size: .82rem;
    font-weight: 700;
    cursor: pointer;
    transition: color 180ms ease, border-color 180ms ease, background 180ms ease;
}

.typing-demo__button--go { color: #0b0b0e; background: var(--product-accent); border-color: transparent; }
.typing-demo__button:hover { border-color: var(--product-accent-border); color: var(--text); }
.typing-demo__button--go:hover { color: #0b0b0e; background: var(--product-accent-strong); }
.typing-demo__button:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }

/* Without JS the strip is still a legible sample of the app's own screen. */
.typing-demo__fallback { color: var(--text-muted); font-size: .9rem; }
.js-ready .typing-demo__fallback { display: none; }
.typing-demo__live { display: none; }

/* ---- mode table ---- */
.mode-table { width: 100%; min-width: 34rem; border-collapse: collapse; font-size: .95rem; }
.mode-table caption {
    caption-side: bottom;
    padding-top: .8rem;
    color: var(--text-soft);
    font-size: .84rem;
    text-align: left;
}
.mode-table th, .mode-table td { padding: .8rem 1.1rem; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; }
.mode-table thead th {
    background: var(--bg-muted);
    color: var(--text-soft);
    font-family: var(--mono, ui-monospace, "SF Mono", Menlo, monospace);
    font-size: .67rem;
    letter-spacing: .1em;
    text-transform: uppercase;
}
.mode-table tbody th { color: var(--text); font-weight: 650; width: 30%; }
.mode-table tbody td { color: var(--text-muted); }
.mode-table tbody td.num {
    font-family: var(--mono, ui-monospace, "SF Mono", Menlo, monospace);
    font-variant-numeric: tabular-nums;
    color: var(--text);
    white-space: nowrap;
}
.mode-table tbody tr:last-child th, .mode-table tbody tr:last-child td { border-bottom: 0; }
.mode-table__scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--bg-elevated); }

/* On a phone a three-column table either squeezes its prose into one word per
   line or hides a column off-screen. Stack each mode as a labelled block
   instead: no sideways scroll, nothing hidden, rows no taller than their text. */
@media (max-width: 660px) {
    .mode-table__scroll { overflow-x: visible; border: 0; border-radius: 0; background: transparent; }
    /* A flex column rather than display:block, because once a table stops being
       a table its caption loses caption-side and shrink-wraps to a word-wide
       column at the top. Ordering puts it back underneath the blocks. */
    .mode-table { min-width: 0; font-size: .92rem; display: flex; flex-direction: column; }
    .mode-table caption {
        order: 2;
        display: block;
        width: auto;
        padding-top: 1rem;
    }
    .mode-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
    .mode-table tbody { order: 1; display: grid; gap: .6rem; }

    .mode-table tbody tr {
        display: grid;
        gap: .3rem;
        padding: .95rem 1.05rem;
        border: 1px solid var(--line);
        border-radius: var(--radius-md);
        background: var(--bg-elevated);
    }

    .mode-table tbody th,
    .mode-table tbody td {
        width: auto;
        padding: 0;
        border-bottom: 0;
    }

    .mode-table tbody th {
        font-size: 1.02rem;
        letter-spacing: -.01em;
    }

    .mode-table tbody td::before {
        content: attr(data-label) " ";
        color: var(--text-soft);
        font-family: var(--mono, ui-monospace, "SF Mono", Menlo, monospace);
        font-size: .64rem;
        letter-spacing: .11em;
        text-transform: uppercase;
    }

    .mode-table tbody td.num { white-space: normal; }
    .mode-table tbody td.num::before { display: block; margin-bottom: .1rem; }
}

/* Reduce motion: the blinking caret and the error shake are the two moving parts
   here, and neither carries information the colours do not already carry. */
@media (prefers-reduced-motion: reduce) {
    .run-field__words .is-active::after { animation: none; }
    .run-field__words .word.is-shaking { animation: none; }
    .run-rail__fill { transition: none; }
}

/* On a phone the stat line and the run controls need to stack rather than wrap
   into ragged columns, and the words want a touch more room per line. */
@media (max-width: 560px) {
    .run-bar__clock { font-size: 1.25rem; }
    /* Three lines on a phone: the app drops to two on a short screen. */
    .run-field__words { font-size: 1rem; --run-line: 2.15; --run-lines: 3; }
    .run-stats { gap: .3rem 1.1rem; font-size: .86rem; }
    .typing-demo__foot { gap: .5rem .6rem; }
    .typing-demo__note { flex-basis: 100%; }
}
