/* ==========================================================================
   LambdaPi — primitives
   --------------------------------------------------------------------------
   Six building blocks that replace 103 independently-styled surfaces, each of
   which had its own padding, border and shadow. Loaded after tokens.css and
   before the page stylesheets, so a page rule can still override a primitive
   where a screen genuinely differs.

     .card       the one surface
     .card-head  title + description + trailing action
     .stack      vertical rhythm on the spacing scale
     .field      label + control + hint + error
     .btn        primary / secondary / quiet / danger, in two sizes
     .chip       a state label — the sixth, added after the plan was written

   Adding a new surface means using .card, not writing a sixty-first family.
   ========================================================================== */

/* ── .card ────────────────────────────────────────────────────────────────
   Flat by default: a 1px border carries the edge. Shadow means elevation and
   nothing else, so only .card--raised has one — modals, dropdowns, the open
   mobile sidebar.

   The lesson-library names below are part of this definition rather than a copy
   of it, for the same reason .btn-primary is part of .btn: .topic-accordion
   appears 317 times across the library modules, .subtopic-accordion 247 and
   .worked-example 166. The value is in one shared definition, not in reaching
   1 300 call sites — and markup that is never touched cannot regress. */
.card,
/* .glass-container is this primitive under its old name — 19 call sites across
   auth, profile, history, results, subscribe, break and the admin sub-pages, and
   its own rule already said surface + 1px --line + --radius. It joins the
   definition for the same reason .btn-primary joins .btn. */
.glass-container,
.admin-log,
.subject-accordion,
.topic-accordion,
.subtopic-accordion,
.topic-introduction,
.geometry-program-intro,
.combination-topic-intro,
.worked-example,
.trig-method-card,
.definition-grid > div,
.derivative-rate-grid > div,
.derivative-visual,
.word-motion-chart,
.work-diagram,
.work-area-figure,
.function-value-table,
.function-plot,
.inverse-graph-atlas,
.inverse-triangle-atlas,
.triangle-visual,
.poly-visual-card,
.combination-rule-grid,
.combination-symbol-key,
.combination-formula-sheet,
.combination-problem-visual {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

/* Three padding tiers, and every surface picks one. The library had eleven.

   .glass-container belongs here as well as in the surface list above. It was
   only in the surface list, so `class="x glass-container"` got the border and
   the radius but no padding — and deleting a page's own padding on the
   assumption that the alias supplied it silently produced a flush card. Every
   call site declares its own padding, so this changes nothing that renders; it
   makes the alias mean the whole primitive rather than two thirds of it. */
.card,
.glass-container,
.topic-introduction,
.geometry-program-intro,
.worked-example,
.trig-method-card,
.definition-grid > div { padding: var(--space-5); }

.card--tight,
.admin-log,
.derivative-rate-grid > div,
.derivative-visual,
.word-motion-chart,
.work-diagram,
.work-area-figure,
.function-value-table,
.function-plot,
.inverse-graph-atlas,
.inverse-triangle-atlas { padding: var(--space-4); }

.card--roomy { padding: var(--space-6); }

/* A row in a list of rows: the same surface, at row padding. Ten families across
   the admin screens and the PDF answer sheet are this and nothing more. */
.card--row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-4);
}

/* Flush surfaces hold their own padded rows or a full-bleed figure. */
.card--flush,
.subject-accordion,
.topic-accordion,
.subtopic-accordion,
.triangle-visual,
.poly-visual-card,
.combination-rule-grid,
.combination-symbol-key,
.combination-formula-sheet,
.combination-problem-visual { padding: 0; }

/* Nested panels. --library-paper resolved to --surface, so these were white on
   a white card and the border was doing all of the work. */
.card--sunken,
.admin-log,
.definition-grid > div,
.derivative-rate-grid > div,
.math-statement { background: var(--surface-sunken); }

.card--wash { background: var(--accent-wash); border-color: var(--accent); }
/* The inverse card's edge matches its own fill, so it is there to keep the box
   the same size as its siblings, not to draw a line. Declared transparent
   rather than --ink: background paints under the border area, so it renders
   identically, and it stops reading as a distinct border treatment. */
