/* ==========================================================================
   Times Table Stars theme — violet and gold.

   Loaded after game-core.css, so these :root declarations win on source order.
   Custom properties resolve at use time, which is why redefining the identity
   tokens here also re-points the legacy --ss-* aliases in game-core.css.

   Everything below the token block is a .ts-* selector this game owns, plus a
   short list of overrides for colours game-core hardcoded.
   ========================================================================== */

:root {
    --game-accent: #7C3AED;
    --game-accent-light: #A78BFA;

    /* Deliberately the same gold as Spelling Stars and Math Stars. The star is
       the family mark — a child who earns one here should recognise it as the
       same star they earn in the other two games. Only the accent changes. */
    --game-highlight: #D4A017;
}

/* The eight structural tokens (--game-bg, --game-card, --game-surface,
   --game-border, --game-text, --game-muted, --game-success, --game-info) are
   intentionally not redefined here. They are what makes three games feel like
   one platform rather than three sites. */

/* ---------- Reds game-core hardcoded ----------
   Six rules in game-core.css bake rgba(198, 40, 40, …) or #B71C1C straight into
   the declaration, so a theme cannot reach them through a token. Left alone
   they flash Spelling Stars red inside a violet game — on the shared join,
   dashboard and results screens, which Times Table Stars renders unchanged.

   Fixing them in core would also fix the same bug in Math Stars, but the blast
   radius is two live games and every screen in them, so that is a Phase 2 job
   with its own visual pass. Here we just re-express each one in violet. */

.ss-input:focus {
    box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.35);
}

.ss-btn-primary:hover {
    background-color: #6D28D9;
    box-shadow: 0 4px 15px rgba(124, 58, 237, 0.45);
}

/* The border on these two already resolves to violet through --ss-red-light;
   only the fill was stranded. */
.mc-option.wrong {
    background-color: rgba(124, 58, 237, 0.30);
}

.ss-error {
    background-color: rgba(124, 58, 237, 0.20);
}

/* Inert in this game — TimesContent.hasWeeks is false, so the week selector
   never renders. Included anyway so the theme is complete and a future weekly
   feature does not ship a red focus ring. */
.week-selector:focus {
    box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.35);
}

/* ---------- Shared focus ring ----------
   game-core has no global :focus-visible style, and every control below is a
   custom-painted button whose default outline we would otherwise lose. */
.ts-chip:focus-visible,
.ts-go:focus-visible,
.ts-tile:focus-visible,
.ts-cell:focus-visible {
    outline: 3px solid var(--game-highlight);
    outline-offset: 2px;
}

/* ---------- Pre-round: pick your tables ---------- */
.ts-pre {
    text-align: center;
}

.ts-chips {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
    margin: 0.75rem 0 1.25rem;
}

.ts-chip {
    background-color: var(--game-surface);
    border: 2px solid var(--game-border);
    color: var(--game-text);
    border-radius: 1.5rem;
    padding: 0.5rem 0.9rem;
    font-family: 'Montserrat', sans-serif;
    font-size: 1rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    min-height: 44px;
    min-width: 56px;
    transition: background-color 0.15s, border-color 0.15s, transform 0.1s;
}

.ts-chip:active {
    transform: scale(0.95);
}

.ts-chip-on {
    background-color: var(--game-accent);
    border-color: var(--game-accent-light);
    color: var(--game-text);
}

.ts-go {
    display: block;
    width: 100%;
    background-color: var(--game-accent);
    color: var(--game-text);
    border: none;
    border-radius: 1rem;
    padding: 1rem;
    font-family: 'Montserrat', sans-serif;
    font-size: 1.35rem;
    font-weight: 700;
    cursor: pointer;
    min-height: 56px;
    transition: background-color 0.15s, transform 0.1s;
}

.ts-go:active {
    transform: scale(0.97);
}

.ts-go:disabled {
    background-color: var(--game-border);
    color: var(--game-muted);
    cursor: default;
}

/* ---------- HUD: clock, score, streak ----------
   tabular-nums plus a min-width in ch on each number: the row must not jog
   sideways when 60 becomes 9, or when the score goes from 9 to 10 mid-sprint. */
.ts-hud {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    font-variant-numeric: tabular-nums;
}

.ts-clock {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--game-muted);
    min-width: 3ch;
    text-align: left;
}

/* Last ten seconds. Brightening rather than reddening: red is not in this
   game's palette, and the brighter violet reads as "hurry" against the muted
   default without introducing a colour that means nothing else here. */
.ts-clock-urgent {
    color: var(--game-accent-light);
    animation: ts-clock-pulse 0.7s ease-in-out infinite;
}

@keyframes ts-clock-pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.15); }
}

.ts-score {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--game-highlight);
    min-width: 3ch;
    text-align: center;
}

.ts-streak {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--game-muted);
    min-width: 3.5ch;
    justify-content: flex-end;
}

