/*
 * The design system's own authored layer.
 * Every colour, radius and shadow used below reads a CSS custom property
 * the token <style> block already defines (--bs-primary, --bs-secondary,
 * --bs-border-radius, --bs-box-shadow-sm, --theme-muted-color,
 * --theme-focus-tint, ...) or a Bootstrap variable that already inherits
 * from one. Nothing here is a hardcoded colour or font stack — a theme
 * change in the admin screen reaches every rule in this file for free.
 *
 * No JS framework, no build step: this is hand-written CSS, loaded once,
 * after Bootstrap and after that token <style> block, in every layout
 * (see partials/theme_head.php).
 */

/* ---------- The structural half of the token layer ----------
 * App\Libraries\ThemeService emits every token that *changes when an
 * admin changes the theme* — colour roles, the type scale, radii,
 * shadows. The tokens below are the other half: measurements that have
 * no theme setting behind them and never should have one — the spacing
 * rhythm, the shell's dimensions, the two densities. They live here
 * rather than in ThemeService precisely because they are constants:
 * putting them through the theme cache would imply they vary, and they
 * don't. Between the two blocks there is no third place a value may come
 * from, and no rule below this line invents a number of its own.
 *
 * Spacing is a 4px rhythm expressed in rem. rem is stable at the
 * browser's root size here — the theme's base font size lands on `body`
 * (--bs-body-font-size), never on `html` — so a spacing step is the same
 * physical distance whatever the reader has chosen, which is what makes
 * it a rhythm rather than a multiplier. */

:root {
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4rem;

    /* The admin shell. The sidebar is wide enough for the longest Arabic
       group label at --text-sm without wrapping; it was measured, not
       guessed. */
    --shell-sidebar-width: 16.5rem;
    --shell-topbar-height: 3.25rem;

    /* Density: two settings, not a spectrum.
       Admin surfaces are worked in; student surfaces are read. */
    --density-row-y: var(--space-2);
    --density-section-gap: var(--space-6);

    /* The reading measure for student prose. Arabic sets wider than Latin
       at the same point size, so this is generous on purpose. */
    --measure: 68ch;
}

.account-shell,
.player {
    --density-row-y: var(--space-3);
    --density-section-gap: var(--space-7);
}

/* ---------- Typography ---------- */

h1, .h1, h2, .h2, h3, .h3 {
    font-weight: 600;
}

/* Two weights carry the whole system: 400 for everything that is read,
   600 for everything that is a label or a heading. A third weight is how
   a hierarchy stops being legible. */

.eyebrow {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-subtle);
}

/* Arabic has no letter case and no small-caps tradition — uppercasing
   does nothing to Arabic glyphs but the extra tracking still loosens the
   joins between them, so RTL gets the same eyebrow without it. */
[dir="rtl"] .eyebrow {
    letter-spacing: 0;
    text-transform: none;
}

.prose {
    max-width: var(--measure);
}

.prose p,
.prose ul,
.prose ol {
    margin-bottom: var(--space-4);
}

.lead {
    color: var(--theme-muted-color);
}

/* ---------- RTL-aware directional icons ----------
 * icon_helper.php / the &larr;/&rarr; back-links now emit
 * .icon-directional — a "this arrow's meaning depends on reading
 * direction" marker, not "this arrow is decorative." Mirrored under RTL so
 * a "back" arrow still points toward where "back" actually is. */
.icon-directional {
    display: inline-block;
}

[dir="rtl"] .icon-directional {
    transform: scaleX(-1);
}

/* ---------- Cards used as clickable summaries (course cards, My Learning
   entries) — a plain .card stays exactly as Bootstrap ships it (used
   throughout the admin panel for plain grouping, which must not move). */

.card-interactive {
    transition: transform .15s ease-in-out, box-shadow .15s ease-in-out;
    border-color: var(--bs-border-color);
}

.card-interactive:hover {
    transform: translateY(-2px);
    box-shadow: var(--bs-box-shadow);
}

.course-card .card-img-top {
    aspect-ratio: 16 / 9;
    object-fit: cover;
    background-color: var(--bs-tertiary-bg, var(--bs-body-bg));
}

