/* /Components/Account/Pages/ExternalLogin.razor.rz.scp.css */
/* Terms/Privacy consent row for the external-login (Google) new-account gate. Mirrors the
   Register page's consent styling — body-size text at full contrast with clearly-clickable
   underlined links, using the shared auth theme tokens so it reads correctly in light and dark.
   (Scoped CSS is per-component, so the rules are repeated here rather than shared, which keeps the
   Tailwind source→output pipeline untouched.) */
.ff-auth-consent[b-99z27bdhm8] {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    margin: 1rem 0 0.25rem;
}

.ff-auth-consent-box[b-99z27bdhm8] {
    flex: 0 0 auto;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: 0.15rem;
    accent-color: rgb(var(--ff-action-line));
    cursor: pointer;
}

.ff-auth-consent-text[b-99z27bdhm8] {
    font-size: 0.9375rem; /* 15px — body size, above the 14px floor */
    line-height: 1.45;
    color: var(--ff-auth-heading);
    cursor: pointer;
}

.ff-auth-consent-text a[b-99z27bdhm8] {
    color: var(--ff-auth-link);
    text-decoration: underline;
    font-weight: 600;
}

.ff-auth-consent-text a:hover[b-99z27bdhm8],
.ff-auth-consent-text a:focus[b-99z27bdhm8] {
    text-decoration: underline;
    filter: brightness(1.1);
}
/* /Components/Account/Pages/ReacceptTerms.razor.rz.scp.css */
/* Re-acceptance gate. The consent row mirrors the Register/ExternalLogin styling (scoped CSS is
   per-component, so the rules are restated here) so it reads as first-class form content, not a
   de-emphasized caption. */
.ff-reaccept-list[b-txjj14if5q] {
    margin: 0 0 1.25rem;
    padding-left: 1.2rem;
    list-style: disc;
}

.ff-reaccept-list li[b-txjj14if5q] {
    margin: 0.35rem 0;
    font-size: 0.95rem;
    color: var(--ff-auth-heading);
}

.ff-reaccept-summary[b-txjj14if5q] {
    display: block;
    opacity: 0.8;
    font-size: 0.9rem;
}

.ff-auth-consent[b-txjj14if5q] {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    margin: 1rem 0 0.25rem;
}

.ff-auth-consent-box[b-txjj14if5q] {
    flex: 0 0 auto;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: 0.15rem;
    accent-color: rgb(var(--ff-action-line));
    cursor: pointer;
}

.ff-auth-consent-text[b-txjj14if5q] {
    font-size: 0.9375rem;
    line-height: 1.45;
    color: var(--ff-auth-heading);
    cursor: pointer;
}

.ff-auth-consent-text a[b-txjj14if5q] {
    color: var(--ff-auth-link);
    text-decoration: underline;
    font-weight: 600;
}

.ff-auth-consent-text a:hover[b-txjj14if5q],
.ff-auth-consent-text a:focus[b-txjj14if5q] {
    text-decoration: underline;
    filter: brightness(1.1);
}
/* /Components/Account/Pages/Register.razor.rz.scp.css */
/* Terms/Privacy consent row. Deliberately NOT a de-emphasized caption: body-size text, full
   contrast (reuses the same --ff-auth-heading colour as the form labels), and clearly-clickable
   underlined links. Under-styling this is the most common reason click-wrap acceptance gets thrown
   out, so it is styled as first-class form content. Uses the shared auth theme tokens so it is
   legible in both light and dark mode. */
.ff-auth-consent[b-303roux85w] {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    margin: 0.75rem 0 0.25rem;
}

.ff-auth-consent-box[b-303roux85w] {
    flex: 0 0 auto;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: 0.15rem;
    accent-color: rgb(var(--ff-action-line));
    cursor: pointer;
}

.ff-auth-consent-text[b-303roux85w] {
    font-size: 0.9375rem; /* 15px — body size, above the 14px floor */
    line-height: 1.45;
    color: var(--ff-auth-heading);
    cursor: pointer;
}

.ff-auth-consent-text a[b-303roux85w] {
    color: var(--ff-auth-link);
    text-decoration: underline;
    font-weight: 600;
}

.ff-auth-consent-text a:hover[b-303roux85w],
.ff-auth-consent-text a:focus[b-303roux85w] {
    text-decoration: underline;
    filter: brightness(1.1);
}

/* ── Compact vertical rhythm (this page only) ─────────────────────────────────
   Register is the tallest auth card — five fields plus consent plus the external picker — and the
   coupon field pushed it past a laptop viewport. These overrides shave whitespace, not content;
   the shared .ff-auth-* rhythm is untouched, so Login and the Manage pages keep the roomier
   default. Scoped rules out-rank the globals by the CSS-isolation attribute selector. */
.ff-auth-card[b-303roux85w]    { padding-top: 1.25rem; padding-bottom: 1.25rem; }
.ff-auth-title[b-303roux85w]   { font-size: 1.4rem; }
.ff-auth-subtitle[b-303roux85w] { margin-top: 0.25rem; }
.ff-auth-rule[b-303roux85w]    { margin: 0.9rem 0; }
.ff-auth-field[b-303roux85w]   { margin-bottom: 0.65rem; }
.ff-auth-label[b-303roux85w]   { margin-bottom: 0.25rem; }
.ff-auth-input[b-303roux85w]   { padding-top: 0.55rem; padding-bottom: 0.55rem; }
.ff-auth-submit[b-303roux85w]  { padding-top: 0.7rem; padding-bottom: 0.7rem; }

/* Child components need ::deep: the Log in / Register tabs above the title, and the
   "or" divider + provider button from ExternalLoginPicker below the form. */
.ff-auth-card[b-303roux85w]  .ff-auth-tabs     { margin-bottom: 1rem; }
.ff-auth-card[b-303roux85w]  .ff-auth-divider  { margin: 0.9rem 0 0.7rem; }
.ff-auth-card[b-303roux85w]  .ff-auth-external { padding-top: 0.55rem; padding-bottom: 0.55rem; }
/* /Components/Account/Shared/ExternalLoginPicker.razor.rz.scp.css */
/* Provider buttons: the provider's mark to the left of the label. The base look is the shared
   .ff-auth-external (tailwind.source.css); only the logo layout and Apple's own colours live here. */
.ff-auth-external[b-hxtrguh0g2] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
}

.ff-external-logo[b-hxtrguh0g2] {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}

/* Apple's guidelines want a black (or white) button with its logo in the button's ink. */
.ff-external-apple[b-hxtrguh0g2] {
    background: #000000;
    border-color: #000000;
    color: #ffffff;
}

.ff-external-apple:hover[b-hxtrguh0g2] {
    background: #1f1f1f;
}

.fox .ff-external-apple[b-hxtrguh0g2] {
    background: #ffffff;
    border-color: #ffffff;
    color: #000000;
}

.fox .ff-external-apple:hover[b-hxtrguh0g2] {
    background: #e5e5e5;
}
/* /Components/Chat/ContentOverlay.razor.rz.scp.css */
/* Modal reassurance overlay over the dimmed door. The native <dialog> (showModal) supplies the top
   layer + ::backdrop; we theme it with the --ff-* tokens, so dark mode (.fox) is inherited for free.
   Desktop: a centered card. Mobile (<=560px): a slide-up bottom sheet that dismisses back down. */

.ff-content-overlay[b-kwvgbi2apu] {
    border: none;
    padding: 0;
    margin: auto;                 /* center in the viewport; the backdrop fills around it */
    width: min(560px, 92vw);
    max-height: 85vh;
    background: transparent;      /* the inner panel paints the surface */
    color: var(--ff-chat-text);
    overflow: visible;
}

.ff-content-overlay[b-kwvgbi2apu]::backdrop {
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(2px);
}

.ff-co-panel[b-kwvgbi2apu] {
    display: flex;
    flex-direction: column;
    max-height: 85vh;
    background: var(--ff-chat-column-bg);
    border: 1px solid var(--ff-tutor-bubble-border);
    border-radius: 14px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
    overflow: hidden;
}

.ff-co-header[b-kwvgbi2apu] {
    flex: 0 0 auto;
    display: flex;
    justify-content: flex-end;
    padding: 10px 10px 0;
}

.ff-co-close[b-kwvgbi2apu] {
    width: 34px;
    height: 34px;
    border: none;
    border-radius: 50%;
    background: var(--ff-tutor-bubble-bg);
    color: var(--ff-chat-text);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s;
}

.ff-co-close:hover[b-kwvgbi2apu] {
    background: rgba(var(--ff-accent-purple), 0.15);
}

.ff-co-body[b-kwvgbi2apu] {
    flex: 1 1 auto;
    padding: 4px 22px 22px;
    overflow-y: auto;             /* long articles scroll INSIDE the overlay */
}

.ff-co-title[b-kwvgbi2apu] {
    margin: 0 0 10px;
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--ff-chat-text);
}

.ff-co-article[b-kwvgbi2apu] {
    font-size: 15px;
    line-height: 1.55;
    color: var(--ff-chat-text);
}

.ff-co-article[b-kwvgbi2apu]  p:first-child { margin-top: 0; }
.ff-co-article[b-kwvgbi2apu]  p:last-child  { margin-bottom: 0; }

.ff-co-faq-lead[b-kwvgbi2apu] {
    margin-bottom: 6px;
    padding-bottom: 12px;
    font-size: 15px;
    line-height: 1.55;
    color: var(--ff-chat-text);
    border-bottom: 1px solid var(--ff-tutor-bubble-border);
}

.ff-co-faq-item[b-kwvgbi2apu] { margin-top: 16px; }

.ff-co-faq-q[b-kwvgbi2apu] {
    margin: 0 0 4px;
    font-size: 1rem;
    font-weight: 650;
    color: var(--ff-chat-text);
}

.ff-co-faq-a[b-kwvgbi2apu] {
    font-size: 14px;
    line-height: 1.5;
    color: var(--ff-chat-text-muted);
}

.ff-co-nudge[b-kwvgbi2apu] {
    margin-top: 20px;
    padding-top: 14px;
    border-top: 1px solid var(--ff-tutor-bubble-border);
    text-align: center;
    font-weight: 600;
    color: rgba(var(--ff-accent-purple), 1);
}

.ff-co-demo-intro[b-kwvgbi2apu] {
    margin-bottom: 12px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--ff-chat-text);
}

.ff-co-video[b-kwvgbi2apu] {
    display: block;
    width: 100%;
    border-radius: 10px;
    background: #000;
}

/* The demo-video payload goes large — as big as the lightbox — while the article/pricing cards stay
   compact. Scoped to wider screens so the mobile bottom-sheet rules below still win on small ones. */
@media (min-width: 561px) {
    .ff-content-overlay--wide[b-kwvgbi2apu] {
        width: min(1400px, 95vw);
        max-width: 95vw;
        max-height: 95vh;
    }

    .ff-content-overlay--wide .ff-co-panel[b-kwvgbi2apu] {
        max-height: 95vh;
    }

    .ff-content-overlay--wide .ff-co-video[b-kwvgbi2apu] {
        max-height: 82vh;
        object-fit: contain;
    }
}

.ff-co-nodemo[b-kwvgbi2apu] {
    padding: 20px;
    font-size: 15px;
    text-align: center;
    color: var(--ff-chat-text-muted);
}

.ff-co-demo-outro[b-kwvgbi2apu] {
    margin-top: 14px;
    text-align: center;
    font-weight: 600;
    color: rgba(var(--ff-accent-purple), 1);
}

/* Mobile: full-width slide-up sheet pinned to the bottom edge. */
@media (max-width: 560px) {
    .ff-content-overlay[b-kwvgbi2apu] {
        width: 100vw;
        max-width: 100vw;
        max-height: 90vh;
        margin: auto 0 0;          /* pin to the bottom of the viewport */
    }

    .ff-co-panel[b-kwvgbi2apu] {
        max-height: 90vh;
        border-radius: 16px 16px 0 0;
    }

    .ff-content-overlay[open][b-kwvgbi2apu] {
        animation: ff-co-slide-up-b-kwvgbi2apu 0.25s ease-out;
    }
}

@keyframes ff-co-slide-up-b-kwvgbi2apu {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}
/* /Components/Chat/CookieNotice.razor.rz.scp.css */
/* CookieNotice — a full-bleed app-shell BAND across the bottom of the lesson surface. Deliberately
   NOT confined to the transcript's centered column: it spans the full width of the content area so it
   reads unmistakably as dismissible chrome, not a message in the conversation.

   Theme-aware via the shared --ff-* tokens. The band surface (--ff-tutor-bubble-bg) + top border sit
   distinctly against the page canvas (beige gradient light / slate dark) so the bar lifts off the
   content above it. Non-blocking — it lives in the surface's flex flow, so the chat simply shrinks to
   make room; it never overlays the lesson.

   Deliberately NOT a de-emphasized caption: body-size text (14px) at full --ff-chat-text contrast
   (≥4.5:1 on the band in both themes), an underlined + colour-distinct link, and a dismiss button
   with a visible focus ring. */

.ff-cookie-notice[b-gy3nosxkv0] {
    flex: 0 0 auto;
    width: 100%;
    box-sizing: border-box;
    padding: 14px clamp(16px, 4vw, 40px);
    background: var(--ff-tutor-bubble-bg);
    border-top: 1px solid var(--ff-tutor-bubble-border);
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.06);
}

.ff-cookie-card[b-gy3nosxkv0] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;               /* text + button stack on narrow (375px) viewports */
    gap: 10px 28px;
    width: 100%;
}

.ff-cookie-text[b-gy3nosxkv0] {
    flex: 1 1 480px;
    min-width: 0;
    max-width: 900px;              /* keep the copy readable even though the band is full-width */
}

.ff-cookie-heading[b-gy3nosxkv0] {
    margin: 0 0 4px;
    font-size: 14px;
    font-weight: 700;
    color: var(--ff-chat-text);
}

.ff-cookie-body[b-gy3nosxkv0] {
    margin: 0;
    font-size: 14px;               /* full body size — not a caption */
    line-height: 1.5;
    color: var(--ff-chat-text);
}

.ff-cookie-body + .ff-cookie-body[b-gy3nosxkv0] {
    margin-top: 6px;
}

.ff-cookie-link[b-gy3nosxkv0] {
    color: rgb(var(--ff-accent-purple));
    text-decoration: underline;    /* underlined AND coloured — clickable without hovering */
    text-underline-offset: 2px;
    font-weight: 600;
    white-space: nowrap;
}

.ff-cookie-link:hover[b-gy3nosxkv0] {
    text-decoration-thickness: 2px;
}

/* Single control — an acknowledgement, not a consent choice, so it's a neutral secondary button
   (not the accent "action" fill, which would read as "Accept"). Pushed to the far right edge of the
   band so it's unmistakably the way to dismiss the bar. */
.ff-cookie-actions[b-gy3nosxkv0] {
    flex: 0 0 auto;
    margin-left: auto;
}

.ff-cookie-dismiss[b-gy3nosxkv0] {
    padding: 8px 22px;
    border-radius: 999px;
    border: 1px solid var(--ff-tutor-bubble-border);
    background: var(--ff-bubble-bg);
    color: var(--ff-chat-text);
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}

.ff-cookie-dismiss:hover[b-gy3nosxkv0] {
    border-color: var(--ff-input-border);
}

.ff-cookie-dismiss:focus-visible[b-gy3nosxkv0] {
    outline: 2px solid rgb(var(--ff-accent-purple));
    outline-offset: 2px;
}
/* /Components/Chat/EntranceInput.razor.rz.scp.css */
/* ChatGPT-style hero input for the cold-open door. Centered by the entrance wrapper's align-items;
   a rounded pill on the chat surface with an inline send button. Themed via the --ff-* tokens. */
.ff-entrance-input[b-3lfi77a4au] {
    display: flex;
    align-items: center;
    gap: 6px;
    width: min(560px, 92%);
    margin: 4px auto 6px;
    padding: 6px 6px 6px 18px;
    background: var(--ff-entrance-input-bg);
    border: 1px solid var(--ff-entrance-input-border);
    border-radius: 26px;
    box-shadow: var(--ff-entrance-input-shadow);   /* deep warm-brown float in light, subtle in dark */
    transition: border-color 0.15s, box-shadow 0.15s;
}

.ff-entrance-input:focus-within[b-3lfi77a4au] {
    border-color: rgba(var(--ff-support-line), 0.7);
    box-shadow: 0 0 0 3px rgba(var(--ff-support-line), 0.15);
}

.ff-entrance-input-box[b-3lfi77a4au] {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    color: var(--ff-chat-text);
    font-size: 15px;
    line-height: 1.4;
    padding: 8px 0;
}

.ff-entrance-input-box[b-3lfi77a4au]::placeholder {
    color: var(--ff-chat-text-muted);
}

.ff-entrance-input-send[b-3lfi77a4au] {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border: none;
    border-radius: 50%;
    background: var(--ff-action-fill);   /* warm orange gradient, lifted for dark */
    box-shadow: 0 6px 16px var(--ff-action-ring);
    color: var(--ff-action-ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 0.15s, transform 0.1s;
}

.ff-entrance-input-send:hover:not(:disabled)[b-3lfi77a4au] {
    transform: scale(1.05);
}

.ff-entrance-input-send:disabled[b-3lfi77a4au] {
    opacity: 0.4;
    cursor: default;
}

/* "Speak a language" mic — outlined circle matching the lesson mic (warm orange in light mode via
   the --ff-mic-button-* tokens). Sits between the input and the send button. */
.ff-entrance-input-mic[b-3lfi77a4au] {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border: 2px solid var(--ff-mic-button-border);
    border-radius: 50%;
    background: var(--ff-mic-button-bg);
    color: var(--ff-mic-button-text);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, border-color 0.15s, transform 0.1s;
}

.ff-entrance-input-mic:hover:not(:disabled)[b-3lfi77a4au] {
    background: var(--ff-mic-button-hover-bg);
    border-color: var(--ff-mic-button-hover-border);
    transform: scale(1.05);
}

.ff-entrance-input-mic.ff-recording[b-3lfi77a4au] {
    background: var(--ff-mic-button-border);
    border-color: var(--ff-mic-button-border);
    color: #ffffff;
}

.ff-entrance-input-mic:disabled[b-3lfi77a4au] {
    cursor: default;
}

/* Narrow phones: the two 38px buttons and the left padding left the placeholder ~half its width
   ("Tell me a language to lear"). Tighter chrome and a slightly smaller face give the text room. */
@media (max-width: 420px) {
    .ff-entrance-input[b-3lfi77a4au] {
        gap: 4px;
        padding: 4px 4px 4px 14px;
    }

    .ff-entrance-input-box[b-3lfi77a4au] {
        font-size: 14px;
    }

    .ff-entrance-input-send[b-3lfi77a4au],
    .ff-entrance-input-mic[b-3lfi77a4au] {
        width: 34px;
        height: 34px;
    }
}
/* /Components/Chat/EntranceMarketingBand.razor.rz.scp.css */
/* Cold-open marketing band. Surfaces and text ride the --ff-* theme tokens so it follows light and
   dark like the rest of the door; the MrFox orange (card titles, mascot glow) is the one constant
   brand accent, same as the welcome-back card. */

.ff-mkt-band[b-osgoftiy1a] {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 940px;
    margin: 22px auto 4px;
    text-align: center;
    animation: ff-mkt-fade-up-b-osgoftiy1a 0.6s ease;
}

/* ── Mascot over a soft orange bloom ── */

.ff-mkt-mascot[b-osgoftiy1a] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 10px 0 6px;
}

.ff-mkt-mascot[b-osgoftiy1a]::before {
    content: "";
    position: absolute;
    width: 210px;
    height: 210px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(241, 90, 36, 0.18) 0%, transparent 70%);
}

.ff-mkt-mascot img[b-osgoftiy1a] {
    position: relative;
    width: 116px;
    animation: ff-mkt-float-b-osgoftiy1a 5s ease-in-out infinite;
}

/* ── Hook + support ── */

.ff-mkt-hook[b-osgoftiy1a] {
    margin: 8px 0 6px;
    font-size: 20px;
    font-weight: 600;
    color: var(--ff-chat-text);
}

.ff-mkt-support[b-osgoftiy1a] {
    max-width: 520px;
    font-size: 15px;
    line-height: 1.6;
    color: var(--ff-chat-text-soft);
}

.ff-mkt-support[b-osgoftiy1a]  p {
    margin: 0;
}

/* Rotating support tagline. Only the rotator variant animates — a single-line band renders the same
   .ff-mkt-support-line child but none of these rules touch it, so it stays static. Lines stack in
   one grid cell (height reserved by the tallest, nothing below jumps). A line waits below its slot,
   rises in (.is-in), and leaves upward (.is-out); it is fully transparent at both ends, which hides
   the JS reset back to the waiting position. */
.ff-mkt-support-rotator[b-osgoftiy1a] {
    display: grid;
}

.ff-mkt-support-rotator .ff-mkt-support-line[b-osgoftiy1a] {
    grid-area: 1 / 1;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.5s ease, transform 0.5s ease;
}

.ff-mkt-support-rotator .ff-mkt-support-line.is-in[b-osgoftiy1a] {
    opacity: 1;
    transform: translateY(0);
}

.ff-mkt-support-rotator .ff-mkt-support-line.is-out[b-osgoftiy1a] {
    opacity: 0;
    transform: translateY(-8px);
}

/* Reduced motion: the JS never starts the interval, so only the first line ever holds .is-in — kill
   the transition too so nothing slides even on that initial state. */
@media (prefers-reduced-motion: reduce) {
    .ff-mkt-support-rotator .ff-mkt-support-line[b-osgoftiy1a] {
        transition: none;
    }
}

/* ── Value-prop cards ── */

.ff-mkt-cards[b-osgoftiy1a] {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 14px;
    margin-top: 20px;
}

.ff-mkt-card[b-osgoftiy1a] {
    flex: 0 1 240px;
    min-width: 200px;
    padding: 14px 16px;
    text-align: left;
    background: var(--ff-mkt-card-bg);
    border: 1px solid var(--ff-mkt-card-border);
    border-top: 3px solid var(--ff-mkt-card-accent);   /* brand stripe — orange in light, violet in dark */
    border-radius: 12px;
    box-shadow: var(--ff-mkt-card-shadow);
}

.ff-mkt-card-title[b-osgoftiy1a] {
    margin-bottom: 8px;
    font-size: 19px;
    font-weight: 600;
    color: var(--ff-mkt-card-accent);
}

.ff-mkt-card-icon[b-osgoftiy1a] {
    margin-right: 8px;
}

.ff-mkt-card-body[b-osgoftiy1a] {
    font-size: 14px;
    line-height: 1.55;
    color: var(--ff-chat-text-soft);
}

.ff-mkt-card-body[b-osgoftiy1a]  p {
    margin: 0;
}

/* ── Founder note — the only tinted panel: a real person, not a fourth card ── */

.ff-mkt-founder[b-osgoftiy1a] {
    display: flex;
    gap: 24px;
    align-items: center;
    width: 100%;
    max-width: 620px;
    margin: 30px auto 0;
    padding: 26px 28px;
    text-align: left;
    border-radius: 16px;
    background: linear-gradient(180deg, rgba(var(--ff-accent-purple), 0.07), rgba(var(--ff-accent-purple), 0.02));
    border: 1px solid rgba(var(--ff-accent-purple), 0.24);
}

/* The avatar PNGs already carry their own orange ring — no CSS border/glow/shadow here, or it reads
   as a second "target" ring around them. */
.ff-mkt-founder-photo[b-osgoftiy1a] {
    flex: none;
    width: 144px;
    height: 144px;
    border-radius: 50%;
    object-fit: cover;
}

.ff-mkt-founder-body[b-osgoftiy1a] {
    min-width: 0;
}

.ff-mkt-founder-quote[b-osgoftiy1a] {
    margin: 0;
    font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
    font-size: 17.5px;
    line-height: 1.55;
    color: var(--ff-chat-text);
}

/* ── Teacher testimonial — mirrors the founder note, untinted and quieter ── */

.ff-mkt-proof[b-osgoftiy1a] {
    display: flex;
    flex-direction: row-reverse;
    gap: 24px;
    align-items: center;
    width: 100%;
    max-width: 620px;
    margin: 0 auto;
    padding-top: 30px;
    margin-top: 30px;
    text-align: left;
    border-top: 1px solid var(--ff-mkt-card-border);
}

