/* Shared design tokens + base resets. Extracted from
   core/_design_tokens.html; that template now emits the <link> tags.
   Colours, type, focus styles and motion behaviour for every page. */
/* Shared design tokens + base resets - included inside <head> on every page so
   colours, type, focus styles, and motion behaviour stay consistent. */

:root {
    /* ── Brand ───────────────────────────────────────────────────────────── */
    --brand:            #22c55e;   /* bright green — decorative fills / success  */
    --brand-strong:     #15803d;   /* accessible green — links, text-on-white (AA) */
    --brand-strong-hover:#166534;
    --brand-tint:       #f0fdf4;   /* pale green surface (hover/selected)        */

    /* Top bar — the bright welcome-page green (#22c55e) with white text, to
       match home.html. Change these two tokens to restyle every top bar.
       (For WCAG AA on this exact green, set --topbar-text to #06281a instead.) */
    --topbar-bg:        #22c55e;
    --topbar-text:      #ffffff;

    /* ── Neutrals ────────────────────────────────────────────────────────── */
    --bg:        #f8f8f8;
    --surface:   #ffffff;
    --text:      #1e293b;
    --muted:     #64748b;
    --border:    #cbd5e1;

    /* ── Semantic accents ────────────────────────────────────────────────────
       One source of truth for the non-brand accents used across the chrome
       (drawers, badges, buttons). Each has a saturated base, a darker "strong"
       for hover/text, and a pale tint surface. Prefer these over one-off hexes
       so the whole UI stays coherent and themable from one place.
         info    — blue   : classes, links, neutral emphasis
         danger  — red    : reviews due, destructive actions
         warning — amber  : timeouts, caution
         accent  — indigo : quizzes / "next" actions  */
    --info:          #2563eb;  --info-strong:   #1d4ed8;  --info-tint:    #eff6ff;
    --danger:        #ef4444;  --danger-strong: #dc2626;  --danger-tint:  #fee2e2;
    --warning:       #f59e0b;  --warning-strong:#b45309;  --warning-tint: #fffbeb;
    --accent:        #6366f1;  --accent-strong: #4f46e5;  --accent-tint:  #eef2ff;

    /* ── Shape & depth ───────────────────────────────────────────────────── */
    --radius-sm: 8px;
    --radius:    12px;
    --radius-lg: 20px;
    --radius-pill: 999px;
    --shadow-sm: 0 2px 8px rgba(0,0,0,.08);
    --shadow:    0 4px 16px rgba(0,0,0,.10);

    /* ── Type ────────────────────────────────────────────────────────────── */
    --font: 'Nunito', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;

    /* Lesson-content text-size multiplier, controlled by the A−/A/A+ widget and
       persisted per learner. 1 = default; the widget clamps roughly 0.8–1.6. */
    --content-scale: 1;

    /* ── Focus ───────────────────────────────────────────────────────────────
       TWO-TONE keyboard-focus ring. Every focus-visible ring in the app points
       at these tokens (base.html, signup, dashboard, graph) so they can never
       drift.

       Why two tones rather than one colour: WCAG 2.2 SC 2.4.11 wants the
       indicator to reach 3:1 against what sits next to it, and this app puts
       focusable controls on backgrounds that span the whole luminance range —
       a white card, a saturated subject banner (green/yellow/red/indigo), a
       dark toast, the walnut bookshelf. The previous single blue (#1d4ed8)
       managed 6.7:1 on white but only 2.94:1 on the green top bar and 1.34:1
       on the green primary button, i.e. invisible exactly where the primary
       call to action lives.

       No single colour clears 3:1 everywhere, so the ring carries one dark
       band and one light band: whichever background it lands on, one of the
       two contrasts. Measured for --focus-ring #0b1220:
         vs white 18.7 · page 17.6 · topbar green 8.2 · brand button 3.7 ·
         computers yellow 9.8 · math blue 5.1 · music 4.4 · language 5.0
       and on the dark surfaces where that band goes quiet (#1e293b toast,
       walnut) the white halo carries it instead.

       Geometry: halo occupies 0–2px outside the border box (box-shadow spread),
       the dark band 2–5px (outline at offset 2px), so the two abut exactly. */
    --focus-ring: #0b1220;
    --focus-ring-halo: #ffffff;
}

*, *::before, *::after { box-sizing: border-box; }

/* Body text defaults to a regular weight — bold is reserved for headings and
   buttons so hierarchy reads clearly and long passages stay easy on the eyes. */