/* Three in a row and up. Gold, because a streak is the thing that earns stars. */
.ts-streak-hot {
    color: var(--game-highlight);
    animation: ts-streak-pulse 0.9s ease-in-out infinite alternate;
}

@keyframes ts-streak-pulse {
    from { transform: scale(1); }
    to   { transform: scale(1.12); }
}

/* ---------- Score bar and the ghost of your best run ----------
   JS sets .ts-bar-fill style.width and .ts-ghost style.left, both as
   percentages of the track. */
.ts-bar {
    position: relative;
    height: 10px;
    margin-bottom: 1rem;
    background-color: var(--game-surface);
    border-radius: 1rem;
}

.ts-bar-fill {
    height: 100%;
    width: 0;
    border-radius: 1rem;
    background-image: linear-gradient(90deg, var(--game-accent), var(--game-accent-light));
    transition: width 0.3s ease;
}

/* "You, before" — a hollow, translucent marker. A solid fill here would read as
   a second score competing with the child's own bar instead of a target to
   chase. Decorative: give it aria-hidden in the markup. */
.ts-ghost {
    position: absolute;
    top: -3px;
    bottom: -3px;
    left: 0;
    width: 8px;
    margin-left: -4px;
    border: 2px solid var(--game-accent-light);
    border-radius: 1rem;
    background-color: transparent;
    opacity: 0.55;
    pointer-events: none;
    transition: left 0.9s linear;
}

/* ---------- The fact itself ----------
   The hero of both screens. Fixed min-height so the tiles below never move when
   the fact goes from "7 × 8" to "12 × 11". */
.ts-fact {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 5.5rem;
    font-size: 3.5rem;
    font-weight: 700;
    line-height: 1;
    text-align: center;
    white-space: nowrap;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
}

/* The 180ms swap. transform and opacity only — both are composited on the GPU,
   so this stays smooth on the cheap school tablets these run on. Animating
   width or left instead would relayout the whole screen every frame and drop
   the answer the child is already typing. */
.ts-fact-in {
    animation: ts-fact-in 180ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.ts-fact-out {
    animation: ts-fact-out 180ms cubic-bezier(0.55, 0.06, 0.68, 0.19) forwards;
}

@keyframes ts-fact-in {
    from { opacity: 0; transform: translate3d(32px, 0, 0); }
    to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes ts-fact-out {
    from { opacity: 1; transform: translate3d(0, 0, 0); }
    to   { opacity: 0; transform: translate3d(-32px, 0, 0); }
}

/* ---------- Answer tiles ----------
   A fixed 2x2 grid, never a flex wrap. After a dozen facts the finger stops
   reading and starts reaching, and that only works if "bottom left" is always
   in the same place. Fixed rows plus tabular-nums keep it there when a 3-digit
   answer replaces a 2-digit one. */
.ts-tiles {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(2, minmax(64px, 1fr));
    gap: 0.75rem;
    max-width: 420px;
    margin: 0 auto;
}

.ts-tile {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 64px;
    background-color: var(--game-surface);
    border: 2px solid var(--game-border);
    border-radius: 0.875rem;
    color: var(--game-text);
    font-family: 'Montserrat', sans-serif;
    font-size: 1.9rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    /* Kills the ~300ms double-tap-to-zoom wait. At this pace that delay is the
       difference between a fast answer and a timed-out one. */
    touch-action: manipulation;
    transition: background-color 0.12s, border-color 0.12s, transform 0.1s;
}

.ts-tile:active {
    transform: scale(0.97);
}

.ts-tile-right {
    background-color: rgba(57, 181, 74, 0.35);
    border-color: var(--game-success);
    animation: ts-tile-pop 120ms ease-out;
}

@keyframes ts-tile-pop {
    0%   { transform: scale(1); }
    55%  { transform: scale(1.06); }
    100% { transform: scale(1); }
}

/* Wrong is the accent, not a new red. Green vs violet separates on lightness as
   well as hue, so it still reads for a colour-blind child. */
.ts-tile-wrong {
    background-color: rgba(124, 58, 237, 0.32);
    border-color: var(--game-accent-light);
    animation: ts-tile-shake 200ms ease-in-out;
}

@keyframes ts-tile-shake {
    0%, 100% { transform: translate3d(0, 0, 0); }
    25%      { transform: translate3d(-5px, 0, 0); }
    75%      { transform: translate3d(5px, 0, 0); }
}

/* ---------- Typed answer (Lock In) ---------- */
.ts-numpad-wrap {
    flex-shrink: 0;
}

.ts-answer {
    min-height: 3.25rem;
    margin: 0 auto 0.75rem;
    max-width: 8ch;
    padding-bottom: 0.25rem;
    border-bottom: 3px solid var(--game-highlight);
    text-align: center;
    font-size: 2.25rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--game-highlight);
    font-variant-numeric: tabular-nums;
}

/* ---------- Sprint summary ----------
   Sprint never submits a score, so it never reaches the shared results screen
   and prints its own tally here. */
.ts-summary {
    text-align: center;
    padding: 0.5rem 0;
}

.ts-summary-num {
    font-size: 3.5rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--game-highlight);
    font-variant-numeric: tabular-nums;
    animation: ts-summary-pop 320ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

@keyframes ts-summary-pop {
    from { opacity: 0; transform: scale(0.7); }
    to   { opacity: 1; transform: scale(1); }
}

.ts-summary-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.5rem 0;
    border-top: 1px solid var(--game-border);
    font-size: 1.05rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.ts-summary-label {
    color: var(--game-muted);
    font-weight: 600;
    text-align: left;
}

/* ---------- The fluency grid ----------
   13 columns: a header row and a header column around the 12x12. The wrapper
   scrolls sideways so the page body never does — a child dragging the grid must
   not drag the whole app off-screen. */
.ts-grid-wrap {
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 0.5rem;
}

.ts-grid {
    display: grid;
    grid-template-columns: repeat(13, minmax(24px, 1fr));
    gap: 2px;
    /* 13 x 24px + 12 gaps still fits a 360px viewport inside #game-app's
       padding, so the common case never scrolls; anything narrower does. */
    min-width: 336px;
}

.ts-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1;
    border-radius: 3px;
    font-size: clamp(0.55rem, 2.1vw, 0.8rem);
    font-weight: 600;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    transition: background-color 0.25s ease;
}