.ff-mkt-proof-photo[b-osgoftiy1a] {
    flex: none;
    width: 144px;
    height: 144px;
    border-radius: 50%;
    object-fit: cover;
}

.ff-mkt-proof-body[b-osgoftiy1a] {
    min-width: 0;
}

.ff-mkt-proof-quote[b-osgoftiy1a] {
    margin: 0;
    font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
    font-style: italic;
    font-size: 18.5px;
    line-height: 1.55;
    color: var(--ff-chat-text);
}

/* ── Shared attribution row ── */

.ff-mkt-attribution[b-osgoftiy1a] {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
}

.ff-mkt-attribution-name[b-osgoftiy1a] {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ff-chat-text);
}

.ff-mkt-attribution-dot[b-osgoftiy1a] {
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: var(--ff-chat-text-soft);
}

.ff-mkt-attribution-role[b-osgoftiy1a] {
    font-size: 13px;
    color: var(--ff-chat-text-soft);
}

/* ── Honest footnote — risk-reversal pill ── */

/* Badge + pill sit on one row. Wraps rather than shrinks on narrow screens — the pill's copy is a
   promise and must stay readable, so the badge drops beneath it instead of squeezing it. */
.ff-mkt-foot[b-osgoftiy1a] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 14px;
    margin-top: 26px;
    font-size: 13px;
    color: var(--ff-mkt-foot-text);
}

/* The source art is a 1:1 circle on a transparent field. Width AND height are pinned so the box is
   reserved before the image loads (no reflow of the pill beside it) and stays square whatever the
   exact pixel dimensions of the asset turn out to be. */
/* Deliberately small. Sized up to ~104px the word reads more easily, but the badge then competes with
   the founder and testimonial photos directly above it — three round images down one column, and the
   stock badge wins attention it hasn't earned. It stays a footnote-scale mark next to the pill; the
   word is still legible at this size. Don't "fix" it by enlarging. */
.ff-mkt-nofluff[b-osgoftiy1a] {
    flex: none;
    width: 64px;
    height: 64px;
    /* The badge is a glossy 3D bevel next to our flat wordmark and flat cards; a small knock-back
       settles it into the band without dulling the red. */
    opacity: 0.92;
}

.ff-mkt-pill[b-osgoftiy1a] {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 18px;
    border-radius: 999px;
    background: rgba(var(--ff-accent-purple), 0.08);
    border: 1px solid rgba(var(--ff-accent-purple), 0.28);
    font-size: 14px;
    font-weight: 600;
    color: var(--ff-chat-text);
}

.ff-mkt-pill-dot[b-osgoftiy1a] {
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgb(var(--ff-accent-purple));
}

/* Copyright / operator line — the quietest text in the band, below the closing promise. */
.ff-mkt-legal[b-osgoftiy1a] {
    margin-top: 22px;
    text-align: center;
    font-size: 12px;
    color: var(--ff-mkt-foot-text);
}

/* Desktop shows the full quote; the short variant is mobile-only (swapped below). */
.ff-mkt-quote-short[b-osgoftiy1a] {
    display: none;
}

/* ── Trust-layer stacking on narrow screens ── */

@media (max-width: 560px) {
    .ff-mkt-founder[b-osgoftiy1a],
    .ff-mkt-proof[b-osgoftiy1a] {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .ff-mkt-attribution[b-osgoftiy1a] {
        justify-content: center;
    }

    .ff-mkt-founder-photo[b-osgoftiy1a] {
        width: 124px;
        height: 124px;
    }

    .ff-mkt-proof-photo[b-osgoftiy1a] {
        width: 124px;
        height: 124px;
    }

    .ff-mkt-founder-quote[b-osgoftiy1a] {
        font-size: 16px;
    }

    .ff-mkt-quote-full[b-osgoftiy1a] {
        display: none;
    }

    .ff-mkt-quote-short[b-osgoftiy1a] {
        display: block;
    }

    .ff-mkt-nofluff[b-osgoftiy1a] {
        width: 56px;
        height: 56px;
    }
}

/* ── Motion ── */

@keyframes ff-mkt-fade-up-b-osgoftiy1a {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes ff-mkt-float-b-osgoftiy1a {
    0%, 100% { transform: translateY(5px); }
    50%      { transform: translateY(-5px); }
}

@media (prefers-reduced-motion: reduce) {
    .ff-mkt-band[b-osgoftiy1a] {
        animation: none;
    }

    .ff-mkt-mascot img[b-osgoftiy1a] {
        animation: none;
    }
}
/* /Components/Chat/FfChatUi.razor.rz.scp.css */
/* FfChatUi — owned transcript shell. Stage 1: container, plain bubbles, text footer.
   The rich ff-* card styling (ported from Lesson.razor.css via ::deep) arrives in Stage 2,
   once the host page supplies an EntryTemplate that reuses the leaf components. */

.ff-chat[b-p1jc97tb8g] {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    /* Shared readable column width — transcript rows and the footer input both center to this
       (Claude-Code / ChatGPT style) instead of stretching edge-to-edge on wide screens. */
    --ff-chat-max-width: 768px;
    /* Guard rails: contain the whole conversation (transcript + footer) in a centered column
       with subtle vertical borders, so it reads as a defined panel instead of hanging in mid-air
       on a wide window. The outer width is the readable column + the scroll's 16px side padding
       (×2) so the rails sit just outside the message bubbles; the scrollbar tucks against the
       right rail rather than floating at the window edge. */
    width: 100%;
    max-width: calc(var(--ff-chat-max-width, 768px) + 32px);
    margin: 0 auto;
    border-left: 1px solid var(--ff-chat-column-border);
    border-right: 1px solid var(--ff-chat-column-border);
    /* Transparent — the page canvas (.ff-lesson-surface: beige gradient in light, slate in dark)
       shows through, and the white entrance/lesson surfaces float on it (contrast-fix handoff).
       In dark this is a no-op: the column token matched the page already. --ff-chat-column-bg
       still backs the solid overlay surfaces (ContentOverlay, MessageDialog). */
    background: transparent;
}

/* ── Lesson progress strip ───────────────────────────────── */
/* Pinned above the scroll region (flex: 0 0 auto), centered to the same readable column as the
   rows so it lines up with the bubbles below. Theme-aware via the shared --ff-* tokens. */
.ff-chat-progress[b-p1jc97tb8g] {
    flex: 0 0 auto;
    width: 100%;
    max-width: calc(var(--ff-chat-max-width, 768px) + 32px);
    margin: 0 auto;
    padding: 10px 16px 11px;
    box-sizing: border-box;
    border-bottom: 1px solid var(--ff-chat-column-border);
}

/* The lesson's Pause control floats over the top-right corner (38px at right:12px). Where the chat
   column reaches that corner — phones and narrow windows — keep the strip clear of it, or the "75%"
   and the button overprint each other. The strip only shows mid-lesson, i.e. when Pause shows too. */
@media (max-width: 900px) {
    .ff-chat-progress[b-p1jc97tb8g] {
        padding-right: 62px;
    }
}

.ff-chat-progress-row[b-p1jc97tb8g] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 7px;
}

.ff-chat-progress-label[b-p1jc97tb8g] {
    font-size: 14px;
    font-weight: 600;
    color: var(--ff-chat-text-soft);
}

.ff-chat-progress-pct[b-p1jc97tb8g] {
    font-size: 14px;
    font-weight: 700;
    color: var(--ff-progress-pct);
    font-variant-numeric: tabular-nums;
}

.ff-chat-progress-track[b-p1jc97tb8g] {
    height: 8px;
    border-radius: 99px;
    background: var(--ff-progress-track);
    overflow: hidden;
}

.ff-chat-progress-fill[b-p1jc97tb8g] {
    height: 100%;
    border-radius: 99px;
    background: var(--ff-progress-fill);   /* orange brand gradient */
    transition: width 0.4s ease;
}

.ff-chat-scroll[b-p1jc97tb8g] {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 16px;
    display: flex;
    flex-direction: column;
    align-items: center;   /* center the transcript column; scrollbar stays at the window edge */
    gap: 10px;
    /* Muted, theme-aware scrollbar (tokens in app.css: mid-gray on light, slate on dark — replacing
       the old near-black / pure-white extremes). Firefox uses scrollbar-color; WebKit/Chromium uses
       the ::-webkit-scrollbar pseudo-elements below. Track is transparent so it reads as part of the
       column rather than a painted gutter. */
    scrollbar-width: thin;
    scrollbar-color: var(--ff-scrollbar-thumb) transparent;
}

.ff-chat-scroll[b-p1jc97tb8g]::-webkit-scrollbar {
    width: 8px;
}

.ff-chat-scroll[b-p1jc97tb8g]::-webkit-scrollbar-track {
    background: transparent;
}

.ff-chat-scroll[b-p1jc97tb8g]::-webkit-scrollbar-thumb {
    background: var(--ff-scrollbar-thumb);
    border-radius: 99px;
}

.ff-chat-scroll[b-p1jc97tb8g]::-webkit-scrollbar-thumb:hover {
    background: var(--ff-scrollbar-thumb-hover);
}

.ff-chat-row[b-p1jc97tb8g] {
    display: flex;
    width: 100%;
    max-width: var(--ff-chat-max-width, 768px);
}

.ff-chat-row-other[b-p1jc97tb8g]   { justify-content: flex-start; }
.ff-chat-row-student[b-p1jc97tb8g] { justify-content: flex-end; }

/* ── Default plain bubble (used until the host supplies an EntryTemplate) ── */
.ff-bubble[b-p1jc97tb8g] {
    max-width: 80%;
    padding: 10px 14px;
    border-radius: 12px;
    line-height: 1.5;
    white-space: normal;
    word-wrap: break-word;
    overflow-wrap: anywhere;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}

.ff-bubble-tutor[b-p1jc97tb8g] {
    background: var(--ff-bubble-bg);
    color: var(--ff-chat-text);
    border: 1px solid var(--ff-chat-column-border);
    border-top-left-radius: 4px;
}

.ff-bubble-student[b-p1jc97tb8g] {
    background: #27ae60;
    color: #ffffff;
    border-top-right-radius: 4px;
}

.ff-bubble-status[b-p1jc97tb8g] {
    background: var(--ff-status-plain-bg);
    color: var(--ff-status-plain-text);
    font-style: italic;
}

.ff-bubble-score[b-p1jc97tb8g] {
    background: var(--ff-score-bg);
    color: var(--ff-chat-text);
    border: 1px solid var(--ff-score-border);
}

/* "Thinking…" indicator shown at the foot of the transcript while the engine works. */
.ff-typing[b-p1jc97tb8g] {
    color: var(--ff-chat-text-faint);
    font-style: italic;
}

/* ── Scroll-to-bottom arrow ──────────────────────────────── */
/* Zero-height anchor between the scroll area and the footer: the button hangs above it,
   floating over the transcript's bottom edge without joining the flex flow. Centered to
   the same readable column as the rows. */
.ff-scroll-bottom-anchor[b-p1jc97tb8g] {
    position: relative;
    height: 0;
    width: 100%;
    max-width: calc(var(--ff-chat-max-width, 768px) + 32px);
    margin: 0 auto;
}

/* Hidden by default; JS (ffChatScrollInit) toggles ff-scroll-bottom-visible from scroll
   state — the class arrives outside Blazor's render, which is fine: Blazor never rewrites
   an attribute it didn't change. */
.ff-scroll-bottom[b-p1jc97tb8g] {
    position: absolute;
    bottom: 14px;
    left: 50%;
    transform: translateX(-50%) translateY(8px);
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 1px solid var(--ff-tutor-bubble-border);
    background: var(--ff-bubble-bg);
    color: var(--ff-chat-text);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease, transform 0.15s ease;
    z-index: 5;
}

.ff-scroll-bottom.ff-scroll-bottom-visible[b-p1jc97tb8g] {
    opacity: 1;
    pointer-events: auto;
    transform: translateX(-50%);
}

.ff-scroll-bottom:hover[b-p1jc97tb8g] {
    border-color: var(--ff-input-border);
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.24);
}

/* ── Text footer ─────────────────────────────────────────── */
/* Centered to the SAME column as the transcript — NOT full-bleed. The bar used to span the
   whole window (border + background edge-to-edge), which dwarfed the narrower transcript above
   it. The max-width adds the transcript scroll's 16px side padding (+32px) so the input's edges
   line up exactly with the message bubbles' column; the input itself reads as a floating row. */
.ff-chat-footer[b-p1jc97tb8g] {
    width: 100%;
    max-width: calc(var(--ff-chat-max-width, 768px) + 32px);
    margin: 0 auto;
    padding: 12px 16px;
    box-sizing: border-box;
}

.ff-chat-footer-inner[b-p1jc97tb8g] {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
}

/* Mic-mode footer: center the host-supplied mic content (button + escape hatch) rather than
   left-aligning it the way the text input sits. */
.ff-chat-footer-mic[b-p1jc97tb8g] {
    justify-content: center;
    gap: 12px;
}

/* Text-mode footer: a single rounded pill holding the input + inline send button, matching the
   cold-open entrance input (.ff-entrance-input). The input is transparent inside the pill; the
   pill itself carries the border, background, and focus ring. */
.ff-chat-footer-text[b-p1jc97tb8g] {
    gap: 6px;
    padding: 6px 6px 6px 18px;
    background: var(--ff-tutor-bubble-bg);
    border: 1px solid var(--ff-tutor-bubble-border);
    border-radius: 26px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    transition: border-color 0.15s, box-shadow 0.15s;
}

.ff-chat-footer-text:focus-within[b-p1jc97tb8g] {
    border-color: rgba(var(--ff-support-line), 0.7);
    box-shadow: 0 0 0 3px rgba(var(--ff-support-line), 0.15);
}

.ff-chat-input[b-p1jc97tb8g] {
    flex: 1 1 auto;
    min-width: 0;
    padding: 8px 0;
    border: none;
    border-radius: 0;
    font-size: 15px;
    line-height: 1.4;
    outline: none;
    background: transparent;
    color: var(--ff-chat-text);
}

/* Long free-text answer: a multi-line box with its character counter beneath. */
.ff-chat-footer-long[b-p1jc97tb8g] {
    align-items: flex-end;
    border-radius: 18px;
}

