/* ============================================================
   Biblio Buddy — stylesheet
   Aligned to Crayon Box, Inc. Brand Guidelines v1.0

   Palette : Warm Paper (online surface)
   Type    : Manrope (display) · Geist (body) · Geist Mono (labels)
   Voice   : concrete, sequential, quietly confident
   No italics — hierarchy comes from weight and colour.
   ============================================================ */


/* ───── Tokens ─────
   Names match the brand guidelines verbatim so future designers
   reading both can grep across cleanly. */

:root {
    /* Surfaces */
    --paper:        #ECE6DA;
    --paper-2:      #F3EEE3;
    --panel:        #FBF7EE;
    --panel-2:      #FFFFFF;

    /* Ink */
    --ink:          #1C1B18;
    --ink-soft:     #4A4741;
    --ink-mute:     #8A857B;

    /* Lines */
    --line:         #D7CFBF;
    --line-soft:    #E4DCCC;

    /* Accents */
    --accent:       #1F3A52;   /* focused / selected */
    --accent-warm:  #B8522E;   /* errors / danger */

    /* Brand hues — canonical order: yellow → pink → blue → green */
    --yellow:       #F4C842;
    --pink:         #F08FB6;
    --blue:         #7AAFE8;
    --green:        #5DB07F;
    /* The fifth crayon (only in the logo) */
    --orange:       #F0A86E;

    /* Brand gradient — the signature applied to highlighted nouns,
       CTA hovers, and the auth surface. */
    --brand-gradient: linear-gradient(
        90deg,
        var(--yellow) 0%,
        var(--pink)   33%,
        var(--blue)   66%,
        var(--green)  100%
    );

    /* Shadows — warm, never neutral */
    --shadow-sm: 0 1px 2px rgba(28, 27, 24, 0.06);
    --shadow-md: 0 4px 14px rgba(28, 27, 24, 0.08), 0 1px 3px rgba(28, 27, 24, 0.05);
    --shadow-lg: 0 18px 40px rgba(28, 27, 24, 0.12), 0 4px 10px rgba(28, 27, 24, 0.06);

    /* Type scale — clamps where appropriate */
    --fs-mono:    11px;
    --fs-xs:      12px;
    --fs-sm:      13px;
    --fs-body:    14px;
    --fs-md:      16px;
    --fs-lg:      20px;
    --fs-xl:      clamp(28px, 3.6vw, 44px);   /* DISPLAY SECTION */
    --fs-hero:    clamp(40px, 6.4vw, 88px);   /* DISPLAY HERO    */

    /* Spacing — a calmer scale than the storybook draft */
    --s-1:  4px;
    --s-2:  8px;
    --s-3:  12px;
    --s-4:  20px;
    --s-5:  28px;
    --s-6:  40px;
    --s-7:  64px;

    /* Radii */
    --r-sm: 6px;
    --r-md: 10px;
    --r-lg: 16px;
    --r-pill: 999px;

    /* Motion — measured */
    --ease: cubic-bezier(0.32, 0.08, 0.24, 1);
}


/* ───── Reset & base ───── */

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

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

body {
    margin: 0;
    min-height: 100vh;
    min-height: 100dvh;
    font-family: 'Geist', system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-size: var(--fs-body);
    line-height: 1.55;
    letter-spacing: -0.005em;
    color: var(--ink);
    background-color: var(--paper);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Faint paper warmth via two-corner radial — calm enough that
       the brand thesis ("printed matter rather than software") reads
       without leaning on a heavy texture. */
    background-image:
        radial-gradient(circle at 8% 6%,  rgba(244, 200, 66, 0.08), transparent 50%),
        radial-gradient(circle at 95% 92%, rgba(122, 175, 232, 0.06), transparent 50%);
    background-attachment: fixed;
}

h1, h2, h3, h4 {
    font-family: 'Manrope', system-ui, -apple-system, sans-serif;
    font-weight: 700;
    color: var(--ink);
    margin: 0;
    line-height: 1.1;
    letter-spacing: -0.025em;
}

button, input, select, textarea {
    font-family: inherit;
    font-size: inherit;
    color: inherit;
}
input, select, textarea { font-size: max(16px, 1rem); }   /* no iOS zoom */
button { cursor: pointer; }

.hidden { display: none !important; }


