/* ===========================================================================
   Indigo-Mint LMS — component layer
   ===========================================================================
   The design system ships its components as React with inline styles. Every
   value in them is already a var(--lms-*), so what is portable is the spec, not
   the code: each component below is that spec restated as CSS, keeping the
   class names DESIGN.md §5 uses (.lms-btn-primary, .lms-input, …).

   Rules for working in this file:
     • Never write a literal colour, radius, or duration. If a value is missing
       from lms-tokens.css, add it there.
     • Never write a physical direction. margin-inline-start, not margin-left —
       the app renders RTL for Arabic and a physical property is a mirror bug.
     • Dark mode gets no rules here. Components read semantic tokens, and the
       tokens flip. A component that needs a [data-bs-theme="dark"] rule is a
       component reaching past its semantic token.

   Components marked DERIVED are not in the shipped design system. They were
   built here from its tokens because the app has surfaces the system does not
   cover yet. Docs/DESIGN_SYSTEM_GAPS.md carries the request to fold them back
   into the source.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   Typography utilities — the type scale as classes.
   --------------------------------------------------------------------------- */
.lms-display-xxl { font: var(--lms-display-xxl-weight) var(--lms-display-xxl-size)/var(--lms-display-xxl-lh) var(--lms-font-sans); letter-spacing: var(--lms-display-xxl-track); color: var(--lms-text-heading); }
.lms-display-xl  { font: var(--lms-display-xl-weight) var(--lms-display-xl-size)/var(--lms-display-xl-lh) var(--lms-font-sans); letter-spacing: var(--lms-display-xl-track); color: var(--lms-text-heading); }
.lms-display-lg  { font: var(--lms-display-lg-weight) var(--lms-display-lg-size)/var(--lms-display-lg-lh) var(--lms-font-sans); letter-spacing: var(--lms-display-lg-track); color: var(--lms-text-heading); }
.lms-heading-lg  { font: var(--lms-heading-lg-weight) var(--lms-heading-lg-size)/var(--lms-heading-lg-lh) var(--lms-font-sans); letter-spacing: var(--lms-heading-lg-track); color: var(--lms-text-heading); }
.lms-heading-md  { font: var(--lms-heading-md-weight) var(--lms-heading-md-size)/var(--lms-heading-md-lh) var(--lms-font-sans); letter-spacing: var(--lms-heading-md-track); color: var(--lms-text-heading); }
.lms-heading-sm  { font: var(--lms-heading-sm-weight) var(--lms-heading-sm-size)/var(--lms-heading-sm-lh) var(--lms-font-sans); color: var(--lms-text-heading); }
.lms-body-lg     { font: var(--lms-body-lg-weight) var(--lms-body-lg-size)/var(--lms-body-lg-lh) var(--lms-font-sans); }
.lms-body-md     { font: var(--lms-body-md-weight) var(--lms-body-md-size)/var(--lms-body-md-lh) var(--lms-font-sans); }
.lms-body-sm     { font: var(--lms-body-sm-weight) var(--lms-body-sm-size)/var(--lms-body-sm-lh) var(--lms-font-sans); }
.lms-caption     { font: var(--lms-caption-weight) var(--lms-caption-size)/var(--lms-caption-lh) var(--lms-font-sans); color: var(--lms-text-body); }

.lms-micro {
    font: var(--lms-micro-weight) var(--lms-micro-size)/var(--lms-micro-lh) var(--lms-font-sans);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
}

/* Money, durations, timers, balances. Tabular figures stop a ledger column from
   shifting as digits change — DESIGN.md §1.1 tenet 3. */
.lms-tabular {
    font-weight: var(--lms-tabular-weight);
    letter-spacing: var(--lms-tabular-track);
    font-feature-settings: var(--lms-numeric-tabular);
    font-variant-numeric: tabular-nums;
}

.lms-mono {
    font-family: var(--lms-font-mono);
    font-variant-numeric: tabular-nums;
}

/* Flex and grid children default to min-width:auto, which stops text truncating
   inside them. Bootstrap has no utility for it. */
.lms-min-w-0 { min-width: 0; }

/* Surface utilities. Bootstrap's .bg-white and .bg-light are fixed colours that
   do not follow the theme; these are the theme-aware replacements. */
.lms-bg-card { background-color: var(--lms-surface-card) !important; }
.lms-bg-muted { background-color: var(--lms-surface-muted) !important; }
.lms-bg-accent-soft { background-color: var(--lms-surface-accent-soft) !important; }

.lms-text-heading { color: var(--lms-text-heading) !important; }
.lms-text-body    { color: var(--lms-text-body) !important; }
.lms-text-subtle  { color: var(--lms-text-subtle) !important; }
.lms-text-mint    { color: var(--lms-mint-success) !important; }
.lms-text-cta     { color: var(--lms-orange-cta) !important; }

/* ---------------------------------------------------------------------------
   Buttons — DESIGN.md §5.1
   Variant by intent, not by looks: primary = commerce and commitment, mint =
   learning actions, cta = conversion, danger = destructive, ghost = tertiary.
   --------------------------------------------------------------------------- */
.lms-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--lms-gap-inline);
    padding: var(--lms-pad-control);
    font-family: var(--lms-font-sans);
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-semibold);
    line-height: 1.4;
    letter-spacing: -0.01em;
    white-space: nowrap;
    border: 1px solid transparent;
    border-radius: var(--lms-radius-control);
    cursor: pointer;
    text-decoration: none;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard),
                border-color var(--lms-dur-fast) var(--lms-ease-standard),
                transform var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-btn:hover { text-decoration: none; }
.lms-btn:active:not(:disabled) { transform: var(--lms-press-scale); }

.lms-btn:disabled,
.lms-btn.disabled {
    opacity: 0.45;
    cursor: not-allowed;
    transform: none;
}

.lms-btn--sm { padding: 8px 14px; font-size: var(--lms-caption-size); }
.lms-btn--lg { padding: 12px 24px; font-size: var(--lms-body-md-size); border-radius: 10px; }
.lms-btn--block { display: flex; width: 100%; }

.lms-btn-primary {
    background-color: var(--lms-primary);
    color: var(--lms-on-primary);
}
.lms-btn-primary:hover:not(:disabled) { background-color: var(--lms-primary-hover); color: var(--lms-on-primary); }
.lms-btn-primary:active:not(:disabled) { background-color: var(--lms-primary-press); }

.lms-btn-secondary {
    background-color: var(--lms-surface-card);
    border-color: var(--lms-border-card);
    color: var(--lms-text-heading);
    font-weight: var(--lms-weight-medium);
}
.lms-btn-secondary:hover:not(:disabled) { background-color: var(--lms-surface-muted); color: var(--lms-text-heading); }

.lms-btn-mint {
    background-color: var(--lms-mint-success);
    color: var(--lms-on-primary);
}
.lms-btn-mint:hover:not(:disabled) { background-color: var(--lms-mint-hover); color: var(--lms-on-primary); }

.lms-btn-cta {
    background-color: var(--lms-orange-cta);
    color: var(--lms-on-primary);
}
.lms-btn-cta:hover:not(:disabled) { background-color: var(--lms-orange-cta-hover); color: var(--lms-on-primary); }

/* The design system names this variant after its colour token, `lms-btn-ruby`;
   the app shipped it as `lms-btn-danger` first. Both resolve to the same rule
   rather than to two rules that can drift apart. New work uses `lms-btn-ruby`. */
.lms-btn-danger,
.lms-btn-ruby {
    background-color: var(--lms-ruby-danger);
    color: var(--lms-on-primary);
}
.lms-btn-danger:hover:not(:disabled),
.lms-btn-ruby:hover:not(:disabled) { background-color: var(--lms-ruby-danger-hover); color: var(--lms-on-primary); }

.lms-btn-ghost {
    background-color: transparent;
    color: var(--lms-text-strong);
    font-weight: var(--lms-weight-medium);
}
.lms-btn-ghost:hover:not(:disabled) { background-color: var(--lms-surface-muted); color: var(--lms-text-heading); }

/* Icon-only trigger. Always needs an aria-label — an icon is not an accessible name. */
.lms-icon-btn {
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    border: 1px solid transparent;
    border-radius: var(--lms-radius-md);
    background: transparent;
    color: var(--lms-text-body);
    cursor: pointer;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard),
                color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-icon-btn:hover:not(:disabled) { background: var(--lms-surface-muted); color: var(--lms-text-heading); }