.ff-chat-long[b-p1jc97tb8g] {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.ff-chat-textarea[b-p1jc97tb8g] {
    resize: none;
    font-family: inherit;
    min-height: 4.2em;
    max-height: 40vh;
    overflow-y: auto;
}

.ff-chat-counter[b-p1jc97tb8g] {
    align-self: flex-end;
    font-size: 12px;
    line-height: 1.2;
    padding-bottom: 4px;
    color: var(--ff-chat-text-muted);
    font-variant-numeric: tabular-nums;
}

.ff-chat-counter-limit[b-p1jc97tb8g] {
    color: #c0392b;
    font-weight: 600;
}

.ff-chat-input[b-p1jc97tb8g]::placeholder {
    color: var(--ff-chat-text-muted);
}

.ff-chat-input:disabled[b-p1jc97tb8g] {
    opacity: 0.6;
}

.ff-chat-send[b-p1jc97tb8g] {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--ff-action-fill);   /* warm orange gradient, lifted for dark */
    box-shadow: 0 4px 12px var(--ff-action-ring);
    color: var(--ff-action-ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 0.15s, transform 0.1s;
}

.ff-chat-send:hover:not(:disabled)[b-p1jc97tb8g] { transform: scale(1.05); }
.ff-chat-send:disabled[b-p1jc97tb8g] { opacity: 0.4; cursor: default; }
/* /Components/Chat/FlagEmoji.razor.rz.scp.css */
/* Scoped to FlagEmoji — the ONE home of the flag-glyph font declaration. Windows ships no flag
   emoji glyphs, so flags use a self-hosted subset font containing ONLY the flag emoji:
   TwemojiCountryFlags (from the country-flag-emoji-polyfill package; MIT, Twemoji art CC-BY 4.0).
   The unicode-range keeps it inert for every other character, and platforms are consistent —
   everyone gets the Twemoji flag. (@font-face rules pass through the CSS scoper unscoped.) */
@font-face {
    font-family: "Twemoji Country Flags";
    src: url("/fonts/TwemojiCountryFlags.woff2") format("woff2");
    unicode-range: U+1F1E6-1F1FF;
    font-display: swap;
}

.ff-flag-emoji[b-qrxp5lu23x] {
    font-family: "Twemoji Country Flags", sans-serif;
    line-height: 1;
    /* Twemoji flags are borderless, so white regions (🇷🇺 🇺🇸) wash into light surfaces. A hairline
       drop-shadow traces the glyph's rounded-rect silhouette — an outline a border-box can't give
       a font glyph. Subtle enough to disappear on dark/gradient surfaces. */
    filter: drop-shadow(0 0 0.75px rgba(74, 36, 23, 0.55));
}
/* /Components/Chat/InlineLoginForm.razor.rz.scp.css */
/* Self-contained styling for the inline login form so it looks right wherever it renders —
   the chat transcript's welcome-back bubble AND the sidebar / bottom-nav login popover. Labels
   inherit color from the container (dark text in the light chat bubble, white in the dark
   popover); the input fields are white boxes with dark text in every theme. */
.ff-inline-login[b-6z5ct4621d] {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 340px;
}

.ff-inline-login-field[b-6z5ct4621d] {
    display: flex;
    flex-direction: column;
    gap: 3px;
    font-size: 0.85rem;
    color: inherit;
}

.ff-inline-login-field input[b-6z5ct4621d] {
    padding: 7px 10px;
    border: 1px solid #d0d0d0;
    border-radius: 6px;
    background: #fff;
    color: #1f2937;
    font-size: 0.95rem;
}

/* Remember-me on the left, Forgot-password link on the right, on one row. */
.ff-inline-login-row[b-6z5ct4621d] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.ff-inline-login-remember[b-6z5ct4621d] {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85rem;
    color: inherit;
}

.ff-inline-login button[type="submit"][b-6z5ct4621d] {
    align-self: flex-start;
    margin-top: 2px;
}

.ff-inline-login-register[b-6z5ct4621d],
.ff-inline-login-forgot[b-6z5ct4621d] {
    font-size: 0.85rem;
    color: inherit;          /* white in the dark nav popover */
    text-decoration: underline;
    opacity: 0.9;
}

/* The register link is a standalone column child; the forgot link is positioned by its row. */
.ff-inline-login-register[b-6z5ct4621d] {
    align-self: flex-start;
    margin-top: 2px;
}

/* Keep the forgot link from stretching/wrapping; sits snug at the row's right edge. */
.ff-inline-login-forgot[b-6z5ct4621d] {
    flex-shrink: 0;
    white-space: nowrap;
}
/* /Components/Chat/LessonPlanCardMessage.razor.rz.scp.css */
/* Scoped to LessonPlanCardMessage. Theme-aware like the progress dashboard — the light values are
   the warm-coral design spec verbatim (coral gradient header over a white plan body, espresso
   text); dark swaps to the purple/slate look under the app's `.fox` class (the Blazor scoper
   rewrites `.fox .ff-plan-card` to a scoped selector, so the html-level theme class works from
   inside this file). Font stays the app font by design. */

.ff-plan-card[b-kvx30zdvbw] {
    /* Light theme (default) — warm-coral design-spec values */
    --plan-body-bg: #ffffff;
    --plan-header-gradient: linear-gradient(120deg, #E8552E 0%, #F47A45 100%);
    --plan-accent: #E8552E;
    --plan-lesson-title: #4A2417;
    --plan-lesson-description: #98776A;
    --plan-count: #B79B8E;
    --plan-badge-bg: #FCE9E0;
    --plan-badge-text: #E8552E;
    --plan-lesson-hover: #FBF0EA;
    --plan-shadow: 0 8px 30px rgba(122, 46, 26, 0.12);
    /* Consent notice — deliberately NOT a faint caption; strong enough to read (≥4.5:1 on the white
       body), with a distinct, clearly-clickable link colour. */
    --terms-text: #6E4B3A;
    --terms-link: #C43E1C;
}

.fox .ff-plan-card[b-kvx30zdvbw] {
    /* Dark theme — deep slate body, lifted lilac text, purple gradient header */
    --plan-body-bg: #1e293b;                      /* slate-800 */
    --plan-header-gradient: linear-gradient(120deg, #5B2A86 0%, #7C3AED 100%);
    --plan-accent: #b99cf3;                       /* violet-as-text light step (handoff 3C) */
    --plan-lesson-title: #f1f5f9;                 /* slate-100 */
    --plan-lesson-description: #94a3b8;           /* slate-400 */
    --plan-count: #94a3b8;
    --plan-badge-bg: rgba(124, 80, 207, 0.28);   /* tint of the deep fill violet #7c50cf */
    --plan-badge-text: #c7b4f7;                   /* badge ink from the 2026-09-09 accent handoff — lighter step of the accent violet for the tinted badge */
    --plan-lesson-hover: #273449;
    --plan-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
    --terms-text: #cbd5e1;                        /* slate-300 — ≥4.5:1 on the slate-800 body */
    --terms-link: #b99cf3;                        /* violet-as-text light step — distinct, clearly a link */
}

.ff-plan-card[b-kvx30zdvbw] {
    width: 100%;
    max-width: 660px;            /* match the progress dashboard + welcome-back cards */
    margin: 4px auto 12px;
    border-radius: 22px;
    overflow: hidden;
    background: var(--plan-body-bg);
    box-shadow: var(--plan-shadow);
}

/* ── Profile header — gradient band ─────────────────────────────────────── */
.ff-plan-header[b-kvx30zdvbw] {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 16px 22px;
    background: var(--plan-header-gradient);
}

.ff-plan-name-row[b-kvx30zdvbw] {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 10px;
}

.ff-plan-name[b-kvx30zdvbw] {
    color: #fff;
    font-size: 22px;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.ff-plan-age[b-kvx30zdvbw] {
    color: rgba(255, 255, 255, 0.78);
    font-size: 14px;
    font-weight: 700;
}

.ff-plan-chips[b-kvx30zdvbw] {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.ff-plan-chip[b-kvx30zdvbw] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.18);
    color: #fff;
    font-size: 13px;
    font-weight: 700;
}

.ff-plan-chip-arrow[b-kvx30zdvbw] {
    opacity: 0.7;
}

/* Sizing wrapper for the shared FlagEmoji glyph inside the language-pair chip. An unresolvable
   name renders no glyph; the wrapper collapses to nothing inside the flex chip. */
.ff-plan-flag[b-kvx30zdvbw] {
    font-size: 1.25em;
    line-height: 1;
}

.ff-plan-chip-icon[b-kvx30zdvbw] {
    flex: 0 0 auto;
    width: 13px;
    height: 13px;
    opacity: 0.9;
}

/* ── Plan body ───────────────────────────────────────────────────────────── */
.ff-plan-body[b-kvx30zdvbw] {
    padding: 14px 22px 16px;
}

.ff-plan-body-head[b-kvx30zdvbw] {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 8px;
}

.ff-plan-title[b-kvx30zdvbw] {
    margin: 0;
    font-size: 15px;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--plan-accent);
}

.ff-plan-count[b-kvx30zdvbw] {
    font-size: 13px;
    font-weight: 700;
    color: var(--plan-count);
}

.ff-plan-lessons[b-kvx30zdvbw] {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.ff-plan-lesson[b-kvx30zdvbw] {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 7px 10px;
    border-radius: 14px;
}

.ff-plan-lesson:hover[b-kvx30zdvbw] {
    background: var(--plan-lesson-hover);
}

.ff-plan-lesson-number[b-kvx30zdvbw] {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    margin-top: 2px;
    border-radius: 10px;
    background: var(--plan-badge-bg);
    color: var(--plan-badge-text);
    font-size: 14px;
    font-weight: 800;
}

.ff-plan-lesson-text[b-kvx30zdvbw] {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.ff-plan-lesson-title[b-kvx30zdvbw] {
    font-size: 16px;
    font-weight: 800;
    color: var(--plan-lesson-title);
}

/* Course-plan rows: a hairline between units, no badge/hover box — an eyebrow ("UNIT 1 · LESSONS 1–16":
   unit in the accent colour, range muted), the unit name, then its description when it has one. */
.ff-plan-lesson-unit[b-kvx30zdvbw] {
    padding: 12px 0;
    border-radius: 0;
    border-top: 1px solid var(--plan-lesson-hover);
}

.ff-plan-lesson-unit:first-child[b-kvx30zdvbw] {
    border-top: none;
}

.ff-plan-lesson-unit:hover[b-kvx30zdvbw] {
    background: transparent;
}

.ff-plan-lesson-eyebrow[b-kvx30zdvbw] {
    font-size: 12px;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--plan-lesson-description);
}

.ff-plan-lesson-unitno[b-kvx30zdvbw] {
    color: var(--plan-accent);
}

.ff-plan-lesson-description[b-kvx30zdvbw] {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--plan-lesson-description);
}

/* ── Consent notice under the Start button ──────────────────────────────────
   Reads as part of the button (tight gap, aligned with it), never as page furniture. Kept legible
   on purpose — full body-size, real contrast, and links that look clickable at rest. */
.ff-terms-notice[b-kvx30zdvbw] {
    margin: 0 0 8px;            /* < 16px — visually bound to the button below it */
    font-size: 14px;            /* not smaller than the surrounding body copy */
    line-height: 1.45;
    text-align: right;          /* the Start button is right-aligned (.ff-card-actions) */
    color: var(--terms-text);
}

.ff-terms-notice a[b-kvx30zdvbw] {
    color: var(--terms-link);
    text-decoration: underline;         /* underlined AND coloured — clickable without hovering */
    text-underline-offset: 2px;
    font-weight: 700;
}

.ff-terms-notice a:hover[b-kvx30zdvbw] {
    text-decoration-thickness: 2px;
}
/* /Components/Chat/LessonPreviewCardMessage.razor.rz.scp.css */
/* Scoped to LessonPreviewCardMessage. Coral accent on white in light; violet on slate under the
   app's `.fox` class. Each phrase sits in its own rounded row; Start is the one filled button. */

.ff-preview-card[b-ezgwt06we4] {
    --preview-body-bg: #ffffff;
    --preview-row-border: #F3E3DA;
    --preview-text: #4A2417;
    --preview-meaning: #98776A;
    --preview-quiet: #8A6A5D;
    --preview-quiet-hover: #4A2417;
    --preview-start-bg: #E8552E;
    --preview-start-text: #ffffff;
    --preview-pill-border: #E3C9BC;
    --preview-pill-hover-bg: rgba(232, 85, 46, 0.08);
    --preview-shadow: 0 8px 30px rgba(122, 46, 26, 0.12);
}

.fox .ff-preview-card[b-ezgwt06we4] {
    --preview-body-bg: #1e293b;                   /* slate-800 */
    --preview-row-border: #334155;                /* slate-700 */
    --preview-text: #f1f5f9;                      /* slate-100 */
    --preview-meaning: #94a3b8;                   /* slate-400 */
    --preview-quiet: #94a3b8;
    --preview-quiet-hover: #e2e8f0;
    --preview-start-bg: #a98bf0;                  /* the app's informing violet */
    --preview-start-text: #0f172a;                /* slate-900 */
    --preview-pill-border: #475569;               /* slate-600 */
    --preview-pill-hover-bg: rgba(169, 139, 240, 0.12);
    --preview-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
}

.ff-preview-card[b-ezgwt06we4] {
    width: 100%;
    max-width: 480px;
    margin: 4px auto 12px;
    padding: 28px 28px 20px;
    box-sizing: border-box;
    border-radius: 22px;
    background: var(--preview-body-bg);
    box-shadow: var(--preview-shadow);
}

.ff-preview-unit[b-ezgwt06we4] {
    margin-bottom: 4px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 1.2px;
    text-transform: uppercase;
    color: var(--preview-meaning);
}

.ff-preview-title[b-ezgwt06we4] {
    margin: 0;
    font-size: 26px;
    font-weight: 700;
    line-height: 1.2;
    color: var(--preview-text);
}

.ff-preview-summary[b-ezgwt06we4] {
    margin-top: 6px;
    font-size: 15px;
    color: var(--preview-meaning);
}

.ff-preview-phrases[b-ezgwt06we4] {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 20px;
}

/* Muted, outlined tiles — this is a preview of the lesson, not the lesson, so the phrases stay quiet
   and "Start lesson" is the one filled, coloured thing on the card. */
.ff-preview-phrase[b-ezgwt06we4] {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border: 1px solid var(--preview-row-border);
    border-radius: 12px;
    background: transparent;
}

/* "I know this": kept in the list, struck through — it won't be taught or tested. */
.ff-preview-phrase-known .ff-preview-text[b-ezgwt06we4],
.ff-preview-phrase-known .ff-preview-meaning[b-ezgwt06we4] {
    text-decoration: line-through;
    opacity: 0.55;
}

.ff-preview-words-button[b-ezgwt06we4] {
    padding: 0;
    border: none;
    background: transparent;
    text-align: left;
    font: inherit;
    cursor: pointer;
}

.ff-preview-words[b-ezgwt06we4] {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

.ff-preview-text[b-ezgwt06we4] {
    font-size: 17px;
    font-weight: 600;
    color: var(--preview-text);
}

.ff-preview-meaning[b-ezgwt06we4] {
    margin-top: 2px;
    font-size: 15px;
    font-style: italic;
    color: var(--preview-meaning);
}

/* "I know this" / "Undo": an outlined pill so it reads as a button, but unfilled so "Start lesson"
   stays the one filled thing on the card. Hover tints it in the start colour. */
.ff-preview-phrase-skip[b-ezgwt06we4] {
    flex: 0 0 auto;
    padding: 5px 12px;
    border: 1px solid var(--preview-pill-border);
    border-radius: 999px;
    background: transparent;
    font-size: 14px;
    font-weight: 600;
    white-space: nowrap;
    color: var(--preview-quiet);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}

.ff-preview-phrase-skip:hover[b-ezgwt06we4],
.ff-preview-phrase-skip:focus-visible[b-ezgwt06we4] {
    border-color: var(--preview-start-bg);
    background: var(--preview-pill-hover-bg);
    color: var(--preview-quiet-hover);
}

.ff-preview-actions[b-ezgwt06we4] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    margin-top: 24px;
}

.ff-preview-start[b-ezgwt06we4] {
    padding: 16px;
    border: none;
    border-radius: 14px;
    background: var(--preview-start-bg);
    color: var(--preview-start-text);
    font-size: 17px;
    font-weight: 700;
    cursor: pointer;
    transition: filter 0.15s;
}

.ff-preview-start:hover[b-ezgwt06we4] {
    filter: brightness(1.08);
}

.ff-preview-note[b-ezgwt06we4] {
    margin: 4px 0 -6px;
    text-align: center;
    font-size: 14px;
    color: var(--preview-meaning);
}

.ff-preview-skip-lesson[b-ezgwt06we4],
.ff-preview-back[b-ezgwt06we4] {
    align-self: center;
    padding: 6px 12px;
    border: none;
    background: transparent;
    font-size: 15px;
    font-weight: 500;
    color: var(--preview-quiet);
    cursor: pointer;
    transition: color 0.15s;
}

.ff-preview-skip-lesson:hover[b-ezgwt06we4],
.ff-preview-back:hover[b-ezgwt06we4] {
    color: var(--preview-quiet-hover);
}
/* /Components/Chat/LessonRatingCardMessage.razor.rz.scp.css */
/* Scoped to LessonRatingCardMessage. Same card family as the lesson preview: white in light, slate
   under the app's `.fox` class. Lit stars are warm gold in both themes — a star rating is read by its
   colour, and gold is the convention. */

.ff-rating-card[b-8zqljfw4zf] {
    --rating-body-bg: #ffffff;
    --rating-text: #4A2417;
    --rating-quiet: #8A6A5D;
    --rating-quiet-hover: #4A2417;
    --rating-star-off: #E3C9BC;
    --rating-star-on: #F5A623;
    --rating-shadow: 0 8px 30px rgba(122, 46, 26, 0.12);
}

.fox .ff-rating-card[b-8zqljfw4zf] {
    --rating-body-bg: #1e293b;                    /* slate-800 */
    --rating-text: #f1f5f9;                       /* slate-100 */
    --rating-quiet: #94a3b8;                      /* slate-400 */
    --rating-quiet-hover: #e2e8f0;
    --rating-star-off: #475569;                   /* slate-600 */
    --rating-star-on: #FBBF24;
    --rating-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
}

.ff-rating-card[b-8zqljfw4zf] {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 480px;
    margin: 4px auto 12px;
    padding: 24px 20px 14px;
    box-sizing: border-box;
    border-radius: 22px;
    background: var(--rating-body-bg);
    box-shadow: var(--rating-shadow);
    text-align: center;
}

.ff-rating-title[b-8zqljfw4zf] {
    margin: 0;
    font-size: 20px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--rating-text);
}

.ff-rating-stars[b-8zqljfw4zf] {
    display: flex;
    gap: 4px;
    margin-top: 14px;
}

.ff-rating-star[b-8zqljfw4zf] {
    display: inline-flex;
    padding: 4px;
    border: none;
    border-radius: 10px;
    background: transparent;
    fill: var(--rating-star-off);
    cursor: pointer;
    transition: fill 0.12s, transform 0.12s;
}

.ff-rating-star-static[b-8zqljfw4zf] {
    cursor: default;
}

.ff-rating-star-lit[b-8zqljfw4zf] {
    fill: var(--rating-star-on);
}

button.ff-rating-star:hover[b-8zqljfw4zf] {
    transform: scale(1.1);
}

button.ff-rating-star:focus-visible[b-8zqljfw4zf] {
    outline: 2px solid var(--rating-star-on);
    outline-offset: 1px;
}

.ff-rating-skip[b-8zqljfw4zf] {
    margin-top: 14px;
    padding: 6px 12px;
    border: none;
    background: transparent;
    font-size: 15px;
    font-weight: 500;
    color: var(--rating-quiet);
    cursor: pointer;
    transition: color 0.15s;
}

.ff-rating-skip:hover[b-8zqljfw4zf] {
    color: var(--rating-quiet-hover);
}
/* /Components/Chat/ListeningOverlay.razor.rz.scp.css */
/* ── Live-mic "Listening" overlay ─────────────────────────────────────────────────────────────
   Large, centered, voice-reactive indicator shown while the mic is open — the can't-miss
   counterpart to the small button ring: a lit mic disc with a voice halo behind it, outward
   sonar rings, and a row of live level bars beneath. Click-through by design (pointer-events:
   none): it never intercepts the mic button, Move On, or anything else. Everything reacts to
   --ff-mic-level (0..1, set on #ff-listening-overlay by the JS mic meter each frame), so it
   is calm while waiting and visibly lights up with the learner's actual voice.

   Moved here verbatim from Lesson.razor.css when the indicator became a shared component (the
   dialog panel used to carry a forked copy). Scoped CSS only reaches this component's own
   markup, which is exactly the set of elements these rules address. `.fox` (the app's dark
   class on <html>) is an ancestor, so the dark overrides below still apply under isolation. */

/* The overlay positions against the nearest positioned ancestor — the lesson surface (so it
   centers over the transcript column and ignores the sidebar) or the dialog's karaoke card. */
.ff-listening-overlay[b-ctbx7ycqvk] {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    --ff-mic-level: 0;
}

/* Placement: over the whole lesson surface it stacks above the transcript (below the image
   lightbox at 1080); inside a card it only needs to clear the card's own content. */
.ff-listening-surface[b-ctbx7ycqvk] { z-index: 1070; }
.ff-listening-card[b-ctbx7ycqvk]    { z-index: 5; }

/* Gentle backdrop dim while the mic is live — pulls focus to the listening indicator without
   hiding the transcript (the prompt being answered must stay readable). Lesson-surface placement
   only: inside the dialog card the lines must stay fully readable. The pulse group is a later
   positioned sibling, so it paints above the tint. */
.ff-listening-surface[b-ctbx7ycqvk]::before {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.10);
    animation: ff-listening-dim-in-b-ctbx7ycqvk 0.25s ease-out;
}

/* Dark theme needs a slightly stronger tint to register against the slate surface. */
.fox .ff-listening-surface[b-ctbx7ycqvk]::before {
    background: rgba(0, 0, 0, 0.25);
}

@keyframes ff-listening-dim-in-b-ctbx7ycqvk {
    from { opacity: 0; }
}

.ff-listening-pulse[b-ctbx7ycqvk] {
    position: relative;
    width: 190px;
    height: 190px;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: ff-listening-in-b-ctbx7ycqvk 0.22s ease-out;
    /* A gentle whole-group swell with the voice — small, because the halo, the disc's outer ring
       and the level bars each already answer the level (see the anatomy block below). */
    transform: scale(calc(1 + var(--ff-mic-level, 0) * 0.08));
    transition: transform 0.07s linear;
}

@keyframes ff-listening-in-b-ctbx7ycqvk {
    from { opacity: 0; transform: scale(0.85); }
    to   { opacity: 1; }
}

/* When the overlay is the stop control (Interactive), only the pulse takes clicks; the rest of the
   overlay stays click-through for Move On and anything else beneath. */
.ff-listening-stoppable .ff-listening-pulse[b-ctbx7ycqvk] {
    pointer-events: auto;
    cursor: pointer;
}

/* ── Indicator anatomy (inside .ff-listening-pulse) ──
   .ff-glow           a soft halo behind everything; its size and brightness follow the live level
   .ff-ring-1/2       two outward "sonar" rings — expand and fade on offset phases
   .ff-listening-mic  the disc with the mic glyph (the anchor)
   .ff-listening-bars five level bars under the disc, heights driven by --ff-mic-level
   Everything is level-reactive through the one CSS variable the JS meter sets on the overlay each
   frame, so the picture is honest: a silent room is calm, the learner's voice lights it up. */

/* Voice halo: barely there in silence, blooms with the voice. Blurred so it reads as light. */
.ff-glow[b-ctbx7ycqvk] {
    position: absolute;
    inset: 12px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(59, 130, 246, 0.55) 0%, rgba(59, 130, 246, 0.18) 55%, transparent 72%);
    filter: blur(6px);
    opacity: calc(0.35 + var(--ff-mic-level, 0) * 0.65);
    transform: scale(calc(0.9 + var(--ff-mic-level, 0) * 0.5));
    transition: opacity 0.07s linear, transform 0.07s linear;
}

/* Sonar rings: a thin stroke that starts at the disc edge, grows past the halo and fades out —
   the second ring runs half a cycle behind so there is always one in flight. */
.ff-ring[b-ctbx7ycqvk] {
    position: absolute;
    inset: 52px;               /* starts at the disc's edge (disc 86px in a 190px group) */
    border-radius: 50%;
    border: 2px solid rgba(59, 130, 246, 0.55);
    opacity: 0;
    animation: ff-sonar-b-ctbx7ycqvk 2.2s ease-out infinite;
}
.ff-ring-2[b-ctbx7ycqvk] { animation-delay: 1.1s; }

@keyframes ff-sonar-b-ctbx7ycqvk {
    0%   { transform: scale(1);    opacity: 0.7; }
    100% { transform: scale(2.15); opacity: 0;   }
}

/* The disc with the mic glyph — a gradient so it reads as a lit object, plus a level-driven outer
   ring (the box-shadow spread grows with the voice) so the disc itself answers the learner. */