/* ───── Type utilities ───── */

/* EYEBROW — Geist Mono, tracked, uppercase. Used for status pills,
   section labels, transcript turn headers, anywhere a metadata-style
   nameplate is wanted. */
.eyebrow {
    font-family: 'Geist Mono', ui-monospace, SFMono-Regular, monospace;
    font-weight: 400;
    font-size: var(--fs-mono);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    /* Brand near-black, not the muted grey. Eyebrows in this app
       act as section titles in a quiet-but-readable kiosk context;
       students see them at a distance, so dark wins over subtle. */
    color: var(--ink);
    margin: 0;
}

.eyebrow-on-dark { color: rgba(236, 230, 218, 0.7); }

/* MUTED — secondary text. Used for Spanish translations: same weight
   as the primary, weaker colour. The brand explicitly forbids italics,
   so colour does the work. */
.muted {
    display: block;
    color: var(--ink-mute);
    margin-top: var(--s-1);
}

/* GRAD-TEXT — the brand-hue gradient applied to a single highlighted
   noun in display copy. Used sparingly — once per page at most. */
.grad-text {
    background: var(--brand-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}


/* ============================================================
   LOGIN VIEW
   Per brand guidelines, the four-hue gradient runs across the
   auth page background. We compose it from four soft radial
   blobs in the canonical corners — feels painted, not striped.
   ============================================================ */

.login-view {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: var(--s-4);
    background:
        radial-gradient(ellipse 70% 60% at 12% 14%, rgba(244, 200, 66, 0.55), transparent 70%),
        radial-gradient(ellipse 65% 55% at 88% 18%, rgba(240, 143, 182, 0.50), transparent 70%),
        radial-gradient(ellipse 70% 60% at 90% 88%, rgba(122, 175, 232, 0.50), transparent 70%),
        radial-gradient(ellipse 65% 55% at 10% 90%, rgba(93, 176, 127, 0.50), transparent 70%),
        var(--paper);
}

.login-card {
    width: 100%;
    max-width: 28rem;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: var(--s-6) var(--s-5);
    box-shadow: var(--shadow-lg);
    text-align: center;
}

.login-mark {
    width: 64px;
    height: 64px;
    margin-bottom: var(--s-4);
    /* Image is portrait (taller than wide), container is square —
       contain keeps the aspect ratio with a touch of horizontal
       letterbox padding rather than squashing the shape. */
    object-fit: contain;
}

.login-title {
    font-size: var(--fs-xl);
    font-weight: 700;
    margin: var(--s-2) 0 var(--s-3);
}

.login-sub {
    font-size: var(--fs-md);
    color: var(--ink);
    margin: 0 0 var(--s-4);
    line-height: 1.45;
}

.login-body {
    color: var(--ink-soft);
    margin: 0 0 var(--s-5);
    font-size: var(--fs-body);
}

.btn-hf {
    width: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    padding: 14px var(--s-4);
    background: var(--ink);
    color: var(--paper);
    border: none;
    border-radius: var(--r-md);
    font-family: 'Geist', sans-serif;
    font-weight: 600;
    font-size: var(--fs-md);
    transition: transform 160ms var(--ease), background 160ms var(--ease), box-shadow 200ms var(--ease);
    box-shadow: var(--shadow-md);
}
.btn-hf:hover {
    background: #000;
    transform: translateY(-1px);
    box-shadow: var(--shadow-lg);
}
.btn-hf:active { transform: translateY(0); }

.login-foot {
    margin: var(--s-4) 0 0;
    font-family: 'Geist Mono', ui-monospace, monospace;
    color: var(--ink-mute);
    font-size: var(--fs-mono);
    letter-spacing: 0.02em;
}


/* ============================================================
   TOP BAR
   ============================================================ */

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
    background: rgba(236, 230, 218, 0.85);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    position: sticky;
    top: 0;
    z-index: 10;
}
/* The brand signature: a 2px gradient hairline under the topbar.
   Present on every page; subtle but recognisable. */
.topbar::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    background: var(--brand-gradient);
    opacity: 0.85;
    pointer-events: none;
}