.lms-icon-btn.is-active { color: var(--lms-primary); }
.lms-icon-btn--outline { border-color: var(--lms-border-card); background: var(--lms-surface-card); }
.lms-icon-btn--solid { background: var(--lms-primary); color: var(--lms-on-primary); }
.lms-icon-btn--solid:hover:not(:disabled) { background: var(--lms-primary-hover); color: var(--lms-on-primary); }
.lms-icon-btn--lg { width: 44px; height: 44px; font-size: 19px; }
.lms-icon-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* Tone follows meaning, not decoration: ruby for destructive, mint for a
   completion action, primary for the one action a row is really for. The hover
   tint stays the muted surface so the cluster does not flash four colours. */
.lms-icon-btn--danger { color: var(--lms-ruby-danger); }
.lms-icon-btn--danger:hover:not(:disabled) { background: var(--lms-ruby-soft); color: var(--lms-ruby-danger); }
.lms-icon-btn--primary { color: var(--lms-primary); }
.lms-icon-btn--primary:hover:not(:disabled) { background: var(--lms-primary-bg-subdued); color: var(--lms-primary); }
.lms-icon-btn--mint { color: var(--lms-mint-success); }
.lms-icon-btn--mint:hover:not(:disabled) { background: var(--lms-mint-soft); color: var(--lms-mint-success); }

/* Trailing action cluster for a table or list row — data/RowActions.
   The gap is the point: bare inline buttons in a cell have no separation, and a
   36px square with a text label beside it overflows the square and lands on top
   of its neighbour. Every child is icon-only and carries its own aria-label.
   An irreversible action does NOT belong here — it goes in a .lms-danger-zone
   on the record's own page (REQ-OFR-026). */
.lms-row-actions {
    display: flex;
    align-items: center;
    gap: var(--lms-space-sm);
    justify-content: flex-end;
}

.lms-row-actions--center { justify-content: center; }
.lms-row-actions--start { justify-content: flex-start; }

/* A form wrapping a single action must not break the row of squares. */
.lms-row-actions > form { display: inline-flex; }

/* ---------------------------------------------------------------------------
   Surfaces — DESIGN.md §4.3, §5.3
   Elevation 1 is a hairline plus the faintest shadow; depth comes from the
   border, not from the shadow. Elevation 2 is reserved for hover and popovers.
   --------------------------------------------------------------------------- */
.lms-card {
    background: var(--lms-surface-card);
    color: var(--lms-text-heading);
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-card);
    padding: var(--lms-pad-card);
    box-shadow: var(--lms-elev-1);
    transition: box-shadow var(--lms-dur-base) var(--lms-ease-standard),
                transform var(--lms-dur-base) var(--lms-ease-standard);
}

.lms-card--compact { padding: var(--lms-pad-card-compact); border-radius: var(--lms-radius-panel); }
.lms-card--flat { box-shadow: none; }
.lms-card--flush { padding: 0; overflow: hidden; }

.lms-card--hoverable:hover {
    box-shadow: var(--lms-elev-2);
    transform: var(--lms-lift-hover);
}

.lms-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--lms-space-md);
    margin-block-end: var(--lms-space-lg);
}

.lms-card__title {
    font: var(--lms-heading-sm-weight) var(--lms-heading-sm-size)/var(--lms-heading-sm-lh) var(--lms-font-sans);
    color: var(--lms-text-heading);
    margin: 0;
}

/* The header is space-between, so a title that carries a count has to be one
   group or the count drifts to the far edge and reads as an action. */
.lms-card__heading {
    display: flex;
    align-items: center;
    gap: var(--lms-space-sm);
    min-width: 0;
    flex-wrap: wrap;
}

/* REQ-ADM-009 — the total matching-record count belongs in the list card
   header, beside the title, not under the pager where it is read last.
   `ListPageHeader` in the design system carries it as a subdued indigo pill. */
.lms-card__count {
    padding: 3px 10px;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-primary-bg-subdued);
    color: var(--lms-primary);
    font-size: 12px;
    font-weight: var(--lms-weight-semibold);
    line-height: 1.4;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Frosted panel over hero imagery and gradients.
   LEGAL IN EXACTLY THREE PLACES (readme § Transparency & blur): the sticky
   public header, floating chips over media, and modal scrims. On an opaque
   surface it is decoration, and decoration that costs a compositor layer —
   use `.lms-card` there instead. */
.lms-glass {
    background: var(--lms-glass-bg);
    border: 1px solid var(--lms-glass-border);
    backdrop-filter: var(--lms-blur-overlay);
    -webkit-backdrop-filter: var(--lms-blur-overlay);
}

/* KPI tile for report and dashboard headers — DESIGN.md §5.3. */
.lms-stat-card {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--lms-space-md);
    background: var(--lms-surface-card);
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-card);
    padding: var(--lms-pad-card-compact);
    box-shadow: var(--lms-elev-1);
}

.lms-stat-card__value {
    font-size: 28px;
    font-weight: var(--lms-weight-bold);
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--lms-text-heading);
    font-variant-numeric: tabular-nums;
}

.lms-stat-card__label {
    margin-block-start: 6px;
    font: var(--lms-micro-weight) var(--lms-micro-size)/var(--lms-micro-lh) var(--lms-font-sans);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    color: var(--lms-text-subtle);
}

.lms-stat-card__delta {
    margin-block-start: var(--lms-space-sm);
    display: inline-flex;
    align-items: center;
    gap: var(--lms-space-xs);
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-semibold);
    font-variant-numeric: tabular-nums;
    color: var(--lms-mint-success);
}

.lms-stat-card__delta--down { color: var(--lms-ruby-danger); }

/* The tinted icon box: 40px, 10% tint of its tone. */
.lms-icon-box {
    width: 40px;
    height: 40px;
    flex: 0 0 40px;
    display: grid;
    place-items: center;
    border-radius: var(--lms-radius-lg);
    background: var(--lms-primary-bg-subdued);
    color: var(--lms-primary);
    font-size: 18px;
}

.lms-icon-box--lg { width: 56px; height: 56px; flex-basis: 56px; font-size: 24px; border-radius: var(--lms-radius-xl); }
.lms-icon-box--mint { background: var(--lms-mint-soft); color: var(--lms-mint-success); }
.lms-icon-box--orange { background: var(--lms-orange-soft); color: var(--lms-orange-cta); }
.lms-icon-box--teal { background: var(--lms-teal-soft); color: var(--lms-teal-teacher); }
.lms-icon-box--ruby { background: var(--lms-ruby-soft); color: var(--lms-ruby-danger); }

/* Third-party video providers keep their own brand marks: those colours belong to
   those companies, not to this palette, and a teacher picks the right tile out of
   four by the colour. Same reasoning as .whatsapp-float. They live here rather
   than in a style attribute so no view carries a literal hex. */
.lms-icon-box--bunny { background: rgba(255, 130, 0, 0.12); color: #ff8200; }
.lms-icon-box--vimeo { background: rgba(26, 183, 234, 0.12); color: #1ab7ea; }
.lms-icon-box--kinescope { background: rgba(108, 71, 255, 0.12); color: #6c47ff; }
.lms-icon-box--amber { background: var(--lms-amber-soft); color: var(--lms-amber-warning); }

/* ---------------------------------------------------------------------------
   Badges — DESIGN.md §5.1
   Tone carries meaning: mint = completion, amber = locked, teal = teacher
   scope, ruby = failure, orange = commerce, indigo = active/informational.
   --------------------------------------------------------------------------- */
.lms-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--lms-space-xs);
    padding: 4px 10px;
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-pill);
    background: var(--lms-surface-muted);
    color: var(--lms-text-strong);
    font-family: var(--lms-font-sans);
    font-size: var(--lms-micro-size);
    font-weight: var(--lms-weight-semibold);
    line-height: var(--lms-micro-lh);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    white-space: nowrap;
}

/* Counts and durations read better in their own case than shouted. */
.lms-badge--sentence { text-transform: none; letter-spacing: 0; font-size: var(--lms-caption-size); }