.ff-listening-mic[b-ctbx7ycqvk] {
    position: relative;        /* above the glow and rings */
    width: 86px;
    height: 86px;
    border-radius: 50%;
    background: linear-gradient(145deg, #3b82f6 0%, #1a73e8 60%, #1d4ed8 100%);
    color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow:
        0 0 0 calc(var(--ff-mic-level, 0) * 12px) rgba(59, 130, 246, 0.22),
        0 8px 26px rgba(26, 115, 232, 0.45);
    transition: box-shadow 0.07s linear;
}

/* Level bars: five thin pills under the disc. Each has its own gain (tallest in the middle) so the
   row reads as a little waveform; a slow breathe keeps it alive in silence without faking a voice. */
.ff-listening-bars[b-ctbx7ycqvk] {
    position: absolute;
    left: 50%;
    bottom: 6px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 5px;
    height: 36px;
}
.ff-listening-bars span[b-ctbx7ycqvk] {
    display: block;
    width: 5px;
    border-radius: 3px;
    background: rgba(37, 99, 235, 0.85);
    height: calc(6px + var(--ff-mic-level, 0) * var(--ff-bar-gain, 20px));
    transition: height 0.06s linear;
    animation: ff-bar-breathe-b-ctbx7ycqvk 1.6s ease-in-out infinite;
}
.ff-listening-bars span:nth-child(1)[b-ctbx7ycqvk] { --ff-bar-gain: 14px; animation-delay: 0.00s; }
.ff-listening-bars span:nth-child(2)[b-ctbx7ycqvk] { --ff-bar-gain: 24px; animation-delay: 0.20s; }
.ff-listening-bars span:nth-child(3)[b-ctbx7ycqvk] { --ff-bar-gain: 30px; animation-delay: 0.40s; }
.ff-listening-bars span:nth-child(4)[b-ctbx7ycqvk] { --ff-bar-gain: 24px; animation-delay: 0.60s; }
.ff-listening-bars span:nth-child(5)[b-ctbx7ycqvk] { --ff-bar-gain: 14px; animation-delay: 0.80s; }

@keyframes ff-bar-breathe-b-ctbx7ycqvk {
    0%, 100% { transform: scaleY(0.85); opacity: 0.75; }
    50%      { transform: scaleY(1);    opacity: 1;    }
}

/* Dark theme — keep the same shapes, deepen the halo so it sits IN the slate page, and let the
   disc carry the brightness as the focal point. */
.fox .ff-glow[b-ctbx7ycqvk] {
    background: radial-gradient(circle, rgba(96, 165, 250, 0.50) 0%, rgba(59, 130, 246, 0.16) 55%, transparent 72%);
}
.fox .ff-ring[b-ctbx7ycqvk] { border-color: rgba(147, 197, 253, 0.55); }
.fox .ff-listening-mic[b-ctbx7ycqvk] {
    background: linear-gradient(145deg, #60a5fa 0%, #3b82f6 60%, #2563eb 100%);
    box-shadow:
        0 0 0 calc(var(--ff-mic-level, 0) * 12px) rgba(96, 165, 250, 0.22),
        0 8px 26px rgba(37, 99, 235, 0.55);
}
.fox .ff-listening-bars span[b-ctbx7ycqvk] { background: rgba(147, 197, 253, 0.9); }

/* Arming (mic asked for, capture not yet live — the Arming parameter): the same indicator, visibly
   not-yet-hot. Halo off, rings held, bars dim and flat, disc muted and unlit. The moment capture goes
   live the class drops and the lit, level-reactive treatment snaps in — that transition IS the "mic
   is on" cue, paired with the earcon that plays at the same instant. */
.ff-listening-arming .ff-glow[b-ctbx7ycqvk] { opacity: 0; }
.ff-listening-arming .ff-ring[b-ctbx7ycqvk] {
    animation-play-state: paused;
    opacity: 0.35;
    transform: scale(1.15);
}
.ff-listening-arming .ff-listening-mic[b-ctbx7ycqvk] {
    background: linear-gradient(145deg, #94a3b8 0%, #64748b 100%);
    box-shadow: none;
}
.ff-listening-arming .ff-listening-bars span[b-ctbx7ycqvk] {
    animation: none;
    height: 6px;
    opacity: 0.4;
}
.ff-listening-arming .ff-listening-pulse[b-ctbx7ycqvk] {
    transform: none;   /* no level reactivity until there is a level */
}

/* Honor reduced-motion: no sonar, no breathe, no entrance scale — a static badge whose halo, disc
   ring and bars still follow the live level (they answer real input, they don't animate on their own). */
@media (prefers-reduced-motion: reduce) {
    .ff-listening-pulse[b-ctbx7ycqvk]           { animation: none; transition: none; }
    .ff-ring[b-ctbx7ycqvk]                      { animation: none; opacity: 0.35; transform: scale(1.3); }
    .ff-listening-bars span[b-ctbx7ycqvk]       { animation: none; }
    .ff-listening-surface[b-ctbx7ycqvk]::before { animation: none; }
}

/* Mobile: dead-centered, the pulse group lands directly on top of the prompt being read — most
   painfully the assessment read-aloud paragraph on a short screen. Over the lesson surface, drop it
   to the bottom (and shrink it a touch) so the text the student must read stays fully visible while
   the "we hear you" indicator remains on screen and voice-reactive. Desktop has the vertical room to
   keep it centered, so this is mobile-only — and the card placement stays centered at every width
   (a card is too short to dock anything at its bottom). */
@media (max-width: 639.98px) {
    .ff-listening-surface[b-ctbx7ycqvk] {
        align-items: flex-end;
        padding-bottom: 28px;
    }

    .ff-listening-pulse[b-ctbx7ycqvk] {
        width: 150px;
        height: 150px;
    }

    .ff-listening-mic[b-ctbx7ycqvk] {
        width: 72px;
        height: 72px;
    }

    .ff-ring[b-ctbx7ycqvk] { inset: 39px; }   /* disc edge at the smaller size */

    .ff-listening-bars[b-ctbx7ycqvk] {
        bottom: 2px;
        height: 30px;
    }
}
/* /Components/Chat/MessageDialog.razor.rz.scp.css */
/* Small centered acknowledgment modal (message + OK). Native <dialog> top layer + ::backdrop,
   themed with the --ff-* tokens (dark mode inherited). */
.ff-message-dialog[b-jw71mmky42] {
    border: none;
    padding: 0;
    margin: auto;
    width: min(420px, 90vw);
    background: transparent;
    color: var(--ff-chat-text);
}

.ff-message-dialog[b-jw71mmky42]::backdrop {
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(2px);
}

.ff-md-panel[b-jw71mmky42] {
    background: var(--ff-chat-column-bg);
    border: 1px solid var(--ff-tutor-bubble-border);
    border-radius: 14px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
    padding: 22px 22px 16px;
}

.ff-md-text[b-jw71mmky42] {
    font-size: 15px;
    line-height: 1.5;
    text-align: center;
    color: var(--ff-chat-text);
}

.ff-md-actions[b-jw71mmky42] {
    display: flex;
    justify-content: center;
    margin-top: 18px;
}

.ff-md-ok[b-jw71mmky42] {
    min-width: 96px;
    padding: 9px 22px;
    border: none;
    border-radius: 22px;
    background: var(--ff-action-fill);
    color: var(--ff-action-ink);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.1s, opacity 0.15s;
}

.ff-md-ok:hover[b-jw71mmky42] {
    transform: scale(1.04);
}
/* /Components/Chat/PricingPanel.razor.rz.scp.css */
/* Pricing panel — the one structured "surface", now shown inside the ContentOverlay. Self-scoped
   here (it was previously styled from Lesson.razor.css via ::deep, which can't reach the overlay's
   top-layer <dialog>). Plain copy, no urgency theater. */
.ff-pricing-panel[b-y4d3h2yiy0] {
    background-color: var(--ff-tutor-bubble-bg) !important;
    border: 1px solid var(--ff-tutor-bubble-border) !important;
    border-radius: 10px !important;
    padding: 16px 18px !important;
}

.ff-pricing-head[b-y4d3h2yiy0] {
    margin-bottom: 12px;
}

.ff-pricing-title[b-y4d3h2yiy0] {
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--ff-chat-text);
}

.ff-pricing-intro[b-y4d3h2yiy0] {
    font-size: 13px;
    color: var(--ff-chat-text-muted);
    margin-top: 2px;
}

.ff-pricing-tiers[b-y4d3h2yiy0] {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.ff-pricing-tier[b-y4d3h2yiy0] {
    flex: 1 1 140px;
    border: 1px solid var(--ff-tutor-bubble-border);
    border-radius: 8px;
    padding: 12px 14px;
}

.ff-pricing-tier-featured[b-y4d3h2yiy0] {
    border-color: rgba(var(--ff-accent-purple), 0.6);
    box-shadow: 0 0 0 1px rgba(var(--ff-accent-purple), 0.25) inset;
}

.ff-pricing-tier-name[b-y4d3h2yiy0] {
    font-weight: 600;
    color: var(--ff-chat-text);
}

.ff-pricing-tier-price[b-y4d3h2yiy0] {
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--ff-chat-text);
    margin: 4px 0;
}

.ff-pricing-tier-blurb[b-y4d3h2yiy0] {
    font-size: 12.5px;
    color: var(--ff-chat-text-muted);
    line-height: 1.4;
}

.ff-pricing-note[b-y4d3h2yiy0] {
    margin-top: 12px;
    font-size: 12px;
    color: var(--ff-chat-text-faint);
}
/* /Components/Chat/Stats/LearnerDashboardMessage.razor.rz.scp.css */
/* Scoped to LearnerDashboardMessage. Theme-aware progress card — light by default, dark slate under
   the app's `.fox` dark-mode class (the Blazor scoper rewrites `.fox .ff-dashboard` to
   `.fox .ff-dashboard[scope]`, so the html-level theme class works from inside this scoped file).
   All theme-dependent colors flow through the --dash-* variables; layout rules below are shared.
   Tiles + an activity heatmap; FluentFox orange for the accent. */

.ff-dashboard[b-nv2sli4li0] {
    /* Light theme (default) */
    --dash-bg: #ffffff;
    --dash-border: #e2e8f0;        /* slate-200 */
    --dash-text: #334155;          /* slate-700 */
    --dash-title: #0f172a;         /* slate-900 */
    --dash-muted: #64748b;         /* slate-500 */
    --dash-tile-bg: #f8fafc;       /* slate-50 */
    --dash-foot: #475569;          /* slate-600 */
    --dash-shadow: 0 6px 20px rgba(15, 23, 42, 0.08);
    --dash-heat-empty: #e2e8f0;    /* slate-200 */
    --dash-heat-l1: #fed7aa;       /* orange-200 */
    --dash-heat-l2: #fdba74;       /* orange-300 */
    --dash-heat-l3: #f97316;       /* orange-500 */
    --dash-heat-l4: #c2410c;       /* orange-700 */
}

.fox .ff-dashboard[b-nv2sli4li0] {
    /* Dark theme — the original slate panel */
    --dash-bg: #1e293b;            /* slate-800 */
    --dash-border: #334155;        /* slate-700 */
    --dash-text: #e2e8f0;          /* slate-200 */
    --dash-title: #f8fafc;         /* slate-50 */
    --dash-muted: #94a3b8;         /* slate-400 */
    --dash-tile-bg: #0f172a;       /* slate-900 */
    --dash-foot: #cbd5e1;          /* slate-300 */
    --dash-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
    --dash-heat-empty: #334155;    /* slate-700 */
    --dash-heat-l1: #7c2d12;       /* orange-900 */
    --dash-heat-l2: #c2410c;       /* orange-700 */
    --dash-heat-l3: #f97316;       /* orange-500 */
    --dash-heat-l4: #fdba74;       /* orange-300 */
}

.ff-dashboard[b-nv2sli4li0] {
    width: 100%;
    max-width: 660px;
    margin: 4px auto 12px;
    padding: 16px 18px;
    border-radius: 14px;
    background: var(--dash-bg);
    border: 1px solid var(--dash-border);
    color: var(--dash-text);
    box-shadow: var(--dash-shadow);
}

/* ── Header ─────────────────────────────────────────────────────────────── */
.ff-dash-head[b-nv2sli4li0] {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
}

.ff-dash-fox[b-nv2sli4li0] {
    font-size: 20px;
    line-height: 1;
}

.ff-dash-title[b-nv2sli4li0] {
    font-weight: 700;
    font-size: 15px;
    color: var(--dash-title);
}

.ff-dash-since[b-nv2sli4li0] {
    margin-left: auto;
    font-size: 12px;
    color: var(--dash-muted);
}

/* ── Speaking-Minutes (SM) headline ─────────────────────────────────────── */
.ff-dash-speaking[b-nv2sli4li0] {
    margin-bottom: 14px;
    padding: 12px 14px;
    border-radius: 12px;
    background: var(--dash-tile-bg);
    border: 1px solid var(--dash-border);
    /* Orange accent bar on the leading edge — the headline stat. */
    border-inline-start: 3px solid var(--dash-heat-l3);
}

.ff-sm-main[b-nv2sli4li0] {
    display: flex;
    align-items: baseline;
    gap: 6px;
}

.ff-sm-rate[b-nv2sli4li0] {
    font-size: 30px;
    font-weight: 800;
    line-height: 1;
    color: var(--dash-title);
}

.ff-sm-rate-unit[b-nv2sli4li0] {
    font-size: 14px;
    font-weight: 600;
    color: var(--dash-muted);
}

.ff-sm-caption[b-nv2sli4li0] {
    margin-top: 4px;
    font-size: 12px;
    color: var(--dash-muted);
}

.ff-sm-label[b-nv2sli4li0] { font-weight: 600; }
.ff-sm-dot[b-nv2sli4li0]   { margin: 0 5px; }

.ff-sm-empty[b-nv2sli4li0] {
    font-size: 13px;
    color: var(--dash-muted);
    line-height: 1.4;
}

/* ── Stat tiles ─────────────────────────────────────────────────────────── */
.ff-dash-tiles[b-nv2sli4li0] {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
    margin-bottom: 16px;
}

.ff-tile[b-nv2sli4li0] {
    background: var(--dash-tile-bg);
    border: 1px solid var(--dash-border);
    border-radius: 10px;
    padding: 10px 12px;
    min-width: 0;
}

.ff-tile-label[b-nv2sli4li0] {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--dash-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ff-tile-value[b-nv2sli4li0] {
    margin-top: 4px;
    font-size: 22px;
    font-weight: 700;
    color: var(--dash-title);
    line-height: 1.1;
}

.ff-tile-unit[b-nv2sli4li0] {
    font-size: 13px;
    font-weight: 600;
    color: var(--dash-muted);
    margin-left: 2px;
}

/* ── Activity heatmap ───────────────────────────────────────────────────── */
.ff-dash-heatmap[b-nv2sli4li0] {
    display: flex;
    gap: 3px;
    padding: 2px 0;
    overflow-x: auto;
}

.ff-heat-col[b-nv2sli4li0] {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.ff-heat-cell[b-nv2sli4li0] {
    width: 13px;
    height: 13px;
    border-radius: 3px;
    background: var(--dash-heat-empty);
}

/* Future days in the current week — invisible spacers so the grid stays rectangular. */
.ff-heat-future[b-nv2sli4li0] {
    background: transparent;
}

.ff-heat-l0[b-nv2sli4li0] { background: var(--dash-heat-empty); }  /* no lessons */
.ff-heat-l1[b-nv2sli4li0] { background: var(--dash-heat-l1); }
.ff-heat-l2[b-nv2sli4li0] { background: var(--dash-heat-l2); }
.ff-heat-l3[b-nv2sli4li0] { background: var(--dash-heat-l3); }
.ff-heat-l4[b-nv2sli4li0] { background: var(--dash-heat-l4); }

/* ── Bottom row: streak line (left) + Share button pinned bottom-right ────── */
.ff-dash-bottom[b-nv2sli4li0] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 14px;
}

.ff-dash-foot[b-nv2sli4li0] {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    color: var(--dash-foot);
}

/* Narrow screens — two tile columns instead of four. */
@media (max-width: 520px) {
    .ff-dash-tiles[b-nv2sli4li0] {
        grid-template-columns: repeat(2, 1fr);
    }
}
/* /Components/Chat/Stats/ProgressHistory.razor.rz.scp.css */
/* Scoped to ProgressHistory. Rendered INSIDE the Progress card (.ff-dashboard), so the --dash-* surface
   colors are inherited from it. The buttons are purple pills in the suggestion-chip accent
   (--ff-accent-purple: indigo in light, violet in dark) — outline for "Redo lesson" / "Go", filled for
   "Start review". The support accent marks state (a ticked item and the picks — --ff-support-line,
   orange in light, violet in dark). */

.ff-hist[b-nlvbj66ir9] {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--dash-border);
}

.ff-hist-title[b-nlvbj66ir9] {
    font-weight: 700;
    font-size: 15px;
    color: var(--dash-title);
    margin-bottom: 10px;
}

.ff-hist-lang[b-nlvbj66ir9] {
    font-weight: 600;
    color: var(--dash-muted);
}

/* The suggestion-chip pill (same accent, outline, hover wash). */
.ff-hist-start[b-nlvbj66ir9] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    padding: 6px 12px;
    border-radius: 999px;
    border: 1px solid rgb(var(--ff-accent-purple));
    background: transparent;
    color: rgb(var(--ff-accent-purple));
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.ff-hist-start:hover:not(:disabled)[b-nlvbj66ir9] {
    background: rgba(var(--ff-accent-purple), 0.14);
}

.ff-hist-item--full[b-nlvbj66ir9] {
    opacity: 0.55;
    cursor: not-allowed;
}

.ff-hist-check[b-nlvbj66ir9] {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: rgb(var(--ff-support-line));
    cursor: inherit;
}

.ff-hist-note[b-nlvbj66ir9] {
    margin-top: 10px;
    font-size: 12px;
    color: var(--dash-muted);
}

.ff-hist-items-label[b-nlvbj66ir9] {
    margin: 10px 0 8px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--dash-muted);
}

/* ── Review list ────────────────────────────────────────────────────────── */
.ff-hist-review[b-nlvbj66ir9] {
    margin-top: 6px;
    padding: 12px 14px;
    border: 1px solid var(--dash-border);
    border-radius: 12px;
    background: var(--dash-tile-bg);
}

.ff-hist-review-head[b-nlvbj66ir9] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}

.ff-hist-review-title[b-nlvbj66ir9] {
    font-size: 15px;
    font-weight: 700;
    color: var(--dash-title);
}

.ff-hist-clear[b-nlvbj66ir9] {
    margin-inline-start: auto;
    background: none;
    border: 0;
    padding: 2px 0;
    font: inherit;
    font-size: 13px;
    color: var(--dash-muted);
    cursor: pointer;
}

.ff-hist-clear:hover[b-nlvbj66ir9] {
    color: var(--dash-title);
    text-decoration: underline;
}

.ff-hist-chips[b-nlvbj66ir9] {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

.ff-hist-chip[b-nlvbj66ir9] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    padding: 5px 6px 5px 12px;
    border-radius: 999px;
    border: 1px solid rgb(var(--ff-support-line));
    background: rgba(var(--ff-support-line), 0.1);
}

.ff-hist-chip-text[b-nlvbj66ir9] {
    font-size: 14px;
    font-weight: 700;
    color: var(--dash-title);
    overflow-wrap: anywhere;
}

.ff-hist-chip-remove[b-nlvbj66ir9] {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--dash-muted);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
}

.ff-hist-chip-remove:hover[b-nlvbj66ir9] {
    color: var(--dash-title);
    background: rgba(var(--ff-support-line), 0.15);
}

.ff-hist-review-foot[b-nlvbj66ir9] {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--dash-border);
}

/* ── Recent Lessons ─────────────────────────────────────────────────────── */
.ff-hist-recent[b-nlvbj66ir9] {
    margin-top: 8px;
    padding: 16px 20px 18px;
    border: 1px solid var(--dash-border);
    border-radius: 14px;
    background: var(--dash-tile-bg);
}

.ff-hist-recent-title[b-nlvbj66ir9] {
    padding-bottom: 12px;
    font-size: 16px;
    font-weight: 700;
    color: var(--dash-title);
}

.ff-hist-recent-row[b-nlvbj66ir9] {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 0;
    border-top: 1px solid var(--dash-border);
}

/* The lesson's label ("1.2") on a soft accent tile. */
.ff-hist-recent-badge[b-nlvbj66ir9] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    min-width: 42px;
    height: 42px;
    padding: 0 6px;
    border-radius: 10px;
    background: rgba(var(--ff-accent-purple), 0.14);
    color: rgb(var(--ff-accent-purple));
    font-size: 15px;
    font-weight: 700;
}

.ff-hist-recent-text[b-nlvbj66ir9] {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}

.ff-hist-recent-name[b-nlvbj66ir9] {
    font-size: 16px;
    font-weight: 700;
    color: var(--dash-title);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ff-hist-recent-meta[b-nlvbj66ir9] {
    font-size: 14px;
    color: var(--dash-muted);
}

.ff-hist-recent .ff-hist-start[b-nlvbj66ir9] {
    padding: 9px 16px;
    font-size: 14px;
}

.ff-hist-redo-other[b-nlvbj66ir9] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 4px;
    padding-top: 16px;
    border-top: 1px solid var(--dash-border);
}

.ff-hist-redo-other-label[b-nlvbj66ir9] {
    font-size: 15px;
    color: var(--dash-title);
}

.ff-hist-redo-input[b-nlvbj66ir9] {
    width: 112px;
    padding: 9px 14px;
    border-radius: 10px;
    border: 1px solid var(--dash-border);
    background: var(--dash-bg);
    color: var(--dash-title);
    font: inherit;
    font-size: 15px;
}

.ff-hist-redo-input[b-nlvbj66ir9]::placeholder {
    color: var(--dash-muted);
}

.ff-hist-redo-input:focus[b-nlvbj66ir9] {
    outline: none;
    border-color: rgb(var(--ff-accent-purple));
}

.ff-hist-redo-miss[b-nlvbj66ir9] {
    flex-basis: 100%;
    font-size: 13px;
    color: var(--dash-muted);
}

/* ── Word picker (the collapsible "Review" card) ────────────────────────── */
.ff-hist-picker[b-nlvbj66ir9] {
    margin-top: 8px;
    border: 1px solid var(--dash-border);
    border-radius: 14px;
    background: var(--dash-tile-bg);
}

.ff-hist-picker-head[b-nlvbj66ir9] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 16px 20px;
    background: none;
    border: 0;
    color: inherit;
    font: inherit;
    text-align: start;
    cursor: pointer;
}

.ff-hist-picker-title[b-nlvbj66ir9] {
    font-size: 16px;
    font-weight: 700;
    color: var(--dash-title);
}

.ff-hist-picker-toggle[b-nlvbj66ir9] {
    font-size: 14px;
    font-weight: 600;
    color: rgb(var(--ff-accent-purple));
}

.ff-hist-picker-head:hover .ff-hist-picker-toggle[b-nlvbj66ir9] {
    text-decoration: underline;
}

.ff-hist-picker-body[b-nlvbj66ir9] {
    padding: 0 20px 18px;
}

.ff-hist-search-row[b-nlvbj66ir9] {
    display: flex;
    align-items: center;
    gap: 12px;
}

.ff-hist-search[b-nlvbj66ir9] {
    flex: 1;
    min-width: 0;
    padding: 10px 16px;
    border-radius: 10px;
    border: 1px solid var(--dash-border);
    background: var(--dash-bg);
    color: var(--dash-title);
    font: inherit;
    font-size: 15px;
}

.ff-hist-search[b-nlvbj66ir9]::placeholder {
    color: var(--dash-muted);
}

.ff-hist-search:focus[b-nlvbj66ir9] {
    outline: none;
    border-color: rgb(var(--ff-accent-purple));
}

.ff-hist-learned[b-nlvbj66ir9] {
    flex-shrink: 0;
    font-size: 13px;
    color: var(--dash-muted);
}

/* A scrolling box of every word, three columns. */
.ff-hist-words[b-nlvbj66ir9] {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 4px 16px;
    max-height: 520px;
    overflow-y: auto;
    margin-top: 16px;
    padding: 12px 20px;
    border: 1px solid var(--dash-border);
    border-radius: 12px;
    background: var(--dash-bg);
}

.ff-hist-words-empty[b-nlvbj66ir9] {
    grid-column: 1 / -1;
    padding: 8px 0;
    font-size: 14px;
    color: var(--dash-muted);
}

.ff-hist-word[b-nlvbj66ir9] {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    padding: 7px 0;
    cursor: pointer;
}

.ff-hist-word-text[b-nlvbj66ir9] {
    min-width: 0;
    font-size: 15px;
    font-weight: 700;
    color: var(--dash-title);
    overflow-wrap: anywhere;
}

.ff-hist-word-score[b-nlvbj66ir9] {
    flex-shrink: 0;
    font-size: 14px;
    color: var(--dash-muted);
}

.ff-hist-selected[b-nlvbj66ir9] {
    margin-top: 14px;
    padding-top: 4px;
    border-top: 1px solid var(--dash-border);
}

.ff-hist-picker-foot[b-nlvbj66ir9] {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 16px;
}

.ff-hist-picked-count[b-nlvbj66ir9] {
    font-size: 14px;
    color: var(--dash-muted);
}

.ff-hist-picked-count b[b-nlvbj66ir9] {
    color: var(--dash-title);
}

.ff-hist-clear--inline[b-nlvbj66ir9] {
    margin-inline-start: 0;
    text-decoration: underline;
}

/* The filled pill — the one action of the picker. Text in the card background so it reads on both accents. */
.ff-hist-start--filled[b-nlvbj66ir9] {
    margin-inline-start: auto;
    padding: 12px 24px;
    font-size: 15px;
    background: rgb(var(--ff-accent-purple));
    color: var(--dash-bg);
}

.ff-hist-start--filled:hover:not(:disabled)[b-nlvbj66ir9] {
    background: rgba(var(--ff-accent-purple), 0.85);
}

.ff-hist-start:disabled[b-nlvbj66ir9] {
    opacity: 0.45;
    cursor: not-allowed;
}

/* Phone width — two word columns; "Redo lesson" shrinks to its icon. */
@media (max-width: 520px) {
    .ff-hist-words[b-nlvbj66ir9] {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        padding: 10px 14px;
    }

    .ff-hist-picker-head[b-nlvbj66ir9] {
        padding: 14px 16px;
    }

    .ff-hist-picker-body[b-nlvbj66ir9] {
        padding: 0 14px 14px;
    }

    .ff-hist-search-row[b-nlvbj66ir9],
    .ff-hist-picker-foot[b-nlvbj66ir9] {
        flex-wrap: wrap;
    }

    .ff-hist-recent-row .ff-hist-start[b-nlvbj66ir9] {
        padding: 9px;
    }

    .ff-hist-recent[b-nlvbj66ir9] {
        padding: 14px 16px 16px;
    }

    .ff-hist-redo-label[b-nlvbj66ir9] {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }
}
/* /Components/Chat/Stats/StatsResultMessage.razor.rz.scp.css */
/* Scoped to StatsResultMessage. The chip + bubble styling is inherited from Lesson.razor.css's
   ::deep rules (this component renders inside the lesson message wrapper); only the chart's
   expand affordance is local here, because Lesson's .ff-img-expand is plain-scoped to that page
   and so does not reach a child component. Mirrors .ff-img-expand's look for a consistent cue. */

.ff-stats-chart-inline[b-mulm4enjtd] {
    position: relative;
}

.ff-stats-expand[b-mulm4enjtd] {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    border-radius: 6px;
    border: none;
    background: rgba(0, 0, 0, 0.5);
    color: #ffffff;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, transform 0.15s;
}

.ff-stats-expand:hover[b-mulm4enjtd] {
    background: rgba(0, 0, 0, 0.75);
    transform: scale(1.08);
}

/* Text-result totals — keep any newlines the builder emits, wrap normally otherwise. */
.ff-stats-text[b-mulm4enjtd] {
    white-space: pre-line;
    line-height: 1.6;
}
/* /Components/Chat/Stats/StudyCompositionBar.razor.rz.scp.css */
/* Scoped to StudyCompositionBar. Colours come from the global --ff-* tokens (tailwind.source.css)
   rather than being declared here, because Blazor's scoped CSS cannot see the `.fox` class on <html>
   that drives dark mode — a theme selector written in this file would simply never match. */

.ff-composition[b-x2d0tk3ioi] {
    margin-top: 10px;
}

.ff-composition-head[b-x2d0tk3ioi] {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 6px;
}

.ff-composition-title[b-x2d0tk3ioi] {
    font-size: 0.85em;
    font-weight: 600;
    color: var(--ff-chat-text);
}

/* Deliberately quiet: an honesty disclosure, not a warning. It should inform without implying the
   whole number is untrustworthy. */
.ff-composition-estimated[b-x2d0tk3ioi] {
    font-size: 0.72em;
    color: var(--ff-chat-text-faint);
    border: 1px solid var(--ff-chat-text-faint);
    border-radius: 999px;
    padding: 0 6px;
    line-height: 1.5;
}

/* The bar. Flex-grow carries each share, so the row always fills exactly 100% with no rounding gap.
   The 2px gap is a surface spacer — adjacent fills must never blend into one continuous mass. */
.ff-composition-bar[b-x2d0tk3ioi] {
    display: flex;
    gap: 2px;
    height: 14px;
    width: 100%;
    overflow: hidden;
    border-radius: 4px;
}

.ff-composition-seg[b-x2d0tk3ioi] {
    flex-basis: 0;
    min-width: 3px;          /* a real-but-tiny segment must stay visible rather than vanish */
    height: 100%;
    border: none;
    padding: 0;
    cursor: pointer;
    border-radius: 2px;
    transition: filter 0.15s, transform 0.15s;
}

.ff-composition-seg:hover[b-x2d0tk3ioi],
.ff-composition-seg:focus-visible[b-x2d0tk3ioi] {
    filter: brightness(1.08);
}

/* Selection is shown by lifting the segment, not by recolouring it — the colour is the identity and
   must not change meaning when tapped. */
.ff-composition-seg.is-selected[b-x2d0tk3ioi] {
    transform: scaleY(1.35);
}

.ff-composition-seg--reading[b-x2d0tk3ioi],
.ff-composition-dot.ff-composition-seg--reading[b-x2d0tk3ioi] { background: var(--ff-mod-reading); }

.ff-composition-seg--listening[b-x2d0tk3ioi],
.ff-composition-dot.ff-composition-seg--listening[b-x2d0tk3ioi] { background: var(--ff-mod-listening); }

.ff-composition-seg--speaking[b-x2d0tk3ioi],
.ff-composition-dot.ff-composition-seg--speaking[b-x2d0tk3ioi] { background: var(--ff-mod-speaking); }

.ff-composition-seg--writing[b-x2d0tk3ioi],
.ff-composition-dot.ff-composition-seg--writing[b-x2d0tk3ioi] { background: var(--ff-mod-writing); }

.ff-composition-seg--interface[b-x2d0tk3ioi],
.ff-composition-dot.ff-composition-seg--interface[b-x2d0tk3ioi] { background: var(--ff-mod-interface); }

/* Legend — direct labels. Carries identity as text so the bar never depends on colour alone, and
   supplies the "relief" the light-mode palette requires for its two low-contrast fills. */
.ff-composition-legend[b-x2d0tk3ioi] {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
}

.ff-composition-legend-btn[b-x2d0tk3ioi] {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    padding: 2px 0;
    cursor: pointer;
    font-size: 0.78em;
    color: var(--ff-chat-text-muted);
}

.ff-composition-dot[b-x2d0tk3ioi] {
    width: 9px;
    height: 9px;
    border-radius: 2px;
    flex: 0 0 auto;
}

/* Values and labels wear text tokens, never the series colour — the swatch beside them carries
   identity, so tinting the text too would be redundant and less legible. */
.ff-composition-name[b-x2d0tk3ioi] {
    color: var(--ff-chat-text);
}

.ff-composition-value[b-x2d0tk3ioi] {
    font-variant-numeric: tabular-nums;
    color: var(--ff-chat-text);
}

.ff-composition-pct[b-x2d0tk3ioi] {
    font-variant-numeric: tabular-nums;
    color: var(--ff-chat-text-faint);
}

.ff-composition-what[b-x2d0tk3ioi] {
    margin: 6px 0 0;
    font-size: 0.78em;
    color: var(--ff-chat-text-muted);
}

.ff-composition-footnote[b-x2d0tk3ioi] {
    margin: 8px 0 0;
    font-size: 0.72em;
    line-height: 1.45;
    color: var(--ff-chat-text-faint);
}

.ff-visually-hidden[b-x2d0tk3ioi] {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    .ff-composition-seg[b-x2d0tk3ioi] { transition: none; }
    .ff-composition-seg.is-selected[b-x2d0tk3ioi] { transform: none; outline: 2px solid var(--ff-chat-text); }
}
/* /Components/Chat/SuggestionsBar.razor.rz.scp.css */
/* Scoped to SuggestionsBar. Sizing wrapper for the shared FlagEmoji glyph on language-name chips
   (the font itself is declared in FlagEmoji.razor.css); non-language chips render no flag. */
.ff-chip-flag[b-x77ogii211] {
    font-size: 1.4em;
    line-height: 1;
    vertical-align: -0.15em;    /* optically center against the chip label */
    margin-right: 5px;
}