.course-card-thumb-placeholder {
    aspect-ratio: 16 / 9;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bs-primary);
    background: linear-gradient(135deg, var(--bs-primary) 0%, var(--bs-secondary) 100%);
    color: #fff;
    font-size: 2rem;
}

/* ---------- Front page hero ---------- */

.hero {
    border-radius: var(--bs-border-radius-lg);
    padding: 3.5rem 2rem;
    background: var(--bs-tertiary-bg, var(--bs-body-bg));
    background: linear-gradient(160deg, color-mix(in srgb, var(--bs-primary) 8%, var(--bs-body-bg)) 0%, var(--bs-body-bg) 65%);
    border: 1px solid var(--bs-border-color);
}

.hero h1 {
    font-weight: 700;
    letter-spacing: -0.02em;
}

.hero .lead {
    max-width: 46rem;
    margin-inline: auto;
}

/* A restrained accent rule under a section heading — brand colour, never
   a hardcoded hex, and it flips side automatically under RTL because
   it's a logical (inline-start) border, not a physical (left) one. */
.section-heading {
    display: inline-block;
    padding-inline-start: .75rem;
    border-inline-start: 4px solid var(--bs-primary);
}

.value-prop-icon {
    width: 3rem;
    height: 3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-color: var(--bs-tertiary-bg, var(--bs-body-bg));
    background-color: color-mix(in srgb, var(--bs-primary) 12%, var(--bs-body-bg));
    color: var(--bs-primary);
    font-size: 1.25rem;
}

/* ---------- Empty states (partials/empty_state.php) ---------- */

.empty-state {
    text-align: center;
    padding: 3rem 1.5rem;
    color: var(--theme-muted-color);
}

.empty-state .empty-state-icon {
    font-size: 2.5rem;
    opacity: .35;
    margin-bottom: .75rem;
}

/* ---------- Course player: see the player block further down ---------- */

/* ---------- Admin tables: see the table block further down ---------- */

/* ---------- Error / maintenance pages ---------- */

.error-page {
    min-height: 70vh;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 2rem;
}

.error-page .error-code {
    font-size: 5rem;
    font-weight: 700;
    color: var(--bs-primary);
    line-height: 1;
}

/* ---------- Mobile: student-facing screens ---------- */

@media (max-width: 575.98px) {
    .hero {
        padding: 2rem 1.25rem;
    }

    .hero h1 {
        font-size: calc(var(--bs-body-font-size) * 1.75);
    }
}

/* ---------- Form submission loading state ----------
   Applied by public/assets/theme/app.js to whichever button triggered any
   form's submit — universal, so it never needs a per-form class. Uses
   currentColor for the spinner so it matches any Bootstrap button variant
   without reading a token, and an inline-start margin (not -end) so the
   spinner sits ahead of the label in both directions after RTL mirroring. */

.is-loading {
    opacity: .65;
    cursor: wait;
}

.is-loading::before {
    content: "";
    display: inline-block;
    width: .8em;
    height: .8em;
    margin-inline-end: .4em;
    vertical-align: -.125em;
    border: 2px solid currentColor;
    border-radius: 50%;
    border-top-color: transparent;
    animation: theme-spin .6s linear infinite;
}

@keyframes theme-spin {
    to {
        transform: rotate(360deg);
    }
}

/* ==========================================================================
   The admin shell
   ==========================================================================
   Replaces a wrapping top bar. Every measurement below is a token; the two
   Bootstrap facts this layout has to cooperate with are written down where
   they matter rather than discovered again later:

   1. `.offcanvas-lg` is `position: fixed` only inside a max-width:991.98px
      media query. At >=992px it is an ordinary element, so the sidebar can
      simply be a sticky flex item — no un-fixing required.
   2. At >=992px Bootstrap forces `background-color: transparent !important`
      on both `.offcanvas-lg` and its `.offcanvas-body`. Rather than fight
      that with an !important of our own, the sidebar's surface is painted
      by `.admin-body` underneath it and `.admin-main` paints the sunken
      shell back over its own column. Same result, no specificity war. */

.admin-shell {
    background-color: var(--surface-sunken);
}

.admin-topbar {
    position: sticky;
    top: 0;
    z-index: 1035; /* above the sticky table head (1), below the offcanvas backdrop (1050) */
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--shell-topbar-height);
    padding-inline: var(--space-4);
    background-color: var(--surface);
    border-bottom: 1px solid var(--rule);
}