body { font-family: var(--font); color: var(--text); font-weight: 400; }

/* Keyboard focus visible everywhere (mouse clicks stay clean via :focus-visible).
   The box-shadow halo deliberately replaces any decorative shadow the control
   carries for the moment it holds keyboard focus — a flat, high-contrast ring
   reads better than a soft drop shadow, and it only applies while focused. */
:focus-visible {
    outline: 3px solid var(--focus-ring);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--focus-ring-halo);
    border-radius: 4px;
}

/* Forced-colours / Windows High Contrast: the system supplies its own focus
   colour, and a hard-coded one there is worse than none. */
@media (forced-colors: active) {
    :focus-visible { outline-color: Highlight; box-shadow: none; }
}

/* Respect users who ask for less motion (calms the pulsing/animated UI) */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
}

/* ── Skeleton loaders (gentle pulse — no moving sweep) ───────────────────────
   Shared placeholders used while async content (the node graph, lesson images)
   is still arriving. Reduced-motion users get a static block automatically via
   the media query above. */
@keyframes vsPulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }

.vs-skeleton {
    background: var(--border);
    border-radius: var(--radius-sm);
    animation: vsPulse 1.5s ease-in-out infinite;
}

/* Lesson-image placeholder: a pulsing block holds the image's space and the
   real image fades in once loaded (prevents pop-in / layout shift). */
.vs-img-skel { position: relative; overflow: hidden; }
/* The pulse stays hidden until JS adds .vs-show (after VS_SKELETON_DELAY_MS),
   so quick-loading images never flash a placeholder. The wrapper still reserves
   the image's space immediately, so revealing the pulse causes no layout shift. */
.vs-img-skel::before {
    content: "";
    position: absolute; inset: 0;
    background: var(--border);
    animation: vsPulse 1.5s ease-in-out infinite;
    display: none;
}
.vs-img-skel.vs-show::before { display: block; }
.vs-img-skel.vs-loaded::before { display: none; }   /* must follow .vs-show to win when both set */
.vs-img-skel img { display: block; width: 100%; opacity: 0; transition: opacity .4s ease; }
.vs-img-skel.vs-loaded img { opacity: 1; }

/* ── Page-transition progress bar (top of viewport) ─────────────────────────
   Thin bar that trickles forward while a navigation is in flight, then sweeps
   to full and fades on the next page's load. */
#vs-progress {
    position: fixed;
    top: 0; left: 0;
    height: 3px; width: 0;
    background: var(--brand);
    box-shadow: 0 0 8px rgba(34, 197, 94, .55);
    z-index: 99999;
    opacity: 0;
    pointer-events: none;
    transition: width .2s ease, opacity .35s ease;
}
#vs-progress.vs-active { opacity: 1; }

/* ── Shared top-bar actions ──────────────────────────────────────────────────
   Right-hand controls rendered by core/_topbar.html. The #top-bar container and
   #title styles live per-page (base.html / home.html / dashboard.html) because
   some pages tint the bar per subject; only the action elements are shared here
   so the partial looks identical wherever it is used. */