.lms-badge--mint   { background: var(--lms-mint-soft); color: var(--lms-mint-success); border-color: var(--lms-mint-border); }
.lms-badge--indigo { background: var(--lms-primary-bg-subdued); color: var(--lms-primary); border-color: var(--lms-primary-bg-subdued-hover); }
.lms-badge--amber  { background: var(--lms-amber-soft); color: var(--lms-amber-warning); border-color: #fde68a; }
.lms-badge--ruby   { background: var(--lms-ruby-soft); color: var(--lms-ruby-danger); border-color: #fbcfe8; }
.lms-badge--teal   { background: var(--lms-teal-soft); color: var(--lms-teal-teacher); border-color: #99f6e4; }
.lms-badge--orange { background: var(--lms-orange-soft); color: var(--lms-orange-cta); border-color: #fed7aa; }
.lms-badge--solid  { background: var(--lms-primary); color: var(--lms-on-primary); border-color: transparent; }

/* Filter rail chip — DESIGN.md §6.1. Selected is a solid fill, not a tint. */
.lms-chip {
    height: 36px;
    display: inline-flex;
    align-items: center;
    gap: var(--lms-gap-inline);
    padding-inline: 14px;
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-pill);
    background: var(--lms-surface-card);
    color: var(--lms-text-strong);
    font-family: var(--lms-font-sans);
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-medium);
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard),
                border-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-chip:hover { background: var(--lms-surface-muted); color: var(--lms-text-strong); text-decoration: none; }

.lms-chip.is-selected,
.lms-chip[aria-pressed="true"] {
    background: var(--lms-primary);
    border-color: var(--lms-primary);
    color: var(--lms-on-primary);
}

.lms-chip--mint.is-selected { background: var(--lms-mint-success); border-color: var(--lms-mint-success); }

.lms-chip__count {
    min-width: 20px;
    padding-inline: 6px;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-surface-muted);
    color: var(--lms-text-body);
    font-size: 12px;
    font-weight: var(--lms-weight-semibold);
    line-height: 20px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.lms-chip.is-selected .lms-chip__count {
    background: rgba(255, 255, 255, 0.22);
    color: var(--lms-on-primary);
}

/* Horizontal scrolling rail that hides its scrollbar. */
.lms-chip-rail {
    display: flex;
    gap: var(--lms-space-sm);
    overflow-x: auto;
    padding-block-end: var(--lms-space-xs);
    scrollbar-width: none;
}

.lms-chip-rail::-webkit-scrollbar { display: none; }

/* ---------------------------------------------------------------------------
   Progress — DESIGN.md §5.4
   Mint above the 70% completion threshold, indigo below it. The threshold is a
   product rule (REQ-LRN-004), so the colour change is meaning, not decoration.
   --------------------------------------------------------------------------- */
.lms-progress {
    height: 8px;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-surface-muted);
    overflow: hidden;
}

.lms-progress--thin { height: 4px; }
.lms-progress--thick { height: 12px; }

.lms-progress__fill {
    height: 100%;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-primary);
    transition: width var(--lms-dur-slow) var(--lms-ease-out);
}

.lms-progress__fill--complete { background: var(--lms-mint-success); }

.lms-progress__meta {
    display: flex;
    justify-content: space-between;
    margin-block-end: 6px;
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-body);
}

.lms-progress__value {
    color: var(--lms-text-heading);
    font-weight: var(--lms-weight-semibold);
    font-variant-numeric: tabular-nums;
}

/* Circular progress. --lms-ring-pct is set inline by the view; the conic
   gradient does the drawing so no SVG or script is needed. */
.lms-progress-ring {
    --lms-ring-size: 64px;
    --lms-ring-pct: 0;
    --lms-ring-color: var(--lms-primary);
    /* Thickness is proportional, so a 64px chip and a 150px gauge read as the
       same component rather than one looking like a hoop and the other a disc. */
    --lms-ring-thickness: calc(var(--lms-ring-size) * 0.12);
    width: var(--lms-ring-size);
    height: var(--lms-ring-size);
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: conic-gradient(var(--lms-ring-color) calc(var(--lms-ring-pct) * 1%), var(--lms-surface-muted) 0);
    position: relative;
}

.lms-progress-ring--complete { --lms-ring-color: var(--lms-mint-success); }

.lms-progress-ring::before {
    content: "";
    position: absolute;
    inset: var(--lms-ring-thickness);
    border-radius: 50%;
    background: var(--lms-surface-card);
}

.lms-progress-ring__label {
    position: relative;
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-semibold);
    color: var(--lms-text-heading);
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   Forms — DESIGN.md §5.2
   --------------------------------------------------------------------------- */
.lms-field { display: block; margin-block-end: var(--lms-space-lg); }

.lms-label {
    display: block;
    margin-block-end: 6px;
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-strong);
}

.lms-label__required { color: var(--lms-ruby-danger); margin-inline-start: 2px; }

.lms-input,
.lms-select,
.lms-textarea {
    display: block;
    width: 100%;
    padding: var(--lms-pad-field);
    background: var(--lms-surface-card);
    color: var(--lms-text-heading);
    border: 1px solid var(--lms-border-field);
    border-radius: var(--lms-radius-field);
    font-family: var(--lms-font-sans);
    font-size: var(--lms-body-md-size);
    line-height: var(--lms-body-md-lh);
    transition: border-color var(--lms-dur-fast) var(--lms-ease-standard),
                box-shadow var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-input::placeholder,
.lms-textarea::placeholder { color: var(--lms-text-subtle); }

.lms-input:focus,
.lms-select:focus,
.lms-textarea:focus {
    outline: none;
    border-color: var(--lms-border-focus);
    box-shadow: var(--lms-focus-ring);
}

.lms-input:disabled,
.lms-select:disabled,
.lms-textarea:disabled {
    background: var(--lms-surface-muted);
    color: var(--lms-text-subtle);
    cursor: not-allowed;
}

.lms-input--tabular { font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }

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

.lms-input.is-invalid:focus { box-shadow: 0 0 0 3px rgba(234, 34, 97, 0.15); }

.lms-hint {
    margin-block-start: 6px;
    font-size: var(--lms-caption-size);
    color: var(--lms-text-subtle);
}

.lms-error {
    margin-block-start: 6px;
    display: flex;
    align-items: center;
    gap: var(--lms-space-xs);
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-ruby-danger);
}

/* Leading or trailing icon inside the field. */
.lms-input-group { position: relative; display: flex; align-items: center; }
.lms-input-group__icon {
    position: absolute;
    inset-inline-start: 12px;
    color: var(--lms-text-subtle);
    font-size: 15px;
    pointer-events: none;
}
.lms-input-group .lms-input { padding-inline-start: 38px; }

.lms-switch-label,
.lms-check-label {
    display: inline-flex;
    align-items: center;
    gap: var(--lms-space-sm);
    cursor: pointer;
    font-size: var(--lms-body-sm-size);
    color: var(--lms-text-strong);
}

/* Immediate-effect setting: no Save button, so the control is the commit. */
.lms-switch {
    appearance: none;
    inline-size: 40px;
    block-size: 22px;
    flex: 0 0 40px;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-border-field);
    border: 0;
    position: relative;
    cursor: pointer;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-switch::after {
    content: "";
    position: absolute;
    inset-block-start: 3px;
    inset-inline-start: 3px;
    inline-size: 16px;
    block-size: 16px;
    border-radius: 50%;
    background: #fff;
    transition: translate var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-switch:checked { background: var(--lms-primary); }
.lms-switch:checked::after { translate: 18px 0; }
[dir="rtl"] .lms-switch:checked::after { translate: -18px 0; }
.lms-switch:disabled { opacity: 0.45; cursor: not-allowed; }

/* Quiz answer option — DESIGN.md §6.3. Full width, one per row. */
.lms-option {
    display: flex;
    align-items: center;
    gap: var(--lms-space-md);
    padding: 14px 16px;
    margin-block-end: var(--lms-space-md);
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-lg);
    background: var(--lms-surface-card);
    cursor: pointer;
    transition: border-color var(--lms-dur-fast) var(--lms-ease-standard),
                background-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-option:hover { border-color: var(--lms-border-field); }

.lms-option__input { position: absolute; opacity: 0; width: 0; height: 0; }

.lms-option__marker {
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    display: grid;
    place-items: center;
    border: 1px solid var(--lms-border-field);
    border-radius: 50%;
    background: var(--lms-surface-card);
    color: var(--lms-text-subtle);
    font-size: 12px;
    font-weight: var(--lms-weight-bold);
}

.lms-option__text {
    font-size: var(--lms-body-md-size);
    font-weight: var(--lms-weight-medium);
    line-height: 1.5;
    color: var(--lms-text-heading);
}

.lms-option:has(.lms-option__input:checked),
.lms-option.is-selected {
    border-color: var(--lms-primary);
    background: var(--lms-primary-bg-subdued);
}

.lms-option:has(.lms-option__input:checked) .lms-option__marker,
.lms-option.is-selected .lms-option__marker {
    border-color: var(--lms-primary);
    background: var(--lms-primary);
    color: var(--lms-on-primary);
}

.lms-option.is-correct { border-color: var(--lms-mint-success); background: var(--lms-mint-soft); }
.lms-option.is-correct .lms-option__marker { border-color: var(--lms-mint-success); background: var(--lms-mint-success); color: #fff; }
.lms-option.is-incorrect { border-color: var(--lms-ruby-danger); background: var(--lms-ruby-soft); }
.lms-option.is-incorrect .lms-option__marker { border-color: var(--lms-ruby-danger); background: var(--lms-ruby-danger); color: #fff; }

.lms-option:has(.lms-option__input:focus-visible) {
    outline: var(--lms-focus-outline);
    outline-offset: var(--lms-focus-offset);
}

/* ---------------------------------------------------------------------------
   Data — DESIGN.md §5.3, §6.4
   High-density ledger. Money and count columns get tabular figures so the
   decimal points line up down the column.
   --------------------------------------------------------------------------- */
.lms-table-wrap {
    background: var(--lms-surface-card);
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-card);
    overflow: hidden;
}

.lms-table-scroll { overflow-x: auto; }

.lms-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--lms-body-sm-size);
}