.wordmark {
    display: inline-flex;
    align-items: center;
    gap: var(--s-3);
    text-decoration: none;
    color: inherit;
}
.wordmark-mark {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    /* Portrait aspect ratio fits without squashing. */
    object-fit: contain;
    /* Soft drop so the mark reads on the light topbar without a
       hard outline. Works correctly with the transparent SVG —
       traces the line art itself, not a rectangle. */
    filter: drop-shadow(0 1px 2px rgba(28,27,24,0.10));
}
.wordmark-text {
    display: flex;
    flex-direction: column;
    line-height: 1.05;
}
.wordmark-name {
    font-family: 'Manrope', sans-serif;
    font-weight: 700;
    font-size: var(--fs-lg);
    letter-spacing: -0.025em;
    color: var(--ink);
}
.wordmark-tag {
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-mute);
    margin-top: 2px;
}

.topbar-right {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
}

.status-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: 6px 12px;
    border-radius: var(--r-pill);
    background: var(--panel);
    border: 1px solid var(--line);
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-soft);
    transition: background 200ms var(--ease), color 200ms var(--ease), border-color 200ms var(--ease);
}
.status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--ink-mute);
}
.status-pill[data-state="idle"]       .status-dot { background: var(--ink-mute); }
.status-pill[data-state="connecting"] .status-dot { background: var(--yellow); animation: pulse-dot 1.2s infinite; }
.status-pill[data-state="connected"]  .status-dot { background: var(--green); }
.status-pill[data-state="error"]      .status-dot { background: var(--accent-warm); }
.status-pill[data-state="error"]      { color: var(--accent-warm); border-color: var(--accent-warm); }

@keyframes pulse-dot {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.4; }
}

.icon-btn {
    display: inline-grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--ink-soft);
    border-radius: var(--r-md);
    transition: background 160ms var(--ease), color 160ms var(--ease), border-color 160ms var(--ease);
}
.icon-btn:hover {
    background: var(--panel);
    border-color: var(--line);
    color: var(--ink);
}
.icon-btn svg { width: 20px; height: 20px; }


/* ============================================================
   ROBOT PICKER
   ============================================================ */

.robot-picker {
    margin: var(--s-5) auto var(--s-4);
    max-width: 32rem;
    padding: var(--s-4);
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
}
.robot-picker-title {
    font-size: var(--fs-lg);
    margin: var(--s-1) 0 var(--s-3);
}
.robot-list { display: grid; gap: var(--s-2); }
.robot-list-empty {
    color: var(--ink-mute);
    text-align: center;
    padding: var(--s-3);
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    letter-spacing: 0.18em;
    text-transform: uppercase;
}
.robot-card {
    padding: var(--s-3);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    cursor: pointer;
    transition: border-color 160ms var(--ease), transform 160ms var(--ease);
}
.robot-card:hover { border-color: var(--accent); }
.robot-card.selected { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(31, 58, 82, 0.15); }
.robot-card .name { font-weight: 600; color: var(--ink); }
.robot-card .id {
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    letter-spacing: 0.08em;
    color: var(--ink-mute);
    margin-top: 2px;
}


/* ============================================================
   STAGE — kid-facing surface
   ============================================================ */

.stage {
    max-width: 38rem;
    margin: 0 auto;
    padding: var(--s-4);
    display: grid;
    gap: var(--s-5);
}

/* ───── Welcome ───── */
.welcome { padding: var(--s-3) 0 0; }
.welcome-h1 {
    font-size: var(--fs-hero);
    font-weight: 800;
    letter-spacing: -0.035em;
    line-height: 1.0;
}
.welcome-h1-es {
    display: block;
    color: var(--ink-mute);
    /* Slightly lighter weight to lower visual rank without italics
       and without changing colour past the brand-safe range. */
    font-weight: 600;
    margin-top: var(--s-2);
}
.welcome-sub {
    font-size: var(--fs-md);
    color: var(--ink-soft);
    margin: var(--s-3) 0 0;
    line-height: 1.55;
}


/* ───── Robot video window ─────
   `.robot-view` is the labelled section wrapper that pairs an
   eyebrow heading with the video. It lives directly above the
   transcript card so the librarian sees "what the robot sees"
   and "our conversation" as a paired set. `.robot-window` is the
   actual video container inside — kept unchanged so the dark
   inked surface still frames the live feed correctly. */