.tb-actions { display: flex; align-items: center; gap: 16px; }
.tb-user {
    color: var(--topbar-text); font-weight: 800; font-size: 15px;
    opacity: .95; max-width: 30vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-link {
    color: var(--topbar-text); text-decoration: none; font-weight: 800; font-size: 15px;
    opacity: .92; transition: opacity .15s;
}
.tb-link:hover { opacity: 1; text-decoration: underline; }
.tb-link { white-space: nowrap; }
@media (max-width: 520px) {
    /* A teacher's bar carries two links (Teaching · My learning); at phone width
       the greeting gives up its room so neither link wraps. */
    .tb-actions { gap: 10px; margin-left: 12px; }
    .tb-user { display: none; }
    .tb-link { font-size: 14px; }
    .tb-logout { padding: 6px 11px; font-size: 13px; white-space: nowrap; }
}
.tb-logout {
    background: rgba(255,255,255,.18); border: none; color: var(--topbar-text);
    font-family: var(--font); font-size: 14px; font-weight: 800; cursor: pointer;
    padding: 7px 16px; border-radius: var(--radius-pill); transition: background .15s;
}
.tb-logout:hover { background: rgba(255,255,255,.30); }
.tb-cta {
    background: var(--surface); color: var(--brand-strong);
    text-decoration: none; font-weight: 900; font-size: 14px;
    padding: 8px 18px; border-radius: var(--radius-pill); transition: transform .1s, box-shadow .15s;
    box-shadow: 0 2px 8px rgba(0,0,0,.12);
}
.tb-cta:hover { box-shadow: 0 4px 14px rgba(0,0,0,.18); }
.tb-cta:active { transform: scale(.97); }

/* Supervisor "view as student" pill (node pages only). */
.tb-admin-toggle {
    background: rgba(255,255,255,.18); color: var(--topbar-text, #fff);
    border: 1.5px solid rgba(255,255,255,.55); border-radius: var(--radius-pill);
    padding: 6px 15px; font-family: inherit; font-size: 13px; font-weight: 800;
    letter-spacing: .3px; white-space: nowrap; cursor: pointer;
}
@media (max-width: 640px), (max-height: 520px) {
    /* The banner also carries Back and the review badge at this size, so the
       pill gives up the width it doesn't need. */
    .tb-admin-toggle { padding: 4px 9px; font-size: 11px; letter-spacing: 0; }
}

/* ── A teacher's bar ─────────────────────────────────────────────────────────
   The switch between teaching and their own learning sits in the middle (where
   a supervisor's dashboard shows User Statistics); "Hello, name" opens the menu
   that switches between classes. Below 860px the middle gives up its room and
   the switch moves into the menu. */
.tb-center { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
             display: flex; align-items: center; gap: 18px; white-space: nowrap; }
.tb-switch { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
             color: var(--topbar-text); text-decoration: none; font-family: var(--font);
             font-size: 14.5px; font-weight: 800; letter-spacing: .2px;
             background: rgba(255,255,255,.16); border: 1.5px solid rgba(255,255,255,.5);
             border-radius: var(--radius-pill); padding: 6px 16px; transition: background .15s; }
.tb-switch:hover { background: rgba(255,255,255,.3); }
.tb-hello { position: relative; }
.tb-hello-btn { display: inline-flex; align-items: center; gap: 5px; max-width: 46vw;
                background: none; border: none; cursor: pointer; border-radius: 8px;
                padding: 6px 9px; font-family: var(--font); font-size: 16px; font-weight: 700;
                color: var(--topbar-text); white-space: nowrap; overflow: hidden;
                text-overflow: ellipsis; transition: background .12s; }
.tb-hello-btn:hover, .tb-hello-btn[aria-expanded="true"] { background: rgba(255,255,255,.18); }
.tb-hello-btn .caret { font-size: 11px; opacity: .85; }
.tb-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
           width: 270px; max-width: calc(100vw - 24px); max-height: calc(100vh - 84px);
           overflow-y: auto; padding: 6px; background: #fff; border-radius: 14px;
           box-shadow: 0 12px 34px rgba(15,23,42,.2); }
.tb-menu[hidden] { display: none; }
.tb-menu form { margin: 0; }
.tb-menu-label { padding: 9px 12px 5px; font-size: 11px; font-weight: 900; letter-spacing: .6px;
                 text-transform: uppercase; color: var(--muted); }
.tb-menu-item { display: flex; align-items: center; gap: 9px; width: 100%; box-sizing: border-box;
                padding: 9px 12px; border: none; border-radius: 9px; background: none;
                text-align: left; text-decoration: none; cursor: pointer; font-family: var(--font);
                font-size: 14px; font-weight: 800; color: #1e293b; }
.tb-menu-item:hover { background: #f1f5f9; }
.tb-menu-item[aria-current="page"] { background: var(--brand-tint); color: var(--brand-strong); }
.tb-menu-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-menu-badge { flex: 0 0 auto; min-width: 20px; text-align: center; font-size: 11.5px;
                 font-weight: 900; border-radius: 999px; padding: 1px 7px;
                 background: var(--danger-tint); color: var(--danger-strong); }
.tb-menu-new { color: var(--brand-strong); }
.tb-menu-quiet { color: var(--muted); font-weight: 700; }
.tb-menu-sep { height: 1px; margin: 6px 6px; background: #eef2f6; }
.tb-menu-danger { color: var(--danger-strong); }
.tb-menu-danger:hover { background: #fef2f2; }
.tb-menu-phone { display: none; }
@media (max-width: 860px) {
    .tb-center .tb-switch { display: none; }
    .tb-menu-phone { display: flex; }
}
@media (max-width: 520px) {
    .tb-hello-btn { font-size: 14.5px; padding: 6px 6px; }
}