.lms-table thead th {
    padding: var(--lms-space-md) var(--lms-space-lg);
    background: var(--lms-surface-muted);
    color: var(--lms-text-subtle);
    font: var(--lms-micro-weight) var(--lms-micro-size)/var(--lms-micro-lh) var(--lms-font-sans);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    text-align: start;
    white-space: nowrap;
    border-block-end: 1px solid var(--lms-border-card);
}

.lms-table tbody td {
    padding: var(--lms-space-md) var(--lms-space-lg);
    color: var(--lms-text-strong);
    border-block-end: 1px solid var(--lms-border-card);
    vertical-align: middle;
}

.lms-table tbody tr:last-child td { border-block-end: 0; }
.lms-table tbody tr:hover td { background: var(--lms-surface-accent-soft); }

.lms-table .lms-num,
.lms-table [data-numeric] {
    text-align: end;
    font-variant-numeric: tabular-nums;
    font-feature-settings: var(--lms-numeric-tabular);
    font-weight: var(--lms-weight-medium);
    white-space: nowrap;
}

/* DERIVED — pagination. The design system has no spec for it and 15 views page.
   Built on Bootstrap's .pagination markup so no view has to change. */
.lms-pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--lms-space-md);
    flex-wrap: wrap;
    padding: var(--lms-space-md) var(--lms-space-lg);
    border-block-start: 1px solid var(--lms-border-card);
}

.lms-pagination__summary {
    font-size: var(--lms-caption-size);
    color: var(--lms-text-subtle);
    font-variant-numeric: tabular-nums;
}

.pagination { --bs-pagination-padding-x: 12px; --bs-pagination-padding-y: 6px; gap: var(--lms-space-xs); margin: 0; }
.pagination .page-link { border-radius: var(--lms-radius-md); font-size: var(--lms-body-sm-size); font-variant-numeric: tabular-nums; }
.pagination .page-item.active .page-link { font-weight: var(--lms-weight-semibold); }

/* Chevrons in a pager point along the reading direction; RTL flips them. See
   the global icon-flip rule at the end of this file. */

/* DERIVED — notification menu. The design system specifies dropdown elevation
   (§4.3) but no menu component, and this app has a bell, a wallet menu and an
   account menu that all need to look like the same thing. */
.lms-menu {
    width: min(360px, calc(100vw - 2rem));
    max-height: 420px;
    overflow-y: auto;
    padding: 0;
    border-radius: var(--lms-radius-panel);
}

.lms-menu__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--lms-space-md);
    padding: var(--lms-space-md) var(--lms-space-lg);
    border-block-end: 1px solid var(--lms-border-card);
    position: sticky;
    top: 0;
    background: var(--lms-surface-card);
    z-index: 1;
}

.lms-menu__title {
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-semibold);
    color: var(--lms-text-heading);
}

.lms-menu__action {
    border: 0;
    padding: 0;
    background: none;
    color: var(--lms-primary);
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-medium);
    cursor: pointer;
}

.lms-menu__footer {
    display: block;
    padding: var(--lms-space-md);
    border-block-start: 1px solid var(--lms-border-card);
    color: var(--lms-primary);
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-semibold);
    text-align: center;
    text-decoration: none;
    position: sticky;
    bottom: 0;
    background: var(--lms-surface-card);
}

.lms-menu__footer:hover { background: var(--lms-surface-muted); text-decoration: none; }

.lms-notice {
    display: block;
    width: 100%;
    padding: var(--lms-space-md) var(--lms-space-lg);
    border: 0;
    border-block-end: 1px solid var(--lms-border-card);
    background: none;
    color: var(--lms-text-body);
    text-align: start;
    white-space: normal;
    cursor: pointer;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-notice:hover { background: var(--lms-surface-muted); }

.lms-notice__top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--lms-space-sm);
}

.lms-notice__title {
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-strong);
    margin: 0;
}

.lms-notice__time {
    flex-shrink: 0;
    font-size: var(--lms-caption-size);
    color: var(--lms-text-subtle);
    font-variant-numeric: tabular-nums;
}

.lms-notice__body {
    margin: var(--lms-space-xxs) 0 0;
    font-size: var(--lms-caption-size);
    color: var(--lms-text-subtle);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Unread is carried by a dot and by weight, never by background alone — a tint
   this faint is not a reliable signal, and it disappears entirely in dark mode. */
.lms-notice.is-unread { background: var(--lms-surface-accent-soft); }
.lms-notice.is-unread .lms-notice__title { font-weight: var(--lms-weight-semibold); color: var(--lms-text-heading); }

.lms-notice.is-unread .lms-notice__title::before {
    content: "";
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-inline-end: var(--lms-space-sm);
    border-radius: 50%;
    background: var(--lms-primary);
    vertical-align: middle;
}

/* DERIVED — inverse panel. A midnight card that carries white type: the welcome
   banner and the wallet widget. Its content sits above a decorative watermark,
   which is why the art is absolutely positioned and pointer-inert. */
.lms-panel-inverse {
    position: relative;
    overflow: hidden;
    border-radius: var(--lms-radius-card);
    background: linear-gradient(135deg, var(--lms-canvas-dark) 0%, var(--lms-canvas-dark-alt) 100%);
    color: rgba(255, 255, 255, 0.78);
    box-shadow: var(--lms-elev-2);
}

.lms-panel-inverse__art {
    position: absolute;
    inset-block-start: -20%;
    inset-inline-end: -10%;
    opacity: 0.1;
    pointer-events: none;
    rotate: -15deg;
}

.lms-panel-inverse__art img {
    height: 15rem;
    width: auto;
    border-radius: var(--lms-radius-xl);
    filter: grayscale(100%) brightness(1.5);
}

.lms-panel-inverse__art--icon {
    inset-block-start: auto;
    inset-block-end: -10%;
    inset-inline-end: -5%;
    opacity: 0.16;
    rotate: none;
    font-size: 8rem;
    color: #fff;
    line-height: 1;
}

/* Badge and secondary button variants for use on an inverse panel, where the
   normal surface tokens would put a light card on a dark ground. */
.lms-badge--on-inverse {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.24);
    color: #fff;
}

.lms-btn--on-inverse {
    background: rgba(255, 255, 255, 0.1);
    border-color: rgba(255, 255, 255, 0.32);
    color: #fff;
}

.lms-btn--on-inverse:hover:not(:disabled) { background: rgba(255, 255, 255, 0.18); color: #fff; }

/* A QR code is scanned by a camera, so its plate stays white in both themes.
   This is the one surface on the page that deliberately ignores the theme. */
.lms-qr-plate {
    display: inline-block;
    padding: var(--lms-space-sm);
    border-radius: var(--lms-radius-md);
    background: #fff;
    box-shadow: var(--lms-elev-1);
}

.lms-qr-plate img { display: block; }

/* Weekly activity bars. Heights arrive as percentages so the row can set its own
   height and the bars scale with it. */
.lms-sparkbars {
    display: flex;
    align-items: flex-end;
    gap: var(--lms-space-sm);
    height: 140px;
}

.lms-sparkbars__col {
    flex: 1 1 0;
    min-width: 0;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
}

.lms-sparkbars__value {
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-semibold);
    color: var(--lms-primary);
    font-variant-numeric: tabular-nums;
}