.robot-view {
    display: grid;
    gap: var(--s-2);
    justify-items: center;
}
.robot-view > .eyebrow {
    text-align: center;
}
.robot-window {
    position: relative;
    margin: 0 auto;
    width: 100%;
    max-width: 22rem;
    aspect-ratio: 4 / 3;
    background: var(--ink);
    border-radius: var(--r-lg);
    overflow: hidden;
    box-shadow: var(--shadow-md);
}
.robot-window video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.robot-window-placeholder {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    text-align: center;
    gap: var(--s-2);
    padding: var(--s-4);
    color: rgba(236, 230, 218, 0.65);
    font-size: var(--fs-sm);
    pointer-events: none;
}
.robot-window-placeholder p { margin: 0; }
.robot-window.has-stream .robot-window-placeholder { display: none; }


/* ───── Language picker — three buttons, one per hue ─────
   `.lang-section` is the section wrapper that pairs the bilingual
   eyebrow title with the three-button picker. `.lang-picker` is
   the inner three-column grid. */
.lang-section {
    display: grid;
    gap: var(--s-2);
}
.lang-picker {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-3);
}

.lang-btn {
    position: relative;
    display: grid;
    place-items: center;
    gap: var(--s-2);
    padding: var(--s-4) var(--s-3);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    color: var(--ink);
    min-height: 6.25rem;
    transition: border-color 180ms var(--ease), transform 160ms var(--ease), background 200ms var(--ease);
    overflow: hidden;
    /* Each button carries a hue accent stripe along the top edge,
       coloured per data-hue. Quietly present on idle, brighter on
       selected. */
}
.lang-btn .lang-hue {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    opacity: 0.4;
    transition: opacity 200ms var(--ease), height 200ms var(--ease);
}
.lang-btn[data-hue="yellow"]   .lang-hue { background: var(--yellow); }
.lang-btn[data-hue="pink"]     .lang-hue { background: var(--pink); }
.lang-btn[data-hue="gradient"] .lang-hue { background: var(--brand-gradient); }

.lang-btn:hover {
    border-color: var(--ink-mute);
    transform: translateY(-1px);
}
.lang-btn:hover .lang-hue { opacity: 1; height: 4px; }

.lang-btn[aria-checked="true"] {
    border-color: var(--ink);
    background: var(--panel);
}
.lang-btn[aria-checked="true"] .lang-hue { opacity: 1; height: 4px; }

.lang-flag { font-size: 1.6rem; line-height: 1; }
.lang-label {
    font-family: 'Geist', sans-serif;
    font-weight: 600;
    font-size: var(--fs-md);
    text-align: center;
    line-height: 1.2;
}
.lang-label-sub {
    display: block;
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-mute);
    margin-top: 4px;
}
.lang-btn[aria-checked="true"] .lang-label-sub { color: var(--ink-soft); }


/* ───── Mic — the hero action ───── */
.mic-stage {
    display: grid;
    justify-items: center;
    gap: var(--s-3);
    padding: var(--s-3) 0;
}