.card--inverse { background: var(--ink); border-color: transparent; color: var(--surface); }
.card--raised { box-shadow: var(--shadow-raised); }
.card--overlay { box-shadow: var(--shadow-overlay); }

/* An accent rule marks a surface that opens a section — the home hero, a topic
   introduction, a worked example. One weight, one colour: see --rule in
   tokens.css, which replaced six rules at 2, 3, 4, 5 and 7px in two colours. */
.card--rule,
.geometry-program-intro,
.combination-topic-intro,
.worked-example,
.trig-method-card,
.derivative-rate-grid > div { border-top: var(--rule) solid var(--accent); }

/* A quoted statement is a panel with a rule on its leading edge, not a box. */
.math-statement {
    padding: var(--space-5);
    border-left: var(--rule) solid var(--accent);
    border-radius: var(--radius);
}

/* ── .card--grid ──────────────────────────────────────────────────────────
   A hairline grid: the cells sit on a --line background with 1px gaps, so the
   gaps themselves draw the dividers. Eight of these had grown independently,
   most of them using padding: 1px to draw the outer edge — which is what a
   border is for, and a border is what the radius can clip. */
.card--grid,
.triangle-atlas-grid,
.power-atlas-grid,
.radius-methods-grid,
.solid-visual-grid,
.sphere-section-grid,
.combination-model-grid,
.combination-model-section-grid,
.method-steps {
    gap: 1px;
    padding: 0;
    overflow: hidden;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

/* The same box where the cells carry their own dividing borders instead. */
.formula-list,
.derivative-sign-chart {
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

/* Figures that bleed to the edge need the radius to clip them. */
.subject-accordion,
.topic-accordion,
.subtopic-accordion,
.triangle-visual,
.poly-visual-card,
.combination-topic-intro,
.combination-rule-grid,
.combination-symbol-key,
.combination-formula-sheet,
.combination-problem-visual,
.word-motion-chart,
.work-diagram,
.work-area-figure,
.trig-method-card { overflow: hidden; }

/* A run of cards, and grids of them. */
.card-list { display: grid; gap: var(--space-3); }
.card-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); }

/* Interactive cards: the border carries hover and focus. No lift, no scale. */
.card--action {
    display: block;
    width: 100%;
    text-align: left;
    cursor: pointer;
    transition: border-color 160ms ease, background-color 160ms ease;
}

.card--action:hover { border-color: var(--line-strong); background: var(--surface-sunken); }
.card--action:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 35%, transparent); outline-offset: 2px; border-color: var(--accent); }
.card--action[aria-selected='true'], .card--action.is-selected { border-color: var(--accent); background: var(--accent-wash); }

/* ── .card-head ───────────────────────────────────────────────────────────
   The header pattern the plan found in 7 of 16 places: eyebrow, title,
   description, and one trailing action pinned right. */
.card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}

.card-head-copy { display: grid; gap: var(--space-1); min-width: 0; }

.card-head-eyebrow {
    color: var(--accent);
    font-size: var(--fs-eyebrow);
    font-weight: 700;
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
}

.card-head h1, .card-head h2, .card-head h3, .card-head h4, .card-head strong {
    margin: 0;
    color: var(--ink);
    font-size: var(--fs-lg);
    line-height: var(--lh-tight);
}

.card-head p { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: var(--lh-snug); }
.card-head-action { flex: 0 0 auto; }

/* ── .stack ───────────────────────────────────────────────────────────────
   Vertical rhythm from the spacing scale, so a column of children never needs
   per-child margins. */
.stack { display: grid; gap: var(--space-4); }
.stack--1 { gap: var(--space-1); }
.stack--2 { gap: var(--space-2); }
.stack--3 { gap: var(--space-3); }
.stack--4 { gap: var(--space-4); }
.stack--5 { gap: var(--space-5); }
.stack--6 { gap: var(--space-6); }
.stack--7 { gap: var(--space-7); }

/* Horizontal counterpart, for action rows. */
.row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.row--end { justify-content: flex-end; }
.row--between { justify-content: space-between; }
.row--tight { gap: var(--space-2); }