.ts-cell-hdr {
    background-color: transparent;
    color: var(--game-muted);
    font-weight: 700;
}

/* The four states climb in lightness as well as hue: 13% -> 35% -> 58% -> 64%,
   and in saturation the same way. A child who cannot separate violet from gold
   still sees four clearly different greys, which is the whole point of a grid
   you are meant to read at a glance. */
.ts-cell-dark {
    background-color: #1E1B26;
    color: #6E6A7A;
}

.ts-cell-learning {
    background-color: #4C1D95;
    color: #EDE9FE;
}

.ts-cell-bright {
    background-color: #7C3AED;
    color: #FFFFFF;
}

/* Locked is a lighter tint of the gold highlight rather than the token itself,
   so it stays the brightest step of the ladder instead of dropping below
   "bright". The inset ring means locked never depends on colour alone. */
.ts-cell-locked {
    background-color: #FCD34D;
    color: #2A2000;
    font-weight: 700;
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.45);
}

/* ---------- Legend ----------
   A swatch carries a .ts-cell-* class too, so it picks up the exact colour of
   the state it labels — one definition, no chance of the two drifting apart. */
.ts-legend {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 1rem;
    margin-top: 0.75rem;
    font-size: 0.8rem;
    color: var(--game-muted);
}

.ts-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 3px;
    margin-right: 0.35rem;
    vertical-align: -2px;
}

/* ---------- What changed this round ---------- */
.ts-delta {
    margin-top: 1rem;
}

.ts-delta-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.35rem;
    background-color: var(--game-surface);
    border-left: 3px solid var(--game-highlight);
    border-radius: 0.5rem;
    font-size: 0.95rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    animation: ts-delta-in 250ms ease-out;
}

@keyframes ts-delta-in {
    from { opacity: 0; transform: translate3d(-10px, 0, 0); }
    to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* ---------- Responsive ----------
   game-core turns .ss-screen into a column flexbox under 768px; these are the
   Times Table Stars pieces that need to give ground in that layout. */
@media (max-width: 767.98px) {
    .ts-fact {
        min-height: 4rem;
        font-size: 2.75rem;
    }

    .ts-tiles {
        gap: 0.5rem;
    }

    .ts-tile {
        font-size: 1.65rem;
    }

    /* Sits with the on-screen pad in the thumb zone at the bottom of the screen. */
    .ts-numpad-wrap {
        margin-top: auto;
        padding-bottom: 0.25rem;
    }

    .ts-answer {
        min-height: 2.75rem;
        font-size: 1.9rem;
    }

    .ts-summary-num {
        font-size: 2.75rem;
    }
}

/* ---------- Reduced motion ----------
   Every animation and transition this file introduces, switched off. The JS
   also collapses its own timings to TimesConfig.REDUCED_MOTION_MS, so the game
   still advances — it just stops moving. */
@media (prefers-reduced-motion: reduce) {
    .ts-chip,
    .ts-go,
    .ts-tile,
    .ts-cell,
    .ts-bar-fill,
    .ts-ghost {
        transition: none;
    }

    .ts-chip:active,
    .ts-go:active,
    .ts-tile:active {
        transform: none;
    }

    .ts-clock-urgent,
    .ts-streak-hot,
    .ts-fact-in,
    .ts-fact-out,
    .ts-tile-right,
    .ts-tile-wrong,
    .ts-summary-num,
    .ts-delta-row {
        animation: none;
    }
}