.admin-brand {
    margin-inline-end: auto;
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
}

.admin-brand:hover {
    color: var(--brand);
}

.admin-topbar-toggle {
    padding: var(--space-1) var(--space-2);
    color: var(--ink-muted);
    border: 1px solid var(--rule);
}

.admin-topbar-account {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.admin-topbar-account form {
    margin: 0;
}

.admin-body {
    display: flex;
    align-items: stretch;
    min-height: calc(100vh - var(--shell-topbar-height));
    background-color: var(--surface);
}

.admin-main {
    flex: 1 1 auto;
    min-width: 0; /* without this a wide table stretches the flex item and the page scrolls sideways */
    padding: var(--space-6) var(--space-5);
    background-color: var(--surface-sunken);
}

@media (max-width: 991.98px) {
    .admin-main {
        padding: var(--space-5) var(--space-4);
    }
}

@media (min-width: 992px) {
    .admin-sidebar {
        flex: 0 0 var(--shell-sidebar-width);
        width: var(--shell-sidebar-width);
        position: sticky;
        top: var(--shell-topbar-height);
        height: calc(100vh - var(--shell-topbar-height));
    }

    .admin-sidebar-body {
        flex-grow: 1;
        flex-direction: column;
        overflow-y: auto;
        padding: var(--space-4) var(--space-3);
    }

    .admin-main {
        border-inline-start: 1px solid var(--rule);
    }
}

/* ---------- The nav itself ---------- */

.admin-nav {
    width: 100%;
}

.admin-nav-group {
    margin-bottom: var(--space-1);
}

/* A summary is a button; it should not also look like a text selection
   target or carry the browser's default disclosure triangle, which sits on
   the wrong side under RTL and duplicates the caret we draw ourselves. */
.admin-nav-summary {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--bs-border-radius);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-muted);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.admin-nav-summary::-webkit-details-marker {
    display: none;
}

.admin-nav-summary:hover {
    color: var(--ink);
    background-color: var(--brand-soft);
}

.admin-nav-summary:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

.admin-nav-group.is-active > .admin-nav-summary {
    color: var(--ink);
}

.admin-nav-icon {
    flex: 0 0 1.25rem;
    text-align: center;
    color: var(--ink-subtle);
}

.admin-nav-group.is-active > .admin-nav-summary .admin-nav-icon,
.admin-nav-link.is-active .admin-nav-icon {
    color: var(--brand);
}

.admin-nav-label {
    flex: 1 1 auto;
    min-width: 0;
}

.admin-nav-caret {
    flex: 0 0 auto;
    font-size: var(--text-xs);
    color: var(--ink-subtle);
    /* The only motion in the shell, and it is doing a job: it says which
       way the group is about to move. 120ms, no easing curve worth naming. */
    transition: transform .12s linear;
}

.admin-nav-group[open] > .admin-nav-summary .admin-nav-caret {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .admin-nav-caret {
        transition: none;
    }
}

.admin-nav-items {
    list-style: none;
    margin: 0;
    /* The indent is the icon column's width plus its gap, so second-level
       labels start exactly where first-level labels start. It flips side
       on its own: it is padding-inline-start, not padding-left. */
    padding: 0 0 var(--space-2) calc(1.25rem + var(--space-3));
    padding-inline-start: calc(1.25rem + var(--space-3));
    padding-inline-end: 0;
}

.admin-nav-link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--bs-border-radius);
    font-size: var(--text-sm);
    color: var(--ink-muted);
    text-decoration: none;
    /* The active marker is drawn as a transparent border on every item, so
       an item does not shift sideways by 3px when it becomes active. */
    border-inline-start: 3px solid transparent;
}

.admin-nav-link:hover {
    color: var(--ink);
    background-color: var(--brand-soft);
}

.admin-nav-link:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

.admin-nav-link.is-active {
    color: var(--brand-ink);
    font-weight: 600;
    background-color: var(--brand-tint);
    border-inline-start-color: var(--brand);
}

.admin-nav-link-standalone {
    gap: var(--space-3);
    font-weight: 600;
    color: var(--ink-muted);
    margin-bottom: var(--space-1);
}