.lms-sparkbars__bar {
    width: 100%;
    max-width: 34px;
    min-height: 3px;
    border-start-start-radius: var(--lms-radius-xs);
    border-start-end-radius: var(--lms-radius-xs);
    background: var(--lms-primary);
    transition: height var(--lms-dur-slow) var(--lms-ease-out);
}

.lms-sparkbars__label {
    margin-block-start: var(--lms-space-xs);
    font-size: 11px;
    color: var(--lms-text-subtle);
}

/* Compact list row — used by every "recent things" panel on the dashboard. */
.lms-rows { display: flex; flex-direction: column; }

.lms-row {
    display: flex;
    align-items: center;
    gap: var(--lms-space-md);
    padding-block: var(--lms-space-md);
    color: var(--lms-text-body);
    text-decoration: none;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-rows .lms-row + .lms-row { border-block-start: 1px solid var(--lms-border-card); }
.lms-row:hover { background: var(--lms-surface-muted); text-decoration: none; }

.lms-row--bordered {
    padding: var(--lms-space-sm) var(--lms-space-md);
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-panel);
}

.lms-row--warning {
    padding: var(--lms-space-md);
    border-radius: var(--lms-radius-panel);
    background: var(--lms-amber-soft);
}

.lms-row--warning:hover { background: var(--lms-amber-soft); filter: brightness(0.98); }

/* Small media thumbnail for a list row. */
.lms-thumb {
    inline-size: 96px;
    block-size: 64px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: var(--lms-radius-md);
    background: var(--lms-primary-bg-subdued);
    color: var(--lms-primary);
    font-size: 1.25rem;
}

.lms-thumb--sm { inline-size: 44px; block-size: 44px; }
.lms-thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; }

/* Count bubble pinned to a bell or an avatar. */
.lms-badge-dot {
    position: absolute;
    inset-block-start: 0;
    inset-inline-end: 0;
    min-width: 17px;
    padding-inline: 4px;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-ruby-danger);
    color: #fff;
    font-size: 10px;
    font-weight: var(--lms-weight-semibold);
    line-height: 17px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* DERIVED — avatar. Referenced by CourseCard's teacher chip and §4.2's radii
   guidance, but never specified on its own. */
.lms-avatar {
    --lms-avatar-size: 36px;
    inline-size: var(--lms-avatar-size);
    block-size: var(--lms-avatar-size);
    flex: 0 0 var(--lms-avatar-size);
    display: inline-grid;
    place-items: center;
    border-radius: 50%;
    background: var(--lms-primary-bg-subdued);
    color: var(--lms-primary);
    font-size: calc(var(--lms-avatar-size) * 0.4);
    font-weight: var(--lms-weight-semibold);
    letter-spacing: -0.01em;
    text-transform: uppercase;
    overflow: hidden;
    object-fit: cover;
}

.lms-avatar--sm { --lms-avatar-size: 28px; }
.lms-avatar--lg { --lms-avatar-size: 48px; }
.lms-avatar--xl { --lms-avatar-size: 72px; }
.lms-avatar--ring { border: 2px solid var(--lms-surface-card); box-shadow: 0 0 0 1px var(--lms-border-card); }
.lms-avatar--teacher { background: var(--lms-teal-soft); color: var(--lms-teal-teacher); }

.lms-avatar img { inline-size: 100%; block-size: 100%; object-fit: cover; }

/* Teacher identity chip — a floating avatar plus name, used on course cards. */
.lms-teacher-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--lms-space-sm);
    /* Logical, not physical: the avatar sits at the start edge in both directions. */
    padding-block: 4px;
    padding-inline: 4px 12px;
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-pill);
    background: var(--lms-surface-card);
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-strong);
    text-decoration: none;
    box-shadow: var(--lms-elev-1);
}

[dir="rtl"] .lms-teacher-chip { padding: 4px 4px 4px 12px; }

/* DERIVED — empty state. Every list in the app needs one and none was specified. */
.lms-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--lms-space-md);
    padding: var(--lms-space-huge) var(--lms-space-xl);
    color: var(--lms-text-body);
}

.lms-empty__icon {
    width: 64px;
    height: 64px;
    display: grid;
    place-items: center;
    border-radius: var(--lms-radius-xl);
    background: var(--lms-surface-muted);
    color: var(--lms-text-subtle);
    font-size: 28px;
}

.lms-empty__title {
    font: var(--lms-heading-sm-weight) var(--lms-heading-sm-size)/var(--lms-heading-sm-lh) var(--lms-font-sans);
    color: var(--lms-text-heading);
}

.lms-empty__body { max-width: 44ch; font-size: var(--lms-body-sm-size); }

/* Danger zone — REQ-OFR-026. An irreversible action is a deliberate, named,
   confirmed thing; it does not live in a row-action icon cluster. The panel is
   its own ruby-bordered surface, the consequence is spelled out in full, and
   the button names the object rather than saying "Delete". */
.lms-danger-zone {
    padding: var(--lms-pad-card-compact);
    border-radius: var(--lms-radius-card);
    background: var(--lms-ruby-soft);
    border: 1px solid var(--lms-ruby-danger);
}

.lms-danger-zone__head {
    display: flex;
    align-items: center;
    gap: 10px;
}

.lms-danger-zone__head > .bi {
    color: var(--lms-ruby-danger);
    font-size: 16px;
}

.lms-danger-zone__title {
    margin: 0;
    font: var(--lms-heading-sm-weight) var(--lms-heading-sm-size)/var(--lms-heading-sm-lh) var(--lms-font-sans);
    color: var(--lms-text-heading);
}

.lms-danger-zone__body {
    margin: 10px 0 0;
    max-width: 560px;
    font-size: var(--lms-body-sm-size);
    line-height: 1.55;
    color: var(--lms-text-body);
    text-wrap: pretty;
}

/* Names what the action reaches. Ambiguity about scope is the failure mode
   this panel exists to prevent. */
.lms-danger-zone__target {
    margin-block-start: var(--lms-space-md);
    padding: 10px 14px;
    border-radius: var(--lms-radius-md);
    background: var(--lms-surface-card);
    border: 1px solid var(--lms-border-card);
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-semibold);
    color: var(--lms-text-heading);
}

.lms-danger-zone__target-label {
    display: block;
    margin-block-end: 4px;
    font-size: var(--lms-micro-size);
    font-weight: var(--lms-weight-semibold);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    color: var(--lms-text-subtle);
}

.lms-danger-zone__actions {
    display: flex;
    align-items: center;
    gap: var(--lms-space-sm);
    flex-wrap: wrap;
    margin-block-start: var(--lms-space-lg);
}

/* DERIVED — stepper. The checkout flow already renders one; it had no spec. */
.lms-steps {
    display: flex;
    align-items: center;
    gap: var(--lms-space-sm);
    flex-wrap: wrap;
}

.lms-step {
    display: inline-flex;
    align-items: center;
    gap: var(--lms-space-sm);
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-subtle);
}

.lms-step__badge {
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    border: 1px solid var(--lms-border-card);
    background: var(--lms-surface-card);
    color: var(--lms-text-subtle);
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-semibold);
    font-variant-numeric: tabular-nums;
}