.mic-btn {
    position: relative;
    width: 11rem;
    height: 11rem;
    border-radius: 50%;
    border: none;
    background: var(--ink);
    color: var(--paper);
    display: grid;
    place-items: center;
    transition: transform 240ms var(--ease), background 240ms var(--ease);
    box-shadow: var(--shadow-lg);
}
.mic-btn:hover { background: #000; transform: translateY(-2px); }
.mic-btn:active { transform: translateY(1px); }

.mic-icon {
    width: 3.4rem;
    height: 3.4rem;
    position: relative;
    z-index: 1;
    transition: transform 300ms var(--ease);
}

/* Ring: a static brand-hue gradient just outside the dark circle —
   the signature visible at rest. */
.mic-btn .mic-ring {
    position: absolute;
    inset: -8px;
    border-radius: 50%;
    padding: 4px;
    background: var(--brand-gradient);
    /* Mask trick: keep only the gradient ring, not its inside. */
    -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
            mask-composite: exclude;
    opacity: 0.55;
    transition: opacity 240ms var(--ease), inset 240ms var(--ease);
}
.mic-btn:hover .mic-ring { opacity: 0.85; inset: -10px; }

/* Pulse: appears only on active states. Measured, not bouncy. */
.mic-btn .mic-pulse {
    position: absolute;
    inset: -8px;
    border-radius: 50%;
    border: 3px solid var(--ink);
    opacity: 0;
    pointer-events: none;
}

.mic-btn[data-state="listening"] .mic-ring { opacity: 1; inset: -12px; }
.mic-btn[data-state="listening"] .mic-pulse {
    border-color: var(--green);
    animation: mic-pulse 1.6s ease-out infinite;
}
.mic-btn[data-state="thinking"]  .mic-ring { opacity: 1; }
.mic-btn[data-state="thinking"]  .mic-icon { animation: mic-spin-slow 4s linear infinite; }
.mic-btn[data-state="speaking"]  .mic-ring { opacity: 1; inset: -10px; }
.mic-btn[data-state="speaking"]  .mic-pulse {
    border-color: var(--blue);
    animation: mic-pulse 2.2s ease-out infinite;
}

@keyframes mic-pulse {
    0%   { opacity: 0.55; transform: scale(1); }
    100% { opacity: 0;    transform: scale(1.35); }
}
@keyframes mic-spin-slow {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

.mic-prompt {
    text-align: center;
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--ink);
    margin: 0;
}


/* ───── Conversation starters ─────
   Soft suggestion cards that pre-fill a common question and send
   it to the robot. Visually quieter than the lang picker (no hue
   accents) — they're alternatives to the primary mic CTA, not
   categorical choices. Bilingual: English primary on top, Spanish
   muted below, but the actual question sent matches the current
   language mode (see the JS click handler in app.js). */

.quick-starters {
    display: grid;
    gap: var(--s-2);
}
.quick-starter-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--s-2);
}
@media (min-width: 520px) {
    /* Side-by-side once there's room. Below this, stacked so neither
       card is forced to wrap awkwardly. */
    .quick-starter-row {
        grid-template-columns: 1fr 1fr;
    }
}

.quick-starter {
    display: grid;
    gap: 2px;
    padding: var(--s-3) var(--s-3);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    text-align: left;
    cursor: pointer;
    font: inherit;
    color: var(--ink);
    transition:
        border-color 160ms var(--ease),
        background 200ms var(--ease),
        transform 120ms var(--ease),
        box-shadow 160ms var(--ease);
}
.quick-starter:hover {
    border-color: var(--accent);
    background: var(--panel);
    transform: translateY(-1px);
    box-shadow: var(--shadow-sm);
}
.quick-starter:active {
    transform: translateY(0);
}
.quick-starter:disabled {
    opacity: 0.55;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}
.quick-starter-en {
    font-family: 'Geist', sans-serif;
    font-weight: 500;
    font-size: var(--fs-body);
    line-height: 1.3;
}
.quick-starter-es {
    font-size: var(--fs-sm);
    line-height: 1.3;
}


/* ───── Text fallback ───── */
.text-fallback {
    display: grid;
    gap: var(--s-2);
}
.text-fallback-form {
    display: flex;
    gap: var(--s-2);
    align-items: stretch;
}
.text-input {
    flex: 1;
    padding: 12px var(--s-3);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    color: var(--ink);
    outline: none;
    transition: border-color 160ms var(--ease), box-shadow 160ms var(--ease);
}
.text-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(31, 58, 82, 0.15);
}
.text-input::placeholder { color: var(--ink-mute); }

.text-send {
    width: 48px;
    background: var(--ink);
    color: var(--paper);
    border: none;
    border-radius: var(--r-md);
    display: grid;
    place-items: center;
    transition: background 160ms var(--ease), transform 160ms var(--ease);
}
.text-send:hover { background: #000; transform: translateY(-1px); }
.text-send svg { width: 18px; height: 18px; }


/* ───── Transcript ───── */
.transcript {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: var(--s-4);
    min-height: 8rem;
    display: grid;
    gap: var(--s-3);
}
.transcript-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--s-3);
}

.transcript-turn {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--s-2) var(--s-3);
    align-items: start;
    padding-top: var(--s-2);
    border-top: 1px solid var(--line-soft);
}
.transcript-turn:first-child { border-top: 0; padding-top: 0; }

.transcript-turn-bullet {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: 6px;
}
.transcript-turn.from-user  .transcript-turn-bullet { background: var(--blue); }
.transcript-turn.from-buddy .transcript-turn-bullet { background: var(--pink); }