/* The part of a chip that differs (onboarding gender choice: "estoy cansad<b>o</b>"): violet — the
   informational accent — with an underline as a second cue that doesn't rely on colour. --ff-accent-purple
   is theme-aware (deep violet in light mode, light violet in dark), so contrast holds in both. The text is
   rendered as markup, so ::deep is needed to reach it. */
.ff-suggestion-btn[b-x77ogii211]  b {
    font-weight: 800;
    color: rgb(var(--ff-accent-purple));
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}
/* /Components/Chat/WelcomeBackCardMessage.razor.rz.scp.css */
/* Scoped to WelcomeBackCardMessage. Light theme is the warm-coral palette (coral gradient header,
   espresso text on white); dark swaps to the purple/slate look under the app's `.fox` class (the
   Blazor scoper rewrites `.fox .ff-wb-card` to a scoped selector, so the html-level theme class
   works from inside this file). Buttons are sized to the app's standard chat pill
   (.ff-suggestion-btn: radius 16px, padding 5px 16px, 13px). */

.ff-wb-card[b-lc2mqiuktw] {
    /* Light theme (default) — warm coral */
    --wb-body-bg: #ffffff;
    --wb-header-gradient: linear-gradient(120deg, #E8552E 0%, #F47A45 100%);
    --wb-question: #4A2417;
    --wb-name: #4A2417;
    --wb-age: #B79B8E;
    --wb-accent: #E8552E;
    --wb-tile-bg: #FBF0EA;
    --wb-tile-value: #4A2417;
    --wb-primary-bg: linear-gradient(120deg, #E8552E 0%, #F47A45 100%);
    --wb-secondary-border: #EDD3C4;
    --wb-secondary-text: #A9451F;
    --wb-secondary-hover: #FBF0EA;
    --wb-shadow: 0 8px 30px rgba(122, 46, 26, 0.12);
    /* Ink ON the header/primary fills — white in BOTH themes: the coral and the dark deep violet
       (handoff 3C) are both dark enough to carry it. Violet fills always take white letters. */
    --wb-fill-ink: #ffffff;
    --wb-fill-ink-soft: rgba(255, 255, 255, 0.75);
    --wb-header-chip-bg: rgba(255, 255, 255, 0.18);
}

.fox .ff-wb-card[b-lc2mqiuktw] {
    /* Dark theme — deep slate body; header and language pills wear the DEEP accent violet with
       white ink (2026-09-09 handoff 3C: fills deep enough that white passes 4.5:1 — the pale
       #a98bf0 fills measured ~2.9:1 under white). The gradient runs dark → light left-to-right on
       purpose: contrast rises, not falls, as a long greeting runs into the tail. */
    --wb-body-bg: #1e293b;                        /* slate-800 */
    --wb-header-gradient: linear-gradient(120deg, #6f45c8 0%, #8557d8 100%);
    --wb-question: #f1f5f9;                       /* slate-100 */
    --wb-name: #f1f5f9;
    --wb-age: #94a3b8;                            /* slate-400 */
    --wb-accent: #b99cf3;                         /* violet AS TEXT on dark panels — the light step, never the fill value */
    --wb-tile-bg: #273449;
    --wb-tile-value: #f1f5f9;
    --wb-primary-bg: #7c50cf;                     /* solid chip fill — white 15px labels at 4.62:1 */
    --wb-secondary-border: #3b4a63;
    --wb-secondary-text: #e2e8f0;
    --wb-secondary-hover: #273449;
    --wb-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
}

/* Flag glyphs render via the shared FlagEmoji component (which owns the subset-font declaration);
   these wrappers only size and align its 1em glyph for their spot in the card. */
.ff-wb-flag[b-lc2mqiuktw] {
    font-size: 1.35em;          /* relative, so it reads larger beside the tile values */
    line-height: 1;             /* keep the bigger glyph from stretching the row */
    vertical-align: -0.12em;    /* optically center against the adjacent text */
    margin-right: 2px;
}

.ff-wb-card[b-lc2mqiuktw] {
    width: 100%;
    max-width: 660px;            /* match the progress dashboard card above it */
    margin: 4px auto 12px;
    border-radius: 22px;
    overflow: hidden;
    background: var(--wb-body-bg);
    box-shadow: var(--wb-shadow);
}

/* ── Gradient header: icon chip + title, learning language beneath ───────── */
.ff-wb-header[b-lc2mqiuktw] {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 16px 22px;
    background: var(--wb-header-gradient);
}

.ff-wb-icon[b-lc2mqiuktw] {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 14px;                          /* rounded square, per the design */
    background: var(--wb-header-chip-bg);
    color: var(--wb-fill-ink);
}

.ff-wb-icon svg[b-lc2mqiuktw] {
    width: 22px;
    height: 22px;
}

/* The flag-as-icon glyph — the shared FlagEmoji, sized to fill the chip. */
.ff-wb-icon-flag[b-lc2mqiuktw] {
    font-size: 28px;
    line-height: 1;
}

.ff-wb-header-text[b-lc2mqiuktw] {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.ff-wb-title[b-lc2mqiuktw] {
    color: var(--wb-fill-ink);
    font-size: 20px;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.ff-wb-learning[b-lc2mqiuktw] {
    display: inline-flex;
    align-items: center;        /* mixed font sizes — center the label on the larger flag + language */
    gap: 7px;
    font-size: 14px;
}

.ff-wb-learning-label[b-lc2mqiuktw] {
    color: var(--wb-fill-ink-soft);
    font-weight: 600;
}

.ff-wb-learning-value[b-lc2mqiuktw] {
    color: var(--wb-fill-ink);
    font-weight: 800;
    font-size: 18px;            /* larger than the "Learning" label — the language is the point */
}

/* ── Body: the question + any remaining profile detail ───────────────────── */
.ff-wb-body[b-lc2mqiuktw] {
    padding: 16px 22px 18px;
}

.ff-wb-question[b-lc2mqiuktw] {
    font-size: 16px;
    font-weight: 800;
    color: var(--wb-question);
}

.ff-wb-name-row[b-lc2mqiuktw] {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 10px;
}

.ff-wb-name[b-lc2mqiuktw] {
    font-size: 22px;
    font-weight: 800;
    letter-spacing: -0.01em;
    color: var(--wb-name);
}

.ff-wb-age[b-lc2mqiuktw] {
    font-size: 14px;
    font-weight: 700;
    color: var(--wb-age);
}

.ff-wb-tiles[b-lc2mqiuktw] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 12px;
    margin-top: 12px;
}

.ff-wb-tile[b-lc2mqiuktw] {
    padding: 12px 16px;
    border-radius: 14px;
    background: var(--wb-tile-bg);
}

.ff-wb-tile-label[b-lc2mqiuktw] {
    margin-bottom: 4px;
    font-size: 12px;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--wb-accent);
}

.ff-wb-tile-value[b-lc2mqiuktw] {
    font-size: 16px;
    font-weight: 700;
    color: var(--wb-tile-value);
}

/* ── Actions — the standard chat pill size (.ff-suggestion-btn spec) ─────── */
.ff-wb-actions[b-lc2mqiuktw] {
    display: flex;
    flex-wrap: wrap;            /* the multi-profile picker can carry several language pills */
    gap: 10px;
    margin-top: 14px;
}

.ff-wb-btn-primary[b-lc2mqiuktw],
.ff-wb-btn-secondary[b-lc2mqiuktw] {
    display: inline-flex;       /* centers the flag glyph beside the label */
    align-items: center;
    gap: 6px;
    border-radius: 16px;
    padding: 5px 16px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.ff-wb-btn-primary[b-lc2mqiuktw] {
    border: 1px solid transparent;
    background: var(--wb-primary-bg);
    color: var(--wb-fill-ink);
}

.ff-wb-btn-primary:hover[b-lc2mqiuktw] {
    filter: brightness(1.08);
}

.ff-wb-btn-secondary[b-lc2mqiuktw] {
    border: 1px solid var(--wb-secondary-border);
    background: transparent;
    color: var(--wb-secondary-text);
}

.ff-wb-btn-secondary:hover[b-lc2mqiuktw] {
    background: var(--wb-secondary-hover);
}

/* ── Quiet exit ("Done for today") — an understated text link below the pills ── */
.ff-wb-quiet[b-lc2mqiuktw] {
    margin-top: 10px;
}

.ff-wb-quiet-btn[b-lc2mqiuktw] {
    border: none;
    background: transparent;
    padding: 2px 0;
    font-size: 12px;
    font-weight: 500;
    color: var(--wb-secondary-text);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    opacity: 0.75;
}

.ff-wb-quiet-btn:hover[b-lc2mqiuktw] {
    opacity: 1;
}
/* /Components/Chat/WelcomeBackMessage.razor.rz.scp.css */
/* The card itself is the login page's global .ff-auth-card (tailwind.source.css), so it themes with
   the same --ff-auth-* tokens. These rules only cover what the popup adds: the modal <dialog>, the
   wordmark, in-card scrolling, the bottom-sheet treatment on phones, and a TIGHTER vertical rhythm —
   the page's spacing plus the wordmark and guest button made the popup taller than a laptop screen. */

.ff-welcome-back-dialog[b-s1bh7c6vjc] {
    border: none;
    padding: 0;
    margin: auto;                 /* center in the viewport; the backdrop fills around it */
    width: min(30rem, 94vw);
    max-width: none;
    max-height: 92vh;
    background: transparent;      /* the card paints the surface */
    overflow: visible;
    outline: none;                /* showModal focuses the dialog; the card's own border is the frame */
}

.ff-welcome-back-dialog[b-s1bh7c6vjc]::backdrop {
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(2px);
}

.ff-welcome-back[b-s1bh7c6vjc] {
    max-height: 92vh;
    overflow: hidden auto;        /* short screens scroll INSIDE the card */
    padding-top: 1.25rem;
    padding-bottom: 1.1rem;
}

/* ── Compact rhythm: same pieces as the login page, less air between them ── */

.ff-welcome-back[b-s1bh7c6vjc]  .ff-auth-tabs {
    margin-bottom: 1rem;
}

.ff-welcome-back[b-s1bh7c6vjc]  .ff-auth-tab {
    padding: 0.5rem;
}

/* height:auto is load-bearing — the img's width/height attributes carry the lockup's intrinsic
   ratio, but they also set a presentational height that a width-only rule leaves standing, which
   letterboxes the wordmark inside a 344px-tall box. */
.ff-wb-logo[b-s1bh7c6vjc] {
    display: block;
    width: min(160px, 50%);
    height: auto;
    margin: 0 auto 0.5rem;
}

.ff-welcome-back .ff-auth-title[b-s1bh7c6vjc] {
    font-size: 1.4rem;
}

.ff-welcome-back .ff-auth-subtitle[b-s1bh7c6vjc] {
    margin-top: 0.2rem;
}

.ff-welcome-back .ff-auth-rule[b-s1bh7c6vjc] {
    margin: 0.9rem 0 1rem;
}

.ff-welcome-back .ff-auth-field[b-s1bh7c6vjc] {
    margin-bottom: 0.75rem;
}

.ff-welcome-back .ff-auth-input[b-s1bh7c6vjc] {
    padding: 0.55rem 0.85rem;
}

.ff-welcome-back .ff-auth-row[b-s1bh7c6vjc] {
    margin-bottom: 0.75rem;
}

.ff-welcome-back .ff-auth-submit[b-s1bh7c6vjc] {
    margin-top: 0;
    padding: 0.7rem 1rem;
}

.ff-welcome-back[b-s1bh7c6vjc]  .ff-auth-divider {
    margin: 0.9rem 0 0.75rem;
}

.ff-welcome-back[b-s1bh7c6vjc]  .ff-auth-external {
    padding: 0.55rem 1rem;
    margin-bottom: 0.5rem;
}

/* Continue as guest: a plain text button under the providers — clearly offered, but it doesn't
   need a full bordered row of its own. */
.ff-wb-guest[b-s1bh7c6vjc] {
    display: block;
    margin: 0.4rem auto 0;
    padding: 0.4rem 0.75rem;
    border: none;
    background: transparent;
    color: var(--ff-auth-link);
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
}

.ff-wb-guest:hover[b-s1bh7c6vjc] {
    text-decoration: underline;
}

.ff-wb-guest:focus-visible[b-s1bh7c6vjc] {
    outline: none;
    border-radius: 0.5rem;
    box-shadow: 0 0 0 3px var(--ff-action-ring);
}

/* Mobile: full-width slide-up bottom sheet, same treatment as ContentOverlay. */
@media (max-width: 560px) {
    .ff-welcome-back-dialog[b-s1bh7c6vjc] {
        width: 100vw;
        margin: auto 0 0;          /* pin to the bottom of the viewport */
    }

    .ff-welcome-back[b-s1bh7c6vjc] {
        border-radius: 16px 16px 0 0;
        border-bottom: none;
    }

    .ff-welcome-back-dialog[open][b-s1bh7c6vjc] {
        animation: ff-wb-slide-up-b-s1bh7c6vjc 0.25s ease-out;
    }
}

@keyframes ff-wb-slide-up-b-s1bh7c6vjc {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}
/* /Components/Dialog/RealTimeDialogPanel.razor.rz.scp.css */
/* ── Theme-aware palette for the Real Time Dialog panel ────────────────────────────────────────
   The panel's markup uses inline styles that reference these var(--rtd-*) tokens; the tokens carry
   LIGHT values by default and DARK overrides under `.fox` (the app's dark class on <html>). A scoped
   rule on the panel's OWN root element can see `.fox` (it's an ancestor), so this is genuinely
   theme-aware — unlike a `::deep` reach into a child, which cannot. One source of truth, no
   per-theme duplication in the markup. */
.rtd-panel[b-6ovyvtoavo] {
    /* surfaces + structure */
    --rtd-bg: #f7f9fc;
    --rtd-border: #dde3ee;
    --rtd-divider: #e6ebf3;
    /* eyebrows / muted labels */
    --rtd-eyebrow: #6b7688;
    --rtd-eyebrow-dim: #9aa3b2;
    /* speaker accents (tutor blue / learner green) — deepened for contrast on light */
    --rtd-blue: #3b56e0;
    --rtd-green: #12a06a;
    /* bubbles: three states per speaker — spoken (settled), active (speaking-now, a touch lifted +
       accent edge), up-next (ghosted). Blue and green tuned to the same light weight. */
    --rtd-tutor-bubble: #e6ebfc;
    --rtd-tutor-border: #c2ccf5;
    --rtd-tutor-active: #e6ebfc;      /* light keeps its approved look — active reads via edge + scale */
    --rtd-student-bubble: #e9f6f0;
    --rtd-student-border: #bce4d3;
    --rtd-student-active: #e9f6f0;
    /* up-next (ghosted) bubble */
    --rtd-next-bubble: #eef1f6;
    --rtd-next-border: #d7dde8;
    /* bubble text */
    --rtd-text: #1a2233;
    --rtd-text-dim: #9aa3b2;
    /* per-line status labels */
    --rtd-label-done: #8a93a6;
    --rtd-label-next: #aab2c0;
    /* result + chrome */
    --rtd-feedback: #5c6678;
    --rtd-round: #6b7280;
    --rtd-round-strong: #1a2233;
    --rtd-round-dim: #99a1af;
    --rtd-body: #3a4150;
    --rtd-hint: #99a1af;
    --rtd-listening-label: #3b6fd0;
    /* ghost-button (continue / finish / skip) */
    --rtd-ghost-border: #b7cabd;
    --rtd-ghost-text: #17805a;
}

.fox .rtd-panel[b-6ovyvtoavo] {
    /* Dark mode, lifted a few steps off near-black so it breathes (Claude Design handoff). */
    --rtd-bg: #16203a;                /* the two panels (card + start block) */
    --rtd-border: #2a3a5a;
    --rtd-divider: #2a3a5a;
    --rtd-eyebrow: #8a93a6;
    --rtd-eyebrow-dim: #5c6678;
    --rtd-blue: #4b6bff;
    --rtd-green: #2ee6a0;
    --rtd-tutor-bubble: #24354f;      /* spoken tutor */
    --rtd-tutor-border: #3a4d78;
    --rtd-tutor-active: #2b4172;      /* speaking-now — brighter than spoken, with the accent edge */
    --rtd-student-bubble: #1d3d3b;    /* spoken learner */
    --rtd-student-border: #2a6b53;
    --rtd-student-active: #235a4c;
    --rtd-next-bubble: #1e293f;       /* up-next (rendered at 55% opacity in the markup) */
    --rtd-next-border: #2e3d5c;
    --rtd-text: #f2f4fb;
    --rtd-text-dim: #8493ab;
    --rtd-label-done: #6b7688;
    --rtd-label-next: #4d5766;
    --rtd-feedback: #9aa3b2;
    --rtd-round: #9a9aa5;
    --rtd-round-strong: #e6e6ea;
    --rtd-round-dim: #7b8494;
    --rtd-body: #c7ccd6;
    --rtd-hint: #7b8494;
    --rtd-listening-label: #4a86e0;
    --rtd-ghost-border: #2f6b53;
    --rtd-ghost-text: #8fce9e;
}

/* The "we hear you" mic over the card is the shared ListeningOverlay component (Components/Chat) —
   the same indicator the lesson drills show, so it is styled there, not here. */
/* /Components/Layout/MainLayout.razor.rz.scp.css */
.page[b-7mmc6k0zbu] {
    position: relative;
    display: flex;
    flex-direction: column;
}

main[b-7mmc6k0zbu] {
    flex: 1;
}

.sidebar[b-7mmc6k0zbu] {
    background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}

.top-row[b-7mmc6k0zbu] {
    background-color: #f7f7f7;
    border-bottom: 1px solid #d6d5d5;
    justify-content: flex-end;
    height: 3.5rem;
    display: flex;
    align-items: center;
}

    .top-row[b-7mmc6k0zbu]  a, .top-row[b-7mmc6k0zbu]  .btn-link {
        white-space: nowrap;
        margin-left: 1.5rem;
        text-decoration: none;
    }

    .top-row[b-7mmc6k0zbu]  a:hover, .top-row[b-7mmc6k0zbu]  .btn-link:hover {
        text-decoration: underline;
    }

    .top-row[b-7mmc6k0zbu]  a:first-child {
        overflow: hidden;
        text-overflow: ellipsis;
    }

@media (max-width: 640.98px) {
    .top-row[b-7mmc6k0zbu] {
        justify-content: space-between;
    }

    .top-row[b-7mmc6k0zbu]  a, .top-row[b-7mmc6k0zbu]  .btn-link {
        margin-left: 0;
    }
}

@media (min-width: 641px) {
    .page[b-7mmc6k0zbu] {
        flex-direction: row;
    }

    .sidebar[b-7mmc6k0zbu] {
        width: 250px;
        height: 100vh;
        position: sticky;
        top: 0;
    }

    .top-row[b-7mmc6k0zbu] {
        position: sticky;
        top: 0;
        z-index: 1;
    }

    .top-row.auth[b-7mmc6k0zbu]  a:first-child {
        flex: 1;
        text-align: right;
        width: 0;
    }

    .top-row[b-7mmc6k0zbu], article[b-7mmc6k0zbu] {
        padding-left: 2rem !important;
        padding-right: 1.5rem !important;
    }
}

/* NOTE: #blazor-error-ui is styled GLOBALLY in tailwind.source.css, not here. It's a framework
   singleton shared by every layout (MainLayout AND AccountLayout); a scoped rule here only matched
   MainLayout's copy, leaving AccountLayout's div permanently visible ("An unhandled error has
   occurred" with no error). Keep it unscoped so `display: none` applies everywhere. */
/* /Components/Layout/PublicLayout.razor.rz.scp.css */
/* Shell for public shareable pages. The app's MainLayout wraps content in an overflow-hidden,
   viewport-tall <main>; this one deliberately does the opposite — the document scrolls normally,
   because a public page has no chat pane to keep pinned and a stranger's browser should behave the
   way every other page on the web does. */

.ff-public-shell[b-cji3lcawfx] {
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    background: var(--ff-public-bg, oklch(0.972 0.018 75));
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
.components-offline-visible[b-obhcwastue],
.components-away-visible[b-obhcwastue],
.components-device-offline-visible[b-obhcwastue],
.components-pause-visible[b-obhcwastue],
.components-resume-failed-visible[b-obhcwastue] {
    display: none;
}

/* The Continue button shows in every "connection lost" state — first attempt, retrying, failed — and
   while the device itself reports offline (.components-offline, set by the page script). The message
   beside it is "you were away" for a lost circuit, and "you're offline" whenever the device says so
   (that one wins when both hold). */
#components-reconnect-modal.components-reconnect-show .components-offline-visible[b-obhcwastue],
#components-reconnect-modal.components-reconnect-retrying .components-offline-visible[b-obhcwastue],
#components-reconnect-modal.components-reconnect-failed .components-offline-visible[b-obhcwastue],
#components-reconnect-modal.components-offline .components-offline-visible[b-obhcwastue],
#components-reconnect-modal.components-reconnect-show:not(.components-offline) .components-away-visible[b-obhcwastue],
#components-reconnect-modal.components-reconnect-retrying:not(.components-offline) .components-away-visible[b-obhcwastue],
#components-reconnect-modal.components-reconnect-failed:not(.components-offline) .components-away-visible[b-obhcwastue],
#components-reconnect-modal.components-offline .components-device-offline-visible[b-obhcwastue],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-obhcwastue],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-obhcwastue] {
    display: block;
}

#components-reconnect-modal.components-reconnect-retrying[b-obhcwastue],
#components-reconnect-modal.components-reconnect-failed[b-obhcwastue] {
    display: block;
}


#components-reconnect-modal[b-obhcwastue] {
    /* Theme-aware, softened panel instead of a stark pure-white box — a gentle off-white in light
       mode and the slate card colour in dark, so it doesn't jar over the dimmed page. Tokens
       inherit from :root / .fox (app.css); referencing them via var() is scope-safe (only
       `.fox descendant` selectors break under CSS isolation, which we don't use here). */
    background-color: var(--ff-tutor-bubble-bg, #eef1f6);
    color: var(--ff-chat-text, #1a1a2e);
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 1px solid var(--ff-chat-column-border, #e6e6ef);
    border-radius: 0.5rem;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-obhcwastue 0.5s both;
    &[open]

{
    animation: components-reconnect-modal-slideUp-b-obhcwastue 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-obhcwastue 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

}

#components-reconnect-modal[b-obhcwastue]::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity-b-obhcwastue 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-obhcwastue {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-obhcwastue {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-obhcwastue {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-obhcwastue] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-obhcwastue] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-obhcwastue] {
    border: 0;
    background-color: #6b9ed2;
    color: white;
    padding: 4px 24px;
    border-radius: 4px;
}

    #components-reconnect-modal button:hover[b-obhcwastue] {
        background-color: #3b6ea2;
    }

    #components-reconnect-modal button:active[b-obhcwastue] {
        background-color: #6b9ed2;
    }

.components-rejoining-animation[b-obhcwastue] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-obhcwastue] {
        position: absolute;
        border: 3px solid #0087ff;
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-obhcwastue 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-obhcwastue] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-obhcwastue {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}
/* /Components/Pages/Card.razor.rz.scp.css */
/* Public Report Card.

   Self-contained palette on purpose: this page is a shared artifact a stranger opens, so it must
   look identical for everyone and must not depend on the viewer's app theme (which they have never
   set) or on the theme toggle's `.fox` class. Colours here mirror the brand — warm cream ground,
   FluentFox purple, deep slate text — but are declared locally rather than pulled from the app's
   --ff-* tokens.

   Design rule: the metric is the headline, the brand is the frame. The hero number is by far the
   largest thing on the page; the logo is 28px in the footer. It should read as an achievement, not
   as an ad. */

.ff-card-page[b-ic9d8ckctv] {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem 3rem;
}

.ff-card-inner[b-ic9d8ckctv] {
    width: 100%;
    max-width: 34rem;
}

.ff-card[b-ic9d8ckctv] {
    background: oklch(0.995 0.008 75);
    border: 1px solid oklch(0.9 0.022 68);
    border-radius: 1.25rem;
    padding: 2.5rem 2rem 1.5rem;
    box-shadow: 0 1px 2px rgb(74 45 143 / 6%), 0 12px 32px rgb(74 45 143 / 8%);
    text-align: center;
    color: oklch(0.3 0.025 55);
}

/* ── Who ── */

.ff-card-who[b-ic9d8ckctv] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
}

.ff-card-name[b-ic9d8ckctv] {
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.2;
    /* A display name is capped at 40 chars but can still be one long unbroken token. */
    overflow-wrap: anywhere;
}

.ff-card-lang[b-ic9d8ckctv] {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.95rem;
    color: oklch(0.5 0.02 265);
}

/* ── Hero ── */

.ff-card-hero[b-ic9d8ckctv] {
    margin: 1.75rem 0 1.5rem;
}

.ff-card-hero-value[b-ic9d8ckctv] {
    font-size: clamp(4.5rem, 22vw, 7rem);
    font-weight: 700;
    line-height: 0.95;
    letter-spacing: -0.03em;
    color: #4a2d8f;
    font-variant-numeric: tabular-nums;
}

.ff-card-hero-label[b-ic9d8ckctv] {
    margin-top: 0.5rem;
    font-size: 1rem;
    font-weight: 500;
    color: oklch(0.45 0.02 265);
}

/* ── Secondary stats ── */

.ff-card-stats[b-ic9d8ckctv] {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 1.75rem;
    margin: 0 0 1.25rem;
    padding: 1.25rem 0 0;
    border-top: 1px solid oklch(0.93 0.015 68);
}

.ff-card-stat[b-ic9d8ckctv] {
    min-width: 5rem;
}

.ff-card-stat dt[b-ic9d8ckctv] {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: oklch(0.58 0.02 265);
}

.ff-card-stat dd[b-ic9d8ckctv] {
    margin: 0.15rem 0 0;
    font-size: 1.5rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.ff-card-period[b-ic9d8ckctv] {
    margin: 0 0 1.5rem;
    font-size: 0.8rem;
    color: oklch(0.58 0.02 265);
}

/* ── Call to action ── */

.ff-card-cta[b-ic9d8ckctv] {
    display: inline-block;
    padding: 0.7rem 1.5rem;
    border-radius: 999px;
    background: #4a2d8f;
    color: #fff;
    font-weight: 600;
    text-decoration: none;
}

.ff-card-cta:hover[b-ic9d8ckctv],
.ff-card-cta:focus-visible[b-ic9d8ckctv] {
    background: #3b2372;
}

/* ── Brand frame ── */

.ff-card-brand[b-ic9d8ckctv] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    margin-top: 1.75rem;
    padding-top: 1.25rem;
    border-top: 1px solid oklch(0.93 0.015 68);
}

.ff-card-brand-link[b-ic9d8ckctv] {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    text-decoration: none;
    color: inherit;
}

/* The lockup is a wide wordmark (4.7:1), so it is sized by HEIGHT and carries its own name —
   it replaced the old square icon + separate "FluentFox" text span that sat beside it. */
.ff-card-logo[b-ic9d8ckctv] {
    height: 20px;
    width: auto;
}

.ff-card-tagline[b-ic9d8ckctv] {
    font-size: 0.8rem;
    font-style: italic;
    color: oklch(0.58 0.02 265);
}

/* ── 404 / 410 ── */

.ff-card-empty[b-ic9d8ckctv] {
    text-align: center;
    color: oklch(0.3 0.025 55);
}

.ff-card-empty h1[b-ic9d8ckctv] {
    font-size: 1.5rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
}

.ff-card-empty p[b-ic9d8ckctv] {
    margin-bottom: 1.5rem;
    color: oklch(0.5 0.02 265);
}
/* /Components/Pages/LegalChangelog.razor.rz.scp.css */
/* Legal changelog — reuses the .ff-legal-doc container width from the document pages (that class is
   scoped to LegalDocumentView, so the container styles are restated here for this standalone page). */
.ff-legal-doc[b-mzye489y4f] {
    max-width: 46rem;
    margin: 0 auto;
    padding: 2rem clamp(1rem, 4vw, 2rem) 4rem;
    line-height: 1.6;
}

.ff-legal-doc h1[b-mzye489y4f] {
    font-size: 1.9rem;
    font-weight: 700;
    margin: 0 0 0.5rem;
}

.ff-changelog-group[b-mzye489y4f] {
    margin-top: 2rem;
}

.ff-changelog-group h2[b-mzye489y4f] {
    font-size: 1.3rem;
    font-weight: 700;
    margin: 0 0 0.75rem;
}

.ff-changelog-list[b-mzye489y4f] {
    list-style: none;
    padding: 0;
    margin: 0;
}

.ff-changelog-entry[b-mzye489y4f] {
    padding: 0.9rem 0;
    border-top: 1px solid var(--ff-auth-card-border, #e2d3bd);
}

.ff-changelog-head[b-mzye489y4f] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem 0.75rem;
}

.ff-changelog-head a[b-mzye489y4f] {
    color: var(--ff-auth-link, #2563eb);
    text-decoration: underline;
    font-weight: 600;
}

.ff-changelog-date[b-mzye489y4f] {
    font-size: 0.9rem;
    opacity: 0.75;
}

.ff-changelog-badge[b-mzye489y4f] {
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.06);
    border: 1px solid var(--ff-auth-card-border, #e2d3bd);
}

.ff-changelog-badge-material[b-mzye489y4f] {
    background: rgba(241, 90, 36, 0.12);
    border-color: var(--ff-mkt-card-accent, #f15a24);
}

.ff-changelog-summary[b-mzye489y4f] {
    margin: 0.4rem 0 0;
    font-size: 0.95rem;
}
/* /Components/Pages/LegalDocumentView.razor.rz.scp.css */
/* Readable long-form legal document. Colours inherit from the app layout (theme-correct in light and
   dark); only the superseded banner and links set their own. The document body is injected as raw
   markdown HTML (a MarkupString), so its child elements don't carry the scoped attribute — they are
   styled via ::deep. */
/* Full-height scroll container — the app shell's .ff-main-content is overflow-hidden and taller than
   the viewport, so without this the long document is clipped with no way to scroll. Full width so the
   scrollbar sits at the viewport edge; the readable column is centered by .ff-legal-inner. */
.ff-legal-doc[b-6gurumuk4a] {
    height: 100%;
    overflow-y: auto;
}

.ff-legal-inner[b-6gurumuk4a] {
    max-width: 46rem;
    margin: 0 auto;
    padding: 2rem clamp(1rem, 4vw, 2rem) 4rem;
    line-height: 1.6;
}

.ff-legal-banner[b-6gurumuk4a] {
    margin-bottom: 1.5rem;
    padding: 0.85rem 1.1rem;
    border: 1px solid var(--ff-auth-card-border, #d9c3a5);
    border-left: 4px solid var(--ff-mkt-card-accent, #f15a24);
    border-radius: 0.5rem;
    background: rgba(241, 90, 36, 0.06);
    font-size: 0.95rem;
}

.ff-legal-banner a[b-6gurumuk4a] {
    color: var(--ff-auth-link, #2563eb);
    text-decoration: underline;
    font-weight: 600;
}

.ff-legal-operator[b-6gurumuk4a] {
    margin-top: 2.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--ff-auth-card-border, #d9c3a5);
    font-size: 0.9rem;
    opacity: 0.8;
}

.ff-legal-body[b-6gurumuk4a]  h1 {
    font-size: 1.9rem;
    font-weight: 700;
    margin: 0 0 0.75rem;
}

.ff-legal-body[b-6gurumuk4a]  h2 {
    font-size: 1.3rem;
    font-weight: 700;
    margin: 2rem 0 0.6rem;
}

.ff-legal-body[b-6gurumuk4a]  h3 {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 1.5rem 0 0.5rem;
}

.ff-legal-body[b-6gurumuk4a]  p,
.ff-legal-body[b-6gurumuk4a]  li {
    font-size: 1rem;
    margin: 0.6rem 0;
}

.ff-legal-body[b-6gurumuk4a]  ul {
    padding-left: 1.4rem;
    list-style: disc;
}

.ff-legal-body[b-6gurumuk4a]  blockquote {
    margin: 1rem 0;
    padding: 0.75rem 1rem;
    border-left: 4px solid var(--ff-auth-card-border, #d9c3a5);
    background: rgba(0, 0, 0, 0.03);
    border-radius: 0.35rem;
    font-size: 0.95rem;
}

.ff-legal-body[b-6gurumuk4a]  a {
    color: var(--ff-auth-link, #2563eb);
    text-decoration: underline;
}

.ff-legal-body[b-6gurumuk4a]  strong {
    font-weight: 700;
}
/* /Components/Pages/Lesson.razor.rz.scp.css */
/* Lesson page scoped styles — the rich ff-* card styling, reused via ::deep into the leaf message
   components that FfChatUi's EntryTemplate renders.

   The EntryTemplate is authored in this page (Lesson.razor), so its rendered leaves carry this page's
   scope and these ::deep rules pierce into them. Because FfChatUi has no Syncfusion `.e-chat-ui` base,
   this file also supplies the base `.e-message-content` bubble plus the student-green hook. The owned
   mic footer, inline image / lightbox, and etymology styles live further down. */

/* ── Base bubble (replaces Syncfusion's `.e-chat-ui .e-message-content`) ── */
[b-v9s3hk0fve] .e-message-content {
    display: inline-block;
    max-width: 100%;
    background: var(--ff-bubble-bg);
    color: var(--ff-chat-text);
    border-radius: 12px;
    padding: 10px 14px;
    line-height: 1.5;
    white-space: normal !important;
    word-wrap: break-word;
    overflow-wrap: anywhere;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

/* The default (tutor) bubble would otherwise be white-on-white now that the chat column is its own
   white sheet — give the tutor's plain bubbles a soft neutral tint + hairline so they read as cards.
   Every other speaker/card (student green, status lavender, score, the pastel drill cards, coral
   tests) sets its own background below, so this only touches the plain tutor prompts. */
[b-v9s3hk0fve] .ff-mw-tutor .e-message-content,
[b-v9s3hk0fve] .ff-mw-convertprompt .e-message-content,
[b-v9s3hk0fve] .ff-mw-gettingstartedvideo .e-message-content {
    background: var(--ff-tutor-bubble-bg);
    border: 1px solid var(--ff-tutor-bubble-border);
    border-bottom-left-radius: 4px;                 /* speech tail — tight corner on the speaker's side */
    box-shadow: 0 1px 2px rgba(60, 45, 20, 0.05);   /* warm whisper — the third lift cue alongside fill + border */
}

/* Guest-conversion bubble ("Save your progress" / hard gate): tutor-styled bubble with a primary
   Create-account link, then an inline login form — the in-transcript replacement for the old modal +
   auth popup. The child ConvertPromptMessage / InlineLoginForm render inside this page's
   EntryTemplate, so these ::deep rules reach their elements (same pattern as welcome-back). */
[b-v9s3hk0fve] .ff-mw-convertprompt .ff-convert-actions {
    margin-top: 12px;
}

/* A quiet "Log in" section header above the inline form, separating the create-account path from the
   already-have-an-account path. */
[b-v9s3hk0fve] .ff-mw-convertprompt .ff-convert-login {
    margin-top: 14px;
    border-top: 1px solid var(--ff-tutor-bubble-border);
    padding-top: 12px;
}

[b-v9s3hk0fve] .ff-mw-convertprompt .ff-convert-login-label {
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--ff-chat-text-muted);
    margin-bottom: 8px;
}

/* The form itself is styled by InlineLoginForm's own scoped CSS; here we only own the spacing. */
[b-v9s3hk0fve] .ff-mw-convertprompt .ff-inline-login {
    margin-top: 0;
}

/* Welcome-back login: a split-hero card in a modal <dialog> over the dimmed lesson — all of its
   styling lives in WelcomeBackMessage.razor.css, and the open dialog sits in the top layer, so its
   transcript wrapper renders nothing inline. This page keeps only the progressive-disclosure rule
   below, which spans two child components (the WelcomeBackMessage actions row keyed off the
   InlineLoginForm email field) and so needs this page's ::deep reach.

   Progressive disclosure: the moment the learner starts typing an email, the path is "log in" — so
   hide the secondary Register / Continue-as-guest buttons to keep one obvious action. They return as
   soon as the field is cleared. Pure CSS: the email input always carries a placeholder, so
   :placeholder-shown is true only while it's empty. */
[b-v9s3hk0fve] .ff-mw-welcomebacklogin .ff-welcome-back:has(input[name="email"]:not(:placeholder-shown)) .ff-welcome-back-actions {
    display: none;
}

/* Getting Started video: a click-to-watch poster in the bubble (the first frame), capped so it never
   dominates the column. Clicking it opens the lightbox below. The child component is rendered inside
   this page's EntryTemplate, so ::deep reaches its elements. */

/* Intro heading embedded in the video bubble (was a separate tutor line). */
[b-v9s3hk0fve] .ff-getting-started-video .ff-gs-heading {
    font-weight: 600;
    color: var(--ff-chat-text);
    margin-bottom: 2px;
}

[b-v9s3hk0fve] .ff-gs-video-wrap {
    position: relative;
    display: inline-block;
    width: 100%;
    max-width: 480px;
    margin-top: 8px;
    cursor: pointer;
}

[b-v9s3hk0fve] .ff-gs-video-wrap .ff-gs-video {
    display: block;
    width: 100%;
    /* preload=none means no metadata, so the element can't learn the clip's real aspect ratio — pin a
       standard 16:9 box and let the poster fill it. Make the uploaded .png 16:9 (e.g. 1280x720) to match. */
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: 10px;
    background: #000;
    pointer-events: none;   /* the wrapper owns the click — the poster never plays inline */
}

/* Centered play glyph over the poster — the "watch" affordance, kept visible (not hover-gated) so
   it reads on touch too. Click-through so the wrapper handles the open. */
[b-v9s3hk0fve] .ff-gs-video-wrap .ff-gs-play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    color: #ffffff;
    font-size: 26px;
    line-height: 64px;
    text-align: center;
    padding-left: 4px;        /* optically center the triangle */
    pointer-events: none;
    transition: background 0.15s, transform 0.15s;
}

[b-v9s3hk0fve] .ff-gs-video-wrap:hover .ff-gs-play {
    background: rgba(0, 0, 0, 0.75);
    transform: translate(-50%, -50%) scale(1.08);
}

[b-v9s3hk0fve] .ff-getting-started-video .ff-getting-started-video-actions {
    margin-top: 10px;
}

/* Hint shown in place of Continue until the student starts the video. */
[b-v9s3hk0fve] .ff-getting-started-video .ff-gs-watch-hint {
    font-size: 0.85rem;
    font-style: italic;
    opacity: 0.7;
}

/* The expanded player in the lightbox — mirrors `.ff-lightbox img`. Plain (page-scoped), since the
   lightbox is authored lexically in Lesson.razor. */
.ff-lightbox-video[b-v9s3hk0fve] {
    max-width: 90vw;
    max-height: 90vh;
    border-radius: 8px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
    background: #000;
    cursor: default;
}

[b-v9s3hk0fve] .e-message-content .e-text {
    white-space: normal;
    /* Shared card BODY size — the single base for every bubble's running text (tutor prompts,
       scenario, score, dialog, student, repeat). Matches the LEARN cards' body rows (vocab / key
       word / etymology all 14px) so no card's base text reads larger than another's. Headers stay
       their own size: eyebrows, the vocab headword (1.5em), status lines (0.8em) compute from the
       unchanged 16px container, not from this. */
    font-size: 14px;
}

/* Full-width phase divider (e.g. "Set Up", "Device check") — centered label with a hairline rule
   reaching both edges of the chat column, à la Teams' "Last read" separator. The wrapper is forced
   to full width so the divider isn't shrunk to its label. */
[b-v9s3hk0fve] .ff-mw-sectiondivider {
    width: 100%;
}

[b-v9s3hk0fve] .ff-section-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    margin: 10px 0;
}

[b-v9s3hk0fve] .ff-section-divider::before,
[b-v9s3hk0fve] .ff-section-divider::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--ff-tutor-bubble-border, rgba(255, 255, 255, 0.15));
}

[b-v9s3hk0fve] .ff-section-divider > span {
    flex: 0 0 auto;
    color: var(--ff-chat-text);
    opacity: 0.7;
    font-weight: 700;
    /* One size for EVERY section divider label ("Set Up", "Device check", "Test", ...) —
       they all render through this single rule, so they stay uniform. */
    font-size: 1rem;
    letter-spacing: 0.05em;
    white-space: nowrap;
}

/* ── Per-entry wrapper + student-green hook (Syncfusion supplied neither) ── */
[b-v9s3hk0fve] .ff-message-wrapper {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    max-width: 100%;
    min-width: 0;
}

[b-v9s3hk0fve] .ff-mw-student {
    align-items: flex-end;
}

/* Initial page-load spinner — centered over the lesson surface while the opening cold-open door is
   being generated (before any transcript entry exists). Absolutely positioned against the
   position:relative .ff-lesson-surface so it sits dead-center, not in the transcript flow. */
.ff-initial-loading[b-v9s3hk0fve] {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 5;
}

[b-v9s3hk0fve] .ff-mw-student .e-message-content {
    background: #27ae60;
    color: #ffffff;
}

/* Student bubble with a "replay what I said" button — text and button on one line. */
[b-v9s3hk0fve] .ff-mw-student .e-message-content.ff-has-replay {
    display: inline-flex;
    align-items: center;
    gap: 10px;
}

/* White-on-green variant of ff-play-btn so it reads on the student bubble. */
[b-v9s3hk0fve] .ff-mw-student .ff-student-replay {
    margin: 0;
    flex: none;
    width: 24px;
    height: 24px;
    border-color: rgba(255, 255, 255, 0.6);
    color: rgba(255, 255, 255, 0.9);
}

[b-v9s3hk0fve] .ff-mw-student .ff-student-replay:hover {
    background: rgba(255, 255, 255, 0.15);
    border-color: #ffffff;
    color: #ffffff;
}

[b-v9s3hk0fve] .ff-mw-student .e-text {
    color: #ffffff;
}

/* Center the lesson illustration in its row — the one non-text card, so it reads as a deliberate
   break from the left-aligned bubble stack. The wrapper fills the row width and centers the
   inline-block image (and its absolutely-pinned expand button rides along, top-right of the image). */
[b-v9s3hk0fve] .ff-mw-image {
    width: 100%;
    align-items: center;
}

/* Center the returning-student progress card the same way: its wrapper would otherwise
   shrink-wrap and sit at the row's flex-start, leaving the card hugging the left rail. */
[b-v9s3hk0fve] .ff-mw-learnerdashboard {
    width: 100%;
    align-items: center;
}

/* Lesson-plan card — same near-full-width, centered treatment as the dashboard card. */
[b-v9s3hk0fve] .ff-mw-lessonplancard {
    width: 100%;
    align-items: center;
}

/* Welcome-back card (returning-learner gates + the multi-language picker) — same treatment. */
[b-v9s3hk0fve] .ff-mw-welcomebackcard {
    width: 100%;
    align-items: center;
}

/* ── Status / system messages (Welcome, Profile, Session complete) ── */
[b-v9s3hk0fve] .ff-status-message {
    background-color: var(--ff-status-bg) !important;
    border-left: 4px solid #8e44ad;
    border-radius: 12px !important;
    padding: 8px 12px !important;
    color: var(--ff-status-text);
}

[b-v9s3hk0fve] .ff-status-message .e-text {
    text-align: left;
}

/* Transient status spinners (Generating lesson… / Starting lesson…) — plain and subtle,
   no coloured band, so they read as fleeting app chatter rather than an exchange card. */
[b-v9s3hk0fve] .ff-status-message-plain {
    background-color: var(--ff-status-plain-bg) !important;
    border-radius: 12px !important;
    padding: 8px 12px !important;
    color: var(--ff-status-plain-text);
    font-style: italic;
}

[b-v9s3hk0fve] .ff-status-message-plain .e-text {
    text-align: left;
}

/* Exchange instruction label — base style, overridden per exchange type. */
[b-v9s3hk0fve] .ff-exchange-label {
    font-size: 0.8em;
    font-weight: 600;
    font-style: normal;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    line-height: 1.2;
    margin-bottom: 2px;
    color: var(--ff-chat-text-faint);
}

/* Collapse any default flex gap + top margin on every card type. */
[b-v9s3hk0fve] .ff-vocab-message,
[b-v9s3hk0fve] .ff-dup-message,
[b-v9s3hk0fve] .ff-model-message,
[b-v9s3hk0fve] .ff-dialog-message {
    gap: 0 !important;
    row-gap: 0 !important;
}

[b-v9s3hk0fve] .ff-dup-message .e-text,
[b-v9s3hk0fve] .ff-model-message .e-text,
[b-v9s3hk0fve] .ff-dialog-message .e-text,
[b-v9s3hk0fve] .ff-status-message .e-text {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

/* ── Vocabulary card — warm amber ── */
[b-v9s3hk0fve] .ff-vocab-message {
    background-color: var(--ff-vocab-bg) !important;
    border-left: 4px solid #e6a817 !important;
    border-radius: 12px !important;
    padding: 10px 14px !important;
    position: relative;   /* anchors the absolutely-positioned skip ⋮ in the top-right corner */
}

[b-v9s3hk0fve] .ff-vocab-message .ff-exchange-label {
    color: var(--ff-vocab-label);
}

/* Vocab body — our own class, free of Syncfusion's .e-text rules. */
[b-v9s3hk0fve] .ff-vocab-body {
    margin-top: 0;
    padding-top: 0;
}

/* Make the <em> target phrase large and prominent. */
[b-v9s3hk0fve] .ff-vocab-body em {
    display: block;
    font-size: 1.5em;
    font-weight: 700;
    font-style: italic;
    color: var(--ff-vocab-word);
    margin: 4px 0 2px;
    line-height: 1.2;
}

/* Translation line — plain, slightly muted. */
[b-v9s3hk0fve] .ff-translation {
    color: var(--ff-chat-text-muted);
    font-size: 14px;   /* shared LEARN-card body size — keep in sync with usage/pronunciation/origin/etymology */
    margin-bottom: 4px;
}

/* ── Duplication card — soft blue ── */
[b-v9s3hk0fve] .ff-dup-message {
    background-color: var(--ff-dup-bg) !important;
    border-left: 4px solid #3a8fd1 !important;
    border-radius: 12px !important;
    padding: 10px 14px !important;
}

[b-v9s3hk0fve] .ff-dup-message .ff-exchange-label {
    color: var(--ff-dup-label);
}

/* ── Model dialog card — soft indigo ── */
[b-v9s3hk0fve] .ff-model-message {
    background-color: var(--ff-model-bg) !important;
    border-left: 4px solid #7c5cbf !important;
    border-radius: 12px !important;
    padding: 10px 14px !important;
}

[b-v9s3hk0fve] .ff-model-message .ff-exchange-label {
    color: var(--ff-model-label);
}

[b-v9s3hk0fve] .ff-model-message .e-text {
    line-height: 1.9;
}

/* Person 1 / Person 2 lines inside a model dialog card. */
[b-v9s3hk0fve] .ff-dialog-line {
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding: 3px 0;
}

[b-v9s3hk0fve] .ff-person-label {
    font-weight: 700;
    color: var(--ff-model-label);
    white-space: nowrap;
    min-width: 4.5em;
}

[b-v9s3hk0fve] .ff-dialog-text {
    color: var(--ff-chat-text);
}

/* ── Dialog (Scenario model) card — soft green ── */
[b-v9s3hk0fve] .ff-dialog-message {
    background-color: var(--ff-dialog-bg) !important;
    border-left: 4px solid #27ae60 !important;
    border-radius: 12px !important;
    padding: 10px 14px !important;
}

[b-v9s3hk0fve] .ff-dialog-message .ff-exchange-label {
    color: var(--ff-dialog-label);
}

/* ── Score message — plain background, colour-coded text ── */
[b-v9s3hk0fve] .ff-score-message {
    background-color: var(--ff-score-bg) !important;
    border: 1px solid var(--ff-score-border) !important;
    border-radius: 12px !important;
    padding: 4px 12px !important;
    font-weight: 500;
}

[b-v9s3hk0fve] .ff-score-pass .e-text    { color: var(--ff-score-pass); }
[b-v9s3hk0fve] .ff-score-partial .e-text { color: var(--ff-score-partial); }
[b-v9s3hk0fve] .ff-score-fail .e-text    { color: var(--ff-score-fail); }

/* Speaker label — "Tutor:". */
[b-v9s3hk0fve] .ff-speaker {
    font-weight: 600;
    margin-right: 4px;
}

/* Dictionary-style usage + pronunciation guides shown under the vocabulary word. */
[b-v9s3hk0fve] .ff-usage-note {
    color: var(--ff-chat-text-soft);
    font-size: 14px;   /* shared LEARN-card body size */
    margin-top: 4px;
}

[b-v9s3hk0fve] .ff-pronunciation {
    font-style: italic;
    color: var(--ff-chat-text-faint);
    font-size: 14px;   /* shared LEARN-card body size */
    margin-top: 4px;
    letter-spacing: 0.3px;
}

/* Pronunciation tip — a grapheme sound note shown just beneath the phonetic guide. A plain,
   readable sentence (not italic like the phonetic line), softly coloured so it reads as a friendly
   aside rather than required content. */
[b-v9s3hk0fve] .ff-pronunciation-tip {
    color: var(--ff-chat-text-soft);
    font-size: 14px;   /* shared LEARN-card body size */
    line-height: 1.4;
    margin-top: 5px;
}

/* Admin edit mode only: points the editor at the tip's real authoring surface. */
[b-v9s3hk0fve] .ff-vocab-edit-hint {
    color: var(--ff-chat-text-faint);
    font-size: 12px;
    font-style: italic;
    margin-top: 2px;
    display: block;
}

/* A word-card section whose value was never generated. Always rendered (the row never vanishes) so
   missing data reads as missing data rather than as a narrower card. */
[b-v9s3hk0fve] .ff-card-missing {
    color: var(--ff-chat-text-faint);
    opacity: 0.7;
    letter-spacing: 1px;
}

/* Admin-only grapheme-tip editor hosted by the vocab card (edit mode). Same quiet etymology-editor
   chrome; the label carries the "applies to every word with this grapheme" safeguard. */
[b-v9s3hk0fve] .ff-tip-editor {
    margin-top: 8px;
    padding: 8px 10px;
    border: 1px dashed var(--ff-etym-spine);
    border-radius: 10px;
}

[b-v9s3hk0fve] .ff-tip-editor-label {
    color: var(--ff-chat-text-soft);
    font-size: 12px;
    margin-bottom: 4px;
}

[b-v9s3hk0fve] .ff-tip-editor-label code {
    font-size: 11px;
}

[b-v9s3hk0fve] .ff-tip-editor-input {
    min-height: 64px;
}

[b-v9s3hk0fve] .ff-tip-editor-preview {
    margin-top: 4px;
    font-size: 14px;
}

[b-v9s3hk0fve] .ff-tip-editor-error {
    color: #b3261e;
    font-size: 12px;
    margin-top: 4px;
}

/* Admin-only generic exercise editor hosted by the tutor bubble (Fill in the Blank, Add to the Phrase). */
[b-v9s3hk0fve] .ff-exercise-editor {
    margin-top: 8px;
    padding: 8px 10px;
    border: 1px dashed var(--ff-etym-spine);
    border-radius: 10px;
}

[b-v9s3hk0fve] .ff-exercise-editor-field {
    display: block;
    margin-bottom: 8px;
}

[b-v9s3hk0fve] .ff-exercise-editor-input {
    min-height: 0;
}

[b-v9s3hk0fve] .ff-exercise-editor-hint,
[b-v9s3hk0fve] .ff-exercise-editor-status {
    display: block;
    color: var(--ff-chat-text-faint);
    font-size: 12px;
    margin-top: 2px;
}

/* Privacy-policy link — a quiet, separate element beneath the onboarding mic-privacy note (kept out
   of the sanitized message text, which strips anchors). */
[b-v9s3hk0fve] .ff-privacy-link {
    margin-top: 8px;
    font-size: 13px;
}

[b-v9s3hk0fve] .ff-privacy-link a {
    color: rgb(var(--ff-accent-purple));
    text-decoration: underline;
    text-underline-offset: 2px;
}

[b-v9s3hk0fve] .ff-privacy-link a:hover {
    text-decoration-thickness: 2px;
}

/* ── Language tip — a standalone big-picture card shown at the start of a lesson. Cooler accent
   (purple) so it reads as distinct from the amber vocabulary card; calm and inviting, not a drill. */
[b-v9s3hk0fve] .ff-language-tip-message {
    position: relative;
    background: var(--ff-tip-bg) !important;
    border: 1px solid var(--ff-tip-border) !important;
    /* Solid colored left accent (like the other cards) so it curves cleanly into the rounded corners.
       A ::before / background spine renders square-ended in this setup; only a real border follows
       the border-radius — so the accent is a single colour rather than the old orange→indigo gradient. */
    border-left: 4px solid var(--ff-tip-spine) !important;
    border-radius: 14px !important;
    padding: 16px 20px 14px 18px !important;
    box-shadow: var(--ff-tip-shadow);
    gap: 0 !important;
    row-gap: 0 !important;
}

[b-v9s3hk0fve] .ff-language-tip-header {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 10px;
}

/* Emoji sits in a small rounded amber chip (light); in dark the chip is transparent so the glyph reads plain. */
[b-v9s3hk0fve] .ff-language-tip-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    border-radius: 8px;
    background: var(--ff-tip-icon-bg);
    color: var(--ff-tip-icon-color);
    font-size: 15px;
}

[b-v9s3hk0fve] .ff-language-tip-title {
    font-size: 12.5px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    line-height: 1.2;
    color: var(--ff-tip-eyebrow);
}

[b-v9s3hk0fve] .ff-language-tip-body {
    color: var(--ff-chat-text);
    font-size: 14px;   /* shared card body size — matches the LEARN cards + base .e-text */
    line-height: 1.6;
    margin-bottom: 16px;
}

/* Filled indigo Continue button — scoped to the tip/RWA card only (the shared .ff-suggestion-btn
   keeps its outline style everywhere else). Light = solid indigo gradient; dark = outline violet. */
[b-v9s3hk0fve] .ff-language-tip-message .ff-card-actions {
    display: flex;
    justify-content: flex-end;
}

[b-v9s3hk0fve] .ff-language-tip-message .ff-suggestion-btn {
    border-radius: 12px !important;
    padding: 10px 20px !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    background: var(--ff-tip-cta-bg) !important;
    color: var(--ff-tip-cta-text) !important;
    border: 1px solid var(--ff-tip-cta-border) !important;
    box-shadow: var(--ff-tip-cta-shadow);
    transition: filter 0.15s ease, transform 0.15s ease;
}

[b-v9s3hk0fve] .ff-language-tip-message .ff-suggestion-btn:hover {
    background: var(--ff-tip-cta-bg) !important;
    filter: brightness(1.06);
    transform: translateY(-1px);
}

/* ── Tip of the Day card ── (reuses the tip/RWA card chrome above; no Continue button) */
[b-v9s3hk0fve] .ff-tip-message .ff-tip-body {
    margin-bottom: 0;   /* no Continue button follows; the images (if any) add their own top gap */
}

[b-v9s3hk0fve] .ff-tip-images {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 14px;
}

/* Screenshots sit side by side on a wide card and wrap to a stack on a narrow one. */
[b-v9s3hk0fve] .ff-tip-images img {
    flex: 1 1 0;
    min-width: 0;
    max-width: 100%;
    height: auto;
    border-radius: 10px;
    border: 1px solid var(--ff-tip-border);
}

/* ── Key-word definition card ── */
[b-v9s3hk0fve] .ff-key-word-message {
    background-color: var(--ff-card-bg) !important;   /* shared spine-card fill — purple border/label carries the cue */
    border-left: 4px solid rgba(var(--ff-accent-purple), 0.85) !important;
    border-radius: 12px !important;
    padding: 10px 14px !important;
    gap: 0 !important;
    row-gap: 0 !important;
}

[b-v9s3hk0fve] .ff-key-word-header {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    line-height: 1.2;
    color: var(--ff-chat-text-faint);
    margin-bottom: 4px;
}

[b-v9s3hk0fve] .ff-key-word-icon {
    font-size: 14px;
}

[b-v9s3hk0fve] .ff-key-word-term {
    color: var(--ff-chat-text);
    font-size: 16px;
    font-weight: 700;
    line-height: 1.3;
    margin-bottom: 2px;
}

[b-v9s3hk0fve] .ff-key-word-definition {
    color: var(--ff-chat-text);
    font-size: 14px;
    line-height: 1.5;
}

[b-v9s3hk0fve] .ff-key-word-usage {
    color: var(--ff-chat-text);
    font-size: 14px;
    line-height: 1.5;
    margin-top: 4px;
}

[b-v9s3hk0fve] .ff-key-word-sentence {
    color: var(--ff-card-note-text);   /* italic like a dictionary example, but readable — not muted */
    font-size: 14px;
    font-style: italic;
    line-height: 1.5;
    margin-top: 4px;
}

/* The inline row labels ("Definition:", "Meaning:") — bold and quietly toned so the value leads.
   Shared by the Key Word and New Vocabulary cards so both scan identically. */
[b-v9s3hk0fve] .ff-key-word-label,
[b-v9s3hk0fve] .ff-card-label {
    font-weight: 700;
    font-style: normal;
    color: var(--ff-chat-text-faint);
}

/* ── Suggestion / Continue row ── */
[b-v9s3hk0fve] .ff-suggestion-container {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
    padding-left: 2px;
}

/* Pill-shaped suggestion + Continue buttons (Syncfusion e-btn base, available globally).
   Syncfusion's default e-primary/e-outline purple is a mid-tone that reads as low-contrast on our
   dark surfaces (the vocab bubble's slate, the chat sheet), so retint to the dark-legible
   --ff-accent-purple token (deep purple in light mode, violet-300 in dark) shared with the drill-card
   spines — readable in both themes for both the suggestion bar and the LEARN card's Continue.
   VIOLET, not the orange action accent: chips are offered suggestions ("informs"), and coloring
   them orange diluted the page's real CTAs (tried 2026-09-09, reverted 2026-09-10). */
[b-v9s3hk0fve] .ff-suggestion-btn {
    border-radius: 16px !important;
    padding: 5px 16px !important;
    font-size: 13px !important;
    cursor: pointer;
    color: rgb(var(--ff-accent-purple)) !important;
    border-color: rgb(var(--ff-accent-purple)) !important;
}
[b-v9s3hk0fve] .ff-suggestion-btn:hover {
    background-color: rgba(var(--ff-accent-purple), 0.14) !important;
}

/* ── Lesson-complete Share button ── */
[b-v9s3hk0fve] .ff-share-container {
    display: flex;
    margin-top: 10px;
    padding-left: 2px;
}
/* Quiet, pill-shaped — a low-key celebration affordance, not a loud growth CTA (friendly brand
   voice). Reuses the dark-legible accent token like the suggestion pills. */
[b-v9s3hk0fve] .ff-share-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    border-radius: 16px;
    padding: 5px 16px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    background: transparent;
    color: rgb(var(--ff-accent-purple));
    border: 1px solid rgb(var(--ff-accent-purple));
    transition: background-color 0.15s;
}
[b-v9s3hk0fve] .ff-share-btn:hover {
    background-color: rgba(var(--ff-accent-purple), 0.14);
}

/* LEARN card action footer — replay pinned bottom-LEFT, Continue bottom-right. */
[b-v9s3hk0fve] .ff-card-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 10px;
}

/* Left-align the replay button (Continue stays right). Without this the base .ff-play-btn's
   margin-left:auto would shove the replay button across to sit beside Continue — the same
   override ff-action-row uses to split the replay/mic pair. */
[b-v9s3hk0fve] .ff-card-actions .ff-play-btn {
    margin-top: 0;
    margin-left: 0;
    margin-right: auto;
}

/* ── Stage 3: spoken-production cards + mic + PA breakdown ──────────────────────────────────── */

/* Test / image-prompt card — coral, signals "your turn" (cold test). Used by ImageTestMessage /
   ReadAloudMessage / RepeatTestMessage, which host the inline mic in their ff-action-row (next to
   any replay button). The base .e-message-content bubble + .ff-exchange-label are defined above. */
[b-v9s3hk0fve] .ff-image-test-message {
    background-color: var(--ff-test-bg) !important;
    border-left: 4px solid #e8835f !important;
    border-radius: 12px !important;
    padding: 10px 14px !important;
}

[b-v9s3hk0fve] .ff-image-test-message .ff-exchange-label {
    color: var(--ff-test-label);
}

/* ── Pronunciation Assessment breakdown — folded into the score bubble by HandleScoreMessage ── */

/* Row of per-word chips — each chip is the reference word + its accuracy score. */
[b-v9s3hk0fve] .ff-pa-words {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
}

[b-v9s3hk0fve] .ff-pa-word {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.92em;
    font-weight: 600;
    border: 1px solid transparent;
}

[b-v9s3hk0fve] .ff-pa-word .ff-pa-score {
    font-size: 0.75em;
    font-weight: 500;
    opacity: 0.8;
}

/* Colour bands — green / amber / red by accuracy. */
[b-v9s3hk0fve] .ff-pa-good { background: var(--ff-pa-good-bg); color: var(--ff-score-pass); border-color: var(--ff-pa-good-border); }
[b-v9s3hk0fve] .ff-pa-meh  { background: var(--ff-pa-meh-bg); color: var(--ff-score-partial); border-color: var(--ff-pa-meh-border); }
[b-v9s3hk0fve] .ff-pa-bad  { background: var(--ff-pa-bad-bg); color: var(--ff-score-fail); border-color: var(--ff-pa-bad-border); }

/* Syllable / phoneme detail for the worst-scoring word. */
[b-v9s3hk0fve] .ff-pa-phonemes {
    font-size: 0.85em;
    color: var(--ff-chat-text-muted);
    margin-top: 6px;
    font-weight: 400;
}

[b-v9s3hk0fve] .ff-pa-phonemes em {
    font-style: normal;
    font-weight: 600;
    color: var(--ff-chat-text);
}

[b-v9s3hk0fve] .ff-pa-phonemes .ff-pa-good { color: var(--ff-score-pass); font-weight: 600; }
[b-v9s3hk0fve] .ff-pa-phonemes .ff-pa-meh  { color: var(--ff-score-partial); font-weight: 600; }
[b-v9s3hk0fve] .ff-pa-phonemes .ff-pa-bad  { color: var(--ff-score-fail); font-weight: 600; }

/* Four aggregate scores at the bottom. */
/* "+2 words learned and used" — the reward a passed test earns towards the leaderboard. Violet informs (the accent
   policy); a translucent fill + the chat text colour reads in both light and dark mode. */
[b-v9s3hk0fve] .ff-words-earned {
    display: inline-block;
    margin-top: 6px;
    padding: 2px 10px;
    border-radius: 999px;
    border: 1px solid rgba(169, 139, 240, 0.55);
    background: rgba(169, 139, 240, 0.16);
    color: var(--ff-chat-text);
    font-size: 0.85em;
    font-weight: 600;
    animation: ff-words-earned-pop-b-v9s3hk0fve 360ms ease-out;
}

@keyframes ff-words-earned-pop-b-v9s3hk0fve {
    0%   { transform: scale(0.6); opacity: 0; }
    70%  { transform: scale(1.08); opacity: 1; }
    100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    [b-v9s3hk0fve] .ff-words-earned { animation: none; }
}

[b-v9s3hk0fve] .ff-pa-totals {
    font-size: 0.82em;
    color: var(--ff-chat-text-soft);
    margin-top: 6px;
    font-style: italic;
    font-weight: 400;
}

/* ── Action row — replay + mic on one line (RepeatTest replay arrives with TTS in Stage 4) ── */
[b-v9s3hk0fve] .ff-action-row {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 10px;
}

[b-v9s3hk0fve] .ff-action-row .ff-play-btn {
    margin-top: 0;
    margin-left: 0;
    margin-right: auto;   /* left-align the replay button; the mic is pushed to the right */
}

/* Replay icon button (ReplayIcon glyph, sized by font-size) — standalone uses margin-top; inside
   ff-action-row it is overridden to 0. */
[b-v9s3hk0fve] .ff-play-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 10px;
    margin-left: auto;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 1.5px solid rgba(var(--ff-replay-accent), 0.55);
    background: transparent;
    color: rgba(var(--ff-replay-accent), 0.85);   /* GREEN = replay; the mic is blue, recording red */
    cursor: pointer;
    font-size: 18px;   /* the glyph; 10px was a bare triangle nobody read as a control */
    padding: 0;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

[b-v9s3hk0fve] .ff-play-btn:hover {
    background: rgba(var(--ff-replay-accent), 0.1);
    border-color: rgba(var(--ff-replay-accent), 0.9);
    color: rgba(var(--ff-replay-accent), 1);
}

/* Labelled replay pill — the audio-only cards (DictationTest / RepeatTest), where hearing the
   phrase IS the exercise and replay is the learner's ONLY control. The 28px icon-only circle above
   is an incidental "hear it again" affordance on chat bubbles; here it was too easy to miss. Same
   colours and glyph, but a pill with a visible "Replay" word and a bigger icon. */
[b-v9s3hk0fve] .ff-play-btn--labeled {
    width: auto;
    height: 34px;
    gap: 6px;
    padding: 0 14px 0 10px;
    border-radius: 17px;
    border-color: rgba(var(--ff-replay-accent), 0.75);
    color: rgba(var(--ff-replay-accent), 1);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    line-height: 1;
}

/* The glyph outsizes the 14px label on purpose — it is the part a learner recognises. */
[b-v9s3hk0fve] .ff-play-btn--labeled .ff-play-icon {
    width: 26px;
    height: 26px;
}

/* ── Microphone button — rendered inline in the active spoken-production card's ff-action-row
      (MicButton's internals), next to any replay button. ── */
[b-v9s3hk0fve] .ff-mic-btn {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 2px solid var(--ff-mic-button-border);
    background: var(--ff-mic-button-bg);
    color: var(--ff-mic-button-text);
    font-size: 18px;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    position: relative;        /* anchors the live-level ring (::after) while recording */
    --ff-mic-level: 0;         /* 0..1 live mic input level, set from JS each frame */
    transition: background 0.2s, border-color 0.2s, color 0.2s, transform 0.2s;
}

[b-v9s3hk0fve] .ff-mic-btn:hover:not(:disabled) {
    background: var(--ff-mic-button-hover-bg);
    border-color: var(--ff-mic-button-hover-border);
    transform: scale(1.05);
}

[b-v9s3hk0fve] .ff-mic-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

[b-v9s3hk0fve] .ff-mic-recording {
    background: #e74c3c !important;
    border-color: #c0392b !important;
    color: #ffffff;
}

/* Voice-level ring — the "we hear you" feedback. It blooms outward and brightens
   with live mic input (--ff-mic-level, set from JS each frame) so the student can
   SEE their voice is reaching the mic. A silent room leaves it as a faint armed
   halo at the button edge; talking makes it pulse with the actual sound — unlike
   the old timer-based pulse, which animated even when nothing was being heard. */
[b-v9s3hk0fve] .ff-mic-recording::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid rgba(231, 76, 60, 0.75);
    transform: scale(calc(1 + var(--ff-mic-level, 0) * 1.1));
    opacity: calc(0.18 + var(--ff-mic-level, 0) * 0.62);
    transition: transform 0.06s linear, opacity 0.06s linear;
    pointer-events: none;
}

/* Honor reduced-motion: keep the ring as a static armed halo, no live scaling. */
@media (prefers-reduced-motion: reduce) {
    [b-v9s3hk0fve] .ff-mic-recording::after {
        transform: none;
        opacity: 0.4;
        transition: none;
    }
}

[b-v9s3hk0fve] .ff-mic-icon {
    line-height: 1;
}

/* ── Stage 4: etymology (word-origin) note + hoverable dialog words ──────────────────────────── */

/* These render inside the VocabularyMessage / DialogMessage leaf components (authored in their own
   .razor files, not here), so they carry a different scope and must be reached via ::deep — exactly
   like every other card internal above. Values are lifted verbatim from Lesson.razor.css. */

/* Word-origin / word-building note shown below the pronunciation. */
[b-v9s3hk0fve] .ff-derivation {
    font-style: italic;
    color: var(--ff-card-note-text);   /* italic like a dictionary etymology, but readable — not muted */
    font-size: 14px;
    margin-top: 4px;
}

/* ── Etymology card — the full word-origin expansion, spliced into the transcript right beneath its
   vocabulary card. It renders through the generic TutorMessage, so it is reached by the
   ff-etymology-message class the entry carries on its ExchangeCss. Given the same card system as the
   New Vocabulary card it sits under — warm fill + left accent spine + eyebrow + soft shadow — but in
   its own terracotta/rose hue so the three LEARN cards read as a family, not clones. */
[b-v9s3hk0fve] .ff-etymology-message {
    background: var(--ff-etym-bg) !important;
    border: 1px solid var(--ff-etym-border) !important;
    /* Solid colored spine (a real border-left, so it follows the 18px radius cleanly — a ::before
       spine renders square-ended here, matching how the Language Tip card does it). */
    border-left: 5px solid var(--ff-etym-spine) !important;
    border-radius: 18px !important;
    padding: 12px 16px !important;
    box-shadow: var(--ff-etym-shadow);
    gap: 0 !important;
    row-gap: 0 !important;
}

/* Eyebrow ("ETYMOLOGY:") — same uppercase/weight/tracking as the other cards' eyebrows, in the
   terracotta accent. Overrides the base .ff-exchange-label (two classes → wins on specificity). */
[b-v9s3hk0fve] .ff-etymology-message .ff-exchange-label {
    font-size: 12.5px;
    font-weight: 800;
    letter-spacing: 1.2px;
    text-transform: uppercase;
    line-height: 1.2;
    margin-bottom: 6px;
    color: var(--ff-etym-eyebrow);
}

/* Body — readable, no Syncfusion .e-text top gap; keeps the inline italics on foreign terms. */
[b-v9s3hk0fve] .ff-etymology-message .e-text {
    color: var(--ff-etym-body);
    font-size: 14px;   /* shared LEARN-card body size — was inheriting the larger base bubble size */
    margin-top: 0 !important;
    padding-top: 0 !important;
}

/* Admin-only inline curation. Never rendered for a student — the Edit button is gated on the
   Administrator role — so these deliberately stay quiet rather than competing with the card's
   Continue: outline buttons in the card's own terracotta, at eyebrow size. */
[b-v9s3hk0fve] .ff-etym-editor {
    width: 100%;
    min-height: 92px;
    resize: vertical;
    font: inherit;
    font-size: 14px;   /* matches the body it replaces, so the text does not reflow while editing */
    color: var(--ff-etym-body);
    background: var(--ff-etym-bg);
    border: 1px solid var(--ff-etym-spine);
    border-radius: 10px;
    padding: 8px 10px;
}

/* Pushes the edit group to the left edge, leaving Continue on the right (the same margin trick
   .ff-card-actions .ff-play-btn uses against the row's justify-content: flex-end). */
[b-v9s3hk0fve] .ff-etym-edit-actions {
    display: flex;
    gap: 8px;
    margin-right: auto;
}

[b-v9s3hk0fve] .ff-etym-edit-btn {
    background: transparent;
    border: 1px solid var(--ff-etym-spine);
    color: var(--ff-etym-eyebrow);
    border-radius: 12px;
    padding: 5px 12px;
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: 0.3px;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

[b-v9s3hk0fve] .ff-etym-edit-btn:hover:not(:disabled) {
    background: var(--ff-etym-border);
}

[b-v9s3hk0fve] .ff-etym-edit-btn:disabled {
    opacity: 0.55;
    cursor: default;
}

/* Outcome / refusal line under the admin edit controls ("Saved and translated to es-MX"). */
[b-v9s3hk0fve] .ff-etym-edit-notice {
    margin: 4px 0 2px;
    font-size: 12px;
    color: var(--ff-etym-eyebrow);
}

[b-v9s3hk0fve] .ff-etym-edit-notice--error {
    color: #dc2626;
}

/* Inline loading spinner shown while the etymology fetch is in-flight. */
[b-v9s3hk0fve] .ff-deriv-spinner {
    width: 11px;
    height: 11px;
    margin-left: 6px;
    vertical-align: middle;
    color: var(--ff-vocab-label);
    border-width: 2px;
}

/* Inline "more" button — unobtrusive, sits right after the derivation text. */
[b-v9s3hk0fve] .ff-deriv-more {
    display: inline;
    margin-left: 5px;
    padding: 0 5px;
    font-size: 11px;
    font-style: normal;
    color: var(--ff-vocab-label);
    background: transparent;
    border: 1px solid var(--ff-vocab-chip-border);
    border-radius: 4px;
    cursor: pointer;
    vertical-align: middle;
    line-height: 1.4;
    transition: background 0.15s, color 0.15s;
}

[b-v9s3hk0fve] .ff-deriv-more:hover {
    background: var(--ff-vocab-chip-hover-bg);
    color: var(--ff-vocab-action-strong);
}

/* "Skip" on a vocab card — one tap skips a word the learner already knows (taken on trust; the
   capstone dialog still uses it). Gold, the vocabulary card's own accent (its header and the "more"
   chip): easy to spot on the card, and never the violet (dark) / orange (light) of Continue, so it
   can't be mistaken for the way forward. */
[b-v9s3hk0fve] .ff-vocab-know-btn {
    border-radius: 16px;
    padding: 5px 16px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    background: transparent;
    color: var(--ff-vocab-action-strong);
    border: 1px solid var(--ff-vocab-action-strong);
    transition: background-color 0.15s;
}

/* Pinned bottom-LEFT (the row is justify-content:flex-end). When the replay button or the
   Administrator's Edit group follows, that one's own margin-right:auto keeps them together on the
   left instead. */
[b-v9s3hk0fve] .ff-card-actions .ff-vocab-know-btn {
    margin-right: auto;
}

[b-v9s3hk0fve] .ff-card-actions .ff-vocab-know-btn:has(+ .ff-play-btn),
[b-v9s3hk0fve] .ff-card-actions .ff-vocab-know-btn:has(+ .ff-etym-edit-actions) {
    margin-right: 0;
}

[b-v9s3hk0fve] .ff-vocab-know-btn:hover:not(:disabled) {
    background: var(--ff-vocab-chip-hover-bg);
}

[b-v9s3hk0fve] .ff-vocab-know-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

/* Hoverable dialog words — dotted underline signals the word can be translated on hover. */
[b-v9s3hk0fve] .ff-dialog-word {
    cursor: help;
    text-decoration-line: underline;
    text-decoration-style: dotted;
    text-decoration-color: rgba(var(--ff-accent-purple), 0.4);
    text-underline-offset: 3px;
}

/* ── Stage 4: inline lesson image + expand affordance + ephemeral lightbox ──────────────────────

   The image folds INTO the transcript (replacing Lesson's SfSplitter right pane). Both the inline
   container and the lightbox overlay are authored lexically in Lesson.razor, so they carry this
   page's scope and match plain rules. The lightbox in particular MUST be plain (not ::deep): it is a
   top-level sibling of the scoped root with no scoped ancestor, so a ::deep rule would never match. */

/* Inline image container — sits in the transcript flow; relative so the expand button can pin to
   its top-right corner. */
.ff-inline-image[b-v9s3hk0fve] {
    position: relative;
    display: inline-block;
    max-width: 100%;
    margin-top: 4px;
}

.ff-inline-image img[b-v9s3hk0fve] {
    display: block;
    max-width: 100%;
    max-height: 320px;
    object-fit: contain;
    border-radius: 12px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

/* Loading placeholder — reserves the SAME square footprint as the finished picture (1024×1024,
   rendered contained at max-height 320), so the transcript never jumps when the image fills in. This
   matters now the picture sits ABOVE the vocab card: any height change here would shove the card down.
   aspect-ratio keeps it square as it scales down on narrow screens, matching the image box. */
.ff-inline-image-loading[b-v9s3hk0fve] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 320px;
    max-width: 100%;
    aspect-ratio: 1 / 1;
    background: var(--ff-image-loading-bg);
    border-radius: 12px;
    margin-top: 4px;
}

/* Expand affordance — always-visible top-right glyph button making it clear the image opens
   full-size in the lightbox (kept visible rather than hover-gated so the cue reads on touch too). */
.ff-img-expand[b-v9s3hk0fve] {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    border-radius: 6px;
    border: none;
    background: rgba(0, 0, 0, 0.5);
    color: #ffffff;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, transform 0.15s;
}

.ff-img-expand:hover[b-v9s3hk0fve] {
    background: rgba(0, 0, 0, 0.75);
    transform: scale(1.08);
}

/* ── Live-mic "Listening" overlay ─────────────────────────────────────────────────────────────
   The indicator itself is the shared ListeningOverlay component (Components/Chat/ListeningOverlay
   .razor + .razor.css) — its rules moved there when the Real Time Dialog started showing the same
   one. Scoped CSS here cannot reach a child component's markup, so nothing overlay-specific stays
   in this file; only the positioning context below is the lesson's own. */
/* The overlay positions against the lesson surface (not the viewport), so it centers over the
   transcript column and ignores the sidebar. */
.ff-lesson-surface[b-v9s3hk0fve] {
    position: relative;
    /* Column so the cookie/tracking notice can sit as a full-bleed band BELOW the chat: the chat
       grows to fill the space, the band takes its natural height at the very bottom. When the notice
       is dismissed it renders nothing and the chat reclaims the full height. The absolutely-positioned
       overlays (pause button, initial spinner, listening overlay) are out of flow and unaffected. */
    display: flex;
    flex-direction: column;
}

/* The chat shell fills the surface above the notice band. Overriding its own height:100% with
   flex:1 lets the band claim its slice without overflowing the overflow-hidden surface. Scoped here
   (::deep) so it only applies inside the lesson, never to any other FfChatUi host. */
.ff-lesson-surface[b-v9s3hk0fve]  .ff-chat {
    flex: 1 1 0%;
    height: auto;
    min-height: 0;
}

@media (max-width: 639.98px) {
    /* Narrow screens can't fit the door on one line — text-wrap:balance splits the wrapped headline
       into evenly balanced lines. Safe on mobile: the column already constrains the width. */
    [b-v9s3hk0fve] .ff-entrance-prompt {
        text-wrap: balance;
    }
}

/* Full-screen overlay — dismissed on backdrop / close / Esc-equivalent; the image itself stops
   propagation so clicking the picture doesn't close it. */
.ff-lightbox[b-v9s3hk0fve] {
    position: fixed;
    inset: 0;
    z-index: 1080;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.8);
    cursor: zoom-out;
}

.ff-lightbox img[b-v9s3hk0fve] {
    max-width: 90vw;
    max-height: 90vh;
    object-fit: contain;
    border-radius: 8px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
    cursor: default;
}

.ff-lightbox-close[b-v9s3hk0fve] {
    position: absolute;
    top: 16px;
    right: 24px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.15);
    color: #ffffff;
    font-size: 28px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s;
}