.lms-step.is-active { color: var(--lms-text-heading); }
.lms-step.is-active .lms-step__badge { background: var(--lms-primary); border-color: var(--lms-primary); color: var(--lms-on-primary); }
.lms-step.is-done { color: var(--lms-mint-success); }
.lms-step.is-done .lms-step__badge { background: var(--lms-mint-success); border-color: var(--lms-mint-success); color: #fff; }

.lms-step__rule {
    flex: 1 1 24px;
    min-width: 24px;
    height: 1px;
    background: var(--lms-border-card);
}

/* ---------------------------------------------------------------------------
   Feedback — DESIGN.md §5.5
   The toast stack and confirmation modal already exist in lms-theme.css and are
   wired to lms-ui.js; only the in-flow banner is new here.
   --------------------------------------------------------------------------- */
.lms-banner {
    display: flex;
    align-items: flex-start;
    gap: var(--lms-space-md);
    padding: var(--lms-space-lg);
    border: 1px solid var(--lms-border-card);
    border-inline-start: 4px solid var(--lms-primary);
    border-radius: var(--lms-radius-panel);
    background: var(--lms-surface-accent-soft);
    color: var(--lms-text-strong);
    font-size: var(--lms-body-sm-size);
}

.lms-banner__icon { font-size: 18px; color: var(--lms-primary); line-height: 1.4; }
.lms-banner__title { font-weight: var(--lms-weight-semibold); color: var(--lms-text-heading); margin-block-end: 2px; }
.lms-banner__body { flex: 1; min-width: 0; }

.lms-banner--success { border-inline-start-color: var(--lms-mint-success); background: var(--lms-mint-soft); }
.lms-banner--success .lms-banner__icon { color: var(--lms-mint-success); }
.lms-banner--warning { border-inline-start-color: var(--lms-amber-warning); background: var(--lms-amber-soft); }
.lms-banner--warning .lms-banner__icon { color: var(--lms-amber-warning); }
.lms-banner--danger { border-inline-start-color: var(--lms-ruby-danger); background: var(--lms-ruby-soft); }
.lms-banner--danger .lms-banner__icon { color: var(--lms-ruby-danger); }

/* Defensive, and the rule the ASP.NET project template ships. The validation summary
   tag helper suppresses its own output on a clean first render, so nothing is hidden
   today — but jQuery-validate re-adds the container and marks it
   `validation-summary-valid` once a client-side pass clears, and without this the
   banner would then sit on the form empty. */
.validation-summary-valid { display: none !important; }

.lms-banner ul { margin: 0; padding-inline-start: var(--lms-space-lg); }
.lms-banner ul:only-child { padding-inline-start: 0; list-style: none; }

/* A dismissible banner has to keep Bootstrap's `.alert` class, because the dismiss
   plugin resolves its target with closest('.alert'). This stops .alert painting its
   own background, border and padding underneath the banner. */
.lms-banner.alert {
    --bs-alert-bg: transparent;
    --bs-alert-border-color: transparent;
    --bs-alert-color: inherit;
    --bs-alert-border: 0;
    margin-block-end: 0;
}

.lms-banner.alert-dismissible { padding-inline-end: var(--lms-space-xxl); }
.lms-banner.alert-dismissible .btn-close { padding: var(--lms-space-md); }

/* ---------------------------------------------------------------------------
   Learning — DESIGN.md §5.4, §6.2
   --------------------------------------------------------------------------- */
.lms-module {
    background: var(--lms-surface-card);
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-panel);
    overflow: hidden;
    margin-block-end: var(--lms-space-md);
}

.lms-module__header {
    display: flex;
    align-items: center;
    gap: var(--lms-space-md);
    width: 100%;
    padding: var(--lms-pad-tree-header);
    border: 0;
    background: var(--lms-surface-card);
    color: var(--lms-text-heading);
    font-size: var(--lms-body-md-size);
    font-weight: var(--lms-weight-semibold);
    text-align: start;
    cursor: pointer;
}

.lms-module__header:hover { background: var(--lms-surface-muted); }
.lms-module__title { flex: 1; min-width: 0; }

.lms-module__meta {
    font-size: var(--lms-caption-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-subtle);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.lms-module__body { border-block-start: 1px solid var(--lms-border-card); }

/* The chevron follows the open state. Rotating rather than swapping glyphs keeps
   it animated, and -180deg in RTL so it turns the same way the eye expects. */
.lms-module__chevron {
    flex-shrink: 0;
    color: var(--lms-text-subtle);
    transition: rotate var(--lms-dur-base) var(--lms-ease-standard);
}

.lms-module__header.collapsed .lms-module__chevron { rotate: -90deg; }
[dir="rtl"] .lms-module__header.collapsed .lms-module__chevron { rotate: 90deg; }

/* A lesson row inside a module has a divider; the last one does not. */
.lms-module__body .lms-lesson + .lms-lesson { border-block-start: 1px solid var(--lms-border-card); }

/* Lesson row states: completed (mint check), active (indigo bar + tint),
   locked (amber padlock + prerequisite badge). */
.lms-lesson {
    display: flex;
    align-items: center;
    gap: var(--lms-space-md);
    padding: var(--lms-pad-tree-item);
    border-inline-start: 3px solid transparent;
    color: var(--lms-text-strong);
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-lesson:hover { background: var(--lms-surface-muted); color: var(--lms-text-strong); text-decoration: none; }

.lms-lesson__marker {
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    display: grid;
    place-items: center;
    border: 1px solid var(--lms-border-field);
    border-radius: 50%;
    background: transparent;
    color: var(--lms-text-subtle);
    font-size: 10px;
}

.lms-lesson__title {
    flex: 1;
    min-width: 0;
    font-size: var(--lms-body-sm-size);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lms-lesson__runtime {
    font-family: var(--lms-font-mono);
    font-size: 12px;
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-subtle);
    font-variant-numeric: tabular-nums;
}

.lms-lesson.is-active {
    border-inline-start-color: var(--lms-primary);
    background: var(--lms-primary-bg-subdued);
}

.lms-lesson.is-active .lms-lesson__title { color: var(--lms-primary); font-weight: var(--lms-weight-semibold); }
.lms-lesson.is-active .lms-lesson__marker { background: var(--lms-primary); border-color: transparent; color: #fff; }

.lms-lesson.is-completed .lms-lesson__marker { background: var(--lms-mint-success); border-color: transparent; color: #fff; }

.lms-lesson.is-locked { cursor: not-allowed; }
.lms-lesson.is-locked:hover { background: transparent; }
.lms-lesson.is-locked .lms-lesson__title { color: var(--lms-text-subtle); }
.lms-lesson.is-locked .lms-lesson__marker { color: var(--lms-amber-warning); }

.lms-lesson__prereq {
    display: inline-block;
    margin-block-start: var(--lms-space-xs);
    padding: 2px 8px;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-amber-soft);
    color: var(--lms-amber-warning);
    font-size: 10px;
    font-weight: var(--lms-weight-semibold);
    letter-spacing: 0.03em;
    text-transform: uppercase;
    white-space: normal;
}

/* ---------------------------------------------------------------------------
   Curriculum tree, Learning Room variant
   The public course page rebuilds its tree from .lms-module / .lms-lesson. This
   one cannot: its markup carries contracts that other code reads — `.lesson-item`
   and `.locked` are asserted by the E2E suite (TC-008), `.accordion-collapse` is
   what the deep-link initialiser opens, and the Alpine click handler is bound to
   the <li>. So the Bootstrap accordion structure stays exactly as it is and this
   block re-skins it from the outside. Same Mintlify result, no moving parts
   touched.
   --------------------------------------------------------------------------- */
.lms-tree .accordion-item {
    border: 0;
    border-block-end: 1px solid var(--lms-border-card);
    background: transparent;
}

.lms-tree .accordion-button {
    padding: var(--lms-pad-tree-header);
    background: var(--lms-surface-card);
    color: var(--lms-text-heading);
    font-size: var(--lms-body-md-size);
    font-weight: var(--lms-weight-semibold);
    box-shadow: none;
}

.lms-tree .accordion-button:not(.collapsed) {
    background: var(--lms-surface-card);
    color: var(--lms-text-heading);
}

.lms-tree .accordion-button:hover { background: var(--lms-surface-muted); }
.lms-tree .accordion-button:focus { box-shadow: var(--lms-focus-ring); }
.lms-tree .accordion-body { padding: 0; }

/* Lesson rows. The marker column is a fixed 20px so titles align down the list
   whatever glyph a row happens to carry. */
.lms-tree .list-group-item {
    padding: var(--lms-pad-tree-item);
    border: 0;
    background: transparent;
    color: var(--lms-text-strong);
    /* Transparent until active, so selecting a lesson does not shift the row. */
    border-inline-start: 3px solid transparent;
}

.lms-tree .lesson-item {
    cursor: pointer;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-tree .lesson-item:hover { background: var(--lms-surface-muted); }

/* The Alpine binding adds these two Bootstrap classes to mark the current lesson.
   Kept as the selector so the view's :class expression does not have to change. */
.lms-tree .lesson-item.bg-light {
    background: var(--lms-primary-bg-subdued) !important;
    border-inline-start-color: var(--lms-primary);
}

.lms-tree .lesson-item.locked { cursor: not-allowed; }
.lms-tree .lesson-item.locked:hover { background: transparent; }

.lms-tree .lesson-item__title {
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-medium);
    color: var(--lms-text-strong);
}

.lms-tree .lesson-item.bg-light .lesson-item__title {
    color: var(--lms-primary);
    font-weight: var(--lms-weight-semibold);
}

.lms-tree .lesson-item.locked .lesson-item__title { color: var(--lms-text-subtle); }

.lms-tree .lesson-item__meta {
    font-size: var(--lms-caption-size);
    color: var(--lms-text-subtle);
    font-variant-numeric: tabular-nums;
}

/* Assignment and quiz rows sit under their lesson on the muted surface, indented
   along the inline axis so the hierarchy survives RTL.
   Scoped under .lms-tree deliberately: `.lms-tree .list-group-item` above sets a
   transparent background at two classes of specificity, so a single-class rule
   here would lose to it and the sub-rows would render flat. */
.lms-tree .lms-tree__sub {
    padding-inline-start: var(--lms-space-xxl);
    background: var(--lms-surface-muted);
}

.lms-tree__section {
    padding: var(--lms-space-md) var(--lms-space-lg) var(--lms-space-xs);
    border-block-start: 1px solid var(--lms-border-card);
    background: var(--lms-surface-muted);
    font: var(--lms-micro-weight) var(--lms-micro-size)/var(--lms-micro-lh) var(--lms-font-sans);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    color: var(--lms-text-subtle);
}

/* Cinema stage — 16:9, black, rounded, clipped. */
.lms-stage {
    position: relative;
    aspect-ratio: 16 / 9;
    width: 100%;
    border-radius: var(--lms-radius-card);
    background: #000;
    overflow: hidden;
    box-shadow: var(--lms-elev-2);
}

.lms-stage > iframe,
.lms-stage > video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.lms-stage__watermark {
    position: absolute;
    inset-block-start: var(--lms-space-md);
    inset-inline-end: var(--lms-space-md);
    z-index: 3;
    padding: 4px 10px;
    border-radius: var(--lms-radius-sm);
    background: rgba(0, 0, 0, 0.45);
    color: rgba(255, 255, 255, 0.75);
    font-family: var(--lms-font-mono);
    font-size: 11px;
    letter-spacing: 0.04em;
    /* Inert and unselectable: it identifies the viewer on a leaked recording, so
       it must not be something the page lets anyone interact with or copy off. */
    pointer-events: none;
    user-select: none;
}

/* Play affordance over a lesson thumbnail. */
.lms-stage__play {
    inline-size: 80px;
    block-size: 80px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
    font-size: 3rem;
    transition: transform var(--lms-dur-base) var(--lms-ease-standard),
                background-color var(--lms-dur-base) var(--lms-ease-standard);
}

/* Hover lives on the parent, so moving the pointer anywhere over the poster
   grows the button — chasing an 80px target was the previous behaviour. */
*:hover > .lms-stage__play {
    transform: scale(1.08);
    background: var(--lms-primary);
}

.lms-stage__loading {
    position: absolute;
    inset: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.88);
    color: #fff;
    text-align: center;
}

/* Countdown chip — DESIGN.md §6.3. Server time only; never a client clock. */
.lms-timer {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-md);
    background: var(--lms-surface-muted);
    color: var(--lms-text-strong);
}

.lms-timer__label {
    font-size: var(--lms-micro-size);
    font-weight: var(--lms-weight-semibold);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    opacity: 0.75;
}

.lms-timer__value {
    font-family: var(--lms-font-mono);
    font-size: 18px;
    font-weight: var(--lms-weight-semibold);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}

.lms-timer.is-warning {
    background: var(--lms-ruby-soft);
    border-color: var(--lms-ruby-danger);
    color: var(--lms-ruby-danger);
}

/* Question navigator grid. */
.lms-palette {
    display: grid;
    grid-template-columns: repeat(var(--lms-palette-cols, 5), minmax(0, 1fr));
    gap: var(--lms-space-sm);
}

.lms-palette__item {
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-md);
    background: var(--lms-surface-card);
    color: var(--lms-text-body);
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-semibold);
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    transition: background-color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-palette__item:hover { background: var(--lms-surface-muted); }
.lms-palette__item.is-answered { background: var(--lms-primary-bg-subdued); border-color: var(--lms-primary-bg-subdued-hover); color: var(--lms-primary); }
.lms-palette__item.is-flagged { background: var(--lms-amber-soft); border-color: #fde68a; color: var(--lms-amber-warning); }
.lms-palette__item.is-current { background: var(--lms-primary); border-color: var(--lms-primary); color: var(--lms-on-primary); }

/* ---------------------------------------------------------------------------
   Navigation — DESIGN.md §5, §6
   --------------------------------------------------------------------------- */
.lms-breadcrumbs {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--lms-space-sm);
    font-size: var(--lms-body-sm-size);
    color: var(--lms-text-subtle);
}