/* ==========================================================================
   The student account area
   ==========================================================================
   Deliberately quieter than the admin shell: seven destinations, no groups,
   no second surface colour, and it sits inside the public layout's own
   container rather than taking over the window. A student is reading; an
   administrator is working. */

.account-shell {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-6);
}

.account-nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    justify-content: flex-start;
    padding: var(--space-2) var(--space-3);
    color: var(--ink);
    border: 1px solid var(--rule);
    background-color: var(--surface);
}

.account-sidebar {
    flex: 0 0 100%;
}

.account-content {
    flex: 1 1 0;
    min-width: 0;
}

@media (min-width: 992px) {
    .account-sidebar {
        flex: 0 0 14rem;
        width: 14rem;
        position: sticky;
        top: var(--space-4);
    }

    .account-sidebar-body {
        flex-direction: column;
    }
}

.account-nav-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    width: 100%;
}

.account-nav-link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--density-row-y) var(--space-3);
    border-radius: var(--bs-border-radius);
    color: var(--ink-muted);
    text-decoration: none;
    border-inline-start: 3px solid transparent;
}

.account-nav-link:hover {
    color: var(--ink);
    background-color: var(--brand-soft);
}

.account-nav-link:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

.account-nav-link.is-active {
    color: var(--brand-ink);
    font-weight: 600;
    background-color: var(--brand-tint);
    border-inline-start-color: var(--brand);
}

.account-nav-icon {
    flex: 0 0 1.25rem;
    text-align: center;
    color: var(--ink-subtle);
}

.account-nav-link.is-active .account-nav-icon {
    color: var(--brand);
}

.account-nav-label {
    flex: 1 1 auto;
    min-width: 0;
}

.account-nav-badge {
    flex: 0 0 auto;
    min-width: 1.5rem;
    padding: 0 var(--space-2);
    border-radius: 999px;
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.5rem;
    text-align: center;
    color: var(--brand-contrast);
    background-color: var(--brand);
}

/* ---------- Student page head and the record list ----------
   Two patterns, used by every page in the account area, so a screen added
   next month inherits the rhythm rather than reinventing it.

   The record list is what a list of things-that-happened-to-you looks
   like here: a title, a line of metadata, an action at the far end of the
   row. A certificate, a course's grades, a conversation and a
   notification are all the same shape, so they get the same component and
   differ only in what they put in it. It is a <ul>, because it is a list;
   the row is flex, so the action sits at the reading-end in both
   directions without a single left/right property. */

.account-page-head {
    margin-bottom: var(--space-5);
}

.account-page-lead {
    max-width: var(--measure);
    margin-bottom: 0;
    color: var(--ink-muted);
}

.record-list {
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--rule);
    border-radius: var(--bs-border-radius);
    background-color: var(--surface-raised);
}

.record {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
}

.record + .record {
    border-top: 1px solid var(--rule-subtle);
}

.record-main {
    flex: 1 1 16rem;
    min-width: 0;
}

.record-title {
    display: block;
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
}

.record-title:hover {
    color: var(--brand);
    text-decoration: underline;
}

.record-meta {
    margin: var(--space-1) 0 0;
    font-size: var(--text-sm);
    color: var(--ink-muted);
}

.record-sep {
    margin-inline: var(--space-2);
    color: var(--ink-subtle);
}

.record-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

/* A record whose subject is no longer valid — a revoked certificate, a
   rejected application. Stated in words by the badge beside it; the muted
   treatment is the second, quieter signal, never the only one. */
.record.is-void .record-title,
.record.is-void .record-meta {
    color: var(--ink-subtle);
}

/* ---------- Status pills ----------
   Bootstrap's .badge is a solid block of colour, which at table density
   turns a status column into a row of traffic lights and makes every
   status shout equally loudly. A status pill is a tinted background with
   ink of the same hue: legible, quiet, and readable by someone who cannot
   distinguish the hues, because the word is the message and the colour
   only ever agrees with it.

   Four states and no more — this is a vocabulary, not a palette:
     positive  something is done, active, passed
     pending   something is waiting on a person
     void      something was revoked, rejected, deactivated
     neutral   a fact with no valence

   The hues are Bootstrap's own semantic variables (which the theme layer
   already owns), mixed toward the configured surface so they sit at the
   same weight as everything else on the page. */