.ff-lightbox-close:hover[b-v9s3hk0fve] {
    background: rgba(255, 255, 255, 0.3);
}

/* Admin-only curation actions (delete / upload) — top-left of the lightbox, opposite the close
   button. Both pills share the same shape; delete is red, upload is blue. */
.ff-lightbox-actions[b-v9s3hk0fve] {
    position: absolute;
    top: 16px;
    left: 24px;
    display: flex;
    gap: 10px;
}

.ff-lightbox-delete[b-v9s3hk0fve],
.ff-lightbox-upload[b-v9s3hk0fve] {
    height: 40px;
    padding: 0 16px;
    border-radius: 20px;
    border: none;
    color: #ffffff;
    font-size: 15px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
}

.ff-lightbox-delete[b-v9s3hk0fve] {
    background: rgba(220, 38, 38, 0.85);
}

.ff-lightbox-delete:hover[b-v9s3hk0fve] {
    background: rgba(220, 38, 38, 1);
    transform: scale(1.04);
}

.ff-lightbox-upload[b-v9s3hk0fve] {
    background: rgba(37, 99, 235, 0.85);
    margin: 0;   /* labels pick up a default bottom margin from Bootstrap's reset */
}

.ff-lightbox-upload:hover[b-v9s3hk0fve] {
    background: rgba(37, 99, 235, 1);
    transform: scale(1.04);
}