.lms-breadcrumbs a { color: var(--lms-text-subtle); text-decoration: none; }
.lms-breadcrumbs a:hover { color: var(--lms-primary); }
.lms-breadcrumbs__current { color: var(--lms-text-heading); font-weight: var(--lms-weight-medium); }

.lms-tabs {
    display: flex;
    gap: var(--lms-space-xl);
    border-block-end: 1px solid var(--lms-border-card);
}

.lms-tabs__link {
    display: inline-flex;
    align-items: center;
    gap: var(--lms-space-sm);
    padding: 0 0 var(--lms-space-md);
    margin-block-end: -1px;
    border: 0;
    border-block-end: 2px solid transparent;
    background: transparent;
    color: var(--lms-text-body);
    font-size: var(--lms-body-sm-size);
    font-weight: var(--lms-weight-medium);
    text-decoration: none;
    cursor: pointer;
    transition: color var(--lms-dur-fast) var(--lms-ease-standard);
}

.lms-tabs__link:hover { color: var(--lms-primary); text-decoration: none; }

/* `.active` is here because Bootstrap's tab plugin owns that class and its
   _elemIsActive() looks for exactly it — a tab bar driven by data-bs-toggle
   would otherwise have to be styled by aria alone. */
.lms-tabs__link.is-active,
.lms-tabs__link.active,
.lms-tabs__link[aria-selected="true"] {
    border-block-end-color: var(--lms-primary);
    color: var(--lms-primary);
    font-weight: var(--lms-weight-semibold);
}

.lms-tabs__count {
    padding-inline: 6px;
    border-radius: var(--lms-radius-pill);
    background: var(--lms-surface-muted);
    color: var(--lms-text-subtle);
    font-size: 11px;
    font-weight: var(--lms-weight-semibold);
    line-height: 18px;
    font-variant-numeric: tabular-nums;
}

.lms-tabs__link.is-active .lms-tabs__count,
.lms-tabs__link.active .lms-tabs__count { background: var(--lms-primary-bg-subdued); color: var(--lms-primary); }

/* Four operator tabs do not fit a phone. Scrolling the rail is what stops them
   wrapping into a second row with a stray underline under it. */
.lms-tabs--scroll {
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
}

.lms-tabs--scroll::-webkit-scrollbar { display: none; }
.lms-tabs--scroll .lms-tabs__link { white-space: nowrap; }

/* Pill variant for compact switchers. */
.lms-tabs--pill {
    gap: var(--lms-space-xs);
    padding: 4px;
    border: 0;
    border-radius: var(--lms-radius-md);
    background: var(--lms-surface-muted);
}

.lms-tabs--pill .lms-tabs__link {
    padding: 7px 14px;
    margin: 0;
    border: 0;
    border-radius: var(--lms-radius-sm);
}

.lms-tabs--pill .lms-tabs__link.is-active {
    background: var(--lms-surface-card);
    color: var(--lms-text-heading);
    box-shadow: var(--lms-shadow-sm);
}

/* ---------------------------------------------------------------------------
   DERIVED — uploader
   The teacher lesson form drives Uppy, whose Dashboard ships its own palette.
   These map its CSS variables onto ours so an upload panel does not arrive as a
   differently-coloured island in the middle of a themed form.
   --------------------------------------------------------------------------- */
.uppy-Dashboard-inner,
.uppy-Root {
    --uppy-c-primary: var(--lms-primary);
    font-family: var(--lms-font-sans) !important;
}