.status-pill {
    display: inline-block;
    padding: var(--space-1) var(--space-3);
    border-radius: 999px;
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
    color: var(--ink);
    background-color: var(--surface-sunken);
    border: 1px solid var(--rule-subtle);
}

.status-pill-positive {
    color: var(--bs-success-text-emphasis, #0a3622);
    background-color: var(--bs-success-bg-subtle, #d1e7dd);
    border-color: var(--bs-success-border-subtle, #a3cfbb);
}

.status-pill-pending {
    color: var(--bs-warning-text-emphasis, #664d03);
    background-color: var(--bs-warning-bg-subtle, #fff3cd);
    border-color: var(--bs-warning-border-subtle, #ffe69c);
}

.status-pill-void {
    color: var(--bs-danger-text-emphasis, #58151c);
    background-color: var(--bs-danger-bg-subtle, #f8d7da);
    border-color: var(--bs-danger-border-subtle, #f1aeb5);
}

.status-pill-neutral {
    color: var(--ink-muted);
}

/* A certificate number is an identifier a person reads aloud, copies, and
   compares character by character. Tabular figures and a monospace stack
   are what make 0/O and 1/l distinguishable; it is also the one place in
   the student area that is deliberately not the body face. */
.certificate-number {
    font-family: var(--bs-font-monospace);
    font-variant-numeric: tabular-nums;
    letter-spacing: .02em;
    color: var(--ink);
}

/* Pages that predate the account shell wrapped themselves in a centred
   Bootstrap column, because they used to sit alone in the full public
   container. The shell owns the measure now, so those wrappers became
   inert: kept as elements (they carry dir= on some pages) but no longer
   a grid. */

.account-page-col {
    min-width: 0;
}

/* ==========================================================================
   The course player
   ==========================================================================
   A reading surface, worked to that: a measure on the prose, one panel for
   the outline, and exactly one brand-coloured thing on screen at a time —
   the lesson you are on, or the button that takes you to the next one. */

.player-outline-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
    padding: var(--space-2) var(--space-3);
    color: var(--ink);
    border: 1px solid var(--rule);
    background-color: var(--surface);
}

.player-grid {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-7);
}

.player-main {
    flex: 1 1 22rem;
    min-width: 0;
}

.player-aside {
    flex: 0 0 100%;
}

@media (min-width: 992px) {
    .player-aside {
        flex: 0 0 20rem;
        width: 20rem;
        position: sticky;
        top: var(--space-4);
        max-height: calc(100vh - var(--space-6));
    }

    .player-aside-body {
        flex-direction: column;
        overflow-y: auto;
    }
}

.player-aside-inner {
    width: 100%;
    border: 1px solid var(--rule);
    border-radius: var(--bs-border-radius);
    background-color: var(--surface-raised);
}

.player-eyebrow {
    margin-bottom: var(--space-2);
}

.player-eyebrow-sep {
    margin-inline: var(--space-2);
    opacity: .6;
}

.player-lesson-title {
    margin-bottom: var(--space-3);
    max-width: var(--measure);
}

.player-lesson-summary {
    max-width: var(--measure);
    margin-bottom: var(--density-section-gap);
    color: var(--ink-muted);
}

.player-content {
    margin-bottom: var(--density-section-gap);
}

.player-media {
    display: block;
    width: 100%;
    max-width: 52rem;
    border-radius: var(--bs-border-radius);
    background-color: var(--surface-sunken);
}

.player-note {
    font-size: var(--text-sm);
    color: var(--ink-muted);
}

.player-locked {
    display: inline-flex;
    align-items: center;
    font-size: var(--text-sm);
    color: var(--ink-subtle);
}

.player-tasks,
.player-comments {
    max-width: var(--measure);
    margin-bottom: var(--density-section-gap);
}

.player-tasks h2,
.player-comments h2 {
    margin-bottom: var(--space-3);
}

/* A neutral bordered box, used where a card's shadow and padding would be
   more emphasis than the content deserves. */
.panel {
    padding: var(--space-4);
    border: 1px solid var(--rule);
    border-radius: var(--bs-border-radius);
    background-color: var(--surface-raised);
}

/* ---------- Player: the course summary at the top of the outline ------- */

.player-course {
    padding: var(--space-4);
    border-bottom: 1px solid var(--rule-subtle);
}

.player-back {
    display: inline-block;
    margin-bottom: var(--space-3);
    font-size: var(--text-sm);
    color: var(--ink-muted);
    text-decoration: none;
}

.player-back:hover {
    color: var(--brand);
}

.player-course-title {
    margin-bottom: var(--space-3);
    font-size: var(--text-base);
    font-weight: 600;
}

.player-progress {
    height: .375rem;
    background-color: var(--surface-sunken);
}

.player-progress-text {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--ink-muted);
}

.player-course-link {
    display: inline-flex;
    align-items: center;
    margin-top: var(--space-3);
    font-size: var(--text-sm);
    color: var(--ink-muted);
    text-decoration: none;
}

.player-course-link:hover {
    color: var(--brand);
}

.player-announcements {
    padding: var(--space-4);
    border-bottom: 1px solid var(--rule-subtle);
}

.player-announcement + .player-announcement {
    margin-top: var(--space-3);
}

.player-announcement-title {
    font-size: var(--text-sm);
    font-weight: 600;
}

/* ---------- Player: the outline ---------- */

.player-outline {
    padding: var(--space-2) 0 var(--space-3);
}

.player-outline-section + .player-outline-section {
    margin-top: var(--space-4);
}

.player-outline-heading {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin: 0;
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-subtle);
}

[dir="rtl"] .player-outline-heading {
    letter-spacing: 0;
    text-transform: none;
}

.player-outline-progress {
    font-weight: 400;
    font-variant-numeric: tabular-nums;
}

.player-outline-lessons {
    list-style: none;
    margin: 0;
    padding: 0;
}

.player-outline-item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding-inline: var(--space-3);
}

.player-tick {
    flex: 0 0 auto;
    line-height: 1;
}

.player-tick-button {
    padding: var(--space-1);
    border: 0;
    background: transparent;
    line-height: 1;
    color: var(--ink-subtle);
    cursor: pointer;
}

.player-tick-button:hover {
    color: var(--ink);
}

.player-tick-button.completed {
    color: var(--bs-success, #198754);
}

.player-lesson-link {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    padding: var(--space-2) var(--space-2);
    border-radius: var(--bs-border-radius-sm);
    font-size: var(--text-sm);
    color: var(--ink-muted);
    text-decoration: none;
    /* Same trick as the admin nav: the marker exists on every row as a
       transparent border, so becoming current does not nudge the text. */
    border-inline-start: 2px solid transparent;
}

a.player-lesson-link:hover {
    color: var(--ink);
    background-color: var(--brand-soft);
}

a.player-lesson-link:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

.player-lesson-link.active {
    font-weight: 600;
    color: var(--brand-ink);
    background-color: var(--brand-tint);
    border-inline-start-color: var(--brand);
}

.player-lesson-link.is-locked {
    color: var(--ink-subtle);
}

/* ---------- Player: the footer pair ---------- */

.player-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    max-width: var(--measure);
    padding-top: var(--space-5);
    border-top: 1px solid var(--rule-subtle);
}

/* The completion control is a two-state toggle, not two buttons. app.js
   flips .is-complete on it; nothing else about it changes. */
.lesson-complete-toggle {
    display: inline-flex;
    align-items: center;
    color: var(--ink-muted);
    border: 1px solid var(--rule);
    background-color: var(--surface);
}

.lesson-complete-toggle:hover {
    color: var(--ink);
    border-color: var(--rule-strong);
}

.lesson-complete-toggle.is-complete {
    color: var(--bs-success-text-emphasis, #0a3622);
    background-color: var(--bs-success-bg-subtle, #d1e7dd);
    border-color: var(--bs-success-border-subtle, #a3cfbb);
}

.player-next {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    text-align: start;
}

.player-next-label {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.player-next-eyebrow {
    font-size: var(--text-xs);
    opacity: .8;
}

.player-next-title {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 16rem;
}

/* ==========================================================================
   Admin tables and filter bars
   ========================================================================== */

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
    padding: var(--space-4);
    border: 1px solid var(--rule);
    border-radius: var(--bs-border-radius);
    background-color: var(--surface-raised);
}

.filter-field {
    flex: 1 1 12rem;
    min-width: 0;
}

.filter-field .form-label {
    margin-bottom: var(--space-1);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--ink-subtle);
}

.filter-submit {
    flex: 0 0 auto;
}

/* The table sits on its own raised surface with one border, rather than
   the page's own edge doubling as the table's. */
.table-surface {
    border: 1px solid var(--rule);
    border-radius: var(--bs-border-radius);
    background-color: var(--surface-raised);
    overflow: hidden;
}

.table-admin {
    margin-bottom: 0;
    --bs-table-bg: transparent;
}

/* Deliberately NOT position: sticky. A wide admin table has to sit in a
   horizontally scrollable wrapper (.table-responsive), and that wrapper is
   then also the sticky containing block — so a header told to stick to the
   *page* offsets itself inside a box that never scrolls vertically, leaving
   a permanent empty band above the first row. A sticky header here would
   mean nesting a vertical scroller inside the page, which trades one small
   annoyance for a worse one. */
.table-admin thead th {
    padding: var(--space-3) var(--space-4);
    background-color: var(--surface-raised);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-subtle);
    border-bottom: 1px solid var(--rule);
    white-space: nowrap;
}

/* Arabic gains nothing from uppercase and loses legibility to the extra
   tracking; see .eyebrow, same reasoning. */
[dir="rtl"] .table-admin thead th {
    letter-spacing: 0;
    text-transform: none;
}

.table-admin tbody td,
.table-admin tbody th {
    padding: var(--density-row-y) var(--space-4);
    vertical-align: middle;
    border-bottom-color: var(--rule-subtle);
    font-weight: 400;
}

.table-admin tbody tr:last-child td,
.table-admin tbody tr:last-child th {
    border-bottom: 0;
}

.table-admin tbody tr:hover {
    background-color: var(--brand-soft);
}

/* The one column set in full ink: the thing the eye scans down. */
.cell-identity {
    line-height: 1.35;
}

.cell-identity-name {
    display: block;
    font-weight: 600;
    color: var(--ink);
}

.cell-identity-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--ink-muted);
}

.cell-meta {
    font-size: var(--text-sm);
    color: var(--ink-muted);
}

.cell-numeric {
    font-variant-numeric: tabular-nums;
    text-align: end;
}

/* Actions hug the row's reading-end and never claim more width than they
   need — under RTL that end is the left, which `text-align: end` and a
   `justify-content: flex-end` in a dir-aware flex row both get right
   without a single left/right property. */
.cell-actions {
    width: 1%;
    white-space: nowrap;
    text-align: end;
}

.row-actions {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
}

.row-actions form {
    margin: 0;
}

/* A destructive action that appears on every row. It reads as an ordinary
   quiet control until you are about to use it, and only then admits what
   it is — because a table of forty rows with forty red buttons has taught
   the reader to stop seeing red. */
.btn-quiet-danger {
    color: var(--ink-muted);
    border: 1px solid var(--rule);
    background-color: transparent;
}

.btn-quiet-danger:hover,
.btn-quiet-danger:focus-visible {
    color: var(--bs-danger-text-emphasis, #58151c);
    background-color: var(--bs-danger-bg-subtle, #f8d7da);
    border-color: var(--bs-danger-border-subtle, #f1aeb5);
}

/* ---------- Offcanvas drawers on small screens ----------
   Bootstrap's default offcanvas width is 400px, which is wider than a
   375px phone: the drawer covers the screen completely and there is no
   visible page edge to tell you what closing it will reveal. Capped
   *below* the viewport, deliberately, so that edge stays.

   Note for whoever measures this later: a closed drawer is position:fixed
   and translated fully off-screen, and Chrome still counts it in
   document.documentElement.scrollWidth. That makes scrollWidth >
   clientWidth a false positive for "the page scrolls sideways" — the page
   does not; it was checked by actually setting scrollLeft and finding no
   range. Measure scrollability, not scrollWidth. */

.admin-sidebar,
.account-sidebar,
.player-aside {
    --bs-offcanvas-width: min(20rem, 86vw);
}