/* ── .field ───────────────────────────────────────────────────────────────
   label + control + hint + error, one shape everywhere.

   .input-shell and .glass-input are this primitive under its old names — 32 and
   36 call sites, a wrapper that was already `display: grid; gap: 8px` around a
   label and a control. They join the definition for the same reason .btn-primary
   joins .btn. Before this they were three stacked layers: a base in styles.css,
   an override further down the same file, and a third in
   platform-shell.css, which won. */
.field,
.input-shell { display: grid; gap: var(--space-2); text-align: left; }

.field > label,
.input-shell > label,
.field-label {
    color: var(--ink-soft);
    font-size: var(--fs-sm);
    font-weight: 600;
}

/* --line-control, not --line-strong: WCAG 1.4.11 asks for 3:1 on the edge of a
   control, and --line-strong reaches 1.77. See tokens.css. */
.field > input,
.field > select,
.field > textarea,
.field-control,
.glass-input,
.open-answer-input {
    min-height: 44px;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--line-control);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    font-family: var(--font-text);
    font-size: var(--fs-sm);
    transition: border-color 160ms ease, box-shadow 160ms ease;
}

.field > input::placeholder,
.field > textarea::placeholder,
.field-control::placeholder,
.glass-input::placeholder,
.open-answer-input::placeholder { color: var(--muted); }

/* :focus, not only :focus-visible — a text field shows its focus whether you
   arrived by keyboard or by click, and the legacy rules these replace were on
   :focus. The ring is the accent one every other control uses; the controls
   here used to get a grey --ink ring instead. */
.field > input:focus,
.field > select:focus,
.field > textarea:focus,
.field-control:focus,
.glass-input:focus,
.open-answer-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

.field > input:disabled,
.field > select:disabled,
.field > textarea:disabled,
.field-control:disabled,
.glass-input:disabled,
.open-answer-input:disabled { background: var(--surface-sunken); color: var(--muted); cursor: not-allowed; }

.field-hint { color: var(--muted); font-size: var(--fs-caption); }
.field-error { color: var(--danger); font-size: var(--fs-caption); font-weight: 600; }

.field.is-invalid > input,
.field.is-invalid > select,
.field.is-invalid > textarea { border-color: var(--danger); }

/* ── .btn ─────────────────────────────────────────────────────────────────
   The legacy names are part of the same definition rather than a copy of it:
   .btn-primary and .btn-secondary appear ~200 times across app.js, and one
   shared rule list is what makes every button agree without rewriting them. */
.btn,
.btn-primary,
.btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: var(--space-2) var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: none;
    color: var(--ink);
    font-family: var(--font-text);
    font-size: var(--fs-sm);
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    cursor: pointer;
    transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

.btn--primary,
.btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
}

.btn--primary:hover,
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn--secondary,
.btn-secondary {
    background: var(--surface);
    border-color: var(--line-strong);
    color: var(--ink);
}

.btn--secondary:hover,
.btn-secondary:hover { border-color: var(--accent); color: var(--accent); }

.btn--quiet { background: none; border-color: transparent; color: var(--muted); }
.btn--quiet:hover { background: var(--surface-sunken); color: var(--ink); }

.btn--danger { background: var(--surface); border-color: var(--line-strong); color: var(--danger); }
.btn--danger:hover { background: var(--danger); border-color: var(--danger); color: var(--surface); }

/* --sm is a lighter button, not a smaller target: the label and the horizontal
   padding shrink, the 44px height does not. A 36px button clears WCAG 2.5.8 but
   not the 44px the plan asks for, and the compact rows it is used in — the quiz
   HUD, admin row actions — are all at least 44px tall anyway. */
.btn--sm,
.btn-primary.compact,
.btn-secondary.compact { padding: var(--space-1) var(--space-3); font-size: var(--fs-caption); }

.btn--block,
.btn.w-100,
.btn-primary.w-100,
.btn-secondary.w-100 { width: 100%; }

.btn:focus-visible,
.btn-primary:focus-visible,
.btn-secondary:focus-visible {
    outline: 3px solid color-mix(in srgb, var(--accent) 35%, transparent);
    outline-offset: 2px;
}

