/* ============================================================================
   ReziDay — lesson page stylesheet.
   Single warm-cream theme. Design tokens come from tokens.css (loaded first);
   use var(--x) throughout, hardcode a hex only when no token fits.
   Visual source of truth: design_import/ReziDay v1.0/Insuficienta cardiaca cronica.dc.html
   Comments/UI are English; the rendered medical content itself is Romanian.
   ========================================================================== */

/* ============================================================================
   1. PAGE LAYOUT
   ========================================================================== */

body.lesson-page {
    margin: 0;
    background: var(--canvas);
    color: var(--ink-warm);
    font-family: var(--font-body);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    /* normal document scroll — NOT an inner scroll container */
}

.lesson {
    /* Column widths + the reading measure are variables so the three of them
       stay in step: the rails are as narrow as they can be while staying
       legible, and the centre column spends what they give back on a WIDER
       text measure — not on bigger type. Nothing here scales font size, so a
       larger screen shows more content, never a zoomed-in page. */
    --lesson-rail-w: 264px;
    --lesson-ksb-w: 296px;
    /* The measure is FLUID: it fills the centre column (viewport minus the two
       side columns), floored at the old 940px and capped at --lesson-read-cap —
       the one typographic guard left; past the cap the extra pixels go back to
       centring. On a 13" laptop this resolves to the old 940; on a wider
       MacBook the pixels that used to die as side margins go to content.
       ⚠️ This one reads --lesson-ksb-w, so it MOVES with the panel state. It is
       the ≤1099px measure only — where the panel is not a column at all. From
       1100px up the desktop geometry below takes over, precisely because a
       measure that follows the panel state also moves the text. */
    --lesson-read-cap: 1200px;
    --lesson-read-max: clamp(940px, calc(100vw - var(--lesson-rail-w) - var(--lesson-ksb-w)), var(--lesson-read-cap));

    /* ── Desktop geometry: one left edge, in every panel state ──────────────
       The text starts in the SAME place whether the knowledge panel is docked,
       collapsed or absent, and closing it lengthens the lines to the right
       instead of sliding the whole column. That needs the left offset computed
       from the panel-docked layout ("nat" = natural) and then held fixed, which
       is why these four do NOT read --lesson-ksb-w — that variable is 0 while
       collapsed, and the old `margin: 0 auto` re-centred the block against it
       (a 128px jump sideways on a 1532px screen, every time you closed it).
       Only --lesson-content-max changes with the state: what the panel gives
       back is spent on the right-hand end of the line. */
    --lesson-ksb-nat: 296px;
    --lesson-col-nat: calc(100vw - var(--lesson-rail-w) - var(--lesson-ksb-nat));
    --lesson-read-nat: clamp(940px, var(--lesson-col-nat), var(--lesson-read-cap));
    --lesson-left: max(0px, calc((var(--lesson-col-nat) - var(--lesson-read-nat)) / 2));
    --lesson-content-max: var(--lesson-read-nat);

    /* The tab row's geometry is fixed rather than content-derived, because the
       KSB caption row on the right is aligned to it: the sidebar's title, its
       brain mark and its close chip sit on the same baseline as the Lecție /
       Grile / Flashcards / Cazuri toggle. Change these and both sides move
       together. --lesson-tabrow-top / -bottom are the strips of the sticky
       topbar above and below the toggle (read bar + padding, padding + rule). */
    --lesson-tab-h: 31px;
    --lesson-tabrow-h: calc(var(--lesson-tab-h) + 8px);   /* + .lesson-tabs padding */
    --lesson-tabrow-top: 10px;                            /* 3px read bar + 7px */
    --lesson-tabrow-bottom: 7px;                          /* 6px + 1px divider */
    /* ⚠️ The topbar grows a SECOND row below 1480px: the rail goes icon-only and
       can no longer show the lesson name, so .lesson-topbar-title takes over and
       the toggle is pushed down by its height. The KSB head is inset by
       --lesson-tabrow-top alone, which is measured from a topbar that has no such
       row — so it stayed level with the TITLE while the tabs sat 29.7px lower,
       i.e. the alignment this whole block exists to keep broke over the entire
       1100–1479 band (icon rail, panel still docked). This is that row's height,
       0 wherever it isn't drawn; the ≤1479 block below turns it on next to the
       `display: flex` that creates it, so the two can't drift apart. */
    --lesson-topbar-title-h: 0px;

    display: grid;
    grid-template-columns: var(--lesson-rail-w) minmax(0, 1fr) var(--lesson-ksb-w);
    align-items: start;
}
/* Wide desktop: the sidebar gets a little wider (the measure is fluid and
   needs no step of its own — it absorbs the rest of the width by itself). */
@media (min-width: 1660px) {
    /* Both, and they must move together: -w is the grid column (0 while
       collapsed), -nat is what the column would be if it were docked. */
    .lesson { --lesson-ksb-w: 320px; --lesson-ksb-nat: 320px; }
}
/* The panel's width, given back to the line rather than to the margins. */
body.ksb-collapsed .lesson,
body.lesson-no-ksb .lesson {
    --lesson-content-max: calc(var(--lesson-read-nat) + var(--lesson-ksb-nat));
}

/* ── Left rail ─────────────────────────────────────────────────────────── */
.lesson-rail {
    position: sticky;
    top: 0;
    height: 100vh;
    /* The rail itself no longer scrolls — .lesson-toc does. Otherwise a lesson with
       a long Cuprins scrolled the profile foot off the bottom and you had to scroll
       the rail to reach logout. */
    overflow: hidden;
    box-sizing: border-box;
    border-right: 1px solid var(--border);
    background: var(--card);
    /* The bottom inset is 16px, not 20px, to match .app-sidebar's: the profile
       foot is the rail's last child, so this padding is what decides how high it
       sits, and it must land on the same line as on the app-shell pages. */
    padding: 20px 11px 16px;
    display: flex;
    flex-direction: column;
}

/* Positioning only — the lockup (mark 30px + 19px wordmark + 10px gap) is defined
   once in components.css as .brand-lockup. Insets match .app-sidebar-brand so the
   logo sits in exactly the same spot as on the app-shell pages. */
.lesson-rail-brand { padding: 4px 12px 18px; }

.lesson-rail-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.lesson-rail-navitem {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: 10px;
    font-size: 14px;
    font-weight: 500;
    color: var(--muted);
    text-decoration: none;
    cursor: pointer;
    background: none;
    border: none;
    width: 100%;
    text-align: left;
    box-sizing: border-box;
}
.lesson-rail-navitem svg { flex: none; }
/* A label never spills past the rail: it truncates instead of being sliced
   mid-word by the column edge. */