.transcript-turn-body { display: grid; gap: 2px; }
.transcript-turn-who {
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    font-weight: 400;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-mute);
}
.transcript-turn-text { font-size: var(--fs-body); color: var(--ink); line-height: 1.55; }


/* ───── Footer ───── */
.footer {
    text-align: center;
    padding: var(--s-6) var(--s-4) var(--s-5);
    color: var(--ink-mute);
}
.footer .eyebrow { color: var(--ink-mute); margin-bottom: var(--s-2); }
.footer-thesis { margin: 0; font-size: var(--fs-sm); color: var(--ink-soft); }


/* ============================================================
   ADMIN MODAL
   ============================================================ */

.modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: end center;
}
.modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(28, 27, 24, 0.55);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    animation: fade-in 200ms var(--ease);
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }

.modal-card {
    position: relative;
    width: 100%;
    max-width: 42rem;
    max-height: 92dvh;
    background: var(--paper);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    display: grid;
    grid-template-rows: auto 1fr auto;
    box-shadow: var(--shadow-lg);
    animation: slide-up 280ms var(--ease);
    border: 1px solid var(--line);
    border-bottom: 0;
}

.modal-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s-3);
    padding: var(--s-4) var(--s-5);
    border-bottom: 1px solid var(--line);
    background: var(--panel);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.modal-header .eyebrow { margin-bottom: var(--s-1); }
.modal-header h2 { font-size: var(--fs-xl); }

.modal-close {
    display: inline-grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--ink-soft);
    border-radius: var(--r-md);
    transition: background 160ms var(--ease), border-color 160ms var(--ease);
    flex-shrink: 0;
}
.modal-close:hover { background: var(--panel-2); border-color: var(--line); color: var(--ink); }
.modal-close svg { width: 18px; height: 18px; }

.modal-body {
    overflow-y: auto;
    padding: var(--s-5);
    display: grid;
    gap: var(--s-6);
}

.modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--s-2);
    padding: var(--s-4) var(--s-5);
    background: var(--panel);
    border-top: 1px solid var(--line);
}

/* Password gate */
.admin-gate { display: grid; gap: var(--s-2); }
.admin-gate .form-section-title { margin-top: var(--s-1); }
.admin-gate-form {
    display: flex;
    gap: var(--s-2);
    margin-top: var(--s-2);
}
.admin-gate-form .form-input { flex: 1; }
.admin-gate-error {
    color: var(--accent-warm);
    font-size: var(--fs-sm);
    margin: 0;
}

/* Form sections */
.form-section { display: grid; gap: var(--s-2); }
.form-section .eyebrow { margin-bottom: 0; }
.form-section-title {
    font-size: var(--fs-lg);
    font-weight: 600;
    margin: 0;
}
.form-section-help {
    color: var(--ink-soft);
    font-size: var(--fs-sm);
    margin: 0;
    line-height: 1.55;
}
.form-section-help code {
    background: var(--panel);
    border: 1px solid var(--line);
    padding: 1px 6px;
    border-radius: 4px;
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: 0.9em;
}
.form-section-actions {
    display: flex;
    gap: var(--s-2);
    margin-top: var(--s-1);
}

.form-input,
.form-textarea {
    width: 100%;
    padding: 10px var(--s-3);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    color: var(--ink);
    font-size: max(16px, var(--fs-body));
    outline: none;
    transition: border-color 160ms var(--ease), box-shadow 160ms var(--ease);
}
.form-textarea {
    resize: vertical;
    min-height: 4rem;
    line-height: 1.55;
    font-family: 'Geist', sans-serif;
}
.form-input:focus, .form-textarea:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(31, 58, 82, 0.15);
}

/* File drop zone */
.file-drop {
    display: grid;
    justify-items: center;
    gap: var(--s-1);
    padding: var(--s-5);
    background: var(--panel-2);
    border: 1px dashed var(--ink-mute);
    border-radius: var(--r-md);
    cursor: pointer;
    text-align: center;
    transition:
        border-color 160ms var(--ease),
        background 160ms var(--ease),
        box-shadow 160ms var(--ease),
        transform 160ms var(--ease);
}
.file-drop:hover { border-color: var(--accent); background: var(--panel); border-style: solid; }