/* The label IS the button — hide the InputFile control it wraps (clicking the label opens the
   picker). ::deep because InputFile is a child component, so its input lacks the scope attribute. */
.ff-lightbox-upload[b-v9s3hk0fve]  input[type="file"] {
    display: none;
}

/* ── Cold-open entrance ─────────────────────────────────────────────────────────────────────────
   The door is the only LOUD thing on screen: a large, centered language question. The language
   chips reuse the standard .ff-suggestion-btn pills; the reassurance chips below are deliberately
   quieter (text-style), serving the hesitant visitor without competing with the action. */
/* Bare headline — Pass 2 removed the filled/bordered panel that used to wrap it. Two stacked
   containers (panel + input pill) made the ask read as a form and competed with the one element that
   should own attention. Now it's just centered text on the page surface: the .ff-mw-entranceprompt
   column centers it, and the prompt's own margin owns the spacing. */
[b-v9s3hk0fve] .ff-entrance-prompt {
    text-align: center;
    /* Fluid so the type scales with the viewport — one line at the 34px cap on desktop, shrinking to
       the 24px floor and wrapping to two balanced lines on phones. No nowrap: forcing a single line at
       any width overflows the panel on narrow screens; letting it wrap is the correct behaviour.
       text-wrap:balance is deliberately NOT here — on the fit-content panel it collapses the text to
       its balance point (two lines) even when there's room for one; it lives in the mobile rule where
       the column already constrains the width. The 30px cap is the largest that still fits the door
       on one line in this column; 34px orphaned "today?" onto a second line. */
    font-size: clamp(22px, 3.4vw, 30px);
    font-weight: 800;
    letter-spacing: -0.8px;            /* tight, intentional at large size */
    line-height: 1.2;
    color: var(--ff-entrance-ink);     /* cool near-black (light) / light primary (dark) on the page surface */
    margin: 6px 0 30px;                 /* own the spacing now the panel wrapper is gone */
}