.uppy-Dashboard-inner {
    background: var(--lms-surface-card) !important;
    border: 1px solid var(--lms-border-card) !important;
    border-radius: var(--lms-radius-card) !important;
}

.uppy-Dashboard-AddFiles {
    border-color: var(--lms-border-field) !important;
    border-radius: var(--lms-radius-panel) !important;
}

.uppy-Dashboard-AddFiles-title,
.uppy-Dashboard-browse { color: var(--lms-text-body) !important; }
.uppy-Dashboard-browse { color: var(--lms-primary) !important; }

.uppy-StatusBar-actionBtn--upload {
    background: var(--lms-primary) !important;
    border-radius: var(--lms-radius-control) !important;
}

.lms-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--lms-space-sm);
    padding: var(--lms-space-xl);
    border: 1px dashed var(--lms-border-field);
    border-radius: var(--lms-radius-panel);
    background: var(--lms-surface-muted);
    color: var(--lms-text-body);
    text-align: center;
    font-size: var(--lms-body-sm-size);
}

.lms-dropzone.is-dragging { border-color: var(--lms-primary); background: var(--lms-primary-bg-subdued); }

/* ---------------------------------------------------------------------------
   DERIVED — report card
   DESIGN.md §6.4 asks for Chart.js reports but specifies no shell to put one in.
   Ten partials in Views/Shared share the same structure — a filter head, an
   error slot, a canvas or a table, an empty state — and analytics-charts.js
   finds every moving part by [data-role] or [data-chart], never by class. So
   this skins them from the outside, the way .lms-tree skins the Learning Room
   accordion, and no JS contract is touched.
   --------------------------------------------------------------------------- */
.lms-report__head {
    padding: var(--lms-pad-card);
    border-block-end: 1px solid var(--lms-border-card);
}

.lms-report__title {
    font: var(--lms-heading-sm-weight) var(--lms-heading-sm-size)/var(--lms-heading-sm-lh) var(--lms-font-sans);
    color: var(--lms-text-heading);
    margin: 0;
}

.lms-report__meta {
    font-size: var(--lms-caption-size);
    color: var(--lms-text-subtle);
    font-variant-numeric: tabular-nums;
}

.lms-report__value {
    color: var(--lms-text-heading);
    font-weight: var(--lms-weight-semibold);
    font-variant-numeric: tabular-nums;
}

.lms-report__hint {
    margin: var(--lms-space-xs) 0 var(--lms-space-lg);
    font-size: var(--lms-body-sm-size);
    color: var(--lms-text-body);
}

.lms-report__body { padding: var(--lms-pad-card); }

/* A dense filter bar: full-size fields would push the chart below the fold. */
.lms-report__filters .lms-input,
.lms-report__filters .lms-select {
    padding: 8px 12px;
    font-size: var(--lms-body-sm-size);
}

.lms-report__filters .lms-label {
    font-size: var(--lms-caption-size);
    color: var(--lms-text-subtle);
}

/* Section label above a canvas — "over time", "by teacher". */
.lms-report__label {
    margin-block-end: var(--lms-space-sm);
    font: var(--lms-micro-weight) var(--lms-micro-size)/var(--lms-micro-lh) var(--lms-font-sans);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    color: var(--lms-text-subtle);
}

.lms-report__canvas {
    position: relative;
    height: 260px;
}

.lms-report__canvas--tall { height: 340px; }

.lms-report__empty {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--lms-space-sm);
    color: var(--lms-text-subtle);
    font-size: var(--lms-body-sm-size);
}

.lms-report__rule {
    margin-block: var(--lms-space-xl);
    border: 0;
    border-block-start: 1px solid var(--lms-border-card);
    opacity: 1;
}

/* KPI tiles above the platform-fee trend. */
.lms-report__kpi {
    height: 100%;
    padding: var(--lms-space-md);
    border: 1px solid var(--lms-border-card);
    border-radius: var(--lms-radius-panel);
    background: var(--lms-surface-muted);
}

.lms-report__kpi-label {
    font: var(--lms-micro-weight) var(--lms-micro-size)/var(--lms-micro-lh) var(--lms-font-sans);
    letter-spacing: var(--lms-micro-track);
    text-transform: uppercase;
    color: var(--lms-text-subtle);
}

.lms-report__kpi-value {
    margin-block-start: 2px;
    font-size: var(--lms-heading-sm-size);
    font-weight: var(--lms-weight-semibold);
    color: var(--lms-text-heading);
    font-variant-numeric: tabular-nums;
}

.lms-report__kpi-value--mint  { color: var(--lms-mint-success); }
.lms-report__kpi-value--amber { color: var(--lms-amber-warning); }
.lms-report__kpi-value--ruby  { color: var(--lms-ruby-danger); }

/* analytics-charts.js builds result rows in JS and tags numeric cells `text-end`
   — a Bootstrap utility, so it flips correctly in RTL. Giving those cells the
   same tabular figures the static header uses is what keeps a column of money
   aligned whether the markup came from Razor or from a fetch. */
.lms-report .lms-table .text-end {
    font-variant-numeric: tabular-nums;
    font-feature-settings: var(--lms-numeric-tabular);
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   DERIVED — chart palette
   DESIGN.md §6.4 calls for Chart.js reports but specifies no series colours.
   These are published as custom properties so analytics-charts.js reads them
   from the stylesheet rather than carrying a second palette of its own — the
   series colour of a donut and the badge beside it can then never disagree.
   The ramp opens on the brand hues and continues into distinguishable,
   AA-legible companions on both canvases.
   --------------------------------------------------------------------------- */
:root {
    --lms-chart-1: #533afd;
    --lms-chart-2: #0c8c5e;
    --lms-chart-3: #f97316;
    --lms-chart-4: #0d9488;
    --lms-chart-5: #ea2261;
    --lms-chart-6: #d97706;
    --lms-chart-7: #6366f1;
    --lms-chart-8: #0891b2;
    --lms-chart-9: #a855f7;
    --lms-chart-10: #65a30d;
    --lms-chart-11: #db2777;
    --lms-chart-12: #475569;
    --lms-chart-other: #94a3b8;
    --lms-chart-grid: rgba(13, 37, 61, 0.08);
    --lms-chart-tick: #50617a;
}

[data-bs-theme="dark"] {
    --lms-chart-1: #8b5cf6;
    --lms-chart-2: #34d399;
    --lms-chart-3: #fb923c;
    --lms-chart-4: #2dd4bf;
    --lms-chart-5: #fb7185;
    --lms-chart-6: #fbbf24;
    --lms-chart-7: #818cf8;
    --lms-chart-8: #22d3ee;
    --lms-chart-9: #c084fc;
    --lms-chart-10: #a3e635;
    --lms-chart-11: #f472b6;
    --lms-chart-12: #94a3b8;
    --lms-chart-other: #62666d;
    --lms-chart-grid: rgba(255, 255, 255, 0.1);
    --lms-chart-tick: #a2a0b0;
}

/* ---------------------------------------------------------------------------
   Global rules — DESIGN.md §7
   --------------------------------------------------------------------------- */

/* Keyboard focus is explicit everywhere: 2px in the primary colour, 2px offset. */
*:focus-visible {
    outline: var(--lms-focus-outline);
    outline-offset: var(--lms-focus-offset);
}

/* Directional icons follow the reading direction. Without this, "next" points
   back the way you came on every Arabic page. */
[dir="rtl"] .bi-chevron-right,
[dir="rtl"] .bi-chevron-left,
[dir="rtl"] .bi-arrow-right,
[dir="rtl"] .bi-arrow-left,
[dir="rtl"] .bi-arrow-right-short,
[dir="rtl"] .bi-arrow-left-short,
[dir="rtl"] .bi-arrow-bar-right,
[dir="rtl"] .bi-arrow-bar-left,
[dir="rtl"] .bi-box-arrow-right,
[dir="rtl"] .bi-box-arrow-left,
[dir="rtl"] .bi-chevron-double-right,
[dir="rtl"] .bi-chevron-double-left,
[dir="rtl"] .bi-caret-right-fill,
[dir="rtl"] .bi-caret-left-fill {
    display: inline-block;
    transform: rotate(180deg);
}

/* A play glyph is a physical shape, not a direction — it must not flip. Listed
   explicitly because it lives in the same icon family as the arrows above. */
[dir="rtl"] .bi-play-fill,
[dir="rtl"] .bi-play-circle,
[dir="rtl"] .bi-play-circle-fill {
    transform: none;
}