.lesson-rail-navitem span,
.lesson-rail-back span {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.lesson-rail-navitem:hover { background: var(--surface); color: var(--ink); }
.lesson-rail-navitem.is-active { background: var(--teal-tint); color: var(--teal-ink); }

.lesson-rail-divider {
    height: 1px;
    background: var(--divider);
    margin: 14px 0;
    border: none;
}

.lesson-rail-back {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 8px;
}
.lesson-rail-back svg { flex: none; }
.lesson-rail-back:hover { color: var(--ink); }

/* Lesson name, always in view because the rail is sticky. Two lines max. */
.lesson-rail-title {
    margin: 14px 6px 0;
    font-family: var(--font-serif);
    font-size: 15px;
    font-weight: 600;
    line-height: 1.25;
    color: var(--ink-warm);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* One Cuprins per pane — head + list — of which exactly one is ever on screen (see
   `Toc` in lesson.js). It is the flex item the rail's column sizes, so the head stays put
   and the list below it scrolls; `min-height: 0` is what lets it shrink at all.
   ⚠️ Its own `[hidden]` escape: the UA's `[hidden] { display: none }` loses to the
   `display: flex` above it, so without this every pane's Cuprins would be on screen at
   once — the attribute would be inert. */
.lesson-rail-toc {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}
.lesson-rail-toc[hidden] { display: none; }
.lesson-rail-toc-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin: 14px 0 8px;
    padding: 0 6px;
    text-transform: uppercase;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.09em;
    color: var(--muted-2);
}
/* The section counter and the TOC badges below are figures — serif, like every
   number in the app (see "Typography" in CLAUDE.md). */
.lesson-rail-toc-head .mono {
    font-family: var(--font-serif);
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
    font-size: 12px;
}

/* The Cuprins is the rail's only scrolling region, so the nav above it and the
   profile foot below it stay put. `min-height: 0` is what actually lets it shrink:
   a flex item's default `min-height: auto` refuses to go below its content, which
   would push the foot off the bottom of a rail with a long lesson. */
.lesson-toc {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* Profile + logout at the bottom of the rail. The shared block is styled in
   components.css; this only docks it. `margin-top: auto` keeps it on the bottom
   edge when a short Cuprins leaves slack. */
.lesson-rail .app-sidebar-foot { margin-top: auto; flex: none; }
/* The rail is 264px against the app sidebar's 216px and is inset 11px against
   its 14px — so without this the whole profile block jumped 3px left the moment
   you opened a lesson. The block stays put and the extra width is spent on the
   right instead: the logout chip is what moves out to the wider edge. */
.lesson-rail .app-prof-row { padding-left: 13px; }
.lesson-toc-item {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 7px 10px;
    min-height: 48px;
    border-radius: 11px;
    text-align: left;
    width: 100%;
    background: transparent;
    border: none;
    cursor: pointer;
    box-sizing: border-box;
}
.lesson-toc-item:hover { background: var(--surface-2); }
/* current section row = teal-tinted pill (matches design) */
.lesson-toc-item.is-active { background: var(--teal-tint-3); }
.lesson-toc-item.is-active:hover { background: var(--teal-tint-3); }

/* circular status badge: todo (grey) · done (filled teal) · current (teal ring) */
.lesson-toc-num {
    flex: none;
    width: 28px;
    height: 28px;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 600;
    color: var(--muted-2);
    background: #F0EEE9;
    box-sizing: border-box;
}
.lesson-toc-item.is-done .lesson-toc-num {
    background: var(--teal);
    color: #fff;
}
.lesson-toc-item.is-active .lesson-toc-num {
    background: #fff;
    box-shadow: inset 0 0 0 2px var(--teal);
    color: var(--teal-ink);
}
.lesson-toc-labelwrap {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
/* A row, not a text run: the exam dot sits beside the title and must survive it
   being ellipsised. `min-width: 0` on the text is what lets it shrink instead of
   pushing the dot out of the box. */
.lesson-toc-label {
    display: flex;
    align-items: center;
    min-width: 0;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.25;
    color: var(--muted-2);
}
.lesson-toc-label-text {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.lesson-toc-item.is-done .lesson-toc-label { color: var(--ink-warm); }
.lesson-toc-item.is-active .lesson-toc-label { color: var(--teal-ink); font-weight: 600; }
.lesson-toc-meta {
    font-size: 11.5px;
    color: #B9B5AD;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* rail can push a footer (profile) to the bottom */
.lesson-rail-spacer { flex: 1; min-height: 14px; }

/* ── Center column ─────────────────────────────────────────────────────── */
.lesson-main {
    min-width: 0;
}

.lesson-topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--canvas);
}

/* ── Exam-subject sections ──────────────────────────────────────────────────
   A section whose meta carries `exam_subject` covers a subject set at the MF
   specialty exam in previous years. Per the "Subiect din anii anteriori"
   handoff: the whole section sits on a warm panel with a coral tab stitched
   onto its top edge.

   The treatment is per-section and static — several sections per lesson will
   carry the flag once the pipeline regenerates the content, so nothing here
   keys off the scroll-active section (that would strobe on the way down) and
   none of it needs JS.

   Every selector below doubles up as `.lb-section.lb-section--exam` on purpose:
   the generic section rules (`.lb-section`, `.lesson-content .lesson-pane > * +
   .lb-section`) live further down this file at the SAME specificity, so source
   order would hand them the win on padding, border and scroll-margin. The extra
   class is what makes the panel actually beat them. */
.lb-section.lb-section--exam {
    position: relative;
    /* 15px more than a plain section (line 774): the tab overhangs the panel's
       top edge by that much, and it must clear the sticky header too. */
    scroll-margin-top: 105px;
    background: var(--exam-panel);
    border: 1.5px solid var(--exam-panel-edge);
    border-radius: 16px;
    padding: 30px 26px 26px;   /* extra top padding clears the tab */
    box-shadow: none;
    margin-inline: 0;
}
/* The panel carries its own border, so it replaces the plain section rule —
   and it needs room above for the tab, which overhangs by 15px. */
.lesson-content .lesson-pane > * + .lb-section.lb-section--exam {
    border-top: 1.5px solid var(--exam-panel-edge);
    padding-top: 30px;
    /* The fallback matters: --lb-sec-gap is declared on `.lesson`, so without it
       the calc() would be invalid and the whole declaration dropped — leaving the
       tab to overlap whatever sits above. The panel must stand on its own. */
    margin-top: calc(var(--lb-sec-gap, 28px) + 14px);
}
/* Inside the panel the section's index turns coral — the teal would fight the
   wash it now sits on. */
.lb-section--exam .lb-h2-num,
.lb-section--exam .lb-eyebrow { color: var(--exam-tab); }

/* The tab, sewn onto the panel's top edge. */
.lb-exam-tab {
    position: absolute;
    top: -15px;
    left: 22px;
    display: inline-flex;
    align-items: center;
    /* A tab stays one line. It is absolutely positioned over the panel's top
       edge, so it can neither grow downwards (it would cover the heading) nor
       overflow sideways (it would scroll the document) — a long year list is
       ellipsised instead, with the full list on the tooltip. */
    flex-wrap: nowrap;
    gap: 8px;
    max-width: calc(100% - 44px);
    background: var(--exam-tab);
    border-radius: 8px;
    border-bottom: 3px solid var(--exam-tab-edge);
    padding: 5px 12px;
}
.lb-exam-tab svg { width: 17px; height: 17px; flex: none; }
.lb-exam-tab-label {
    flex: none;
    font-family: var(--font-eyebrow);
    font-size: 13px;
    font-weight: 700;
    color: #fff;
    white-space: nowrap;
}
/* The years. Figures ⇒ the serif face with tabular figures, per the typography
   rule in CLAUDE.md — the handoff set these in Geist Mono, which this codebase
   reserves for calculator inputs and mnemonics. */
.lb-exam-tab-years {
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 600;
    color: var(--exam-tab-meta);
    /* min-width:0 is what lets a flex item actually shrink below its content. */
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* The subject's name, when it is worth showing: same slot as the years, but it
   is prose, so it takes the body face rather than the tabular-figure serif. */
.lb-exam-tab-subject {
    font-family: var(--font-body);
    font-size: 12.5px;
    font-weight: 500;
    color: var(--exam-tab-meta);
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (max-width: 760px) {
    .lb-section.lb-section--exam { padding: 28px 16px 20px; }
    .lesson-content .lesson-pane > * + .lb-section.lb-section--exam { padding-top: 28px; }
    .lb-exam-tab { left: 14px; max-width: calc(100% - 28px); }
}

/* Cuprins marker — which sections are exam subjects, before you scroll to them. */
.lesson-toc-exam {
    display: inline-block;
    flex: none;
    width: 6px;
    height: 6px;
    margin-left: 6px;
    border-radius: 999px;
    background: var(--coral);
}
/* ⚠️ The row's LABEL is not recoloured. There used to be a
   `.lesson-toc-item.is-exam .lesson-toc-label { color: var(--exam-ink) }` here and
   `--exam-ink` was never defined anywhere — an unresolvable `var()` with no fallback is
   invalid at computed-value time, and for an inherited property like `color` that means
   `inherit`, not "ignore the declaration". So the label silently took the button's dark ink:
   an exam row read as permanently selected, and because the rule sits after `.is-active`'s
   at the same specificity it ate the active row's teal too.
   The dot is the whole marker. It is its own element with its own colour, so it is
   unaffected by what the label does. */

.lesson-readbar {
    height: 3px;
    background: var(--divider);
}
.lesson-readbar-fill {
    height: 100%;
    width: 0;
    background: var(--teal);
    transition: width 0.3s ease;
}
/* Lesson name in the sticky topbar — only where the rail can't show it
   (collapsed rail / phone); see the responsive section. */
/* ⚠️ The padding-top here and the span's font-size below are the two numbers
   --lesson-topbar-title-h mirrors, because CSS cannot measure an element and the
   KSB's top inset has to know how tall this row is. Change either and change it
   there too, or the panel head drifts off the tab row again. */
.lesson-topbar-title {
    display: none;
    justify-content: center;
    padding: 8px clamp(12px, 4vw, 20px) 0;
}
.lesson-topbar-title span {
    /* min-width:0 or the nowrap title would set the flex item's min-content
       width and widen the whole page on a phone. */
    min-width: 0;
    max-width: min(var(--lesson-read-max), 100%);
    font-family: var(--font-serif);
    font-size: 14px;
    font-weight: 600;
    color: var(--ink-warm);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The 3px read bar above + this padding = --lesson-tabrow-top, and the bottom
   padding + the rule = --lesson-tabrow-bottom; the KSB caption row is inset by
   those two, so touching either here means updating them on .lesson. */
.lesson-tabs-wrap {
    display: flex;
    justify-content: center;
    padding: 7px 0 6px;
    border-bottom: 1px solid var(--divider);
}
/* Same box as .lesson-content, so the bar's edges line up with the text column. */
.lesson-tabs-inner {
    position: relative;
    width: 100%;
    max-width: var(--lesson-read-max);
    padding: 0 clamp(16px, 4vw, 40px);
    display: flex;
    align-items: center;
}
.lesson-tabs {
    display: flex;
    width: 100%;
    align-items: stretch;
    gap: 3px;
    background: #F0EEE9;
    border-radius: 13px;
    padding: 4px;
}
.lesson-tab {
    flex: 1 1 0;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    /* fixed height, not padding-derived — see --lesson-tab-h on .lesson */
    height: var(--lesson-tab-h);
    padding: 0 8px;
    border-radius: 10px;
    font-family: var(--font-body);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--muted);
    background: none;
    border: none;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
}
.lesson-tab svg { flex: none; }
.lesson-tab-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.lesson-tab:hover { color: var(--ink); }
.lesson-tab.is-active {
    background: var(--card);
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(28, 27, 25, 0.08);
}
/* Mute the inactive tab icons so the active tab leads. */
.lesson-tab:not(.is-active) svg {
    opacity: 0.55;
    filter: saturate(0.4);
}
.lesson-content {
    max-width: var(--lesson-read-max);
    margin: 0 auto;
    padding: clamp(20px, 5vw, 44px) clamp(16px, 4vw, 40px) 152px;
}

.lesson-h1 {
    margin: 0;
    font-family: var(--font-serif);
    font-size: clamp(30px, 7vw, 42px);
    line-height: 1.08;
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--ink-warm);
    overflow-wrap: break-word;
}

.lesson-pane { display: block; }
.lesson-pane[hidden] { display: none; }
.lesson-pane-head { margin: 0; }
.lesson-pane-title {
    margin: 0;
    font-family: var(--font-serif);
    font-size: 27px;
    line-height: 1.2;
    font-weight: 600;
    color: var(--ink-warm);
}
.lesson-pane-sub {
    margin: 4px 0 0;
    font-size: 14px;
    color: var(--muted);
}

/* ── Right column: knowledge sidebar (KSB) ─────────────────────────────── */
.lesson-ksb {
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
    box-sizing: border-box;
    /* Top inset = the strip of the sticky topbar above the tab toggle, so the
       caption row below starts exactly where the toggle starts. The title row is
       part of that strip wherever it is drawn (≤1479px) and 0 everywhere else —
       adding it is what keeps the head on the toggle's line in BOTH bands.
       Below 1100px the panel is a drawer and its own `padding: 0` takes over. */
    padding: calc(var(--lesson-tabrow-top) + var(--lesson-topbar-title-h)) 14px 22px;
    background: var(--canvas);
}
/* The cards are capped narrower than the column so they read as cards rather
   than as a second wall of text. */
.lesson-ksb-panels {
    position: relative;                /* anchor for the desktop close button */
    display: block;
    max-width: 268px;
    margin: 0 auto;
}
/* One group per pane that has a sidebar; lesson.js hides the ones that are not the
   active pane. Only Lecție has one today — see the note in detail.html. */
.ksb-group[hidden] { display: none; }

/* Panel label row: the brain mark, a quiet serif caption naming the rail, and
   the close chip — aligned to the same width as the cards under it. The row is
   the tab toggle's height and sits at its top inset, so the three of them line
   up horizontally with the Lecție / Grile / … toggle across the page; the
   bottom margin makes up the rest of the topbar, so the first card starts level
   with the prose. In drawer mode the grip joins the row and the sizes reset. */
.lesson-ksb-head {
    display: flex;
    align-items: center;
    gap: 9px;
    height: var(--lesson-tabrow-h);
    box-sizing: border-box;
    max-width: 268px;
    margin: 0 auto var(--lesson-tabrow-bottom);
    padding: 0 4px;
    background: none;
}
.lesson-ksb-head svg { flex: none; color: var(--faint); }
/* The grip is drawer-only; the caption now shows in both forms. */
.lesson-ksb-grip { display: none; }
.lesson-ksb-head .lesson-ksb-close { margin-left: auto; }

/* Desktop: the KSB is a column, but it can still be dismissed — the close
   button collapses it and the centre column takes the space back. The text does
   not move when it goes: its left edge is --lesson-left, which is computed from
   the docked layout and never changes, so the freed width lengthens the lines
   instead (see --lesson-content-max on .lesson). */
/* Collapsing zeroes the column *variable* rather than rewriting the template,
   so the tablet/phone layouts (which define their own template) are untouched;
   `:not(.is-open)` keeps the mobile drawer openable while the flag is set. */
body.ksb-collapsed .lesson { --lesson-ksb-w: 0px; }
body.ksb-collapsed .lesson-ksb:not(.is-open) { display: none; }
/* the FAB comes back as the way to reopen it */
body.ksb-collapsed .lesson-ksb-fab { display: inline-flex; }

/* A tab with no sidebar group of its own (Flashcards, and a reference tab still
   showing "În curând") drops the panel entirely, and the FAB with it
   (`!important` because the FAB's own responsive rules turn it back on below
   1100px). lesson.js sets the flag per tab, from the DOM — see the .ksb-group
   comment in templates/lessons/detail.html. */
body.lesson-no-ksb .lesson-ksb { display: none; }
body.lesson-no-ksb .lesson-ksb-fab { display: none !important; }
body.lesson-no-ksb .lesson-ksb-backdrop { display: none; }

/* ── The one left edge ──────────────────────────────────────────────────────
   Scoped to where the panel is a real grid column; below 1100px the tablet /
   phone templates drop it and `.lesson-content`'s own `margin: 0 auto` stands.

   These rules are deliberately NOT keyed to a body class. Docked, collapsed and
   absent are three states of the same layout, and the text has to start in the
   same place in all three — a `margin: 0 auto` that re-centres against the
   current column slid the whole block sideways by half the panel width on every
   close and every tab switch. So: the left offset is fixed, the RIGHT-hand end
   is what moves, and only --lesson-content-max knows which state it is in.

   The tab bar keeps the docked box in every state (--lesson-read-nat, never the
   grown one) so the buttons stay pixel-identical — a control that stretches
   slides under the cursor mid-click. */
@media (min-width: 1100px) {
    /* the middle column annexes the (zero-width or hidden) panel column */
    body.ksb-collapsed .lesson-main,
    body.lesson-no-ksb .lesson-main { grid-column: 2 / 4; }

    .lesson-content,
    .lesson-tabs-inner {
        margin-left: var(--lesson-left);
        margin-right: 0;
    }
    .lesson-content { max-width: var(--lesson-content-max); }
    .lesson-tabs-inner { max-width: var(--lesson-read-nat); }
    /* the tab bar is normally a centred flex item — anchor the flex line left so
       the margin above is the only thing positioning it */
    .lesson-tabs-wrap { justify-content: flex-start; }
}
.lesson-ksb-grip {
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 40px;
    height: 4px;
    border-radius: 999px;
    background: var(--border-2, #D4CDBE);
}
/* Serif and faint on purpose: it names the rail without competing with the
   card titles right below it. */
.lesson-ksb-head-title {
    font-family: var(--font-serif);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--faint);
}

/* KSB close button — the sand chip from the "Knowledge Sidebar redesign"
   handoff. It rides on the first card's heading row on desktop and returns to
   the label row in drawer mode; the chip reads the same in both. */
.lesson-ksb-close {
    flex: none;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: #F0EBDD;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    cursor: pointer;
    padding: 0;
    color: #6F6C66;
    transition: color 0.12s ease, background 0.12s ease;
}
.lesson-ksb-close:hover { background: #E7E0CE; color: var(--ink); }
.lesson-ksb-close svg { width: 12px; height: 12px; stroke-width: 2.5; }

/* Floating action button that opens the KSB drawer on narrow viewports */
.lesson-ksb-fab {
    display: none;
    position: fixed;
    bottom: 20px;
    right: 16px;
    z-index: 70;
    align-items: center;
    gap: 8px;
    background: var(--card, #fff);
    color: var(--ink-warm);
    padding: 8px 18px 8px 10px;
    border-radius: 999px;
    border: 1px solid var(--border-2, #D4CDBE);
    font-family: var(--font-body);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    box-shadow: var(--shadow-pop);
}
/* The drawn brain is a filled, dark-outlined mark, so the pill itself is light:
   the icon sits straight on it with no tile of its own. */
.lesson-ksb-fab svg { flex: none; }
/* The FAB names what it will actually open: Panou where there is a knowledge panel, the
   assistant where there is not (see `body.ksb-solo`, the same fact that collapses the
   switch). The gap lives on the face, not on the button, so the hidden one costs nothing. */
.lesson-ksb-fab-face { display: inline-flex; align-items: center; gap: 8px; }
.lesson-ksb-fab-chat { display: none; }
body.ksb-solo .lesson-ksb-fab-panou { display: none; }
body.ksb-solo .lesson-ksb-fab-chat { display: inline-flex; }

/* Dim + blur backdrop behind the drawer. Never shown on desktop (the KSB is a
   column there); the JS only opens it together with the drawer. */
.lesson-ksb-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 75;
    background: rgba(20, 19, 17, 0.45);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    animation: ksb-fade-in 0.2s ease;
}
.lesson-ksb-backdrop.is-open { display: block; }

/* While the drawer is open the page underneath must not scroll, and the FAB
   that opened it would otherwise sit under the sheet. */
body.ksb-open { overflow: hidden; }
body.ksb-open .lesson-ksb-fab { display: none !important; }

@keyframes ksb-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ksb-sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes ksb-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }

/* ============================================================================
   2. CONTENT BLOCKS  (prefix lb-) — inside .lesson-pane[data-pane="lectie"]
   ========================================================================== */

/* ── Block rhythm ──────────────────────────────────────────────────────────
   One gap for every boundary between two blocks, whatever they are: text,
   callout card, table, comparison, list, figure, calculator, quiz, flashcard
   grid, matching. The blocks themselves carry NO vertical margin — the gap is
   set once, here, by the adjacent-sibling rules below. That is what keeps it
   equal everywhere and stops two heavy neighbours (a table under a quiz, say)
   from stacking their own margins into a double gap. */
.lesson {
    --lb-gap: 24px;       /* between any two sibling blocks */
    /* Two plain text blocks in a row are just paragraphs of the same thought —
       they read as separated at well under the card gap, and a text-only
       section was spending most of its height on empty space. */
    --lb-text-gap: 15px;
    --lb-sec-gap: 28px;   /* before a new section — a heading gets a bit more air */
    --lb-head-gap: 14px;  /* a heading -> its own first block, tighter than above it */
    /* ONE horizontal inset for every card block (table, comparison, quiz): the
       header bar, the body text and any inner cards all start on this line, so
       stacked cards read as one column instead of drifting a few px apart.
       Phones shrink it once, in the ≤767px block at the end of the file. */
    --lb-card-pad-x: 24px;
}

/* Reset: nothing that sits directly in a pane or a section body brings its own
   vertical margin. Every selector here is deliberately two classes deep, so it
   outranks a block's own single-class `margin` and the rhythm can't be undone
   by accident from the rules further down. */
.lesson-content .lesson-pane > *,
.lesson-content .lb-body > * {
    margin-top: 0;
    margin-bottom: 0;
}
.lesson-content .lesson-pane > * + *,
.lesson-content .lb-body > * + * { margin-top: var(--lb-gap); }
/* …except prose next to prose, which gets the tighter paragraph gap. */
.lesson-content .lesson-pane > .lb-prose + .lb-prose,
.lesson-content .lb-body > .lb-prose + .lb-prose { margin-top: var(--lb-text-gap); }

/* Section boundaries + the title/heading -> body pairs. */
.lesson-content > .lesson-pane { margin-top: var(--lb-gap); }
/* Every top-level section (the ones listed in the left Cuprins) opens with a
   visible rule, so the boundary reads at a glance while scrolling — not only
   from the heading. The first section in a pane has nothing above it to
   separate from, hence `* + .lb-section`. */
.lesson-content .lesson-pane > * + .lb-section {
    margin-top: var(--lb-sec-gap);
    padding-top: var(--lb-sec-gap);
    border-top: 2px solid var(--border-2);
}
.lesson-content .lb-section > .lb-h2 + .lb-body { margin-top: var(--lb-head-gap); }
.lesson-content .lesson-pane > .lesson-pane-head + * { margin-top: var(--lb-head-gap); }

/* ── Population marks ────────────────────────────────────────────────────
   `[la adult]` / `[la copil]` / `[la gravidă]` in the authored text arrive as
   <span class="age-mark age-mark--copil">copil</span> (apply_age_tags). Per the
   "Marcaj adult-copil (1c)" handoff the word stays in the sentence, in the case
   the author wrote, under a hand-drawn marker stroke with tapered ends: an SVG
   path as a background image, stretched to the word's width with
   preserveAspectRatio='none', so its uneven edges fit any length. Adult and
   copil carry DIFFERENT paths and opposite base rotations — that is what keeps
   two marks in one paragraph from looking like the same rubber stamp.

   The fill lives inside the SVG, where var() cannot reach, so each colour is
   its own data-URI. Hence the two blocks below rather than one with a variable:
   the cream canvas takes a step more saturation than a white card, and that
   fill value is the only difference between them. */
.age-mark {
    display: inline-block;          /* required for transform */
    padding: 0 8px;                 /* the stroke overshoots the letters */
    font-weight: 700;
    background-repeat: no-repeat;
    /* The stroke must clear the WHOLE glyph, ascender to descender — a `p` tail
       or a `b` stem escaping the band is what made the mark read as ragged, and
       on a tinted band it also costs legibility. The handoff's .96em / `0 .18em`
       cut the baseline; 1.14em still left the tails and the Romanian cedillas
       `ț` / `ș`, which drop lower than a plain `p`, outside it. Measured at 4x
       over 1.14/1.3/1.45/1.6em against copil · bărbați · gravidă · adulți ·
       școlari: 1.45em is the first that covers every one, and 1.6em+ starts to
       read as a filled block rather than a marker stroke.

       `center center` rather than a top offset, so the band is positioned
       independently of line-height — the same rule then holds in 16.5px body
       prose, a 21px h3 and a table caption, and the whole component ends up with
       one positioning model instead of a px offset per surface.

       This is a deliberate departure from the handoff, which asks for ~70% of
       the line with the descenders left showing. Ciprian asked twice for full
       vertical coverage, for legibility. */
       On the WIDTH: the 8px padding makes the stroke overshoot the word by 8px a
       side, but an inter-word space is only ~4px, so at a full-width band the
       right-hand tip landed on the next word's first letter. Taking 7px off the
       band (not off the padding, which would change the spacing) leaves ~4.5px
       of overshoot each side — still a stroke drawn past the letters, now with a
       clear gap before the next word. */
    background-size: calc(100% - 7px) 1.45em;
    background-position: center center;
}
/* Default = prose on the cream canvas, which is where all but a handful live,
   and also the strongest pair — so the saturated callout cards inherit it. */
.age-mark--adult {
    color: var(--teal-ink);
    transform: rotate(-1deg);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23A8DAD7' opacity='.9'/%3E%3C/svg%3E");
}
.age-mark--copil {
    color: #7A5C15;                 /* deeper than --miss-ink, per the handoff */
    transform: rotate(.9deg);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M1.2,7.2 C5,4.4 16,6.1 32,5.4 C54,4.6 80,6.2 102,4.8 C111,4.2 117,6.6 119,9.8 C116.6,13.4 118,17.2 115.8,20.4 C111,22.2 99,20.8 81,21.6 C57,22.6 33,21 15,22.4 C6.4,23 2.2,20.8 1.6,17.9 C3.4,15 0.6,10.2 1.2,7.2 Z' fill='%23F5CE81' opacity='.9'/%3E%3C/svg%3E");
}
/* gravidă — lilac. A fourth hue was needed because 31 marks is too many to leave
   unhued, and violet is the one direction the palette had free: it reads as its
   own scope next to the teal and the gold, and it is not a state anywhere in the
   app. Ink is AA on both its fills (5.6 / 6.6:1). */
.age-mark--gravida {
    color: #5B3A78;
    transform: rotate(-.7deg);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23D9C4EC' opacity='.9'/%3E%3C/svg%3E");
}
/* vârstnic — dusty blue. Bluer than the adult teal, so the two are told apart at
   a glance, and it is the one remaining hue that collides with no state: teal is
   correct, gold is miss, coral is the exam panel, green is success. A sage green
   was the alternative and was dropped for exactly that last reason (and it only
   reached 4.4:1 on cream). 5.0 / 6.0:1 here. */
.age-mark--varstnic {
    color: #33506E;
    transform: rotate(-.7deg);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23B6CBE0' opacity='.9'/%3E%3C/svg%3E");
}
/* femei / bărbați — a scope, not an age, and two marks in the whole corpus. They
   stay unhued so the four age/state scopes keep the colour vocabulary to
   themselves. */
.age-mark--other {
    color: var(--body-warm);
    transform: rotate(-.7deg);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23E3DBC8' opacity='.9'/%3E%3C/svg%3E");
}
/* On white (lists, comparison cards, tables, quiz cards) the cream-tuned stroke
   is a step too strong. Same path, same rotation — only the fill changes. */
:is(.lb-list, .lb-cmp, .lb-tbl, .quiz) .age-mark--adult {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23BFE4E2' opacity='.9'/%3E%3C/svg%3E");
}
:is(.lb-list, .lb-cmp, .lb-tbl, .quiz) .age-mark--copil {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M1.2,7.2 C5,4.4 16,6.1 32,5.4 C54,4.6 80,6.2 102,4.8 C111,4.2 117,6.6 119,9.8 C116.6,13.4 118,17.2 115.8,20.4 C111,22.2 99,20.8 81,21.6 C57,22.6 33,21 15,22.4 C6.4,23 2.2,20.8 1.6,17.9 C3.4,15 0.6,10.2 1.2,7.2 Z' fill='%23F6D89B' opacity='.9'/%3E%3C/svg%3E");
}
:is(.lb-list, .lb-cmp, .lb-tbl, .quiz) .age-mark--gravida {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23E6D7F2' opacity='.9'/%3E%3C/svg%3E");
}
:is(.lb-list, .lb-cmp, .lb-tbl, .quiz) .age-mark--varstnic {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23CBDCEC' opacity='.9'/%3E%3C/svg%3E");
}
/* On a saturated surface the problem stops being delicacy and becomes contrast,
   so the handoff retunes the stroke there: taller (1.5em, lifted 4px, because a
   card title runs a tighter line-height), OPAQUE — no `opacity` on the path —
   and the ink goes near-black. It covers the letters outright instead of tinting
   them. The handoff lists the five teal card-header bars; `.lb-card` is added
   because the four callouts (navy/teal/gold/coral, white body text) pose the
   same problem, and an opaque band with near-black ink reads on all of them
   where a pale tint does not.

   `.age-mark.age-mark--adult` doubles the class deliberately: the white-card
   rules above also match a header (`.lb-tbl-head` sits inside `.lb-tbl`), and at
   equal specificity source order alone would decide it — the trap this file
   already carries a warning about for the exam panel. Three classes here, two
   there, so context wins on specificity rather than on position. */
:is(.lb-tbl-head, .lb-cmp-head, .lb-list-head, .lb-calc-head, .quiz-head, .lb-card) .age-mark {
    /* The handoff's 1.5em / `center -4px` left descenders hanging outside the
       band — the tail of a `p`, and worse the Romanian cedillas `ț` and `ș`,
       which drop lower still. On a saturated surface that is exactly where the
       word stops being legible, so here the band must clear the whole glyph,
       ascender to descender. Measured at 4x over 1.5/1.65/1.8/1.95em against
       copil · bărbați · gravidă · adulți · școlari: 1.65em is the first that
       covers every one, and 1.8em+ starts reading as a filled block.

       `center center` instead of the handoff's `center -4px`, and this is the
       point Ciprian called out: a lift measured from the TOP is only right at one
       line-height. The handoff uses the same -4px for the bar (1.4) and the
       callouts (1.8), which is why its bar looks framed and its callouts do not.
       Centring makes the offset line-height independent, so one rule frames the
       card titles, the callout bodies and a table caption alike. */
    background-size: calc(100% - 7px) 1.65em;
    background-position: center center;
}
/* The colour rule, from the "pe fundaluri colorate" handoff: on a coloured
   surface the BAND is a shade of white and the demographic colour moves into the
   TEXT. The two whites differ only in temperature — #F1FAF7 cool for adult,
   #FFF6E6 warm for copil — so what tells them apart is the ink, not the band.
   That is why one override now covers the teal bar and all four callouts:
   contrast against the card no longer depends on the card's colour. */
:is(.lb-tbl-head, .lb-cmp-head, .lb-list-head, .lb-calc-head, .quiz-head, .lb-card) .age-mark.age-mark--adult {
    color: #0A4F4E;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23F1FAF7'/%3E%3C/svg%3E");
}
:is(.lb-tbl-head, .lb-cmp-head, .lb-list-head, .lb-calc-head, .quiz-head, .lb-card) .age-mark.age-mark--copil {
    color: #7A4A10;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M1.2,7.2 C5,4.4 16,6.1 32,5.4 C54,4.6 80,6.2 102,4.8 C111,4.2 117,6.6 119,9.8 C116.6,13.4 118,17.2 115.8,20.4 C111,22.2 99,20.8 81,21.6 C57,22.6 33,21 15,22.4 C6.4,23 2.2,20.8 1.6,17.9 C3.4,15 0.6,10.2 1.2,7.2 Z' fill='%23FFF6E6'/%3E%3C/svg%3E");
}
/* gravidă and vârstnic follow the same rule as the other two hues: a white
   tinted toward their own colour, with the colour itself carried by the ink. */
:is(.lb-tbl-head, .lb-cmp-head, .lb-list-head, .lb-calc-head, .quiz-head, .lb-card) .age-mark.age-mark--gravida {
    color: #4A2C63;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23F8F3FD'/%3E%3C/svg%3E");
}
:is(.lb-tbl-head, .lb-cmp-head, .lb-list-head, .lb-calc-head, .quiz-head, .lb-card) .age-mark.age-mark--varstnic {
    color: #2B4560;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23F2F7FC'/%3E%3C/svg%3E");
}
/* femei / bărbați stay unhued here too — a temperature-neutral white. */
:is(.lb-tbl-head, .lb-cmp-head, .lb-list-head, .lb-calc-head, .quiz-head, .lb-card) .age-mark.age-mark--other {
    color: var(--ink-warm);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 26' preserveAspectRatio='none'%3E%3Cpath d='M0.8,11.4 C4,6.8 14,5.2 30,4.6 C52,3.8 78,5.1 100,3.6 C110,3 116,4.2 119.2,6.4 C117,10.2 118.6,15.4 116.4,19.1 C112,21.8 100,20.4 82,21.2 C58,22.2 34,20.6 16,22.1 C7,22.8 2.6,21.4 1.4,18.6 C2.8,16.1 0.2,13.6 0.8,11.4 Z' fill='%23F6F3EC'/%3E%3C/svg%3E");
}
/* Rotation scatter: every second mark, class set by the renderer (_MARK_SPIN).
   The alternate angle is per family, so the class carries only the parity. */
.age-mark--alt.age-mark--adult { transform: rotate(.6deg); }
.age-mark--alt.age-mark--copil { transform: rotate(-1.3deg); }
.age-mark--alt:is(.age-mark--gravida, .age-mark--varstnic, .age-mark--other) { transform: rotate(.5deg); }
/* The overshoot is right before a word but reads as a stray space before
   punctuation that follows directly, so that one side gives it back. */
.age-mark--tight { margin-right: -8px; }

/* ── Prose ─────────────────────────────────────────────────────────────── */
.lb-prose p {
    margin: 14px 0 0;
    font-size: 16.5px;
    line-height: 1.65;
    color: var(--body-warm);
}
.lb-prose > :first-child { margin-top: 0; }
.lb-prose > :last-child { margin-bottom: 0; }
/* A table / comparison card lifted out of a paragraph (raw HTML tables live
   inside a text block) is a block in its own right, so it takes the block gap
   from its prose neighbours — but not at the edges, where the sibling rule
   above already spaces it. */
.lb-prose > .lb-tbl,
.lb-prose > .lb-cmp { margin: var(--lb-gap) 0; }
.lb-prose > .lb-tbl:first-child,
.lb-prose > .lb-cmp:first-child { margin-top: 0; }
.lb-prose > .lb-tbl:last-child,
.lb-prose > .lb-cmp:last-child { margin-bottom: 0; }
/* Sub-headings inside prose (markdown ### / ####). They sat at the browser's
   default 1.17em, a hair above the body text, so a new sub-topic didn't read as
   one — they now land clearly between the 29px section title and the 16.5px
   body. Size alone marks the boundary: no rule, no tick. */
.lb-prose h3 {
    margin: 0;
    font-family: var(--font-serif);
    font-size: 21px;
    line-height: 1.3;
    font-weight: 600;
    color: var(--ink-warm);
}
.lb-prose h4 {
    margin: 0;
    font-family: var(--font-serif);
    font-size: 17.5px;
    line-height: 1.35;
    font-weight: 600;
    color: var(--ink-warm);
}
/* A heading and the text under it are one unit: more air above the heading,
   less between it and its own first paragraph. Each markdown block is its own
   .lb-prose, hence the :has() on the sibling rhythm. */
.lesson-content .lesson-pane > * + .lb-prose:has(> h3:only-child),
.lesson-content .lb-body > * + .lb-prose:has(> h3:only-child) { margin-top: 26px; }
.lesson-content .lesson-pane > .lb-prose:has(> h3:only-child) + *,
.lesson-content .lb-body > .lb-prose:has(> h3:only-child) + * { margin-top: 9px; }
.lb-prose h3 + p,
.lb-prose h4 + p { margin-top: 9px; }
.lb-prose p + h3 { margin-top: 26px; }
.lb-prose p + h4 { margin-top: 20px; }

.lb-prose strong { color: var(--ink-warm); font-weight: 600; }
.lb-prose a { color: var(--teal); text-decoration: none; }
.lb-prose a:hover { color: var(--teal-hover); }
.lb-prose ul,
.lb-prose ol {
    margin: 12px 0;
    padding-left: 22px;
}
.lb-prose li { margin: 6px 0; }

/* ── Section ───────────────────────────────────────────────────────────── */
.lb-section {
    /* Spacing comes from the block-rhythm rules above. */
    scroll-margin-top: 90px;
}
.lb-eyebrow {
    font-family: var(--font-eyebrow);
    font-size: 15px;
    font-weight: 600;
    color: var(--teal);
}
.lb-h2 {
    /* First child of .lb-section — the block-rhythm rules space it above and
       below (--lb-sec-gap / --lb-head-gap). */
    margin: 0;
    font-family: var(--font-serif);
    font-size: 29px;
    line-height: 1.2;
    font-weight: 600;
    color: var(--ink-warm);
}
/* The hand-drawn underline ("SubtitluLectie" handoff). The span wraps the TITLE only —
   without it the stroke would cross the teal `01.` as well.

   ⚠️ A BACKGROUND on an inline span, not the handoff's absolutely-positioned <svg> inside
   an inline-block. The handoff only ever draws single-line headings; ours wrap. An
   inline-block that wraps takes the FULL column width, which did two visible things: it
   pushed `06.` onto a line of its own (it no longer fitted beside a full-width box), and it
   ran the stroke the whole column width under a last line reading just "lung". A background
   on an inline element repeats per LINE FRAGMENT with box-decoration-break, so each line
   gets a stroke as wide as its own words, and the number keeps flowing with the title.
   Measured: 5 of 7 headings on a typical lesson wrap at 390px.

   `padding-bottom` extends the background box WITHOUT touching line height, so the heading
   still measures the same and `--lb-head-gap` and the scroll-spy offsets are untouched.

   8px, not the handoff's 13px. Two reasons the number had to move: the handoff measures its
   offset from an inline-BLOCK's bottom (the line box), while padding on an inline measures
   from the em box, which is higher — so the same number lands differently. And on a wrapped
   heading, 13px put the first line's stroke straight through the ascenders of the second.
   Checked at 13 / 10 / 8 against both a single-line and a wrapped heading. */
.lb-h2-mark {
    /* No cache-buster: the URL is fixed, so a redrawn stroke needs a filename bump.
       `{% asset %}` only versions the stylesheet, not what the stylesheet points at. */
    background: url("../img/h2-pencil.svg") no-repeat left bottom / 100% 16px;
    padding-bottom: 8px;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}
/* …except on a "Dat la examen" section, which already carries the loudest treatment in the
   lesson: the warm panel, the coral tab stitched to its top edge, and a coral section number.
   A hand-drawn underline on top of that is a fourth mark competing for the same glance, and
   the one thing the panel exists to say — this was set at the exam — is not what the pencil
   says. Ciprian's call. See §"Exam-subject sections". */
.lb-section--exam .lb-h2-mark {
    background: none;
    padding-bottom: 0;
}
/* Section number, same 01..NN as the left Cuprins. No font of its own — it
   inherits the heading's serif and is set apart by the teal alone. */
.lb-h2-num {
    /* Same face, same size, same weight as the title — "01. Titlu" reads as one
       line; only the teal marks the number as the index. */
    font-weight: inherit;
    color: var(--teal);
    margin-right: 0.28em;
    font-variant-numeric: tabular-nums;
}
.lb-body { display: block; }

/* ── Callout cards: De ce contează / Perlă / Memorare / Capcană ────────── */
.lb-card {
    border-radius: var(--r-md);
    padding: 15px 18px 14px;
    border-bottom: 4px solid transparent;
    color: #fff;
}
.lb-card--why   { background: var(--why);   border-bottom-color: var(--why-deep);   }
.lb-card--pearl { background: var(--pearl); border-bottom-color: var(--pearl-deep); }
.lb-card--memo  { background: var(--memo);  border-bottom-color: var(--memo-deep);  }
.lb-card--trap  { background: var(--trap);  border-bottom-color: var(--trap-deep);  }

.lb-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}
.lb-card-pill {
    display: inline-flex;
    align-items: center;
    background: rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    padding: 4px 12px;
    font-family: var(--font-eyebrow);
    font-size: 14px;
    font-weight: 600;
    color: #fff;
}
/* The card title now shares the top row with the icon (the type pill that used
   to sit there is gone — the icon and colour say what kind of card it is). */
.lb-card-title {
    flex: 1;
    min-width: 0;
    align-self: center;
    font-size: 17px;
    font-weight: 800;
    line-height: 1.3;
    letter-spacing: -0.01em;
    color: #fff;
}
.lb-card-ico {
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* The glyphs are the design's illustrated ones, carrying their own fills and
   ink stroke — no `stroke: currentColor` recolouring, and no per-variant colour
   rules: the card's tint is already doing the colour-coding, and the white disc
   gives the illustration the light background it was drawn for. */
.lb-card-ico svg { width: 24px; height: 24px; }

.lb-card-body {
    margin-top: 8px;
    font-size: 14px;
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.92);
}
.lb-card-body strong { color: #fff; }
.lb-card--why .lb-card-body,
.lb-card--why .lb-prose p { font-size: 14.5px; line-height: 1.6; }
.lb-card-body a { color: #fff; text-decoration: underline; }

/* Highlighted key-fact strip at the bottom of a callout card. */
.lb-card-key {
    margin-top: 11px;
    display: flex;
    align-items: center;
    gap: 9px;
    background: rgba(255, 255, 255, 0.16);
    border-radius: 10px;
    padding: 9px 13px;
    font-size: 13.5px;
    font-weight: 700;
    color: #fff;
    line-height: 1.4;
}
.lb-card-key strong { color: #fff; }
.lb-card-key-dot { width: 6px; height: 6px; border-radius: 50%; background: #fff; flex: none; }

/* prose nested inside a white-on-color card must invert its colors */
.lb-card .lb-prose p { color: rgba(255, 255, 255, 0.92); font-size: 13.5px; line-height: 1.55; }
.lb-card .lb-prose strong { color: #fff; }
.lb-card .lb-prose a { color: #fff; text-decoration: underline; }

/* highlighted takeaway strip inside a card */
.lb-card-note {
    margin-top: 11px;
    display: flex;
    align-items: center;
    gap: 9px;
    background: rgba(255, 255, 255, 0.16);
    border-radius: 10px;
    padding: 9px 13px;
    font-size: 13.5px;
    font-weight: 700;
    color: #fff;
}
.lb-card-note::before {
    content: "";
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #fff;
}

/* ── Comparison cards · redesign ("Carduri comparative redesign.dc.html") ──
   Sibling of the table card below: same teal header bar + white bordered
   container, holding two tone-coloured columns of bullets. */
.lb-cmp {
    /* Spacing comes from the block-rhythm rules at the top of section 2 — the
       one exception is a card sitting inside a .lb-prose wrapper (see there). */
    margin: 0;
    background: var(--card);
    border: 1.5px solid var(--teal-3);
    border-radius: 22px;
    overflow: hidden;
    box-shadow: 0 8px 24px rgba(14, 124, 123, .10);
    /* Like tables: the columns fold on the CARD's width, not the viewport —
       the lesson rails decide how much room the block really gets. */
    container: lbcmp / inline-size;
}
/* Horizontal padding stays on --lb-card-pad-x: it lines the icon up with the
   first grid column below it. Only the vertical rhythm slimmed down. */
.lb-cmp-head {
    display: flex;
    align-items: center;
    gap: var(--cardhead-gap);
    padding: var(--cardhead-pad-y) var(--lb-card-pad-x, 24px);
    background: var(--teal-3);
}
.lb-cmp-ico {
    width: var(--cardhead-ico);
    height: var(--cardhead-ico);
    flex: none;
    border-radius: 50%;
    background: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* The glyphs carry width/height attributes of their own (drawn at 32-34px);
   override them so the icon follows the shared token. */
.lb-cmp-ico svg { width: var(--cardhead-svg); height: var(--cardhead-svg); }
/* Same face as .lb-tbl-title so the two cards read as one family. The
   "Comparație" kicker that used to sit above it was dropped — the card's own
   title says what it is, and the second line only added chrome. */
.lb-cmp-title {
    font-family: var(--font-eyebrow);
    font-size: var(--cardhead-title);
    font-weight: 700;
    line-height: 1.3;
    color: #FFFFFF;
}
.lb-cmp-body {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    /* Same inset as the header above and as the quiz options — the tone cards
       start exactly where the table's first column does. */
    padding: 18px var(--lb-card-pad-x, 24px);
    background: var(--card);
}
.lb-cmp-card {
    border-radius: 16px;
    padding: 20px 22px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.lb-cmp-card-title {
    font-family: var(--font-eyebrow);
    font-size: 17.5px;
    font-weight: 800;
    line-height: 1.25;
}
/* .lb-prose's own list rules (0,1,1) would otherwise indent and re-bullet
   these — reset before the tone variants paint them. */
.lb-cmp ul.lb-cmp-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 11px;
}
.lb-cmp .lb-cmp-list li {
    margin: 0;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: 14.5px;
    line-height: 1.55;
    color: var(--ink-warm);
}
.lb-cmp .lb-cmp-list li > span { min-width: 0; }
.lb-cmp .lb-cmp-list li::before {
    content: "";
    flex: none;
    width: 6px;
    height: 6px;
    margin-top: 8px;
    border-radius: 50%;
    background: var(--muted-2);
}
.lb-cmp p { margin: 0; font: inherit; color: inherit; }
/* Tone variants — the tone keyword is normalised in the renderer. */
.lb-cmp-card[data-tone="teal"] { background: #DCEEEA; }
.lb-cmp-card[data-tone="teal"] .lb-cmp-card-title { color: var(--teal-ink); }
.lb-cmp-card[data-tone="teal"] li::before { background: var(--teal); }
.lb-cmp-card[data-tone="coral"] { background: #FAECE6; }
.lb-cmp-card[data-tone="coral"] .lb-cmp-card-title { color: #A8452F; }
.lb-cmp-card[data-tone="coral"] li::before { background: #D96A4A; }
.lb-cmp-card[data-tone="amber"] { background: #FBF1DC; }
.lb-cmp-card[data-tone="amber"] .lb-cmp-card-title { color: var(--warn-ink); }
.lb-cmp-card[data-tone="amber"] li::before { background: #D9A03C; }
.lb-cmp-card[data-tone="neutral"] { background: var(--surface); }
.lb-cmp-card[data-tone="neutral"] .lb-cmp-card-title { color: var(--ink-warm); }
@container lbcmp (max-width: 560px) {
    /* Narrow card: only the vertical rhythm tightens — the horizontal inset
       stays on --lb-card-pad-x so the columns keep the shared edge. */
    .lb-cmp-body { grid-template-columns: 1fr; gap: 12px; padding-top: 14px; padding-bottom: 14px; }
    .lb-cmp-card { padding: 16px 18px; gap: 12px; }
    .lb-cmp-card-title { font-size: 16.5px; }
    .lb-cmp .lb-cmp-list li { font-size: 14px; }
    /* The header no longer steps down here: at the shared --cardhead-* sizes it
       is already compact enough that shrinking it further only made the glyph
       muddy. Same reasoning in the table's phone query below. */
}

/* ── Tables · one canonical grid ───────────────────────────────────────────
   Two emitters (a grid and, for the 34 tables that are a list wearing table
   markup, a real list) sharing one card. Column widths are the browser's job:
   nothing here declares a width except the table's own 100% and the card's
   edge inset — the old fixed per-column floors were what forced tables to
   scroll while one column starved. Flat card: no shadows anywhere. */
.lb-tbl {
    /* Spacing comes from the block-rhythm rules at the top of section 2 — the
       one exception is a table inside a .lb-prose wrapper (see there). */
    margin: 0;
    background: var(--card);
    border: 1.5px solid var(--teal-3);
    border-radius: 22px;
    overflow: hidden;

    --tbl-fs: 13.5px;
    --tbl-label-fs: 14.5px;
    --tbl-pad-y: 13px;
    --tbl-pad-x: 16px;
    /* Left/right edge of the grid = the card inset, so the first column lines
       up with the header icon above it and with every other card block. */
    --tbl-edge: var(--lb-card-pad-x, 24px);
    --tbl-gap: 5px;
    /* Row tones live in the COOL (teal) family, not the warm cream one: the page
       canvas is cream, so a cream zebra row read as "the background showing
       through" and the stripe disappeared. */
    --tbl-row: var(--card);
    --tbl-row-alt: #EFF7F6;
    --tbl-head-bg: #E3F2F0;
    --tbl-band-bg: #D7EBE9;
    --tbl-subhead-bg: #F1F8F7;
    --tbl-line: #DCEBEA;
    --tbl-line-soft: #E9F3F2;
}

/* Teal header bar — shares the --cardhead-* tokens with .lb-cmp-head,
   .lb-list-head and .quiz-head, so the card family stays in step. */
.lb-tbl-head {
    display: flex;
    align-items: center;
    gap: var(--cardhead-gap);
    padding: var(--cardhead-pad-y) var(--tbl-edge);
    background: var(--teal-3);
}
.lb-tbl-ico {
    width: var(--cardhead-ico);
    height: var(--cardhead-ico);
    flex: none;
    border-radius: 50%;
    background: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
}
.lb-tbl-ico svg { width: var(--cardhead-svg); height: var(--cardhead-svg); }
/* The header's only line: the table's own title. The "Tabelul N" kicker that
   used to sit above it was dropped — no lesson text refers to a table by
   number, so it was chrome rather than a reference. */
.lb-tbl-title {
    font-family: var(--font-eyebrow);
    font-size: var(--cardhead-title);
    font-weight: 700;
    line-height: 1.3;
    color: #FFFFFF;
}

/* The only overflow container, and the only one in the lesson: it scrolls iff
   the grid's own min-content exceeds the card, so the page body never does.
   No flag, no measurement, no JS. */
.lb-tbl-scroll {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--teal-3) transparent;
    scroll-timeline: --lbtbl inline;
}

table.lb-tbl-grid {
    table-layout: auto;          /* the whole bet, in two words */
    width: 100%;
    /* separate + per-cell borders: with `collapse` a sticky cell loses its
       borders in Chrome/Safari, because they belong to the table, not the cell */
    border-collapse: separate;
    border-spacing: 0;
    background: transparent;
    font: 400 var(--tbl-fs)/1.5 var(--font-body);
    color: var(--body-warm);
}
table.lb-tbl-grid th,
table.lb-tbl-grid td {
    padding: var(--tbl-pad-y) calc(var(--tbl-pad-x) / 2);
    text-align: left;
    vertical-align: top;
    font-weight: 400;
    border-top: 1px solid var(--tbl-line-soft);
    /* No hyphenation here — see the phone query at the end of this section.
       Hyphenation drops a column's min-content to a single syllable and the
       auto layout then starves it: a middle column became "Diskine-zie,
       disto-nie, acati-sie" one word wide. Without it the floor is the longest
       word, which is also the readable minimum. A word longer than its column
       still breaks rather than overflowing, because `break-word` does not lower
       the intrinsic minimum. */
    overflow-wrap: break-word;
    text-wrap: pretty;
}
table.lb-tbl-grid tbody tr:first-child > * { border-top: 0; }
/* Two rows assert no meaningful correspondence between each other — the rule
   between them would claim one that is not there. */
table.lb-tbl-grid tbody tr:only-child > * { border-top: 0; }

/* Edge inset charged exactly once per side, keyed on the GRID column (the
   class is stamped by the renderer) — never :first-child/:last-child, which
   lands mid-grid on a table with ragged rows or a rowspan above. */
.lb-tbl-c0 { padding-left: var(--tbl-edge); }
.lb-tbl-cz { padding-right: var(--tbl-edge); }

/* Zebra painted on the CELLS, with the parity of each cell's own row and
   counting data rows only: a band consumes no stripe slot, and a rowspan cell
   shows exactly one tone instead of two halves. */
table.lb-tbl-grid tr[data-z="1"] > * { background: var(--tbl-row-alt); }
table.lb-tbl-grid tr[data-z="0"] > * { background: var(--tbl-row); }

/* The stub column holds its place while the rest scrolls. Inert when nothing
   overflows, and safe because cell-painted zebra already makes it opaque. The
   seam rides the scroller's own timeline, so a table that fits never draws
   one; engines without scroll-driven animation simply show no seam. */
.lb-tbl-scroll .lb-tbl-c0 {
    position: sticky;
    left: 0;
    z-index: 2;
    border-right: 1px solid transparent;
    animation: lb-tbl-seam linear both;
    animation-timeline: --lbtbl;
    animation-range: 0 24px;
}
.lb-tbl-scroll thead .lb-tbl-c0 { z-index: 3; }
@keyframes lb-tbl-seam {
    from { border-right-color: transparent; }
    to   { border-right-color: var(--tbl-line); }
}

/* Column headers — the design's uppercase eyebrow band. Capitals do cost width
   on the row that helps decide the columns, so the size is a touch smaller than
   the body and the band is allowed to wrap onto two lines rather than push a
   column wider. */
table.lb-tbl-grid thead th,
table.lb-tbl-grid tr.lb-tbl-hd > th {
    padding-top: 11px;
    padding-bottom: 11px;
    background: var(--tbl-head-bg);
    font-family: var(--font-eyebrow);
    font-size: 11.5px;
    font-weight: 800;
    letter-spacing: .07em;
    line-height: 1.35;
    text-transform: uppercase;
    color: var(--teal-ink);
}
table.lb-tbl-grid thead tr:last-child > * { border-bottom: 1px solid var(--tbl-line); }
table.lb-tbl-grid thead tr + tr > * { border-top: 1px solid var(--tbl-line-soft); }
/* A header that groups several leaves below it sits over their middle. */
table.lb-tbl-grid thead th[colspan] { text-align: center; }

/* Full-width rows. All three come from the grid's shape, not from a guess
   about what the table is "about". */
table.lb-tbl-grid tr.lb-tbl-band > td,
table.lb-tbl-grid tr.lb-tbl-sub > td {
    padding: 9px var(--tbl-edge);
    font-family: var(--font-eyebrow);
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: .02em;
    color: var(--teal-ink);
}
table.lb-tbl-grid tr.lb-tbl-band > td { background: var(--tbl-band-bg); }
table.lb-tbl-grid tr.lb-tbl-sub > td { background: var(--tbl-subhead-bg); }
/* A band that is a whole sentence is a preamble, not a heading. */
table.lb-tbl-grid tr.lb-tbl-lede > td {
    padding: 11px var(--tbl-edge);
    background: var(--tbl-row);
    font-size: 13px;
    line-height: 1.55;
    color: var(--muted);
}
/* A trailing note, lifted out of the grid so it reads at prose width instead
   of inheriting the widest column's measure. */
.lb-tbl-note {
    padding: 11px var(--tbl-edge) 14px;
    border-top: 1px solid var(--tbl-line);
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--muted);
}

/* Row label — display face, no marker. Stamped by the renderer only where the
   column really reads as a label (median cell ≤ 32 characters), and carrying
   no width declaration at all: the browser sizes it from its content. */
.lb-tbl-label {
    font-family: var(--font-eyebrow);
    font-size: var(--tbl-label-fs);
    font-weight: 700;
    line-height: 1.4;
    color: var(--ink-warm);
}
.lb-tbl-label strong { font-weight: 700; }
table.lb-tbl-grid strong { color: var(--ink-warm); font-weight: 700; }

/* THE number rule — every figure, in the serif face with tabular figures. */
.lb-tbl-num,
.lb-tbl-n {
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
}
.lb-tbl-num { white-space: nowrap; }
/* A column of nothing but figures reads down the decimal, so it aligns right —
   header included, or the label would sit over the wrong edge. */
table.lb-tbl-grid td.lb-tbl-n,
table.lb-tbl-grid th.lb-tbl-n { text-align: right; }
.lb-tbl-ord {
    display: inline-block;
    min-width: 2ch;
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
    color: var(--muted-2);
}

/* Prose resets FIRST, so the cell-interior rules below can override them.
   Almost every table renders inside a .lb-prose block, whose list/paragraph
   rules would otherwise win on specificity (`.lb-prose ul` = 0,1,1) and push
   the bullets 22px in with 6px margins around each item. */
.lb-tbl p,
.lb-card .lb-tbl p,
.ksb-card-body .lb-tbl p,
.quiz-question .lb-tbl p {
    margin: 0;
    font: inherit;
    color: inherit;
}
.lb-tbl p + p { margin-top: 6px; }
.lb-tbl ul,
.lb-tbl ol { margin: 0; padding: 0; list-style: none; }
.lb-tbl li { margin: 0; }

/* Cell interiors. The bullet decision is per cell: a column of one-liners is
   no longer bulleted just because one sibling cell happens to be a list.
   Every selector here is written `.lb-tbl .lb-tbl-*` (0,2,0) so it outranks
   the `.lb-tbl ol` reset above (0,1,1) — otherwise the reset's `padding: 0`
   pushes an <ol>'s markers outside the cell and they render as stray dots. */
.lb-tbl-ln { display: block; }
.lb-tbl-ln + .lb-tbl-ln { margin-top: 4px; }
.lb-tbl .lb-tbl-ul { margin: 0; padding: 0; list-style: none; }
/* A drawn dot, not a "•" glyph: at 13px the glyph sits on the baseline and
   reads as a stray full stop next to the first word. */
.lb-tbl .lb-tbl-ul > li { position: relative; padding-left: 13px; }
.lb-tbl .lb-tbl-ul > li::before {
    content: "";
    position: absolute;
    left: 1px;
    top: .55em;
    width: 4.5px;
    height: 4.5px;
    border-radius: 50%;
    /* Cool dot: on the teal-washed rows a sand-coloured bullet went muddy. */
    background: #8FC0BE;
}
.lb-tbl .lb-tbl-ul > li + li { margin-top: var(--tbl-gap); }
/* Numbered cells keep the author's own numbering (the order carries meaning),
   emitted as a real <ol start="N"> so nothing is renumbered. */
.lb-tbl .lb-tbl-ol { list-style: decimal; padding-left: 2.4ch; }
.lb-tbl .lb-tbl-ol > li { position: static; padding-left: 3px; }
.lb-tbl .lb-tbl-ol > li::before { content: none; }
.lb-tbl .lb-tbl-ol > li::marker {
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
    color: var(--muted-2);
}

/* Empty cell — the design's em dash, and only where the gap is real (a cell
   with content below it), never on a ragged tail. */
.lb-tbl-e > i { font-style: normal; color: #C6C0B4; }

/* ── List path · a list that was written as a table ────────────────────────
   34 tables carry one content column. They keep the card and its header bar,
   and drop the grid entirely — no rules, no stripes, no scroll container. */
.lb-tbl-list { padding: var(--tbl-pad-y) var(--tbl-edge); }
.lb-tbl-hoist,
.lb-tbl-list .lb-tbl-sub {
    margin: 15px 0 6px;
    font-family: var(--font-eyebrow);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .02em;
    color: var(--teal-ink);
}
.lb-tbl-hoist { margin: 0; padding: 13px var(--tbl-edge) 0; }
.lb-tbl-list > :first-child { margin-top: 0; }
.lb-tbl .lb-tbl-list ul,
.lb-tbl .lb-tbl-list ol { max-width: 68ch; font-size: var(--tbl-fs); color: var(--body-warm); }
.lb-tbl .lb-tbl-list li { line-height: 1.55; margin: 0 0 6px; }
.lb-tbl .lb-tbl-list li:last-child { margin-bottom: 0; }
.lb-tbl .lb-tbl-list ul { list-style: none; }
.lb-tbl .lb-tbl-list ul > li { position: relative; padding-left: 16px; }
.lb-tbl .lb-tbl-list ul > li::before {
    content: "";
    position: absolute;
    left: 2px;
    top: .58em;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: #8FC0BE;
}
/* Real <ol start="N">, so an excerpt that begins at 4 still begins at 4. */
.lb-tbl .lb-tbl-list ol { list-style: decimal; padding-left: 2.6ch; }
.lb-tbl .lb-tbl-list ol > li { padding-left: 4px; }
.lb-tbl .lb-tbl-list ol > li::marker {
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
    color: var(--teal-ink);
}
.lb-tbl-intro { margin: 0 0 6px; font-size: var(--tbl-fs); color: var(--body-warm); }
/* Short items in a long list otherwise leave most of the measure empty. */
@media (min-width: 561px) {
    .lb-tbl .lb-tbl-list[data-twoup] ul,
    .lb-tbl .lb-tbl-list[data-twoup] ol { columns: 2; column-gap: 30px; }
    .lb-tbl .lb-tbl-list[data-twoup] li { break-inside: avoid; }
}

/* Card chrome shrinks with the viewport. One density step — with the width
   machinery gone there is nothing else left that is keyed on width. */
@media (max-width: 767px) {
    /* One step down for EVERY card block at once (header, table edge, quiz,
       comparison) — they must keep sharing one inset on phones too. */
    .lesson { --lb-card-pad-x: 16px; }
    .lb-tbl {
        border-radius: 18px;
        --tbl-fs: 12.5px;
        --tbl-label-fs: 13.5px;
        --tbl-pad-y: 10px;
        --tbl-pad-x: 13px;
    }
    table.lb-tbl-grid thead th,
    table.lb-tbl-grid tr.lb-tbl-hd > th { font-size: 10.5px; letter-spacing: .05em; }
    /* Only here is hyphenation worth its cost: it lets a column fall below its
       longest word, which keeps 57 tables from scrolling on a phone. At reading
       width it starves columns instead, so it stays off above this breakpoint.
       lang="ro" on the table feeds the dictionary. */
    table.lb-tbl-grid th,
    table.lb-tbl-grid td { hyphens: auto; }
}

/* ── Lists (numbered / check / dot) ──────────────────────────────────────
   A card in the same family as the table and comparison blocks: teal header
   bar with the illustrated glyph, items separated by hairlines. Lists are the
   most frequent block of the three (434 of them, ~3 per lesson), which is why
   the shared --cardhead-* sizes matter most here — at the old 52px/19px header
   a lesson turned into a stack of teal bars. */
.lb-list {
    background: var(--card);
    border: 1.5px solid var(--teal-3);
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 8px 24px rgba(14, 124, 123, .10);
}
.lb-list-head {
    display: flex;
    align-items: center;
    gap: var(--cardhead-gap);
    padding: var(--cardhead-pad-y) var(--lb-card-pad-x, 20px);
    background: var(--teal-3);
}
.lb-list-ico {
    flex: none;
    width: var(--cardhead-ico);
    height: var(--cardhead-ico);
    border-radius: 50%;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
}
.lb-list-ico svg { width: var(--cardhead-svg); height: var(--cardhead-svg); }
.lb-list-title {
    font-family: var(--font-eyebrow);
    font-size: var(--cardhead-title);
    font-weight: 700;
    line-height: 1.3;
    color: #FFFFFF;
}
/* A list with no title keeps the card but drops the bar. */
.lb-list--bare { border-radius: 14px; }

.lb-list-body {
    list-style: none;
    margin: 0;
    padding: 4px var(--lb-card-pad-x, 20px);
}
/* Badges sit on the OPTICAL centre of the first text line, not on its baseline:
   `align-items: baseline` lined up the digit's baseline with the prose baseline,
   which leaves a round badge visibly low, and the translateY nudges that used to
   patch it were per-badge guesses. Centring on the line box is exact — hence the
   shared --lb-list-line below, the height of one .lb-list-content line. */
.lb-list-item {
    display: flex;
    gap: 11px;
    align-items: flex-start;
    padding: 11px 0;
    --lb-list-line: calc(14.5px * 1.62);
}
.lb-list-item + .lb-list-item { border-top: 1px solid var(--divider); }
.lb-list-content {
    flex: 1;
    min-width: 0;
    font-size: 14.5px;
    line-height: 1.62;
    color: var(--body-warm);
}
.lb-list-content strong { color: var(--ink); font-weight: 650; }
.lb-list-content p { margin: 0; font: inherit; color: inherit; }

.lb-list-badge {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
/* numbered → teal disc, centred on the first text line */
.lb-list--num .lb-list-badge,
.lb-list-badge.lb-list-num {
    width: 20px;
    height: 20px;
    border-radius: 999px;
    background: var(--teal);
    color: #fff;
    font-family: var(--font-eyebrow);
    font-size: 11.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    margin-top: calc((var(--lb-list-line) - 20px) / 2);
}
/* check → green glyph, no background */
.lb-list-check {
    width: 20px;
    height: 20px;
    color: var(--success);
    margin-top: calc((var(--lb-list-line) - 20px) / 2);
}
.lb-list-check svg { stroke: currentColor; }
/* dot → small teal bullet */
.lb-list-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--teal-2);
    margin-top: calc((var(--lb-list-line) - 6px) / 2);
}

/* ── Figure / image ────────────────────────────────────────────────────── */
/* The card wraps the WHOLE figure — image, source link and caption — so the
   "Ce observi / Ce sugerează" description reads as belonging to the photo. It
   used to sit on the page canvas below a self-contained white image card, which
   made it look like ordinary prose that happened to follow an image. */
.lb-figure {
    margin: 0;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
}
.lb-figure-stage {
    padding: 14px;
    text-align: center;
}
.lb-image {
    max-width: 100%;
    border-radius: 8px;
    display: block;
    margin: 0 auto;
}
/* Clickable image → lightbox */
.lb-image-btn {
    position: relative;
    display: block;
    width: fit-content;
    max-width: 100%;
    margin: 0 auto;
    padding: 0;
    border: 0;
    background: none;
    cursor: zoom-in;
    border-radius: 8px;
}
.lb-image-btn:focus-visible {
    outline: 2px solid var(--teal);
    outline-offset: 3px;
}
.lb-image-zoom {
    position: absolute;
    right: 8px;
    bottom: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    background: rgba(15, 34, 48, .62);
    color: #fff;
    opacity: 0;
    transition: opacity .15s ease;
    pointer-events: none;
}
.lb-image-btn:hover .lb-image-zoom,
.lb-image-btn:focus-visible .lb-image-zoom { opacity: 1; }
.lb-image-ph {
    border: 1px dashed var(--border-2);
    border-radius: 8px;
    color: var(--muted);
    padding: 40px;
    text-align: center;
    font-size: 13.5px;
}
/* Caption panel: the card stays one white surface throughout; only a hairline
   marks where the image area ends. Left-aligned — the stage above centres the
   photo, but running text must not inherit that. */
.lb-figure-cap {
    margin: 0;
    padding: 12px 16px 14px;
    border-top: 1px solid var(--divider);
    text-align: left;
}
.lb-figure-title {
    font-family: var(--font-eyebrow);
    font-size: 13px;
    font-weight: 700;
    line-height: 1.35;
    color: var(--ink);
}
/* Every caption is a "Ce observi / Ce sugerează" list; give it the same teal
   bullet as .lb-list instead of the browser's default disc. */
.lb-figure-cap ul {
    margin: 7px 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.lb-figure-cap li {
    position: relative;
    padding-left: 15px;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--body-warm);
}
.lb-figure-cap li::before {
    content: "";
    position: absolute;
    left: 0;
    top: .62em;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--teal-2);
}
.lb-figure-cap li strong { color: var(--ink); font-weight: 650; }
.lb-figure-cap p {
    margin: 7px 0 0;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--body-warm);
}
/* Sits inside the stage, directly under the image */
.lb-figure-src {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--teal);
    display: inline-block;
    margin-top: 10px;
    text-decoration: none;
}
.lb-figure-src:hover { text-decoration: underline; }

/* ── Image pair (two images that follow each other = a comparison) ─────── */
/* See `_render_image_pair`. The two cards are ordinary .lb-figure elements —
   only their geometry is overridden here, so restyling the single figure
   restyles these. Two things make the pair actually comparable: (1) the caption
   hairline lands at the same height on both, (2) both photos occupy the same
   box. At half the column a fine detail does get small; the stage stays a
   lightbox button, which is how a lone figure is zoomed too. */
.lb-figure-pair {
    /* 3:2 is the aspect most of the paired photos are already shot at, so it is
       the ratio that letterboxes least. It is a RATIO rather than a height on
       purpose: it scales with the column, and — because lesson images are lazy
       and carry no width/height (see initTailRoom in lesson.js) — it reserves
       the space before the photo arrives, so the pair does not jump on load. */
    --lb-pair-ratio: 3 / 2;
    display: grid;
    grid-template-columns: 1fr 1fr;
    /* (1) One stage row + one caption row for BOTH cards, so the hairline under
       the photos sits at the same height even when one caption is longer or one
       image carries no "sursa" link. Subgrid is the only thing that aligns rows
       ACROSS siblings; where it is unsupported the declaration is dropped and
       each card is simply its own two-row grid, which still reads fine. */
    grid-template-rows: auto 1fr;
    gap: 12px;
    align-items: stretch;
}
.lb-figure-pair .lb-figure {
    display: grid;
    grid-row: span 2;
    grid-template-rows: subgrid;
    min-width: 0;
}
.lb-figure-pair .lb-figure-stage { padding: 12px; }
.lb-figure-pair .lb-image-btn { width: 100%; }
/* (2) Both photos get the same box. `contain`, never `cover` — cropping a chest
   X-ray or an ECG can cut out the finding the pair exists to show, so an image
   whose aspect differs letterboxes rather than loses its edges. */
.lb-figure-pair .lb-image {
    width: 100%;
    aspect-ratio: var(--lb-pair-ratio);
    object-fit: contain;
    background: var(--surface-3);
}
.lb-figure-pair .lb-image-ph {
    aspect-ratio: var(--lb-pair-ratio);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 16px;
}
/* Below this each card is under ~260px wide, which is narrower than these photos
   are useful. Stack them and hand each its natural aspect back — a stacked pair
   is then exactly two ordinary figures, which is the right fallback. */
@media (max-width: 620px) {
    .lb-figure-pair { grid-template-columns: 1fr; }
    .lb-figure-pair .lb-figure { grid-row: auto; grid-template-rows: none; }
    .lb-figure-pair .lb-image { aspect-ratio: auto; max-height: 300px; }
    .lb-figure-pair .lb-image-ph { aspect-ratio: auto; }
}

/* ── Image lightbox ────────────────────────────────────────────────────── */
.lb-lightbox {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 32px;
    background: rgba(15, 34, 48, .58);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    opacity: 0;
    transition: opacity .18s ease;
    cursor: zoom-out;
}
.lb-lightbox.is-open { opacity: 1; }
.lb-lightbox img {
    max-width: min(1100px, 100%);
    max-height: calc(100vh - 140px);
    object-fit: contain;
    border-radius: 12px;
    background: #fff;
    box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
    transform: scale(.97);
    transition: transform .18s ease;
    cursor: default;
}
.lb-lightbox.is-open img { transform: scale(1); }
.lb-lightbox-cap {
    max-width: min(1100px, 100%);
    color: #fff;
    font-size: 13.5px;
    font-weight: 600;
    text-align: center;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .4);
}
.lb-lightbox-close {
    position: absolute;
    top: 18px;
    right: 18px;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, .16);
    color: #fff;
    cursor: pointer;
    transition: background .15s ease;
}
.lb-lightbox-close:hover { background: rgba(255, 255, 255, .3); }
body.lb-lightbox-open { overflow: hidden; }

@media (max-width: 640px) {
    .lb-lightbox { padding: 16px; }
    .lb-lightbox img { max-height: calc(100vh - 120px); }
}

/* ── Outro: end-of-lesson next steps ───────────────────────────────────── */
.lesson-outro {
    margin-top: 40px;
    padding-top: 26px;
    border-top: 1px solid var(--divider);
}
.lesson-outro-title {
    font-family: var(--font-serif);
    font-size: 19px;
    font-weight: 600;
    color: var(--ink);
}
.lesson-outro-sub {
    margin-top: 5px;
    font-size: 13.5px;
    color: var(--muted);
}
/* The two buttons size to their own labels and sit at the start of the measure.
   They used to be a `repeat(auto-fit, minmax(220px, 1fr))` grid, which stretched
   both to half the column — on a wide reading column that is a pair of banners
   with a word floating in the middle of each, and the width carried no meaning:
   nothing about "Lecția următoare" is 500px long. Content width is the honest
   size, and it is what makes the block read the same at any column width. */
.lesson-outro-actions {
    margin-top: 18px;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}
.lesson-outro-btn {
    height: 48px;
    padding: 0 22px;
    border-radius: 11px;
    font-size: 14.5px;
    gap: 11px;
}
.lesson-outro-btn svg { flex: none; }
/* The lead action carries an object icon, so it wears the same white disc as
   every other icon in the app (.lb-card-ico / .quiz-head-ico / .lb-tbl-ico) and
   is sized from the same two tokens. The arrow on "Lecția următoare" stays bare
   — it is a direction, not an object, and a disc would read as a second action. */
.lesson-outro-ico {
    flex: none;
    width: var(--cardhead-ico);
    height: var(--cardhead-ico);
    margin-left: -8px;          /* the disc is heavier than a glyph; pull it back
                                   toward the edge so the label stays centred */
    border-radius: 50%;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
}
.lesson-outro-ico svg { width: var(--cardhead-svg); height: var(--cardhead-svg); }
/* On a phone the pair becomes one per row at full width — there a button is a
   thumb target, and the label is no longer competing with a wide column. */
@media (max-width: 560px) {
    .lesson-outro-actions { display: grid; grid-template-columns: 1fr; }
    .lesson-outro-btn { width: 100%; }
    /* Full width centres icon + label as one group, so the optical nudge that
       balanced the disc against the button's left edge would now just push that
       group off centre. */
    .lesson-outro-ico { margin-left: 0; }
}
.lesson-outro-nextname {
    margin-top: 10px;
    font-size: 12.5px;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Calculator ────────────────────────────────────────────────────────── */
.lb-calc {
    /* Same gutter as the rest of the card family, so the icon discs line up. */
    --lb-calc-pad-x: var(--lb-card-pad-x, 24px);
    border-radius: var(--r-md);
    overflow: hidden;
    border: 1px solid var(--teal-border);
}
/* Header bar in the shared card family — same --cardhead-* tokens, icon disc and
   eyebrow title as .lb-tbl-head / .quiz-head / .lb-list-head. */
.lb-calc-head {
    display: flex;
    align-items: center;
    gap: var(--cardhead-gap);
    padding: var(--cardhead-pad-y) var(--lb-calc-pad-x);
    background: var(--teal-3);
}
.lb-calc-ico {
    width: var(--cardhead-ico);
    height: var(--cardhead-ico);
    flex: none;
    border-radius: 50%;
    background: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
}
.lb-calc-ico svg { width: var(--cardhead-svg); height: var(--cardhead-svg); }
.lb-calc-title {
    font-family: var(--font-eyebrow);
    font-size: var(--cardhead-title);
    font-weight: 700;
    line-height: 1.3;
    color: #FFFFFF;
}
.lb-calc-body {
    padding: var(--lb-calc-pad-x);
    background: #fff;
}
.lb-calc-inputs {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    align-items: flex-end;
}
.lb-calc-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.lb-calc-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--slate);
}
.lb-calc-input {
    height: 36px;
    border: 1px solid var(--border-2);
    border-radius: 9px;
    padding: 0 11px;
    font-family: var(--font-mono);
    font-size: 13.5px;
    color: var(--ink-warm);
    background: #fff;
    outline: none;
    box-sizing: border-box;
}
.lb-calc-input:focus {
    border-color: var(--teal);
    box-shadow: 0 0 0 3px rgba(14, 124, 123, 0.12);
}
.lb-calc-seg {
    display: inline-flex;
    background: var(--surface);
    border-radius: 9px;
    padding: 3px;
    gap: 2px;
}
.lb-calc-seg-btn {
    padding: 6px 12px;
    border-radius: 7px;
    font-family: var(--font-body);
    font-size: 13px;
    font-weight: 600;
    color: var(--muted);
    background: none;
    border: none;
    cursor: pointer;
}
.lb-calc-seg-btn.is-active {
    background: #fff;
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(28, 27, 25, 0.08);
}
.lb-calc-seg--wrap { flex-wrap: wrap; }
/* Yes/no criterion: the question fills the row, Da/Nu sits at the end. Both
   answers are visible and neither is pre-given — an unticked checkbox reads as
   "nu" even when nobody has read the question yet. Wells has ten of these and
   Caprini thirty-seven, so the row stays tight. */
.lb-calc-field--bool {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 34px;
}
.lb-calc-field--bool .lb-calc-label {
    font-weight: 500;
    line-height: 1.35;
}
.lb-calc-seg--bool {
    flex: none;
    align-self: center;
}
.lb-calc-seg--bool .lb-calc-seg-btn {
    padding: 5px 14px;
    min-width: 46px;
}
@media (max-width: 560px) {
    .lb-calc-field--bool { flex-direction: column; align-items: flex-start; gap: 6px; }
}
/* Score calculators: stack the (many, longer-labelled) items vertically. */
.lb-calc-inputs--stack {
    flex-direction: column;
    align-items: stretch;
    gap: 9px;
}
.lb-calc-inputs--stack .lb-calc-field { width: 100%; }
/* Checkbox item (point-scored yes/no criterion): checkbox + label on one row. */
.lb-calc-field--check {
    flex-direction: row;
    align-items: center;
    gap: 9px;
    cursor: pointer;
}
/* The same control the grile session uses for a complement-multiplu option
   (`.sim-opts--multi .sim-opt-ctl`): a rounded square that fills with teal and shows a
   white tick, on the same .15s ease. Scaled down from 22px to 18px — a criterion here sits
   in a list of ten, not on a full-width answer row, so the grile size would shout.
   `appearance: none` is what makes it ours; the element stays a real checkbox, so the
   label, the keyboard and the change event are untouched. Radius, border weight and tick
   are proportional to grile's 7px / 1.8px / 13px. */
.lb-calc-check {
    --lb-check-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
    box-sizing: border-box;
    margin: 0;
    border: 1.8px solid var(--muted-4);
    border-radius: 6px;
    background: var(--card);
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}
.lb-calc-check::after {
    content: "";
    position: absolute;
    inset: 0;
    background-color: #FFFFFF;
    /* lucide "check", the same path and stroke weight the session draws as an inline SVG.
       A mask, not a background image, so the tick takes its colour from the box. */
    -webkit-mask: var(--lb-check-mask) center / 11px 11px no-repeat;
    mask: var(--lb-check-mask) center / 11px 11px no-repeat;
    opacity: 0;
    transform: scale(.6);
    transition: opacity .15s ease, transform .15s ease;
}
.lb-calc-check:checked {
    background: var(--teal);
    border-color: var(--teal);
}
.lb-calc-check:checked::after { opacity: 1; transform: scale(1); }
.lb-calc-check:hover { border-color: var(--teal); }
.lb-calc-check:focus-visible {
    outline: 2px solid var(--teal);
    outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
    .lb-calc-check, .lb-calc-check::after { transition: none; }
}
.lb-calc-field--check .lb-calc-label { font-weight: 500; }
/* Select item (graded option with per-answer points). */
.lb-calc-select {
    height: 36px;
    max-width: 320px;
    border: 1px solid var(--border-2);
    border-radius: 9px;
    padding: 0 11px;
    font-family: var(--font-body);
    font-size: 13.5px;
    color: var(--ink-warm);
    background: #fff;
    outline: none;
    box-sizing: border-box;
    cursor: pointer;
}
.lb-calc-select:focus {
    border-color: var(--teal);
    box-shadow: 0 0 0 3px rgba(14, 124, 123, 0.12);
}
/* Scale calculators (ECOG, Karnofsky, …): pick exactly one level. Each row is
   label + defining description, so rows are full-width and wrap — Karnofsky has
   11 of them and CFS descriptions run to a sentence. */
.lb-calc-scale {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.lb-calc-scale-btn {
    display: flex;
    align-items: baseline;
    gap: 10px;
    width: 100%;
    text-align: left;
    padding: 9px 12px;
    border: 1px solid var(--border-2);
    border-radius: 9px;
    background: #fff;
    font-family: var(--font-body);
    cursor: pointer;
}
.lb-calc-scale-btn:hover { border-color: var(--teal-border); }
.lb-calc-scale-btn:focus-visible {
    outline: none;
    border-color: var(--teal);
    box-shadow: 0 0 0 3px rgba(14, 124, 123, 0.12);
}
.lb-calc-scale-btn.is-active {
    border-color: var(--teal);
    background: rgba(14, 124, 123, 0.06);
}
.lb-calc-scale-val {
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 700;
    color: var(--teal);
}
.lb-calc-scale-desc {
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-warm);
}
/* Stacked (score) layout: graded items carry long labels — let the select use the row. */
.lb-calc-inputs--stack .lb-calc-select { max-width: 100%; width: 100%; }
/* Result footer. It bleeds to the card edges and reads as the closing band of the card:
   the figure on the left, and one text column beside it carrying what the figure MEANS
   and what to DO about it. JS sets --band (the band's colour) and data-state here.

   Until there is a result it is indistinguishable from the card body — same white, and
   its rule is the card's own border, not a second, louder one. A result tints it, and the
   tint is deliberately faint: the band colour carries far better on the verdict text than
   as a wash behind it, so a 1px hairline plus ~7% is all it takes to read as "this band". */
/* The compute row. Sits between the fields and the result, and disappears once
   a score is on screen — the inputs cannot have changed since, so pressing it
   again could only produce the same number. Any edit brings it back. */
.lb-calc-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 16px;
}
/* `display: flex` outranks the user-agent's `[hidden] { display: none }`, so
   without these two the strip and the button both stayed on screen while the
   script believed it had hidden them — a stale score sitting under a Calculează
   button, which is the exact pairing this feature exists to prevent. */
.lb-calc-actions[hidden],
.lb-calc-result[hidden] { display: none; }
.lb-calc-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 38px;
    padding: 0 22px;
    border: none;
    border-radius: var(--r-pill);
    background: var(--teal);
    color: #fff;
    font-family: var(--font-body);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s ease;
}
.lb-calc-btn:hover:not(:disabled) { background: var(--teal-hover); }
.lb-calc-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(14, 124, 123, 0.22);
}
.lb-calc-btn:disabled {
    background: var(--surface);
    color: var(--muted);
    cursor: not-allowed;
}
.lb-calc-hint {
    font-size: 12.5px;
    color: var(--muted);
}
@media (prefers-reduced-motion: reduce) {
    .lb-calc-btn { transition: none; }
}

.lb-calc-result {
    --band: #C9C5BD;
    margin: 18px calc(var(--lb-calc-pad-x) * -1) calc(var(--lb-calc-pad-x) * -1);
    padding: 14px var(--lb-calc-pad-x);
    border-top: 1px solid var(--teal-border);
    background: #fff;
    display: flex;
    align-items: center;
    gap: 16px;
}
.lb-calc-result[data-state="filled"] {
    border-top-color: color-mix(in srgb, var(--band) 55%, var(--teal-border));
    background: color-mix(in srgb, var(--band) 7%, #fff);
}
.lb-calc-result-main {
    display: flex;
    align-items: baseline;
    gap: 5px;
    flex: none;
}
/* The column that fills the gap the right-aligned verdict used to leave empty. */
.lb-calc-text {
    min-width: 0;
    flex: 1;
}
/* On a phone the figure beside the text leaves the column ~120px, which shreds a two-line
   conduită into a ragged strip — a long unit like "mL/min/1.73m²" is enough on its own.
   Stacking is right here and only here: there is no empty space left to fill. */
@media (max-width: 560px) {
    .lb-calc-result {
        flex-direction: column;
        align-items: flex-start;
        gap: 7px;
    }
}
.lb-calc-value {
    font-family: var(--font-body);
    font-variant-numeric: tabular-nums;
    font-size: 30px;
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.02em;
    color: var(--ink);
}
.lb-calc-result[data-state="empty"] .lb-calc-value { color: var(--muted-2); }
.lb-calc-unit {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--muted-2);
}
/* The verdict is where the band colour lives now that the left rule is gone. Only 40% of it
   though: the mid band is a soft gold (#E4C465) that turns illegible against its own tint
   at anything higher, and the tint + hairline already say which band this is. */
.lb-calc-verdict {
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.35;
    color: color-mix(in srgb, var(--band) 40%, var(--ink));
}
.lb-calc-result[data-state="empty"] .lb-calc-verdict {
    font-weight: 500;
    color: var(--muted);
}
/* Conduită: what the number means you should DO. It sits under the verdict inside the same
   column — no divider and no full-width row of its own, so a two-line footer stays as tall
   as the figure beside it instead of doubling the card's closing band. */
.lb-calc-action {
    margin-top: 3px;
    display: flex;
    gap: 7px;
    align-items: baseline;
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-warm);
}
.lb-calc-action-lbl {
    flex: none;
    font-family: var(--font-eyebrow);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted-2);
}
.lb-calc-action-txt { min-width: 0; }
/* `display: flex` above outranks the `hidden` attribute, so the empty state kept showing a
   bare "CONDUITĂ" kicker with nothing after it. */
.lb-calc-action[hidden] { display: none; }
.lb-calc--soon { border-style: dashed; }
.lb-calc-soon-msg {
    padding: var(--lb-calc-pad-x);
    color: var(--muted);
    font-size: 14px;
}

/* ============================================================================
   3. QUIZ CARDS  (Lecție inline + Grile tab)
   ========================================================================== */

/* Quiz (grilă) — "Quiz header redesign" (5a): teal header with a drawn check-square
   icon; graded answers follow the four-state colour code (teal / gold / red /
   neutral) and drop every explanation into one "Explicații" block below. */
.quiz {
    margin: 0;
    /* one bottom gutter for the card, whichever of options / explanations /
       "Verifică" happens to end it */
    padding-bottom: 24px;
    background: #fff;
    border: 1.5px solid #3D9694;
    border-radius: 22px;
    box-shadow: 0 8px 24px rgba(14, 124, 123, .10);
    overflow: hidden;
}

.quiz-head {
    background: #3D9694;
    padding: var(--cardhead-pad-y) var(--lb-card-pad-x, 24px);
    display: flex;
    align-items: center;
    gap: var(--cardhead-gap);
}
.quiz-head-ico {
    flex: none;
    width: var(--cardhead-ico); height: var(--cardhead-ico);
    border-radius: 50%; background: #fff;
    display: flex; align-items: center; justify-content: center;
}
.quiz-head-ico svg { width: var(--cardhead-svg); height: var(--cardhead-svg); }
.quiz-head-main { flex: 1; min-width: 0; }
.quiz-head-label { font-family: var(--font-eyebrow); font-size: var(--cardhead-title); font-weight: 700; color: #fff; }
/* Counter — same shape and the same title face as the session card's
   (.sim-counter), so a grilă looks the same wherever it is shown. */
.quiz-head-counter {
    flex: none; background: rgba(255, 255, 255, .2); border-radius: var(--r-pill);
    padding: 3px 11px; font-family: var(--font-serif); font-size: 12.5px; font-weight: 600;
    font-variant-numeric: tabular-nums; color: #fff;
}

.quiz-question { padding: 22px var(--lb-card-pad-x, 24px) 0; }
.quiz-question .lb-prose p,
.quiz-question p { margin: 0; font-size: 16px; line-height: 1.6; font-weight: 600; color: var(--ink-warm); }

.quiz-opts { padding: 18px var(--lb-card-pad-x, 24px) 0; display: flex; flex-direction: column; gap: 7px; }
.quiz-opt {
    width: 100%; text-align: left;
    display: flex; align-items: center; gap: 11px;
    border: 1px solid #ECE7DB; border-radius: 13px; padding: 9px 13px; background: #fff;
    cursor: pointer; transition: all .18s ease;
}
.quiz:not(.is-answered) .quiz-opt:hover { border-color: #2FA8A6; background: #FBF6E8; }
.quiz-opt-letter {
    flex: none; width: 25px; height: 25px; border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-eyebrow); font-weight: 700; font-size: 12.5px;
    background: #F0EEE9; color: #8A867E;
}
.quiz-opt-text { flex: 1; min-width: 0; font-size: 14px; line-height: 1.5; color: var(--ink); }
.quiz-opt-text p { margin: 0; }

/* multi-answer: a picked (pre-verify) option */
.quiz-opt.is-chosen { border-color: var(--teal); background: var(--teal-tint); }
.quiz-opt.is-chosen .quiz-opt-letter { background: var(--teal); color: #fff; }

/* ── Answered ────────────────────────────────────────────────
   The colour code shared with a grile session (see --miss in tokens.css):
   teal = correct and you picked it, gold = correct but you missed it, red =
   wrong AND you picked it. A wrong option you left alone keeps its neutral row
   — colour is spent only on what concerns your answer, so three coloured rows
   in a five-option grid read faster than five. What YOU chose is a second
   channel on top: `.is-picked` keeps the teal hover ring around the row, so
   "teal outline = my pick" is one idea learned once. The explanations move to
   one block below, where each chip repeats the state. */
.quiz.is-answered .quiz-opt { cursor: default; }

.quiz-opt.is-correct {
    background: var(--teal-tint-3);
    border: 1.5px solid var(--teal);
    padding: 8.5px 12.5px;
}
.quiz-opt.is-correct .quiz-opt-letter { background: var(--teal); color: #fff; }
.quiz-opt.is-correct .quiz-opt-text { color: var(--teal-ink); font-weight: 600; }

/* correct, but you did not pick it */
.quiz-opt.is-missed {
    background: var(--miss-tint);
    border: 1.5px solid var(--miss);
    padding: 8.5px 12.5px;
}
.quiz-opt.is-missed .quiz-opt-letter { background: var(--miss); color: #fff; }
.quiz-opt.is-missed .quiz-opt-text { color: var(--miss-ink); font-weight: 600; }

.quiz-opt.is-wrong {
    background: var(--error-tint);
    border: 1.5px solid var(--error-2);
    padding: 8.5px 12.5px;
}
.quiz-opt.is-wrong .quiz-opt-letter { background: var(--error); color: #fff; }
.quiz-opt.is-wrong .quiz-opt-text { color: #8A3A2C; font-weight: 600; }

/* "you chose this one" — the hover ring, kept after grading */
.quiz-opt.is-picked {
    border: 2px solid var(--teal-2);
    padding: 8px 12px;
}

/* ── Explanations ─────────────────────────────────────────────
   One row per option, revealed together once graded — the grile session's
   block, same type and same chip colours, and the same four states the rows
   above wear, except that here the key is COMPLETE: teal = correct and you
   picked it, gold = correct and you missed it, red = wrong — picked or not.
   The neutral row belongs to the options; down here every chip is coloured, so
   true vs. false reads straight off one column of letters. */
.quiz-ex {
    margin: 18px var(--lb-card-pad-x, 24px) 0;
    border-top: 1px solid var(--divider);
    padding-top: 16px;
    animation: quizFade .35s ease both;
}
.quiz-ex-label {
    display: block;
    font-family: var(--font-serif);
    font-size: 17px;
    font-weight: 600;
    letter-spacing: -0.005em;
    color: var(--ink-warm);
    margin-bottom: 12px;
}
.quiz-ex-list { display: flex; flex-direction: column; gap: 12px; }
.quiz-ex-row { display: flex; gap: 11px; align-items: flex-start; }
.quiz-ex-chip {
    flex: none;
    width: 22px; height: 22px;
    margin-top: 1px;              /* optical centre of the first 14px/1.62 line */
    border-radius: 7px;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-eyebrow); font-size: 11.5px; font-weight: 700;
    /* neutral only as a fallback — grading tags all three states explicitly */
    background: #F0EEE9; color: #8A867E;
}
.quiz-ex-row.is-correct .quiz-ex-chip { background: var(--teal); color: #fff; }
.quiz-ex-row.is-missed  .quiz-ex-chip { background: var(--miss); color: #fff; }
.quiz-ex-row.is-wrong   .quiz-ex-chip { background: var(--error); color: #fff; }
/* same teal ring the option above wears when you picked it */
.quiz-ex-row.is-picked  .quiz-ex-chip { box-shadow: 0 0 0 2px var(--teal-2); }
.quiz-ex-text {
    flex: 1; min-width: 0;
    font-size: 14px; line-height: 1.62; color: var(--body-warm);
}
.quiz-ex-text p { margin: 0; }

.quiz-submit {
    margin: 18px var(--lb-card-pad-x, 24px) 0; height: 42px; padding: 0 22px;
    background: var(--teal); color: #fff; border: none; border-radius: var(--r-pill);
    font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer;
}
.quiz-submit:hover { background: var(--teal-hover); }

/* Grading failed (the verdict comes from the server) — the pick is kept, a click retries.
   Mirrors .sim-hint.is-error on the session screen. */
.quiz-hint {
    margin: 0 var(--lb-card-pad-x, 24px) 20px;
    font-size: 12.5px; line-height: 1.5;
    color: var(--error); font-weight: 600;
}

@keyframes quizFade { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .quiz-ex { animation: none; } }

/* ============================================================================
   4. FLASHCARDS

   From the "Farmaco si Biochimie (Sindromul anemic)" design handoff, the same
   file the Farmacologie / Biochimie panes came from — so the warm greys are
   declared once on `.fc` exactly as lesson-pages.css declares them on `.lp`
   (the app's own text colours are the blue-grey --muted family).

   ONE set of cards serves both views. `data-fc-mode` on `.fc` is the whole
   switch: it decides whether the group heads show, whether the column is a
   stack or a single card, and which half of a card's head is on. Nothing below
   duplicates a card, which is why a flip or a rating cannot differ between the
   two views.
   ========================================================================== */

.fc {
    /* The card column (660) + the gutter (22) + the navigator (230). Declared on the
       component rather than left to the pane, so the head's view switch lands over the
       navigator's right edge instead of drifting off to the end of a pane that has
       annexed the sidebar's width (the flashcards tab has no knowledge sidebar). */
    max-width: 912px;

    --fc-meta: #6F6C66;          /* the quiet second line */
    --fc-faint: #98948C;         /* hints, keycaps, counters */
    --fc-back: #EAF5F4;          /* the answer face */
    --fc-ans: #22443F;
    --fc-ans-strong: #0B3F3B;
    --fc-no-ink: #B24C3C;        /* "Nu știu" — warm red, distinct from --error */
    --fc-no-edge: #E7CFC7;
    --fc-nu-sq: #BF5B49;         /* the navigator's "de revăzut" fill */
    --fc-seen-bg: #FBF3DD;       /* seen but unmarked */
    --fc-seen-edge: #EDD9A8;
    --fc-seen-ink: #8A6D1F;
}

/* ---- Pane head: the view switch, and nothing else ---- */

.fc-head { display: flex; }
.fc-seg {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px;
    border-radius: var(--r-sm);
    background: var(--sand);
}
.fc-seg-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 31px;
    padding: 0 14px;
    border: 0;
    border-radius: var(--r-xs);
    background: transparent;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--muted);
    white-space: nowrap;
    cursor: pointer;
}
.fc-seg-btn.is-on {
    background: var(--card);
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(28, 27, 25, .08);
}

/* ---- The two-column body (card + navigator) ---- */

.fc-body {
    margin-top: 16px;
    display: flex;
    align-items: flex-start;
    gap: 22px;
    flex-wrap: wrap;
}
.fc-col {
    flex: 1 1 420px;
    min-width: 0;
    max-width: 660px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* Deck: every group collapses to nothing but the one visible card. Plain block
   layout on purpose — a flex column with `gap` would space the zero-height
   groups apart and leave the card floating in the middle of the column. */
.fc-list, .fc-group, .fc-group-cards { display: block; }
.fc-group-head { display: none; }

[data-fc-mode="all"] .fc-list {
    display: flex;
    flex-direction: column;
    gap: 30px;
}
/* "Toate" is for scanning, and it has no navigator to leave room for — so it takes the
   whole component width and lays the cards out as many-up as fit. A single 660px column
   (the handoff's) left half the pane empty on a desktop and made scanning 14 cards a long
   scroll. `start` because each card is sized to its own content: stretching them to the
   tallest in the row would put the dead space straight back. */
[data-fc-mode="all"] .fc-col { max-width: 100%; }
[data-fc-mode="all"] .fc-group-cards {
    margin-top: 12px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    align-items: start;
    gap: 16px;
}
[data-fc-mode="all"] .fc-group-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
}
.fc-group-name {
    font-family: var(--font-eyebrow);
    font-size: 13.5px;
    font-weight: 600;
    color: var(--teal);
}
.fc-group-count { font-size: 12px; color: var(--fc-faint); }

/* The whole right column belongs to the deck: in "Toate" there is no current
   card to navigate to, no run to finish and no arrow keys to advertise. */
[data-fc-mode="all"] .fc-panel,
[data-fc-mode="all"] .fc-keys,
[data-fc-mode="all"] .fc-done { display: none; }
[data-fc-mode="all"] .fc-col { flex-basis: 100%; }

/* ---- One card ---- */

.fc-card {
    background: var(--card);
    border: 1.5px solid var(--teal-3);
    border-radius: var(--r-lg);
    box-shadow: 0 8px 24px rgba(14, 124, 123, .10);
    overflow: hidden;
}
.fc-card[hidden] { display: none; }

/* Sized entirely from the shared --cardhead-* tokens, so this head is the same object
   as the one on a lesson table, a comparison card and a grile question card. The
   handoff drew a taller one (40px icon, serif title, a second line) — that was a
   third card-head dialect in an app that already has one. */
.fc-card-head {
    display: flex;
    align-items: center;
    gap: var(--cardhead-gap);
    padding: var(--cardhead-pad-y) 18px;
    background: var(--teal-3);
}
.fc-card-ico {
    flex: none;
    width: var(--cardhead-ico);
    height: var(--cardhead-ico);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--card);
}
.fc-card-ico svg { width: var(--cardhead-svg); height: var(--cardhead-svg); }
.fc-card-sec {
    flex: 1;
    min-width: 0;
    font-family: var(--font-eyebrow);
    font-size: var(--cardhead-title);
    font-weight: 700;
    line-height: 1.3;
    color: #fff;
    /* One line: a long section name (or the lesson title, which is the fallback when the
       pipeline has not tagged the card) truncates rather than pushing the counter out. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.fc-card-num {
    flex: none;
    padding: 3px 10px;
    border-radius: var(--r-pill);
    background: rgba(255, 255, 255, .18);
    font-size: 11px;
    font-weight: 500;
    color: #fff;
}
/* The stepper, in the head. Sized to sit inside the head's own line rather than to
   stretch it: the whole head is ~48px, so a 26px button and the 30px icon share one row. */
.fc-card-arrow {
    flex: none;
    width: 26px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .18);
    color: #fff;
    cursor: pointer;
    transition: background .15s ease;
}
.fc-card-arrow:hover:not(:disabled) { background: rgba(255, 255, 255, .34); }
.fc-card-arrow:disabled { opacity: .35; cursor: default; }
/* Nothing to step through in the grouped view — every card is already on screen. */
[data-fc-mode="all"] .fc-card-arrow { display: none; }
.fc-card-badge {
    flex: none;
    padding: 3px 10px;
    border-radius: var(--r-pill);
    font-size: 10.5px;
    font-weight: 700;
}
.fc-card-badge[hidden],
[data-fc-mode="deck"] .fc-card-badge { display: none; }
.fc-card-badge[data-rated="stiu"] { background: #fff; color: var(--teal-ink); }
.fc-card-badge[data-rated="nu_stiu"] { background: #F5E1DB; color: #9E4436; }

/* ---- The flip ---- */

.fc-card-flip {
    perspective: 1400px;
    cursor: pointer;
    /* Horizontal drags are the deck's swipe; vertical ones stay the page's scroll. */
    touch-action: pan-y;
}
.fc-card-flip:focus-visible { outline: 2px solid var(--teal); outline-offset: -4px; }
/* A flip needs both faces stacked, which needs a height in px — so `lesson.js` measures
   the two faces and sets it per card (`sizeCard`, clamped between --fc-h-min and
   --fc-h-max). The handoff's flat 340px was the same box for a one-line answer and a
   four-step list, and most cards are the one-line kind: the pane read as mostly air.
   The inline height is the ONLY thing the JS writes here; if it never runs, this
   fallback still gives a usable card. */
.fc-card-inner {
    --fc-h-min: 190px;
    --fc-h-max: 440px;
    position: relative;
    width: 100%;
    height: 260px;
    transform-style: preserve-3d;
    transition: transform .55s cubic-bezier(.35, .15, .25, 1);
}
.fc-card.is-flipped .fc-card-inner { transform: rotateY(180deg); }
.fc-card-face {
    position: absolute;
    inset: 0;
    box-sizing: border-box;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    display: flex;
    flex-direction: column;
}
/* Measuring pass: the faces stack in normal flow for one frame so each reports its own
   content height. Never painted — `sizeCard` adds and removes the class synchronously. */
.fc-card.is-measuring .fc-card-inner { height: auto; transform: none; transition: none; }
.fc-card.is-measuring .fc-card-face {
    position: static;
    transform: none;
    backface-visibility: visible;
    -webkit-backface-visibility: visible;
}
.fc-card.is-measuring .fc-card-a { overflow: visible; }
.fc-card-front {
    background: var(--card);
    padding: 24px 28px 40px;
    justify-content: center;
    overflow: auto;
}
.fc-card-q {
    font-family: var(--font-serif);
    font-size: 21px;
    line-height: 1.45;
    font-weight: 600;
    color: var(--ink-warm);
    text-wrap: pretty;
}
/* Absolute, and the front's 40px bottom padding is the room it sits in — that keeps it
   out of the measured content height, so the hint never stretches a card by its own
   line box. */
.fc-card-hint {
    position: absolute;
    left: 28px;
    bottom: 15px;
    font-size: 12px;
    color: var(--fc-faint);
}
.fc-card-back {
    transform: rotateY(180deg);
    background: var(--fc-back);
    padding: 20px 26px 18px;
}
/* The answer is centred in whatever room the rating row leaves. It scrolls rather than
   growing the card, which only bites past --fc-h-max — below that the card was sized to
   fit this exact content. */
.fc-card-a {
    flex: 1;
    min-height: 0;
    overflow: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    font-size: 15.5px;
    line-height: 1.6;
    color: var(--fc-ans);
}
.fc-card-a > :first-child { margin-top: 0; }
.fc-card-a > :last-child { margin-bottom: 0; }
.fc-card-a p { margin: 0 0 9px; }
.fc-card-a ol, .fc-card-a ul { margin: 0; padding-left: 20px; }
.fc-card-a li { margin-bottom: 6px; }
.fc-card-a li:last-child { margin-bottom: 0; }
.fc-card-a strong { font-weight: 700; color: var(--fc-ans-strong); }

/* Smaller type and a narrower card, so the floor that keeps a card looking like a card
   comes down with them. */
[data-fc-mode="all"] .fc-card-inner { --fc-h-min: 158px; }
[data-fc-mode="all"] .fc-card-front { padding: 20px 24px 38px; }
[data-fc-mode="all"] .fc-card-q { font-size: 18.5px; }
[data-fc-mode="all"] .fc-card-hint { left: 24px; bottom: 13px; }
[data-fc-mode="all"] .fc-card-back { padding: 18px 22px 16px; }
[data-fc-mode="all"] .fc-card-a { font-size: 14.5px; }

/* ---- Rating (Nu știu / Știu) — POSTs to the FSRS backend, see lesson.js ---- */

.fc-card-rate {
    flex: none;
    display: flex;
    gap: 10px;
    margin-top: 16px;
}
.fc-rate-btn {
    flex: 1;
    height: 46px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 13px;
    font: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: filter .15s ease, background .15s ease;
}
.fc-rate-no {
    background: var(--card);
    border: 1px solid var(--fc-no-edge);
    color: var(--fc-no-ink);
}
.fc-rate-no:hover { background: #FBF1EF; }
.fc-rate-yes {
    background: linear-gradient(130deg, var(--teal-3), var(--teal));
    border: 1px solid var(--teal-hover);
    color: #fff;
}
.fc-rate-yes:hover { filter: brightness(1.06); }
[data-fc-mode="all"] .fc-card-rate { margin-top: 12px; }
[data-fc-mode="all"] .fc-rate-btn { height: 42px; border-radius: var(--r-sm); font-size: 14px; }

/* ---- Keyboard legend ---- */

.fc-keys {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    flex-wrap: wrap;
    font-size: 12px;
    color: var(--fc-faint);
}
.fc-key {
    padding: 2px 7px;
    border-radius: 6px;
    background: #F4EFE4;
    font-size: 11px;
    color: var(--fc-meta);
}
.fc-keys[hidden] { display: none; }
.fc-keys-dot { margin: 0 4px; }
/* Nothing to press on a touch screen, and the deck is swiped there instead. */
@media (hover: none) {
    .fc-keys { display: none; }
}

/* ---- End of a run ---- */

.fc-done {
    background: var(--card);
    border: 1px solid #ECE2CD;
    border-radius: var(--r-lg);
    box-shadow: 0 1px 2px rgba(28, 27, 25, .03), 0 8px 24px rgba(28, 27, 25, .05);
    padding: 36px 28px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    text-align: center;
}
.fc-done[hidden] { display: none; }
.fc-done-title {
    margin-top: 8px;
    font-family: var(--font-serif);
    font-size: 24px;
    font-weight: 600;
    color: var(--ink-warm);
}
.fc-done-sub { font-size: 14px; color: var(--fc-meta); }
.fc-done-pills {
    margin-top: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
}
.fc-pill {
    padding: 5px 13px;
    border-radius: var(--r-pill);
    font-size: 12.5px;
    font-weight: 600;
}
.fc-pill[hidden] { display: none; }
.fc-pill--stiu { background: var(--teal-tint); color: var(--teal-ink); }
.fc-pill--nu { background: #F5E1DB; color: #9E4436; }
.fc-pill--omise { background: var(--fc-seen-bg); color: var(--fc-seen-ink); }
.fc-done-actions {
    margin-top: 16px;
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
}
.fc-done-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 44px;
    padding: 0 22px;
    border: 1px solid var(--border-2);
    border-radius: 13px;
    background: var(--card);
    font: inherit;
    font-size: 14.5px;
    font-weight: 600;
    color: var(--slate);
    cursor: pointer;
    transition: background .15s ease, filter .15s ease;
}
.fc-done-btn[hidden] { display: none; }
.fc-done-btn:hover { background: #FCF8EE; }
.fc-done-btn--primary {
    background: linear-gradient(130deg, var(--teal-3), var(--teal));
    border-color: var(--teal-hover);
    color: #fff;
}
.fc-done-btn--primary:hover { background: linear-gradient(130deg, var(--teal-3), var(--teal)); filter: brightness(1.06); }

/* ---- Navigator ---- */

/* Sized by its content, not to the handoff's flat 405px: with 14 cards that box was
   half dead space between the grid and the legend, and it was the biggest single
   contributor to the pane reading as empty. */
.fc-panel {
    flex: none;
    width: 230px;
    box-sizing: border-box;
    position: sticky;
    top: 76px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px;
    background: var(--card);
    border: 1px solid #ECE2CD;
    border-radius: var(--r-md);
    box-shadow: 0 1px 2px rgba(28, 27, 25, .03), 0 8px 24px rgba(28, 27, 25, .05);
}
.fc-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
/* Sheet-only: on a column there is nothing to close. */
.fc-panel-close {
    display: none;
    flex: none;
    width: 28px;
    height: 28px;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 8px;
    background: #F2EBDB;
    color: var(--muted);
    cursor: pointer;
}
.fc-panel-close:hover { background: #E7E0CE; color: var(--ink); }
.fc-panel-title {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted-2);
}
.fc-panel-count { font-size: 12px; color: var(--muted-2); }
.fc-panel-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 7px;
    /* A very long deck scrolls inside the panel so the legend below stays reachable;
       a normal one (≤ ~40 cards) never reaches the cap and the panel just fits it. */
    max-height: 46vh;
    overflow-y: auto;
    align-content: start;
}
.fc-sq {
    height: 36px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid #EEE8D8;
    border-radius: 9px;
    background: var(--card);
    /* A card number is a figure — serif + tabular, per the type rule. */
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    font-weight: 600;
    color: var(--fc-faint);
    cursor: pointer;
    transition: all .15s ease;
}
.fc-sq:hover { border-color: #D9CFB8; color: var(--slate); }
.fc-sq.is-seen {
    background: var(--fc-seen-bg);
    border-color: var(--fc-seen-edge);
    color: var(--fc-seen-ink);
}
.fc-sq.is-seen:hover { border-color: #D9BC74; }
.fc-sq.is-stiu {
    background: var(--teal);
    border-color: var(--teal);
    color: #fff;
}
.fc-sq.is-nu {
    background: var(--fc-nu-sq);
    border-color: #A84A38;
    color: #fff;
}
/* The current card is a ring, not a fill — it has to be readable on top of any
   of the four states above. */
.fc-sq.is-current { box-shadow: inset 0 0 0 1.8px var(--ink); }
.fc-sq.is-current:not(.is-stiu):not(.is-nu) { color: var(--ink); }

.fc-panel-legend {
    padding-top: 12px;
    border-top: 1px solid #F4EDDC;
    display: flex;
    flex-direction: column;
    gap: 7px;
    font-size: 12px;
    color: var(--muted);
}
.fc-panel-legend span { display: inline-flex; align-items: center; gap: 8px; }
.fc-dot {
    flex: none;
    width: 11px;
    height: 11px;
    box-sizing: border-box;
    border-radius: 3px;
}
.fc-dot--stiu { background: var(--teal); }
.fc-dot--nu { background: var(--fc-nu-sq); }
.fc-dot--seen { background: var(--fc-seen-bg); border: 1px solid var(--fc-seen-edge); }
.fc-dot--new { background: var(--card); border: 1px solid var(--border-2); }
.fc-dot--cur { background: var(--card); border: 1.6px solid var(--ink); }

/* ---- Narrow: the navigator becomes a FAB + bottom sheet ----

   Same shape as the grile session's (`.sim-fab` / `.sim-aside.is-open` / `.sim-navback`
   in grile.css), deliberately: a resident meets that navigator far more often than this
   one, and a card grid that is a column here and a sheet there would be two controls.
   The lesson's own Panou FAB is off on this pane (`lesson-no-ksb` hides it), so there is
   never a second button competing for the corner. */
.fc-fab,
.fc-navback { display: none; }

/* ⚠️ Two FABs share this corner now. The panel used to be absent on this pane, so its FAB
   was too; the panel is present everywhere now (showing the assistant alone), so the deck's
   navigator and the panel's button would sit on exactly the same 16/20px spot. The panel's
   rides above the navigator's — the navigator belongs to what you are looking at, so it
   keeps the thumb position. */
@media (max-width: 999px) {
    body.lesson-on-flashcards .lesson-ksb-fab { bottom: 76px; }
}
@media (max-width: 767px) {
    body.lesson-on-flashcards .lesson-ksb-fab { bottom: 140px; }
}

@media (max-width: 999px) {
    .fc-fab {
        display: inline-flex;
        position: fixed;
        right: 16px;
        bottom: 20px;
        z-index: 70;
        align-items: center;
        gap: 9px;
        padding: 10px 18px;
        border: 0;
        border-radius: var(--r-pill);
        background: var(--navy);
        font: inherit;
        font-size: 13px;
        font-weight: 600;
        color: #fff;
        box-shadow: 0 10px 26px rgba(15, 34, 48, .35);
        cursor: pointer;
    }
    .fc-navback {
        position: fixed;
        inset: 0;
        z-index: 75;
        background: rgba(15, 34, 48, .4);
    }
    .fc-navback:not([hidden]) { display: block; }

    .fc-panel {
        display: none;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* Must clear the column rule's `top: 76px` — left in place, `top` + `bottom` on a
           fixed box stretches the sheet up the screen instead of letting it hug its
           content at the bottom edge. */
        top: auto;
        z-index: 80;
        width: auto;
        max-height: 74vh;
        overflow-y: auto;
        border: 0;
        border-top: 1px solid var(--border);
        border-radius: 18px 18px 0 0;
        box-shadow: var(--shadow-sheet);
        padding: 14px 14px calc(14px + env(safe-area-inset-bottom, 0px));
    }
    .fc-panel.is-open { display: flex; }
    .fc-panel-close { display: inline-flex; }
    /* In the sheet the grid is content-sized and the sheet itself scrolls. */
    .fc-panel-grid {
        grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
        gap: 8px;
        max-height: none;
        overflow: visible;
    }
    .fc-sq { height: 42px; font-size: 13px; }
    .fc-panel-legend { flex-direction: row; flex-wrap: wrap; gap: 8px 16px; }
    /* The sheet is open — the button that opened it is underneath it, and the page behind
       it must not scroll (same posture as the Panou drawer). */
    body.fc-nav-open { overflow: hidden; }
    body.fc-nav-open .fc-fab { display: none; }
    /* "Toate" has no current card, so it has no navigator and no way in either. */
    [data-fc-mode="all"] .fc-fab { display: none; }
}

/* The learn-world tab bar takes the bottom edge on a phone; lift the FAB over it,
   exactly like the Panou FAB above. */
@media (max-width: 767px) {
    .fc-fab { bottom: 84px; }
}

@media (max-width: 560px) {
    .fc-card-front { padding: 20px 18px 36px; }
    .fc-card-hint { left: 18px; }
    .fc-card-back { padding: 16px 16px 14px; }
    .fc-card-q, [data-fc-mode="all"] .fc-card-q { font-size: 18px; }
    .fc-card-head { padding: 8px 13px; gap: 9px; }
    /* The head's last ~90px are contested by the counter and the stepper. The counter is
       also printed on every navigator square; the stepper is the only way forward without
       a swipe, so it keeps the room. */
    [data-fc-mode="deck"] .fc-card-num { display: none; }
    .fc-seg { width: 100%; }
    .fc-seg-btn { flex: 1; justify-content: center; padding: 0 8px; }
}

/* ============================================================================
   5. KSB: PANOU ↔ ASISTENT AI
   (Section 5 used to be the adult/copil VARIANT TOGGLE. That is gone — a split
   subject is two separate lessons, not one lesson with a switch — so the slot
   was free. Ported from the "Asistent AI" design handoff.)

   The right column can swap its knowledge cards for an AI chat, IDE-sidebar
   style (VS Code chat view): a mode switch in the head, full-width message
   rows with a name line, a pinned composer, and a typing animation.
   ========================================================================== */

/* Same idiom as the Lecție/Farmacologie/… toggle: sand track, white active pill,
   muted inactive icons — one switching language across the whole page. */
.ksb-switch { flex: 1 1 auto; display: flex; align-items: stretch; gap: 3px; padding: 4px; background: #F0EEE9; border-radius: 13px; }
.ksb-switch-btn {
    flex: 1 1 0; min-width: 0;
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    height: 30px; padding: 0 8px; border: none; border-radius: 10px; background: none;
    font-family: var(--font-body); font-size: 12.5px; font-weight: 600; white-space: nowrap;
    color: var(--muted); cursor: pointer;
}
.ksb-switch-btn svg { flex: none; }
.ksb-switch-btn:hover { color: var(--ink); }
.ksb-switch-btn.is-on { background: var(--card); color: var(--ink); box-shadow: 0 1px 2px rgba(28, 27, 25, 0.08); }
.ksb-switch-btn:not(.is-on) svg { opacity: 0.55; filter: saturate(0.4); }
/* ---- Solo: the assistant on a pane that has no knowledge panel ----
   Semiologie has one on no lesson, Flashcards never, Farmacologie/Biochimie on 7 of 160 —
   so this is the common state, not the edge case. With nothing to switch back TO, the
   control stops being a switch: the Panou half goes entirely and what is left reads as the
   column's caption. A greyed-out half is a dead control explaining itself, and it made the
   panel look broken on the tabs a resident spends most of their time on.
   ⚠️ `display: none`, not `[disabled]` — it also takes the button out of the a11y tree and
   out of tab order, which "a button you cannot press" does not. */
.ksb-switch.is-solo { background: none; padding: 0; }
.ksb-switch.is-solo .ksb-switch-btn[data-ksb-mode="panou"] { display: none; }
.ksb-switch.is-solo .ksb-switch-btn {
    flex: 1 1 auto;
    justify-content: flex-start;
    padding-left: 2px;
    background: none;
    box-shadow: none;
    color: var(--ink);
    cursor: default;
}

.ksb-chat { display: none; }
.lesson-ksb.is-chat .lesson-ksb-panels { display: none; }
.lesson-ksb.is-chat .ksb-chat {
    display: flex; flex-direction: column;
    flex: 1 1 auto; min-height: 0;
    width: 100%; max-width: 268px; margin: 0 auto;
}
/* Desktop: the sticky column becomes a flex shell so the log scrolls inside
   it and the composer pins to the bottom edge. */
@media (min-width: 1100px) {
    .lesson-ksb.is-chat { display: flex; flex-direction: column; overflow: hidden; }
    .lesson-ksb.is-chat .lesson-ksb-head { flex: none; width: 100%; }
}

.ksb-chat-log {
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
    padding-bottom: 12px;
    scrollbar-width: thin; scrollbar-color: var(--border-2) transparent;
}

/* ---- Firele de conversație: „+", meniul „⋮", lista, dialogul de ștergere ----

   Cele două butoane din capul panoului. Apar doar în modul chat: în Panou n-au ce
   controla, iar capul e strâmt (268px pentru comutator + trei comenzi), așa că ce nu
   se folosește nu stă acolo. `.ksb-act` e mai mic decât `.lesson-ksb-close` cu intenție
   — închiderea rămâne comanda cea mai grea din rând. */
.ksb-act {
    flex: none;
    display: none;                     /* modul chat le aprinde, mai jos */
    width: 24px; height: 24px; padding: 0;
    align-items: center; justify-content: center;
    border: none; border-radius: 9px; background: none;
    color: var(--muted-2); cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.lesson-ksb.is-chat .ksb-act { display: inline-flex; }
/* ⚠️ Capul are 268px și în modul chat ține patru lucruri: comutatorul cu DOUĂ segmente
   (pe Lecție), „+", „⋮" și închiderea. Cu gap 9 și butoane de 26px suma e 283px — cu 15px
   mai mult decât cutia, deci chipul de închidere ieșea din coloana de aliniere și era tăiat
   de `overflow` la 1px. Nu se vede într-o captură (dă impresia că e lipit de margine), se
   vede doar măsurând `scrollWidth` față de `clientWidth`. Aici se strânge exact atât cât e
   nevoie: 258px, cu 2px de joc.
   Ultima plasă: comutatorul poate să se strângă (`min-width: 0`) și își taie propriile
   etichete (`overflow: hidden`) — dacă vreodată un font mai lat mută iar socoteala, se
   pierde o literă din „Asistent", nu butonul de închidere din coloană. */
.lesson-ksb.is-chat .lesson-ksb-head { gap: 6px; }
.lesson-ksb.is-chat .ksb-switch { min-width: 0; overflow: hidden; }
.lesson-ksb.is-chat .ksb-switch-btn { padding: 0 6px; }
.ksb-act:hover { background: var(--sand); color: var(--ink); }
.ksb-act[aria-expanded="true"] { background: var(--sand); color: var(--ink); }
/* ⚠️ Culoarea se pune pe SVG, nu doar pe buton: `.lesson-ksb-head svg` (mai sus în foaie)
   îi dă fiecărui glif din capul panoului `color: var(--faint)`, la aceeași specificitate
   (0-1-1) și mai devreme în sursă. Un `color` pus doar pe `.ksb-act` nu ajunge niciodată la
   desen, iar butoanele ar fi rămas gri și la hover, fără nicio eroare. */
.ksb-act svg { display: block; color: inherit; }
/* Cât timp o tură e în zbor, comenzile de fir sunt inerte: răspunsul modelului se întoarce
   într-un rând ținut într-o închidere din initKsbChat, iar o schimbare de fir sub el l-ar
   arunca într-un element detașat, fără nicio eroare. `is-busy` e singurul semnal vizibil din
   afară că se scrie ceva; butoanele stau în capul panoului, iar clasa e pe `.ksb-chat` de
   dedesubt — de aici `:has()` (folosit deja în foaia asta și în grile.css). `pointer-events`
   e doar dublura vizuală: refuzul propriu-zis e în JS, care nu se bazează pe CSS. */
.lesson-ksb:has(.ksb-chat.is-busy) .ksb-act { opacity: .4; pointer-events: none; }

/* Meniul. ⚠️ `position: fixed` și așezat din JS după dreptunghiul butonului — vezi nota
   din detail.html: panoul e `overflow: hidden` în modul chat, deci un panou absolut
   ancorat în capul lui ar fi tăiat. */
.ksb-menu {
    display: block;
    position: fixed; z-index: 210;
    width: 214px; padding: 5px;
    background: var(--card); border: 1px solid var(--border); border-radius: 13px;
    box-shadow: 0 12px 26px rgba(28, 27, 25, .14);
    animation: ksb-fade-in .12s ease;
}
.ksb-menu[hidden] { display: none; }
.ksb-menu-item {
    display: flex; align-items: center; gap: 9px; width: 100%;
    padding: 8px 9px; border: none; border-radius: 9px; background: none;
    font-family: var(--font-body); font-size: 12.5px; font-weight: 550; text-align: left;
    color: var(--body-c); cursor: pointer;
}
/* ⚠️ `display` declarat de autor bate `[hidden] { display: none }` din foaia browserului,
   deci fără regula asta atributul `hidden` e inert — și „Șterge conversația" ar fi rămas pe
   ecran pe o lecție fără nicio conversație, adică un buton care nu poate face nimic. */
.ksb-menu-item[hidden] { display: none; }
.ksb-menu-item svg { flex: none; color: var(--muted-2); }
.ksb-menu-item:hover { background: var(--teal-tint-2); color: var(--ink); }
.ksb-menu-item:hover svg { color: var(--teal); }
.ksb-menu-count { margin-left: auto; font-size: 12px; color: var(--muted-2); }
.ksb-menu-sep { height: 1px; margin: 5px 7px; background: var(--divider); }
/* Ștergerea e singura comandă care pierde ceva: sub o linie, roșie, ultima. */
.ksb-menu-item.is-danger, .ksb-menu-item.is-danger svg { color: var(--error); }
.ksb-menu-item.is-danger:hover { background: var(--error-tint); }
.ksb-menu-item.is-danger:hover svg { color: var(--error); }

/* Lista de conversații — ține locul jurnalului în ACEEAȘI coloană, nu peste el. */
.ksb-threads { display: none; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.ksb-chat.is-threads .ksb-threads { display: flex; }
.ksb-chat.is-threads .ksb-chat-log,
.ksb-chat.is-threads .ksb-chat-composer { display: none; }
.ksb-threads-back {
    flex: none;
    display: flex; align-items: center; gap: 7px;
    padding: 6px 2px 10px; border: none; background: none; cursor: pointer;
    font-family: var(--font-eyebrow); font-size: 10px; font-weight: 700;
    letter-spacing: .08em; text-transform: uppercase; color: var(--faint);
}
.ksb-threads-back:hover { color: var(--ink); }
.ksb-threads-body {
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
    scrollbar-width: thin; scrollbar-color: var(--border-2) transparent;
}
.ksb-threads-list { display: flex; flex-direction: column; gap: 7px; }
.ksb-threads-foot {
    flex: none; margin: 10px 0 0; padding-top: 10px;
    border-top: 1px solid var(--divider);
    font-size: 10px; text-align: center; color: var(--muted-3);
}
.ksb-threads-foot[hidden] { display: none; }
.ksb-threads-empty { margin: 18px 2px; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
/* O acțiune de fir care n-a ajuns pe server. Vecinul ei e `.ksb-msg-err`, aceeași
   postură: refuzul e o linie în panou, nu o fereastră peste el. */
.ksb-thread-msg {
    flex: none; margin: 0; padding: 7px 9px;
    background: var(--error-tint); border-radius: 9px;
    font-size: 11.5px; line-height: 1.45; color: var(--error);
}
.ksb-thread-msg[hidden] { display: none; }

/* Un rând = DOUĂ butoane frați, „deschide" pe toată suprafața și coșul deasupra lui în
   colț. Un buton în interiorul altui buton e HTML invalid, iar în practică ștergerea ar
   deschide și conversația. */
.ksb-thread {
    position: relative;
    background: var(--card); border: 1px solid var(--border); border-radius: 11px;
    transition: border-color .12s ease, background .12s ease;
}
.ksb-thread:hover { border-color: var(--teal-border); }
.ksb-thread.is-current { border-color: var(--teal-border); background: var(--teal-tint-2); }
.ksb-thread-open {
    display: block; width: 100%;
    padding: 9px 32px 9px 11px;        /* dreapta: locul coșului */
    border: none; border-radius: 11px; background: none;
    text-align: left; cursor: pointer; font-family: var(--font-body);
}
.ksb-thread-q {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 12.5px; font-weight: 550; line-height: 1.4; color: var(--ink);
}
.ksb-thread-meta {
    display: flex; align-items: center; gap: 6px;
    margin-top: 5px; font-size: 10.5px; color: var(--muted-2);
}
.ksb-thread-dot { width: 3px; height: 3px; border-radius: 50%; background: var(--muted-3); }
.ksb-thread-tag {
    margin-left: auto;
    font-family: var(--font-eyebrow); font-size: 9px; font-weight: 700;
    letter-spacing: .08em; text-transform: uppercase; color: var(--teal-ink);
}
.ksb-thread-del {
    position: absolute; top: 7px; right: 7px;
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; padding: 0;
    border: none; border-radius: 7px; background: none;
    color: var(--muted-3); cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.ksb-thread-del:hover { background: var(--error-tint); color: var(--error); }
.ksb-thread-del svg { display: block; }

/* Dialogul de confirmare. Singurul modal al paginii de lecție; vecinul lui din aplicație
   e `.pm-*` de pe /profil/ (ștergerea contului), care trăiește în profile.css — o foaie
   pe care pagina de lecție n-o încarcă. Nu-l muta acolo și nu-l copia de acolo: dacă
   apare vreodată un al doilea consumator, atunci se mută în components.css, exact cum a
   pățit indicatorul „se gândește". Vălul reia valorile lui `.lesson-ksb-backdrop` de mai
   sus — pagina asta are deja un fundal blurat, iar două rețete de blur în același ecran
   se văd. */
.ksb-dialog { display: block; position: fixed; inset: 0; z-index: 220; }
.ksb-dialog[hidden] { display: none; }
.ksb-dialog-veil {
    position: absolute; inset: 0;
    background: rgba(20, 19, 17, 0.45);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    animation: ksb-fade-in 0.2s ease;
}
.ksb-dialog-box {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: min(340px, calc(100vw - 40px));
    /* ⚠️ Titlul firului intră în text, iar o întrebare poate avea 2000 de caractere. JS-ul
       îl scurtează, dar plafonul stă și aici: altfel cutia crește peste ecran și butoanele
       ajung sub marginea de jos, fără nicio cale de a le atinge. */
    max-height: calc(100vh - 48px); overflow-y: auto;
    padding: 20px 20px 16px;
    background: var(--card); border: 1px solid var(--border); border-radius: 18px;
    box-shadow: 0 24px 60px rgba(15, 34, 48, .28);
    animation: ksb-dialog-in .18s ease;
}
.ksb-dialog-title {
    margin: 0 0 7px;
    font-family: var(--font-serif); font-size: 18px; font-weight: 700; line-height: 1.3;
    color: var(--ink);
}
.ksb-dialog-text { margin: 0 0 18px; font-size: 13px; line-height: 1.55; color: var(--muted); }
.ksb-dialog-acts { display: flex; gap: 9px; }
.ksb-dialog-btn {
    flex: 1 1 0;
    height: 38px; padding: 0 14px;
    border: 1px solid var(--border-2); border-radius: var(--r-sm); background: var(--card);
    font-family: var(--font-body); font-size: 13px; font-weight: 600;
    color: var(--body-c); cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.ksb-dialog-btn:hover { background: var(--surface-3); }
.ksb-dialog-btn.is-danger { border-color: var(--error); background: var(--error); color: #FFFFFF; }
.ksb-dialog-btn.is-danger:hover { background: var(--error-2); border-color: var(--error-2); }
/* Pe desktop panoul e o coloană, deci `body.ksb-open` (blocarea sertarului) nu e pusă și
   pagina de dedesubt derula liber sub dialog. Clasă separată, tocmai ca închiderea
   dialogului să nu ridice blocarea pe care o ține sertarul deschis. */
body.ksb-dialog-open { overflow: hidden; }
@keyframes ksb-dialog-in {
    from { opacity: 0; transform: translate(-50%, calc(-50% + 8px)); }
    to { opacity: 1; transform: translate(-50%, -50%); }
}
/* ⚠️ Selectoarele se repetă întregi: un media query nu adaugă specificitate, deci o
   scurtătură aici ar pierde în fața regulii vii și animația ar rula exact pentru cine a
   cerut să nu ruleze. Aceeași notă ca la marca Sinapsa, în components.css. */
@media (prefers-reduced-motion: reduce) {
    .ksb-menu, .ksb-dialog-veil, .ksb-dialog-box { animation: none; }
}

/* Empty state: greeting + starter prompts */
.ksb-chat-empty { padding: 16px 2px 8px; }
.ksb-chat-empty-ico svg { display: block; }
.ksb-chat-empty-title { margin: 10px 0 5px; font-family: var(--font-serif); font-size: 15.5px; font-weight: 700; color: var(--ink); }
.ksb-chat-empty p { margin: 0 0 14px; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
.ksb-chat-sugs { display: flex; flex-direction: column; gap: 7px; }
.ksb-chat-sug {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 11px;
    background: var(--card); border: 1px solid var(--border); border-radius: 11px;
    font-family: var(--font-body); font-size: 12px; font-weight: 500; color: var(--body-c);
    text-align: left; cursor: pointer;
    transition: border-color .12s ease, background .12s ease;
}
.ksb-chat-sug svg { flex: none; color: var(--teal); }
.ksb-chat-sug:hover { border-color: var(--teal-border); background: var(--teal-tint-2); }

/* Messages: full-width rows with a small name line, separated by hairlines —
   VS Code chat rows in ReziDay's clothes. */
.ksb-msg { padding: 10px 2px 4px; }
.ksb-msg + .ksb-msg { border-top: 1px solid var(--divider); margin-top: 7px; }
.ksb-msg-head { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; }
.ksb-msg-ava { flex: none; width: 20px; height: 20px; border-radius: 50%; overflow: hidden; }
.ksb-msg-ava svg { width: 20px; height: 20px; display: block; }
.ksb-msg-name { font-family: var(--font-eyebrow); font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
.ksb-msg--ai .ksb-msg-name { color: var(--teal-ink); }
.ksb-msg-body { padding-left: 27px; font-size: 12.5px; line-height: 1.6; color: var(--body-c); overflow-wrap: break-word; }
.ksb-msg-body p { margin: 0 0 7px; }
.ksb-msg-body p:last-child { margin-bottom: 0; }
.ksb-msg-body strong { color: var(--ink); font-weight: 650; }
.ksb-msg-body ul, .ksb-msg-body ol { margin: 0 0 7px; padding-left: 18px; }
.ksb-msg-body li { margin-bottom: 3px; }
/* A refusal (tier, throttle, dead grading service) reads as the assistant's own
   line rather than as a browser error — same posture as the grile session's hint. */
.ksb-msg-err { color: #9E4436; }

/* The waiting animation and the Sinapsa mark's motion are NOT here: they live in
   components.css, which base.html loads on EVERY page. `/grile/sesiune/` renders outside
   the app shell and never loads lesson.css, and the exam simulator wears the same
   thinking mark in its grading overlay — keeping the keyframes here left it still.
   Pinned by `ThinkingMarkTests`, which fails if they come back. */

/* Composer, pinned under the log.
   The handoff puts an "@lecție <titlu>" pill above the input; it is deliberately NOT here.
   It was a label dressed as a control — mono, teal handle, the exact look of a mention token
   you can click or type — and there is no @-mention in this app: the lesson always travels
   with the question and the current section is read off the scroll spy. Ciprian's call
   (2026-08-09) after using it. Bring it back only with a real mention picker behind it. */
.ksb-chat-composer { flex: none; padding-top: 10px; border-top: 1px solid var(--divider); }
.ksb-chat-box {
    display: flex; align-items: flex-end; gap: 7px;
    padding: 7px 7px 7px 11px;
    background: var(--card); border: 1px solid var(--border-2); border-radius: 13px;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.ksb-chat-box:focus-within { border-color: var(--teal-3); box-shadow: 0 0 0 3px var(--teal-tint); }
.ksb-chat-box textarea {
    flex: 1; min-width: 0; border: none; outline: none; background: none; resize: none;
    font-family: var(--font-body); font-size: 12.5px; line-height: 1.5; color: var(--ink-warm);
    padding: 3px 0; min-height: 20px; max-height: 120px;
}
.ksb-chat-box textarea::placeholder { color: var(--muted-3); }
.ksb-chat-send {
    flex: none; width: 28px; height: 28px; border: none; border-radius: 9px;
    background: var(--teal); color: #FFFFFF;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer; transition: background .12s ease, transform .12s ease;
}
.ksb-chat-send:hover { background: var(--teal-hover); }
.ksb-chat-send:active { transform: scale(.94); }
.ksb-chat.is-busy .ksb-chat-send { opacity: .45; pointer-events: none; }
.ksb-chat-hint { margin-top: 6px; font-size: 10px; text-align: center; color: var(--muted-3); }
/* A free lesson is public, the endpoint is not. Shown instead of the composer. */
.ksb-chat-signin {
    display: block; padding: 9px 11px;
    background: var(--card); border: 1px solid var(--border); border-radius: 11px;
    font-size: 12px; font-weight: 500; color: var(--teal-ink); text-align: center; text-decoration: none;
}
.ksb-chat-signin:hover { border-color: var(--teal-border); background: var(--teal-tint-2); }

/* Drawer / bottom-sheet forms (<1100px): the chat fills the drawer body */
@media (max-width: 1099px) {
    .lesson-ksb.is-chat .ksb-chat { max-width: none; padding: 0 16px 14px; }
}

/* ---- Lesson components embedded in a reply ----
   The grilă and the flashcard the assistant hands over are REAL rows from this
   lesson (core/screens/assistant.py picks them; the model only asks). They keep
   their own components and only lose the 27px name-line indent and some type
   size, to fit the 268px column. */
.ksb-msg-body .quiz, .ksb-msg-body .fc-card { margin: 8px 0 4px -27px; }
.ksb-msg-body .quiz { --lb-card-pad-x: 13px; --cardhead-pad-y: 7px; --cardhead-gap: 8px; --cardhead-ico: 24px; --cardhead-svg: 17px; --cardhead-title: 12.5px; }
.ksb-msg-body .quiz-question { padding-top: 12px; }
.ksb-msg-body .quiz-question p { font-size: 12.5px; line-height: 1.55; }
.ksb-msg-body .quiz-opts { padding-top: 10px; gap: 6px; }
.ksb-msg-body .quiz-opt { gap: 8px; }
.ksb-msg-body .quiz-opt-letter { width: 21px; height: 21px; border-radius: 6px; font-size: 10.5px; }
.ksb-msg-body .quiz-opt-text { font-size: 12px; line-height: 1.45; }
/* The handoff's compression stops at the option rows — it never drew a graded card.
   The "Explicații" heading is 17px serif, which in a 268px column stands a head taller
   than the question it explains. */
.ksb-msg-body .quiz-ex-label { font-size: 13.5px; margin-bottom: 8px; }
.ksb-msg-body .quiz-ex-text { font-size: 12px; }
/* The flashcard the assistant hands over is the SAME component as the lesson deck's, and
   `--chat` is its third size step — deck 21px → "Toate" 18.5px → chat 15px on the question,
   with everything else following. It is not a lookalike: same head, same faces, same rating
   pair, same `--cardhead-*` family the quiz above it also scales through, so restyling the
   deck restyles this too.
   What it drops is only what a deck of one cannot have: the prev/next stepper and the
   "03 / 14" counter. The verdict badge stays and is filled on rating, exactly as in "Toate"
   (`[data-fc-mode="deck"]` hides it there; a chat card has no mode ancestor, so it shows). */
.fc-card--chat {
    --cardhead-pad-y: 7px;
    --cardhead-gap: 8px;
    --cardhead-ico: 24px;
    --cardhead-svg: 17px;
    --cardhead-title: 12.5px;
}
.fc-card--chat .fc-card-head { padding: var(--cardhead-pad-y) 12px; }
.fc-card--chat .fc-card-badge { padding: 2px 8px; font-size: 9.5px; }
/* Fixed rather than measured per card by lesson.js: `sizeCard` lives inside
   initFlashcards(), which this card is deliberately kept out of. Chat answers are short,
   and one that is not scrolls inside its own face. */
.fc-card--chat .fc-card-inner { height: 172px; }
.fc-card--chat .fc-card-front { padding: 15px 16px 30px; }
.fc-card--chat .fc-card-q { font-size: 15px; }
.fc-card--chat .fc-card-hint { left: 16px; bottom: 11px; font-size: 10.5px; }
.fc-card--chat .fc-card-back { padding: 14px 16px 13px; }
.fc-card--chat .fc-card-a { font-size: 12.5px; line-height: 1.55; }
.fc-card--chat .fc-card-a p { margin: 0 0 6px; }
.fc-card--chat .fc-card-rate { gap: 8px; margin-top: 10px; }
.fc-card--chat .fc-rate-btn { height: 34px; border-radius: var(--r-sm); font-size: 12.5px; }

/* ============================================================================
   6. KSB PANELS  (right "Panou de cunoștințe")
   ========================================================================== */

.ksb-panel { margin-bottom: 18px; }
.ksb-panel[hidden] { display: none; }

.ksb-panel-eyebrow {
    font-family: var(--font-eyebrow);
    font-size: 14px;
    font-weight: 600;
    color: var(--muted-2);
    margin-bottom: 12px;
}

.ksb-card {
    background: var(--ksb-surface, #fff);
    border: 1px solid var(--ksb-border, #ECE2CD);
    border-radius: 16px;
    padding: 15px 16px;
    margin-bottom: 12px;
    box-shadow: 0 1px 2px rgba(28, 27, 25, .04);
}
/* The first card carries a little more lift — it is the one the close button
   sits on, and it anchors the top of the rail. */
.ksb-panel:first-child .ksb-card:first-child {
    box-shadow: 0 1px 2px rgba(28, 27, 25, .04), 0 10px 26px rgba(28, 27, 25, .05);
}
/* Card head: tinted tile + two text lines. The close button is appended here by
   lesson.js on desktop, hence the auto margin further up this file. */
.ksb-card-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}
.ksb-card-ico {
    flex: none;
    width: 44px;
    height: 44px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ksb-tile, var(--teal-tint));
}
.ksb-card-ico svg { display: block; width: 30px; height: 30px; }
.ksb-card-headtext { min-width: 0; }
/* Serif, in the card's own tone — the quiet line that names the kind. */
.ksb-card-kind {
    font-family: var(--font-serif);
    font-size: 13px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--ksb-kind, var(--teal));
}
.ksb-card-title {
    font-family: var(--font-eyebrow);
    font-size: 14.5px;
    font-weight: 700;
    line-height: 1.2;
    color: var(--ink-warm);
}
.ksb-card-kind + .ksb-card-title { margin-top: 2px; }
.ksb-card-body {
    font-size: 13px;
    line-height: 1.55;
    color: var(--ksb-body, #57544E);
}
.ksb-card-body .lb-prose p {
    margin: 0;
    font-size: 13px;
    line-height: 1.55;
    color: var(--ksb-body, #57544E);
}
.ksb-card-body strong { color: var(--ksb-ink, var(--ink-warm)); }

/* Per-type tone ("Knowledge Sidebar redesign"). One hue per kind — nine of
   them, spread around the wheel so no two neighbours in the rail read as the
   same card: the previous three-tone scheme repeated so often that colour
   stopped carrying information. Each hue is generated at four fixed
   lightnesses, so the family stays even no matter which hue a kind gets:

     --ksb-surface  L 87%   the card
     --ksb-tile     L 97.2% the icon dish — near-white, so the drawn icon reads
     --ksb-border   L 68.5% the hairline
     --ksb-kind     L ≤30%  the serif label (and --ksb-accent, the bullet mark)

   The dish runs the other way from everything else: it used to be a step DEEPER
   than the card, which worked while the cards were pale and stopped working the
   moment they were not — the icons are drawn in ink line-work and need light
   under them. It keeps a breath of the hue rather than going pure white, so it
   still belongs to its card.

   The surfaces used to sit at L 93.5%, which put the warm ones (gold, orange)
   within a hair of the --canvas cream they lie on — the card stopped reading as
   a card. They are a full step deeper now, with ~20% more chroma, so every kind
   separates from the page and from its neighbours.

   Saturation is per-hue rather than constant, because equal saturation does not
   read as equal intensity across the wheel — yellow at teal's chroma shouts,
   violet at it disappears. The body text stays #57544E on every one of them,
   which is what fixes the lightness floor: at L 87% the worst case still clears
   5:1, and each --ksb-kind is darkened until its label clears 4.5:1 on its own
   surface (the label is 13px/600 — too small to claim the large-text 3:1). */
.ksb-card--rezumat {                   /* teal */
    --ksb-surface: #C7F5F2;
    --ksb-border: #76E7E1;
    --ksb-tile: #F3FDFC;
    --ksb-kind: #107571;
    --ksb-accent: #107571;
}
.ksb-card--lectii {                    /* green */
    --ksb-surface: #D9EECD;
    --ksb-border: #A3D786;
    --ksb-tile: #F7FBF4;
    --ksb-kind: #426E2B;
    --ksb-accent: #426E2B;
}
.ksb-card--pearl {                     /* gold */
    --ksb-surface: #F5E9C7;
    --ksb-border: #E7C976;
    --ksb-tile: #FDFAF3;
    --ksb-kind: #826207;
    --ksb-accent: #826207;
}
.ksb-card--funfact {                   /* orange */
    --ksb-surface: #F5DDC7;
    --ksb-border: #E7AC76;
    --ksb-tile: #FDF8F3;
    --ksb-kind: #8D4D0C;
    --ksb-accent: #8D4D0C;
}
.ksb-card--warning {                   /* coral */
    --ksb-surface: #F5D1C7;
    --ksb-border: #E78E76;
    --ksb-tile: #FDF5F3;
    --ksb-kind: #7F2E1A;
    --ksb-accent: #7F2E1A;
}
.ksb-card--stiati {                    /* rose */
    --ksb-surface: #F0CCDD;
    --ksb-border: #DB82AC;
    --ksb-tile: #FCF4F8;
    --ksb-kind: #71284D;
    --ksb-accent: #71284D;
}
.ksb-card--physio {                    /* violet */
    --ksb-surface: #E5D0EC;
    --ksb-border: #C08CD1;
    --ksb-tile: #F9F5FB;
    --ksb-kind: #582F6A;
    --ksb-accent: #582F6A;
}
.ksb-card--mnemonic {                  /* indigo */
    --ksb-surface: #D3D1EB;
    --ksb-border: #948ECF;
    --ksb-tile: #F5F5FB;
    --ksb-kind: #383069;
    --ksb-accent: #383069;
}
.ksb-card--conduita {                  /* blue */
    --ksb-surface: #C7DDF5;
    --ksb-border: #76ACE7;
    --ksb-tile: #F3F8FD;
    --ksb-kind: #1E497B;
    --ksb-accent: #1E497B;
}

/* Bullet lists in the rail. `.ksb-bullets` is what the renderer emits for the
   structured kinds (rezumat / lecții cheie / mnemonic); the free-text kinds come
   through markdown as a plain `.lb-prose ul`, so the second selector drags those
   onto the exact same treatment — a card must not indent differently depending
   on which field it was authored in. Tight and hard left: the rail is a narrow
   column, so every pixel of indent is one the text does not get.

   Every selector is scoped with `.ksb-card` on purpose: the bodies are wrapped
   in `.lb-prose`, and the prose rules (`.lb-prose ul` = 0,1,1) outrank a bare
   `.ksb-bullets` (0,1,0) — which is why the dots used to sit 22px in, indented
   past the card's own left edge. */
.ksb-card .ksb-bullets,
.ksb-card .lb-prose ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.ksb-card .ksb-bullets li,
.ksb-card .lb-prose ul > li {
    position: relative;
    margin: 0;
    padding-left: 11px;
    font-size: 13px;
    line-height: 1.45;
    color: var(--ksb-body, #57544E);
}
/* bullet dots take the card accent */
.ksb-card .ksb-bullets li::before,
.ksb-card .lb-prose ul > li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 6.5px;
    width: 4.5px;
    height: 4.5px;
    border-radius: 50%;
    background: var(--ksb-accent, var(--teal));
}
.ksb-card .ksb-bullets strong,
.ksb-card .lb-prose ul strong { color: var(--ksb-ink, var(--ink-warm)); }
/* A list that follows prose still needs air above it — just not between items. */
.ksb-card .lb-prose p + ul { margin-top: 8px; }

/* The acronym itself (SNOOP4Y, ABCDE…). Letters, not figures — so it takes the
   body face, not the serif number rule and not --font-mono: at 15px the mono
   letterforms read as code, which is not what a mnemonic is. */
.ksb-mnemonic-acronym {
    font-family: var(--font-body);
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--ksb-kind, var(--teal-ink));
    margin-bottom: 5px;
}

.ksb-card-ref {
    font-size: 11.5px;
    color: var(--muted-2);
    margin-top: 8px;
}
.ksb-card-ref a { color: var(--teal); text-decoration: none; }
.ksb-card-ref a:hover { color: var(--teal-hover); }
.ksb-card-ref--panel { margin-top: -4px; }

.ksb-empty {
    font-size: 12.5px;
    color: var(--muted-2);
    padding: 8px;
}

/* ============================================================================
   7. MOBILE BOTTOM TABBAR  (learn-world nav)
   Self-contained copy of the shell tabbar (detail.html doesn't load shell.css),
   shown only ≤767px. The mobile brand header from the shared partial is not used
   on the lesson page — it has its own sticky topbar — so it stays hidden.
   ========================================================================== */

.m-header { display: none; }

.m-tabbar {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 55;
    background: var(--card);
    border-top: 1px solid var(--border);
    box-shadow: 0 -8px 24px rgba(28, 27, 25, 0.07);
}
.m-tabbar-row {
    display: flex;
    align-items: stretch;
    padding: 5px 6px calc(5px + env(safe-area-inset-bottom));
}
.m-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 5px 2px 3px;
    color: var(--muted-2);
    font-size: 10px;
    font-weight: 600;
    text-decoration: none;
}
.m-tab:hover { color: var(--muted-2); }
.m-tab.is-active { color: var(--teal-ink); }
.m-tab-pill {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 54px;
    height: 31px;
    border-radius: var(--r-pill);
}
.m-tab.is-active .m-tab-pill { background: var(--teal-tint); }
.m-tab-world {
    flex: none;
    width: 62px;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    text-decoration: none;
}
.m-tab-world-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 54px;
    height: 54px;
    margin-top: -7px;
    border-radius: var(--r-pill);
    background: var(--ink);
    border: 3px solid var(--canvas);
    box-shadow: 0 8px 20px rgba(15, 34, 48, 0.28);
}

/* ============================================================================
   8. RESPONSIVE
   ========================================================================== */

/* The two rails give way at DIFFERENT widths, and the order is the point: the
   knowledge panel is worth more than the nav labels, so the rail drops to icons
   long before the panel is taken away.

     ≥1480      labelled rail · panel docked
     1100–1479  icon rail · panel STILL docked   ← the band this split buys
     768–1099   icon rail · panel → drawer behind the FAB
     ≤767       no rail · panel → drawer

   Both used to flip together at 1280, which meant a 1200px window spent 264px on
   a menu you already know and hid the panel you are reading against.

   1100 is not a round number, it is the break-even: at the old 1280 cut-off the
   labelled rail + panel left the prose 604px, and 1100 with the icon rail leaves
   it 612px. Below that the panel would be buying its keep out of the text — at
   1024 the measure fell to 536px, narrower than it had ever been. So the panel
   stays docked exactly as long as it costs the reader nothing. */
@media (max-width: 1479px) {
    /* The grid template is untouched — the rail is a variable, so narrowing it
       cannot disturb the third column. */
    /* 72px, the collapsed .app-sidebar's own width: below 1280px both shells are
       icon-only and the avatar is centred in them, so any difference here is the
       profile block shifting sideways when you open a lesson. */
    .lesson { --lesson-rail-w: 72px; }

    /* rail: icons only */
    .lesson-rail { padding: 20px 10px 16px; align-items: stretch; }
    .brand-word,
    .lesson-rail-navitem span,
    .lesson-rail-back span,
    .lesson-rail-toc-head span,
    .lesson-rail-title,
    .lesson-toc-labelwrap {
        display: none;
    }
    /* the rail can no longer show it — the topbar takes over */
    .lesson-topbar-title { display: flex; }
    /* …and the topbar is a row taller for it, so the KSB head has to come down by
       the same amount to stay on the toggle's line. 8px padding-top + a 14px serif
       line box at the sheet's 1.55 = 29.7px; the two numbers are declared on
       .lesson-topbar-title in section 1 and are commented as belonging here. */
    .lesson { --lesson-topbar-title-h: calc(8px + 14px * 1.55); }
    /* the mark is alone now — nothing to align its cap band to (components.css) */
    .lesson-rail-brand { justify-content: center; padding: 0 0 4px; }
    .lesson-rail-brand svg { transform: none; }
    .lesson-rail-navitem { justify-content: center; padding: 10px; gap: 0; }
    .lesson-rail-back { justify-content: center; padding: 6px; }
    .lesson-rail-toc-head { justify-content: center; padding: 0; }
    .lesson-toc-item { justify-content: center; padding: 7px 0; }

    /* Profile foot, icon-only — the same treatment the app sidebar gives it at its
       own breakpoint (shell.css @1279px). Logout goes: there is no room for a second
       target beside the avatar, and the profile page it sits next to has its own. */
    .lesson-rail .app-prof-body { display: none; }
    .lesson-rail .app-prof-row { justify-content: center; padding: 8px 0; }
    .lesson-rail .app-prof-link { flex: none; justify-content: center; }
    .lesson-rail .app-logout { display: none; }
}

/* Only here does the panel go away — 380px later than the rail's labels do. */
@media (max-width: 1099px) {
    /* the third column goes with it */
    .lesson { grid-template-columns: var(--lesson-rail-w) minmax(0, 1fr); }

    /* KSB becomes a right-side drawer. Hidden with display:none (NOT an
       off-screen translateX) so the closed panel can't widen the layout
       viewport / cause horizontal page scroll on tablet + phone. */
    .lesson-ksb {
        position: fixed;
        top: 0;
        right: 0;
        width: 340px;
        max-width: 90vw;
        height: 100vh;
        z-index: 80;
        box-shadow: var(--shadow-pop);
        border-left: 1px solid var(--border);
        display: none;
        padding: 0;
        overflow: hidden;              /* the panels list scrolls, not the shell */
    }
    .lesson-ksb.is-open {
        display: flex;
        flex-direction: column;
        animation: ksb-slide-in 0.24s ease;
    }
    /* drawer header: title + close, pinned above the scrolling panel list */
    .lesson-ksb-head {
        position: relative;
        top: auto;
        right: auto;                   /* the desktop corner offsets go away */
        height: auto;                  /* …and so does the tab-row alignment */
        max-width: none;
        flex: none;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        margin: 0;
        padding: 14px 16px 12px;
        border-bottom: 1px solid var(--border);
        background: var(--canvas);
    }
    .lesson-ksb-grip { display: block; }
    /* A touch bigger in the drawer header, where it is the only control. The
       glyph inside stays small — it used to be sized to the whole chip, which
       filled it edge to edge once the icon became a plain stroke X. */
    .lesson-ksb-close { width: 32px; height: 32px; }
    .lesson-ksb-close svg { width: 14px; height: 14px; }
    .lesson-ksb-panels {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: 18px 16px calc(20px + env(safe-area-inset-bottom, 0px));
        max-width: 320px;              /* padding included (border-box) */
    }

    /* FAB to open the drawer */
    .lesson-ksb-fab { display: inline-flex; }
}

/* Phone: ≤767px — single column, rail hidden, reduced padding */
@media (max-width: 767px) {
    .lesson {
        grid-template-columns: minmax(0, 1fr);
    }
    .lesson-rail { display: none; }

    .lesson-content {
        padding: clamp(16px, 5vw, 28px) clamp(12px, 4vw, 20px) 150px;
    }

    /* shrink + wrap the title so long variant names don't overflow */
    .lesson-h1 { font-size: 26px; }

    /* Tab bar stays full width with five equal, icon-only tabs — no button
       grows or shrinks relative to its neighbours. */
    .lesson-tabs { border-radius: 12px; gap: 3px; padding: 3px; }
    .lesson-tab { padding: 0 4px; }
    .lesson-tab-label { display: none; }
    .lesson-topbar-title { padding-top: 7px; }
    .lesson-topbar-title span { font-size: 13px; }

    /* learn-world bottom tabbar appears; lift the Panou FAB clear of it */
    .m-tabbar { display: block; }
    .lesson-ksb-fab { bottom: 84px; }

    /* On a phone the KSB is a bottom sheet, not a side drawer: it rises from
       the bottom edge, spans the full width and stops short of the top so the
       blurred page stays visible behind it. */
    .lesson-ksb {
        top: auto;
        right: 0;
        left: 0;
        bottom: 0;
        width: auto;
        max-width: none;
        height: auto;
        max-height: 82vh;
        max-height: 82dvh;
        border-left: none;
        border-top: 1px solid var(--border);
        border-radius: 20px 20px 0 0;
    }
    .lesson-ksb.is-open { animation: ksb-sheet-up 0.26s ease; }
    .lesson-ksb-head {
        padding-top: 18px;             /* room for the grip */
        border-radius: 20px 20px 0 0;
    }
    /* the sheet is the whole screen width — let the cards use all of it */
    .lesson-ksb-panels { max-width: none; }

    .lb-compare { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
    .lb-compare { grid-template-columns: 1fr; }
}