/* The single color touch — one accent word. Theme-aware: warm orange in light, violet in dark. */
[b-v9s3hk0fve] .ff-entrance-accent {
    color: var(--ff-headline-accent);
}

/* ── Cold-open marketing header (Pass 3) ──────────────────────────────────────────────────────────
   The door's own top bar, shown while _atEntrance, rendered directly in Lesson's markup (so NO ::deep).
   Seated on the CARD SURFACE (same token the value-prop cards use) so the lockup reads as a bar, and
   FLOATED over the top so it can hide on scroll-down / reveal on scroll-up. It's position:absolute over
   the surface, not position:fixed: the door scrolls INSIDE .ff-chat-scroll (not the window), so
   absolute-to-surface is the robust equivalent — fluentFox.initDoorHeader listens on that container and
   toggles .ff-door-header--hidden. Translucent + blur so page content never shows through while it
   floats. Left: the ownership lockup. Right: theme toggle + muted "sign in" link. The app rail is
   hidden under .ff-at-entrance, so this is the only chrome a first-time visitor sees. */
.ff-door-header[b-v9s3hk0fve] {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    height: 56px;                                   /* fixed slim bar — contents vertically center, padding never drives height */
    padding: 0 24px;
    background: var(--ff-mkt-card-bg);              /* opaque fallback — the value-prop cards' surface */
    border-bottom: 1px solid var(--ff-mkt-card-border);
    transition: transform 180ms ease-out;
}

/* Translucent + blur only where backdrop-filter is supported; the opaque fallback above keeps content
   from showing through everywhere else. */
@supports ((backdrop-filter: blur(8px)) or (-webkit-backdrop-filter: blur(8px))) {
    .ff-door-header[b-v9s3hk0fve] {
        background: color-mix(in srgb, var(--ff-mkt-card-bg) 92%, transparent);
        -webkit-backdrop-filter: blur(8px);
        backdrop-filter: blur(8px);
    }
}

/* Hidden state — slid up out of view. Transform (not display) so it animates and leaves no layout gap. */
.ff-door-header--hidden[b-v9s3hk0fve] {
    transform: translateY(-100%);
}

@media (prefers-reduced-motion: reduce) {
    .ff-door-header[b-v9s3hk0fve] {
        transition: none;
    }
}

.ff-door-brand[b-v9s3hk0fve] {
    display: flex;
    align-items: baseline;
    gap: 0.38em;
    text-decoration: none;
}

/* Horizontal lockup: "Greg Gregory's" as LIVE HTML text before the plain wide wordmark PNG. The text
   colour rides --ff-entrance-ink (cool near-black on the light canvas, light ink on dark), so it
   theme-swaps for free with no baked-in colour — trivial to re-tune. The wordmark PNG carries its own
   black outline, so it reads on either background as-is. */
.ff-door-possessive[b-v9s3hk0fve] {
    font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
    font-style: italic;
    font-weight: 600;
    font-size: 16px;                    /* ~0.73× the 22px wordmark — visibly subordinate, not scaled with it */
    letter-spacing: 0.3px;
    color: var(--ff-entrance-ink);
    white-space: nowrap;
}

/* The wordmark is the stylized "FluentFox.ai" PNG — its 700 weight + tight tracking are baked into the
   artwork; here we only set its rendered height. Trimmed logo (no padding), so 22px reads at 22px. The
   3px translateY drops it onto the possessive's baseline: flex baseline alignment seats the image's
   BOTTOM edge on the text baseline, which leaves the glyphs riding high, so nudge them down. */
.ff-door-wordmark[b-v9s3hk0fve] {
    height: 22px;
    width: auto;
    display: block;
    transform: translateY(3px);
}

.ff-door-header-actions[b-v9s3hk0fve] {
    display: flex;
    align-items: center;
    gap: 16px;
}

/* Theme toggle in the door header: the shared control is a filled segmented pill (built for the side
   rails), which inflates the row. Strip it to a single 32px icon button — kill the filled pill and hide
   the inactive segment so only the current-theme icon shows (one control, not two). Muted icon, hover to
   primary text. The pill lives in the ThemeToggle child, hence ::deep. */
.ff-door-header[b-v9s3hk0fve]  .ff-theme-pill {
    flex-direction: row;
    width: auto;
    padding: 0;
    gap: 0;
    background: transparent;
}

.ff-door-header[b-v9s3hk0fve]  .ff-theme-pill .ff-theme-opt:not(.ff-theme-active) {
    display: none;
}

.ff-door-header[b-v9s3hk0fve]  .ff-theme-pill .ff-theme-active {
    width: 32px;
    height: 32px;
    background: transparent;                /* drop the white thumb — it's what filled the pill */
    box-shadow: none;
}

.ff-door-header[b-v9s3hk0fve]  .ff-theme-pill .ff-theme-opt svg {
    color: var(--ff-chat-text-soft) !important;   /* muted */
}

.ff-door-header[b-v9s3hk0fve]  .ff-theme-pill:hover .ff-theme-opt svg {
    color: var(--ff-chat-text) !important;        /* hover → primary text */
}

/* The door's single account affordance (signed-out). Was a muted text link on the reasoning that it
   must not pull attention from the primary ask — true, but it under-corrected: a first-time visitor
   could not find a way to create an account at all, and the only route to one was Sign in -> Register,
   two pages deep. OUTLINED, not filled: the brand-orange edge makes it findable at a glance while the
   transparent centre keeps the language box the loudest thing on the page. Fills on hover. */
.ff-door-auth[b-v9s3hk0fve] {
    font-size: 13px;
    font-weight: 600;
    padding: 6px 14px;
    border: 1px solid #f5a94a;
    border-radius: 9999px;
    color: var(--ff-entrance-ink);
    text-decoration: none;
    white-space: nowrap;
    transition: background-color 0.15s, color 0.15s;
}

.ff-door-auth:hover[b-v9s3hk0fve],
.ff-door-auth:focus-visible[b-v9s3hk0fve] {
    background: #f5a94a;
    color: #ffffff;
    text-decoration: none;
}

/* ── Door header: the signed-in account affordance ────────────────────────────────────────────────
   Replaces the "sign in" link once the learner is authenticated. Deliberately the LOUDEST small thing
   in the bar (filled brand orange, not the muted outline the rails use): the bug this fixes was a
   learner who could not tell whether they were signed in, so a low-contrast outline circle would only
   half-answer the question. 32px matches the door's stripped-down theme toggle, so the two controls
   sit as a pair rather than one dwarfing the other. */
.ff-door-avatar[b-v9s3hk0fve] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: #f5a94a;
    color: #ffffff;
    font-size: 14px;
    font-weight: 600;
    line-height: 1;
    text-transform: uppercase;
    cursor: pointer;
    transition: box-shadow 0.15s;
}

.ff-door-avatar:hover[b-v9s3hk0fve],
.ff-door-avatar:focus-visible[b-v9s3hk0fve] {
    box-shadow: 0 0 0 3px color-mix(in srgb, #f5a94a 35%, transparent);
}

/* The menu itself. It is a native popover, so the browser promotes it to the TOP LAYER when open —
   that, not a z-index, is what gets it out of the header's stacking context and past the surface's
   overflow:hidden. Positioned fixed under the avatar; `inset: auto` is required to undo the UA's
   default centering of an open popover. Colours are the rail menu's dark panel, which reads on both
   themes (the door header itself theme-swaps around it). */
.ff-door-account-menu[b-v9s3hk0fve] {
    position: fixed;
    inset: auto;
    top: 52px;
    right: 20px;
    margin: 0;
    min-width: 10rem;
    padding: 4px;
    border: 1px solid #475569;              /* slate-600 */
    border-radius: 8px;
    background: #1e293b;                    /* slate-800 */
    color: #ffffff;
    font-size: 14px;
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.3);
}

.ff-door-account-form[b-v9s3hk0fve] {
    margin: 0;
}

/* One shared row style for the Profile link and the Logout submit button, so the two read as one
   menu despite being an <a> and a <button> inside a <form>. */
.ff-door-account-item[b-v9s3hk0fve] {
    display: block;
    width: 100%;
    padding: 8px 12px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: #ffffff;
    font: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}

.ff-door-account-item:hover[b-v9s3hk0fve],
.ff-door-account-item:focus-visible[b-v9s3hk0fve] {
    background: rgb(255 255 255 / 0.1);
    color: #f5a94a;
    text-decoration: none;
}

/* ── Cold-open door: full-width landing mode ──────────────────────────────────────────────────────
   The entrance is a MARKETING page, not a chat, so while the door owns the screen we drop FfChatUi's
   narrow rail'd column (the "chat channels") and let the content breathe in a generous centered
   column. Gated on .ff-at-entrance (set from _atEntrance in Lesson.razor), so the LIVE lesson
   transcript keeps its readable ~800px column and vertical rails untouched — this only opens up the
   cold-open door. */
.ff-at-entrance[b-v9s3hk0fve]  .ff-chat {
    max-width: 100%;                    /* fill the surface — no narrow centered chat column */
    border-left-color: transparent;    /* drop the vertical rails (keep the 1px width, no reflow) */
    border-right-color: transparent;
}

.ff-at-entrance[b-v9s3hk0fve]  .ff-chat-row {
    max-width: 960px;                   /* generous landing width; the door's content centers within */
}

/* The door header floats (position:absolute) over the top of the surface, so pad the entrance scroll
   to clear it — the headline starts just below the bar at rest, and there's no CLS when the bar
   hides/reveals (the padding is constant). Entrance-only, so live lessons keep their normal 16px. The
   value is the 56px bar plus a little breathing room. */
.ff-at-entrance[b-v9s3hk0fve]  .ff-chat-scroll {
    padding-top: 68px;
}

/* Mobile: 48px bar, tighter side padding, a smaller lockup, and a matching scroll offset. */
@media (max-width: 560px) {
    .ff-door-header[b-v9s3hk0fve] {
        height: 48px;
        padding: 0 16px;
    }

    .ff-door-wordmark[b-v9s3hk0fve] {
        height: 19px;
    }

    .ff-door-possessive[b-v9s3hk0fve] {
        font-size: 14px;
    }

    /* The bar is 48px and the brand lockup already owns most of the width, so tighten the row rather
       than shorten the label: "Login/Register" is longest exactly where sign-up discovery matters most
       (phones), and translations run longer still. Shrinking the gap and the pill buys back ~20px. */
    .ff-door-header-actions[b-v9s3hk0fve] {
        gap: 10px;
    }

    .ff-door-auth[b-v9s3hk0fve] {
        font-size: 12px;
        padding: 5px 11px;
    }

    /* Keep the account controls proportional to the 48px bar, and re-anchor the menu beneath it. */
    .ff-door-avatar[b-v9s3hk0fve] {
        width: 28px;
        height: 28px;
        font-size: 13px;
    }

    .ff-door-account-menu[b-v9s3hk0fve] {
        top: 44px;
        right: 12px;
    }

    .ff-at-entrance[b-v9s3hk0fve]  .ff-chat-scroll {
        padding-top: 60px;
    }
}

/* Center everything under the door — headline, language chips, and reassurance bar — as one stack.
   align-items:center centers each child BOX (the chip rows shrink to their content), which the
   per-row justify-content alone can't do once a shrunk container sits at the wrapper's left edge. */
[b-v9s3hk0fve] .ff-mw-entranceprompt {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Fill the row (it defaults to flex-start / shrink-wrap, which pins the headline to the left
       rail); only then does align-items:center truly center the stack between the two rails.
       Same fix as .ff-mw-image / .ff-mw-learnerdashboard above. */
    width: 100%;
}

/* Belt-and-suspenders: also center chips within their row if it ever stretches full width. */
[b-v9s3hk0fve] .ff-entrance-prompt ~ .ff-suggestion-container {
    justify-content: center;
    gap: 10px;
}

/* Entrance-only chip skin (Claude Design spec 2026-09-11) — full pills with a filled surface and
   neutral 1.5px border; the accent lives in the LABEL, never the stroke (a 1px accent stroke on
   the dark navy antialiased into a gray fringe). Light keeps its white-object values, dark the
   navy fill — both via the --ff-entrance-chip-* tokens. Hover is color-only, no transform/glow.
   !important to outrank the generic .ff-suggestion-btn rule above. */
[b-v9s3hk0fve] .ff-mw-entranceprompt .ff-suggestion-btn {
    display: inline-flex;
    align-items: center;
    padding: 9px 18px !important;
    border-radius: 999px !important;
    border-width: 1.5px;
    font-size: 15px !important;
    font-weight: 600;
    background: var(--ff-entrance-chip-bg) !important;
    border-color: var(--ff-entrance-chip-border) !important;
    color: var(--ff-entrance-chip-text) !important;
    box-shadow: var(--ff-entrance-chip-shadow);
    transition: background-color 0.13s ease, border-color 0.13s ease, color 0.13s ease;
}
[b-v9s3hk0fve] .ff-mw-entranceprompt .ff-suggestion-btn:hover {
    background: var(--ff-entrance-chip-hover-bg) !important;
    border-color: var(--ff-entrance-chip-hover-border) !important;
    color: var(--ff-entrance-chip-hover-text) !important;
}
/* The one place the accent may be a stroke: the keyboard-focus outline (solid, not the low-alpha
   ring wash — a 2px outline needs full strength). Orange in light / violet in dark, per token. */
[b-v9s3hk0fve] .ff-mw-entranceprompt .ff-suggestion-btn:focus-visible {
    outline: 2px solid rgb(var(--ff-support-line));
    outline-offset: 2px;
}
/* Wider flag-to-label gap inside the bigger entrance pill (base chip keeps 5px). */
[b-v9s3hk0fve] .ff-mw-entranceprompt .ff-chip-flag { margin-right: 9px; }

/* Quieter, secondary reassurance bar — for the hesitant, not the ready. */
[b-v9s3hk0fve] .ff-reassure-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    justify-content: center;
    margin-top: 14px;
}

[b-v9s3hk0fve] .ff-reassure-chip {
    background: transparent;
    border: none;
    padding: 2px 4px;
    font-size: 12.5px;
    color: var(--ff-reassure-text);   /* soft warm at rest (light) / quiet slate (dark); violet on hover (informs, like the chips) */
    cursor: pointer;
    border-radius: 6px;
    transition: color 0.15s, background 0.15s;
}

[b-v9s3hk0fve] .ff-reassure-chip:hover {
    color: rgb(var(--ff-accent-purple));
    background: rgba(var(--ff-accent-purple), 0.14);
    text-decoration: underline;
}