/* While a file is hovering over the drop zone — gentle pickup,
   solid brand-accent border, and a soft warm shadow. The
   transform scale gives it a touch of lift without being loud
   about it. Children get pointer-events: none so dragleave fires
   on the actual zone boundary rather than every child crossing. */
.file-drop.is-dragover {
    border-style: solid;
    border-color: var(--accent);
    background: var(--panel);
    box-shadow: 0 0 0 4px rgba(31, 58, 82, 0.10), var(--shadow-md);
    transform: translateY(-1px);
}
.file-drop.is-dragover * { pointer-events: none; }

.file-drop-text { color: var(--ink-soft); font-size: var(--fs-body); margin: 0; }
.file-drop-hint {
    color: var(--ink-mute);
    font-size: var(--fs-sm);
    margin: 0;
}
.file-drop-filename {
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    letter-spacing: 0.08em;
    color: var(--green);
    margin: var(--s-1) 0 0;
}
.file-drop-filename:empty { display: none; }

/* ───── Robot controls (volume + sleep/wake) ─────
   Volume slider gets a brand-coloured thumb so it picks up the
   four-hue signature without shouting. Sleep/wake button sits in
   a row with a small status string to its right. */

.volume-control {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    margin-bottom: var(--s-3);
}
.volume-label {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    font-size: var(--fs-body);
    color: var(--ink);
}
.volume-value {
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    letter-spacing: 0.08em;
    color: var(--ink-mute);
    text-transform: uppercase;
}
.volume-help { margin-top: 0; }

/* Range input — custom thumb + track. Browsers split this into
   webkit + moz pseudos; both branches are styled the same. */

input[type="range"]#volumeSlider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 28px;
    background: transparent;
    cursor: pointer;
    margin: 0;
    padding: 0;
}
input[type="range"]#volumeSlider:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* Track — webkit */
input[type="range"]#volumeSlider::-webkit-slider-runnable-track {
    height: 6px;
    background: var(--line);
    border-radius: var(--r-pill);
}
/* Track — moz */
input[type="range"]#volumeSlider::-moz-range-track {
    height: 6px;
    background: var(--line);
    border-radius: var(--r-pill);
}

/* Thumb — webkit. The four-hue brand gradient lands here so the
   slider feels Crayon Box without being loud about it. */
input[type="range"]#volumeSlider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--brand-gradient);
    border: 2px solid var(--panel-2);
    box-shadow: var(--shadow-sm);
    margin-top: -8px;   /* centre on the 6px track */
    cursor: pointer;
    transition: transform 120ms var(--ease), box-shadow 120ms var(--ease);
}
input[type="range"]#volumeSlider::-webkit-slider-thumb:hover {
    transform: scale(1.08);
    box-shadow: var(--shadow-md);
}
input[type="range"]#volumeSlider:disabled::-webkit-slider-thumb {
    background: var(--ink-mute);
    transform: none;
}

/* Thumb — moz */
input[type="range"]#volumeSlider::-moz-range-thumb {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--brand-gradient);
    border: 2px solid var(--panel-2);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    transition: transform 120ms var(--ease), box-shadow 120ms var(--ease);
}
input[type="range"]#volumeSlider::-moz-range-thumb:hover {
    transform: scale(1.08);
    box-shadow: var(--shadow-md);
}
input[type="range"]#volumeSlider:disabled::-moz-range-thumb {
    background: var(--ink-mute);
    transform: none;
}

/* Focus ring — both engines */
input[type="range"]#volumeSlider:focus {
    outline: none;
}
input[type="range"]#volumeSlider:focus::-webkit-slider-thumb {
    box-shadow: 0 0 0 4px rgba(31, 58, 82, 0.15), var(--shadow-sm);
}
input[type="range"]#volumeSlider:focus::-moz-range-thumb {
    box-shadow: 0 0 0 4px rgba(31, 58, 82, 0.15), var(--shadow-sm);
}

/* Sleep / wake row */
.sleep-wake-row {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin-top: var(--s-2);
    flex-wrap: wrap;
}
.sleep-wake-status {
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-mute);
}
.sleep-wake-status[data-state="awake"]      { color: var(--green); }
.sleep-wake-status[data-state="asleep"]     { color: var(--ink-mute); }
.sleep-wake-status[data-state="working"]    { color: var(--accent); }