.btn:disabled,
.btn-primary:disabled,
.btn-secondary:disabled { opacity: 0.55; cursor: not-allowed; }

/* ── .chip ────────────────────────────────────────────────────────────────
   A state label, not a surface: it names the condition of the thing beside it —
   free/paid, visible/hidden, approved/rejected, "1 attempt". Thirteen families
   had grown into this shape independently, at five paddings and three radii.

   The sixth primitive. REDESIGN_PLAN.md lists five, and a chip is genuinely not
   a variant of .card: a card holds content, a chip labels it.

   One fill for all of them and the tone in the label and border, rather than a
   tinted background per tone. That is why: every tinted fill in the app was a
   color-mix against transparent, so the canvas showed through and the pair could
   not be contrast-checked as a fixed value. These were most of what kept the
   runtime background count above target, and this adds no palette entries. All
   six label colours clear AA on the fill they sit on — the closest is --warning
   at 4.74. */
.chip,
.topic-summary-meta,
.subject-summary-meta,
.subscribe-payment-meta span {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    color: var(--ink-soft);
    font-family: var(--font-text);
    font-size: var(--fs-eyebrow);
    font-weight: 750;
    letter-spacing: var(--ls-label);
    line-height: 1.2;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Tones. --accent gets the wash it already owns; the rest keep the neutral fill
   so no new colour has to be invented for each state. */
.chip--accent { background: var(--accent-wash); border-color: var(--accent); color: var(--accent); }
.chip--success { border-color: var(--success); color: var(--success); }
.chip--warning { border-color: var(--warning); color: var(--warning); }
.chip--danger { border-color: var(--danger); color: var(--danger); }
.chip--quiet { background: none; color: var(--muted); }

/* Not every chip is a shouted label: counts and meta read as sentence case. */
.chip--plain,
.topic-summary-meta,
.subject-summary-meta,
.subscribe-payment-meta span {
    font-size: var(--fs-caption);
    font-weight: 700;
    text-transform: none;
}

/* A dot that carries the tone as a fill, for chips that need a status light. */
.chip-dot {
    width: 9px;
    height: 9px;
    flex: 0 0 9px;
    border-radius: 50%;
    background: currentColor;
}

/* ── Shared page furniture ────────────────────────────────────────────────
   Empty and loading states looked different on every screen; the plan asks
   for one of each. */
/* --space-6 all round rather than 48/24: it is the same padding .card--roomy
   uses, so an empty state sits in a card at the card's own rhythm instead of
   introducing a combination that exists nowhere else. */
.state {
    display: grid;
    justify-items: center;
    gap: var(--space-2);
    padding: var(--space-6);
    color: var(--muted);
    text-align: center;
}

.state strong { color: var(--ink); font-size: var(--fs-md); font-weight: 700; }
.state p { margin: 0; font-size: var(--fs-sm); max-width: 46ch; }

.state--loading { color: var(--muted); font-size: var(--fs-sm); }

/* ── Focus ────────────────────────────────────────────────────────────────
   One ring, on everything that takes focus. The primitives above declare their
   own; these are the controls that had none at all — found by sweeping every
   focusable element on all 17 screens against the stylesheets rather than by
   reading the CSS (scripts/redesign-a11y.mjs).

   <summary> is the important one: the lesson library is built from ~550
   accordions, so it was the most-used control in the application and a keyboard
   user could not see which one they were on. */
summary:focus-visible,
.lang-btn:focus-visible,
.footer-brand:focus-visible,
.footer-link-item:focus-visible,
.checkbox-row input:focus-visible,
input[type='checkbox']:focus-visible,
input[type='radio']:focus-visible {
    outline: 3px solid color-mix(in srgb, var(--accent) 35%, transparent);
    outline-offset: 2px;
}

/* An accordion summary fills its row, so an outset ring would be clipped by the
   card that holds it. Inset instead, where it is always visible. */
summary:focus-visible {
    outline-offset: -3px;
}

@media (prefers-reduced-motion: reduce) {
    .card--action,
    .field > input,
    .field > select,
    .field > textarea,
    .field-control,
    .glass-input,
    .open-answer-input,
    .btn,
    .btn-primary,
    .btn-secondary { transition: none; }
}