/* Catalog preview table */
.catalog-preview {
    margin-top: var(--s-2);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow: hidden;
}
.catalog-preview-meta {
    padding: var(--s-2) var(--s-3);
    background: var(--panel);
    border-bottom: 1px solid var(--line);
    margin: 0;
}
.catalog-preview-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
}
.catalog-preview-table th,
.catalog-preview-table td {
    text-align: left;
    padding: 8px var(--s-3);
    border-bottom: 1px solid var(--line-soft);
}
.catalog-preview-table th {
    font-family: 'Geist Mono', ui-monospace, monospace;
    font-size: var(--fs-mono);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-mute);
    font-weight: 400;
    background: var(--panel);
}
.catalog-preview-table tbody tr:last-child td { border-bottom: 0; }

/* Buttons — Ink primary, lined secondary. */
.btn-primary, .btn-secondary {
    padding: 10px 18px;
    border-radius: var(--r-md);
    font-family: 'Geist', sans-serif;
    font-weight: 600;
    font-size: var(--fs-body);
    border: 1px solid transparent;
    transition: transform 160ms var(--ease), background 160ms var(--ease), border-color 160ms var(--ease);
}
.btn-primary {
    background: var(--ink);
    color: var(--paper);
}
.btn-primary:hover { background: #000; transform: translateY(-1px); }

.btn-secondary {
    background: var(--panel-2);
    color: var(--ink);
    border-color: var(--line);
}
.btn-secondary:hover { border-color: var(--ink); }


/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (min-width: 768px) {
    .stage {
        max-width: 44rem;
        padding: var(--s-5);
        gap: var(--s-6);
    }
    .mic-btn { width: 12rem; height: 12rem; }
    .mic-icon { width: 3.8rem; height: 3.8rem; }
    .modal { place-items: center; }
    .modal-card {
        border-radius: var(--r-lg);
        border-bottom: 1px solid var(--line);
        animation: fade-in 220ms var(--ease);
    }
}

@media (min-width: 1100px) {
    .stage {
        max-width: 60rem;
        grid-template-columns: 1fr 1fr;
        grid-template-areas:
            "welcome  welcome"
            "lang     robot"
            "mic      transcript"
            "starters transcript"
            "text     transcript";
        gap: var(--s-5) var(--s-6);
    }
    .welcome         { grid-area: welcome; }
    .robot-view      { grid-area: robot;     align-self: start; }
    .lang-section    { grid-area: lang;      align-self: start; }
    .mic-stage       { grid-area: mic; }
    .quick-starters  { grid-area: starters;  align-self: start; }
    .text-fallback   { grid-area: text;      align-self: start; }
    .transcript      { grid-area: transcript; min-height: 100%; align-self: stretch; }

    /* The transcript card is bounded by its grid area on desktop:
       a long conversation no longer stretches the row, it scrolls
       inside the card. The setup needs three coordinated pieces:
       (1) switch the card from `display: grid` to `display: flex`
       column so the inner list can claim "fill remaining space";
       (2) override `min-height: 100%` to `min-height: 0` so the
       grid lets the card shrink below its content's intrinsic
       size (otherwise grid expands the row to fit and we're back
       to square one); (3) clip overflow on the card and scroll
       on the list. */
    .transcript {
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
    }
    .transcript-list {
        flex: 1 1 0;
        overflow-y: auto;
        min-height: 0;
    }

    /* On desktop the mic-stage shares the left column with smaller
       elements (lang picker above, text input below) and sits next
       to the substantial right-column video + transcript. The
       original 12rem button felt small in that space — 18rem
       gives it the visual weight it deserves as the primary CTA
       without crowding the column. The icon scales proportionally. */
    .mic-btn  { width: 18rem; height: 18rem; }
    .mic-icon { width: 5.5rem; height: 5.5rem; }

    /* Desktop pairing: video fills the right column so its edges
       align with the transcript card directly below. Eyebrow
       left-aligns to match the transcript's own eyebrow placement
       (instead of centred, which is the mobile treatment). */
    .robot-view {
        justify-items: stretch;
    }
    .robot-view > .eyebrow {
        text-align: left;
    }
    .robot-window {
        max-width: none;
        margin: 0;
    }
}


/* ───── Reduced motion ───── */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}