/* ==========================================================================
   dark.css -- the dark theme layer for the web dashboards.

   HOW THIS FILE WORKS
   Every rule below is scoped under [data-theme="dark"], which the server only ever
   puts on <html> when the vs_theme cookie says so (vidsafe/helpers.py). Nothing here
   can match in light mode, so light rendering is byte-for-byte what it was before.
   The file is loaded last in website/base.html purely so it wins on source order.

   Bootstrap 5.3 does most of the work on its own via data-bs-theme="dark" -- cards,
   modals, tables, dropdowns, forms, alerts, offcanvas and the rest all flip without
   help. This file covers the three things it cannot: the custom dashboard chrome, the
   Bootstrap utilities that are hard-coded light (bg-white, bg-light, text-dark ...),
   and the third-party widgets (select2, Chart.js, SweetAlert2, the date picker).

   The app webview is never dark: it is excluded at the source, in the theme resolver.
   ========================================================================== */

:root[data-theme="dark"] {
  /* Surfaces, darkest first. Light counterparts in the comments. */
  --vs-app-bg:        #0a1628;   /* was #f5f6fb -- canvas behind the content column */
  --vs-surface:       #0f1f36;   /* was #ffffff -- sidebar, top bar, cards, modals   */
  --vs-surface-2:     #142943;   /* was #f3f5f9 -- inset rows, table heads, tints    */
  --vs-surface-3:     #0c1d33;   /* was #ffffff -- form controls, recessed wells     */

  /* Lines */
  --vs-hairline:      #17304f;   /* was #eee     -- subtle dividers                  */
  --vs-border:        #1e3a5f;   /* was #e6eaf6  -- card and control borders         */
  --vs-border-strong: #2c4e77;   /* was #c6c7c9  -- input outlines, --bs-border-color*/
  --vs-card-accent:   #24456e;   /* was #d8e8ff  -- dashboard stat card border       */

  /* Text */
  --vs-text:          #f1f5f9;   /* was #212529 */
  --vs-text-muted:    #94a3b8;   /* was #666    */
  --vs-text-nav:      #cbd5e1;   /* was #444    -- sidebar links                     */
  --vs-text-subnav:   #94a3b8;   /* was #4b5563 -- treeview sub-menu links           */

  /* Brand. #044FC0 only reaches ~2:1 on the navy, so it lightens for anything the
     eye has to read, and darkens back down behind white button text. */
  --vs-brand:         #4d90f0;   /* icons, links, active nav, stat numbers */
  --vs-brand-fill:    #1a6ee0;   /* filled primary buttons                 */
  --vs-brand-soft:    #14294a;   /* active nav pill, soft tints            */

  /* Odds and ends */
  --vs-thumb:         #142943;   /* image and video placeholders       */
  --vs-track:         #1e3a5f;   /* progress bar tracks                */
  --vs-success:       #2e9e63;
  --vs-danger:        #d9534f;
  --vs-warning:       #d9a406;
  --vs-shadow:        rgba(0, 0, 0, .45);
  --vs-overlay:       rgba(4, 12, 24, .72);

  /* Bootstrap's own dark theme is neutral grey (#212529 / #2b3035). Repointing its
     body/surface variables at the brand navy is what keeps every component Bootstrap
     themes for free -- tables, modals, dropdowns, offcanvas -- on-palette instead of
     grey, without restyling any of them individually. */
  --bs-body-bg:            var(--vs-app-bg);
  --bs-body-color:         var(--vs-text);
  --bs-emphasis-color:     #ffffff;
  --bs-secondary-bg:       var(--vs-surface-2);
  --bs-secondary-color:    var(--vs-text-muted);
  --bs-tertiary-bg:        var(--vs-surface-2);
  --bs-tertiary-color:     var(--vs-text-muted);
  --bs-border-color:       var(--vs-border-strong);
  --bs-heading-color:      var(--vs-text);
  --bs-link-color:         var(--vs-brand);
  --bs-link-hover-color:   #7ab0f7;
  --bs-link-color-rgb:     77, 144, 240;
  --bs-code-color:         #e685b5;

  /* Bootstrap's .bg-body-*, .text-body-* and .border-* utilities do NOT read the
     variables above -- they read the parallel *-rgb triplets, so that --bs-bg-opacity
     can be applied. Setting only the hex forms leaves those utilities on Bootstrap's
     stock neutral grey: the institute store's navbar rendered #2b3035 against the navy
     until this was added. The triplets must be literal numbers; var() cannot be used
     inside the rgba() Bootstrap builds from them. Keep each in step with its token. */
  --bs-body-bg-rgb:         10, 22, 40;      /* --vs-app-bg     #0a1628 */
  --bs-body-color-rgb:      241, 245, 249;   /* --vs-text       #f1f5f9 */
  --bs-secondary-bg-rgb:    20, 41, 67;      /* --vs-surface-2  #142943 */
  --bs-secondary-color-rgb: 148, 163, 184;   /* --vs-text-muted #94a3b8 */
  --bs-tertiary-bg-rgb:     20, 41, 67;      /* --vs-surface-2  #142943 */
  --bs-tertiary-color-rgb:  148, 163, 184;   /* --vs-text-muted #94a3b8 */
  --bs-emphasis-color-rgb:  255, 255, 255;
  --bs-border-color-rgb:    44, 78, 119;     /* --vs-border-strong #2c4e77 */
}

/* The dashboard's own variables are declared on `body`, not :root (assets/website/
   css/base.css), so they have to be redefined at the same place to be picked up.
   --primary-color is used 18 times: 15 as a text/icon colour, which is why it takes
   the lightened brand; the two background uses are corrected individually below. */
:root[data-theme="dark"] body {
  background-color: var(--vs-app-bg) !important;  /* base.css sets #fff !important */
  color: var(--vs-text);
  --primary-color: var(--vs-brand);
  --primary-color-light: var(--vs-brand-soft);
  --bs-border-color: var(--vs-border-strong);
}

/* The two places --primary-color is a background rather than a text colour: they need
   the darker fill so white sits on it at a readable contrast. */
:root[data-theme="dark"] .trigger:hover,
:root[data-theme="dark"] .custom_steps_wrapper .nav-link.active .step_number {
  background-color: var(--vs-brand-fill);
  color: #fff;
}


/* --- Bootstrap surface elevation -----------------------------------------
   Bootstrap derives most component backgrounds from --bs-body-bg, but it does it
   inside the component's own rule (`.card { --bs-card-bg: var(--bs-body-bg) }`), so
   repointing --bs-body-bg at :root is not enough -- the component rule wins and every
   card, modal and dropdown collapses onto the page canvas, losing the one step of
   elevation that makes a dashboard readable. Naming each one here restores it.
   In light mode these all resolve to #fff on a #f5f6fb page; this is the same
   relationship, one shade up from the navy. */
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .modal-content,
:root[data-theme="dark"] .dropdown-menu,
:root[data-theme="dark"] .offcanvas,
:root[data-theme="dark"] .offcanvas-lg,
:root[data-theme="dark"] .offcanvas-md,
:root[data-theme="dark"] .offcanvas-sm,
:root[data-theme="dark"] .offcanvas-xl,
:root[data-theme="dark"] .offcanvas-xxl,
:root[data-theme="dark"] .list-group,
:root[data-theme="dark"] .accordion,
:root[data-theme="dark"] .toast,
:root[data-theme="dark"] .popover,
:root[data-theme="dark"] .pagination {
  --bs-card-bg:        var(--vs-surface);
  --bs-modal-bg:       var(--vs-surface);
  --bs-dropdown-bg:    var(--vs-surface);
  --bs-offcanvas-bg:   var(--vs-surface);
  --bs-list-group-bg:  var(--vs-surface);
  --bs-accordion-bg:   var(--vs-surface);
  --bs-toast-bg:       var(--vs-surface);
  --bs-popover-bg:     var(--vs-surface);
  --bs-pagination-bg:  var(--vs-surface);
}

/* Sub-surfaces inside those components step up again, exactly as the light theme's
   #f8f9fa headers and footers do against their white card. */
:root[data-theme="dark"] .card {
  --bs-card-cap-bg:       var(--vs-surface-2);
  --bs-card-border-color: var(--vs-border);
}
:root[data-theme="dark"] .modal-content {
  --bs-modal-header-border-color: var(--vs-border);
  --bs-modal-footer-border-color: var(--vs-border);
  --bs-modal-border-color:        var(--vs-border);
}
:root[data-theme="dark"] .dropdown-menu {
  --bs-dropdown-border-color:       var(--vs-border);
  --bs-dropdown-link-hover-bg:      var(--vs-surface-2);
  --bs-dropdown-link-active-bg:     var(--vs-brand-fill);
  --bs-dropdown-divider-bg:         var(--vs-border);
}
:root[data-theme="dark"] .list-group {
  --bs-list-group-border-color:      var(--vs-border);
  --bs-list-group-action-hover-bg:   var(--vs-surface-2);
  --bs-list-group-active-bg:         var(--vs-brand-fill);
  --bs-list-group-active-border-color: var(--vs-brand-fill);
}
:root[data-theme="dark"] .accordion {
  --bs-accordion-border-color: var(--vs-border);
  --bs-accordion-btn-bg:       var(--vs-surface-2);
  --bs-accordion-active-bg:    var(--vs-brand-soft);
  --bs-accordion-active-color: var(--vs-brand);
}
/* Tables sit directly on whatever contains them, so they take their stripes and
   hover from the surface variables rather than a background of their own. */
:root[data-theme="dark"] .table {
  --bs-table-color:       var(--vs-text);
  --bs-table-border-color: var(--vs-border);
  --bs-table-striped-bg:  rgba(148, 163, 184, .06);
  --bs-table-hover-bg:    rgba(148, 163, 184, .10);
  --bs-table-active-bg:   rgba(148, 163, 184, .14);
}
/* The modal scrim: Bootstrap's is already black, but at .5 it is too weak to separate
   a dark modal from a dark page. */
:root[data-theme="dark"] .modal-backdrop {
  --bs-backdrop-bg:      #000;
  --bs-backdrop-opacity: .72;
}


/* --- Bootstrap outline buttons -------------------------------------------
   Outline buttons carry their colour as a literal (--bs-btn-color: #6c757d on
   .btn-outline-secondary), and Bootstrap does not adapt those for dark mode, so the
   label sits at 3.87:1 on the navy -- caught by a contrast sweep of the live Courses
   page, where "Filters" and "Sort by" were both failing. The filled variants are
   fine: they keep their own background. */
:root[data-theme="dark"] .btn-outline-secondary {
  --bs-btn-color:              var(--vs-text-muted);   /* was #6c757d -- 3.87:1 */
  --bs-btn-border-color:       var(--vs-border-strong);
  --bs-btn-hover-bg:           var(--vs-surface-2);
  --bs-btn-hover-border-color: var(--vs-border-strong);
  --bs-btn-hover-color:        var(--vs-text);
  --bs-btn-active-bg:          var(--vs-surface-2);
  --bs-btn-active-border-color: var(--vs-border-strong);
  --bs-btn-active-color:       var(--vs-text);
  --bs-btn-disabled-color:     var(--vs-text-muted);
  --bs-btn-disabled-border-color: var(--vs-border);
}

:root[data-theme="dark"] .btn-outline-primary {
  --bs-btn-color:              var(--vs-brand);        /* was #0d6efd -- 2.7:1 */
  --bs-btn-border-color:       var(--vs-brand);
  --bs-btn-hover-bg:           var(--vs-brand-fill);
  --bs-btn-hover-border-color: var(--vs-brand-fill);
  --bs-btn-active-bg:          var(--vs-brand-fill);
  --bs-btn-active-border-color: var(--vs-brand-fill);
  --bs-btn-disabled-color:     var(--vs-brand);
  --bs-btn-disabled-border-color: var(--vs-brand);
}

/* .btn-outline-dark is near-black on near-black. There is no "dark" left to outline
   in dark mode, so it becomes the neutral outline the design already has. */
:root[data-theme="dark"] .btn-outline-dark {
  --bs-btn-color:              var(--vs-text);         /* was #212529 -- 1.2:1 */
  --bs-btn-border-color:       var(--vs-border-strong);
  --bs-btn-hover-bg:           var(--vs-surface-2);
  --bs-btn-hover-border-color: var(--vs-border-strong);
  --bs-btn-hover-color:        var(--vs-text);
  --bs-btn-active-bg:          var(--vs-surface-2);
  --bs-btn-active-color:       var(--vs-text);
}

/* Link buttons and .link-dark, used in the sidebar billing box's Refresh link. */
:root[data-theme="dark"] .btn-link {
  --bs-btn-color:       var(--vs-brand);
  --bs-btn-hover-color: #7ab0f7;
}
:root[data-theme="dark"] .link-dark {
  color: var(--vs-text) !important;                    /* was #212529 */
}


/* --- Bootstrap colour utilities ------------------------------------------
   .text-primary and friends are literal colours that Bootstrap does NOT adapt for
   dark mode -- .text-primary stays #0d6efd whatever data-bs-theme says. A contrast
   sweep of the live console found these on almost every list page (course names in
   Videos / Study Materials / MCQ Tests are .text-primary links at 4.03:1, coupon
   amounts are .text-success at 3.65:1).

   The replacements are Bootstrap's OWN dark-mode emphasis colours
   (--bs-*-text-emphasis), so the palette stays Bootstrap's rather than invented, and
   every one clears 6:1 on both --vs-app-bg and --vs-surface. */
:root[data-theme="dark"] .text-primary   { color: var(--vs-brand) !important; }   /* 4.03 -> 5.6  */
:root[data-theme="dark"] .text-success   { color: #75b798 !important; }           /* 3.65 -> 6.4  */
:root[data-theme="dark"] .text-danger    { color: #ea868f !important; }           /* 3.4  -> 6.2  */
:root[data-theme="dark"] .text-warning   { color: #ffda6a !important; }           /* 6.9  -> 10.4 */
:root[data-theme="dark"] .text-info      { color: #6edff6 !important; }
:root[data-theme="dark"] .text-secondary { color: var(--vs-text-muted) !important; }

/* The link-* variants of the same utilities, used on anchors. */
:root[data-theme="dark"] .link-primary   { color: var(--vs-brand) !important; }
:root[data-theme="dark"] .link-success   { color: #75b798 !important; }
:root[data-theme="dark"] .link-danger    { color: #ea868f !important; }
:root[data-theme="dark"] .link-secondary { color: var(--vs-text-muted) !important; }

/* Remaining outline buttons. Same failure as the secondary/primary/dark ones handled
   above: the label colour is a literal Bootstrap does not adapt. */
:root[data-theme="dark"] .btn-outline-success {
  --bs-btn-color: #75b798;                       /* was #198754 -- 4.0:1 */
  --bs-btn-border-color: #2e7d5b;
  --bs-btn-hover-bg: var(--vs-success);
  --bs-btn-hover-border-color: var(--vs-success);
  --bs-btn-active-bg: var(--vs-success);
  --bs-btn-disabled-color: #75b798;
  --bs-btn-disabled-border-color: #2e7d5b;
}
:root[data-theme="dark"] .btn-outline-danger {
  --bs-btn-color: #ea868f;                       /* was #dc3545 */
  --bs-btn-border-color: #8f3a40;
  --bs-btn-hover-bg: var(--vs-danger);
  --bs-btn-hover-border-color: var(--vs-danger);
  --bs-btn-active-bg: var(--vs-danger);
  --bs-btn-disabled-color: #ea868f;
  --bs-btn-disabled-border-color: #8f3a40;
}
:root[data-theme="dark"] .btn-outline-warning {
  --bs-btn-color: #ffda6a;                       /* was #ffc107 */
  --bs-btn-border-color: #8a6d0b;
  --bs-btn-hover-bg: var(--vs-warning);
  --bs-btn-hover-border-color: var(--vs-warning);
  --bs-btn-hover-color: #231a00;
  --bs-btn-active-bg: var(--vs-warning);
  --bs-btn-active-color: #231a00;
}
:root[data-theme="dark"] .btn-outline-info {
  --bs-btn-color: #6edff6;
  --bs-btn-border-color: #3a7d8c;
  --bs-btn-hover-bg: #3a97ad;
  --bs-btn-hover-border-color: #3a97ad;
  --bs-btn-active-bg: #3a97ad;
}
:root[data-theme="dark"] .btn-outline-light {
  --bs-btn-color: var(--vs-text);
  --bs-btn-border-color: var(--vs-border-strong);
  --bs-btn-hover-bg: var(--vs-surface-2);
  --bs-btn-hover-border-color: var(--vs-border-strong);
  --bs-btn-hover-color: var(--vs-text);
}


/* --- Status colours that only clear AA on a light page --------------------
   Bootstrap's solid #198754 / #ffc107 / #dc3545 are tuned for near-white. As TEXT on
   the navy they land at 3.65:1 and below, so anywhere a template uses them for a
   score, a label or a figure, the lighter emphasis pair takes over. These are also
   the fallback targets for the var(--vs-score-*) hooks added to templates that
   define their own status classes. */
:root[data-theme="dark"] body {
  --vs-score-high:   #75b798;
  --vs-score-medium: #ffda6a;
  --vs-score-low:    #ea868f;
}

/* --- Solid badges whose white label was already marginal ------------------
   .badge.bg-warning (a custom #ff7807) and .badge.bg-info (#0dcaf0) are rendered with
   white text, which is 2.64:1 and 1.96:1 -- failing in LIGHT mode too, so this is a
   pre-existing issue rather than something dark mode introduced. Light is left exactly
   as it is; on dark the label flips to the dark ink Bootstrap's own .text-bg-warning /
   .text-bg-info already use, which is legible on both fills. */
:root[data-theme="dark"] .badge.bg-warning,
:root[data-theme="dark"] .badge.bg-info {
  color: #231a00 !important;
}
:root[data-theme="dark"] .badge.bg-info {
  color: #032830 !important;
}


/* ==========================================================================
   10-base.css -- dark overrides for assets/website/css/base.css

   Additive only. Every selector is scoped to :root[data-theme="dark"], so light
   mode is untouched. The `body{...}` block at the top of base.css (the
   --primary-color / --bs-border-color declarations and background-color:#fff)
   is handled in 00-tokens.css and is deliberately not repeated here.
   !important is mirrored from the source rule, never added.
   Rules are grouped in source order; headings name the source selector.
   ========================================================================== */


/* --- Dashboard Card: .dashboard_card and friends ------------------------- */

:root[data-theme="dark"] .dashboard_card {
  background: var(--vs-surface);
  border: 1px solid var(--vs-card-accent);
}

/* was color:#000 -- the icon wrapper's own glyph colour, not the <i> inside it */
:root[data-theme="dark"] .dashboard_card .card_icon_wrapper {
  color: var(--vs-text);
}

:root[data-theme="dark"] .dashboard_card .card_text {
  color: var(--vs-text-muted);
}

/* was background:#fbfbfb / border:#e3e2ff -- hover lifts one surface step and
   brightens the border rather than washing out. */
:root[data-theme="dark"] a.dashboard_card:hover {
  background: var(--vs-surface-2);
  border: 1px solid var(--vs-border-strong);
}

/* .dashboard_card .card_icon_wrapper i and .card_number use var(--primary-color),
   which 00-tokens.css already repoints at --vs-brand. Not overridden here. */


/* --- .card.vidsafe_card -------------------------------------------------- */

:root[data-theme="dark"] .card.vidsafe_card {
  border: 1px solid var(--vs-border);
}


/* --- .box / .box-header -------------------------------------------------- */

/* The 8px 4px 36px -4px #dadaff shadow is a pale lilac glow tuned for a white
   page; on navy it reads as a halo, so it becomes a real drop shadow. */
:root[data-theme="dark"] .box {
  background: var(--vs-surface);
  box-shadow: 8px 4px 36px -4px var(--vs-shadow);
  -webkit-box-shadow: 8px 4px 36px -4px var(--vs-shadow);
}

:root[data-theme="dark"] .box-header {
  border-bottom: 1px solid var(--vs-border);
}


/* --- .course_item_box list rows ------------------------------------------ */

:root[data-theme="dark"] .course_item_box {
  border: 1px solid var(--vs-border);
  background: var(--vs-surface);
}

:root[data-theme="dark"] .course_item_box:hover {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .course_item_box.bulk_selected {
  background: var(--vs-brand-soft);
  box-shadow: inset 3px 0 0 var(--vs-brand);
}

/* No token exists for "brand-soft, one step lighter"; --vs-surface-2 is within
   1% of --vs-brand-soft and would read as no hover at all. */
:root[data-theme="dark"] .course_item_box.bulk_selected:hover {
  background: #1b3760;
}

/* .course_item_box.bulk_selected .btn only sets opacity: 0.45, which dims toward
   the page the same way in either theme. Left alone. */

/* movedHighlightPulse pulses #fff -> #fff3cd -> #ffe08a, i.e. it would flash the
   row white and strand the light body text on pale amber. @keyframes cannot be
   theme-scoped (a same-named block would replace the light one globally), so the
   dark theme gets its own animation and only the animation-name is overridden.
   --vs-text on the brightest stop (#574311) is 8.4:1. */
@keyframes vs_dark_moved_highlight_pulse {
  0%, 100%      { background: var(--vs-surface); }
  15%, 45%, 75% { background: #3a3016; }
  30%, 60%, 90% { background: #574311; }
}

:root[data-theme="dark"] .course_item_box.moved_highlight {
  animation-name: vs_dark_moved_highlight_pulse;
}

/* the inset 4px #f0ad4e marker is a saturated amber and reads fine on navy */


/* --- .course_item_icon type chips ---------------------------------------- */

:root[data-theme="dark"] .course_item_icon {
  background: var(--vs-surface-2);
}

/* The per-type chips are colour-coded by meaning, so the hue is kept and only
   the value is inverted: pale tint -> deep tint, dark glyph -> light glyph.
   No tokens exist for amber/red surface tints. */
:root[data-theme="dark"] .course_item_box.type_folder .course_item_icon {
  background-color: #3a2a10 !important;   /* was #FAE9C9 */
  color: #f0a04a;                         /* was #EA780E -- 6.3:1 */
}

:root[data-theme="dark"] .course_item_box.type_video .course_item_icon {
  background: #3a1414 !important;         /* was #FAE4E4 */
  color: #f08a8a;                         /* was #BD2929 -- 6.5:1 */
}

:root[data-theme="dark"] .course_item_box.type_study_material .course_item_icon {
  background: var(--vs-brand-soft) !important;
  color: var(--vs-brand);                 /* 4.4:1, but a 24px glyph (large) */
}

/* .type_live_class keeps #fff on #BD2929 (6.0:1) -- already correct on dark. */

:root[data-theme="dark"] .course_item_file_box {
  background: var(--vs-surface-2);
}


/* --- .nav-underline tabs -------------------------------------------------- */

:root[data-theme="dark"] .nav-underline {
  border-bottom: 1px solid var(--vs-border);
}

:root[data-theme="dark"] .nav-underline .nav-link.active,
:root[data-theme="dark"] .nav-underline .show > .nav-link {
  color: var(--vs-brand) !important;
  border-bottom-color: var(--vs-brand);
}

:root[data-theme="dark"] .nav-underline .nav-link {
  color: var(--vs-text-muted) !important;   /* was #888 */
}


/* --- input::placeholder --------------------------------------------------- */

/* #ccc on a dark control is bright enough to be mistaken for a real value. */
:root[data-theme="dark"] input::placeholder {
  color: var(--vs-text-muted) !important;
}


/* --- sortable list (.sortable_item / .sortable-ghost) --------------------- */

:root[data-theme="dark"] .sortable_item {
  background-color: var(--vs-surface-2);
  border: 1px solid var(--vs-border-strong);
}

/* the drop placeholder: a recessed well instead of a light grey slab */
:root[data-theme="dark"] .sortable-ghost {
  background-color: var(--vs-surface-3);
}


/* --- table.responsive ----------------------------------------------------- */

:root[data-theme="dark"] table.responsive th,
:root[data-theme="dark"] table.responsive td {
  border: 1px solid var(--vs-border);
}

:root[data-theme="dark"] table.responsive th {
  background-color: var(--vs-surface-2);
}

@media screen and (max-width: 768px) {
  :root[data-theme="dark"] .responsive tr {
    border: 1px solid var(--vs-border);
  }
  /* the rgba(0,0,0,0.01) shadow on .responsive tr is a no-op on dark; skipped */

  :root[data-theme="dark"] .responsive th {
    background: var(--vs-surface-2) !important;
  }

  /* stacked cards: background:#fff with no colour of its own would put the light
     body text on white. */
  :root[data-theme="dark"] .responsive td {
    background: var(--vs-surface);
    border-bottom: 1px solid var(--vs-hairline) !important;
  }
}


/* --- .page_top_box -------------------------------------------------------- */

:root[data-theme="dark"] .page_top_box {
  background: var(--vs-surface-2);
  border: 1px solid var(--vs-border);
}


/* --- .testimonial_box ----------------------------------------------------- */

:root[data-theme="dark"] .testimonial_box {
  background-color: var(--vs-surface) !important;
  border-left: 4px solid var(--vs-brand);
}

:root[data-theme="dark"] .testimonial_box .testimonial_image_box img {
  border: 4px solid var(--vs-border);
}

:root[data-theme="dark"] .testimonial_box .testimonial_designation,
:root[data-theme="dark"] .testimonial_box .testimonial_text {
  color: var(--vs-text-muted);
}


/* --- .offering_box -------------------------------------------------------- */

/* the white-to-near-white gradient inverts to surface-2 -> surface, keeping the
   same "slightly tinted at the top-left" reading */
:root[data-theme="dark"] .offering_box {
  background: linear-gradient(135deg, var(--vs-surface-2) 0%, var(--vs-surface) 100%);
  border: 1px solid var(--vs-border);
}

:root[data-theme="dark"] .offering_box .offering_icon {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .offering_box .offering_title {
  color: var(--vs-text);
}

:root[data-theme="dark"] .offering_box .offering_description {
  color: var(--vs-text-muted);
}

/* The 3px brand bar, shared by .offering_box, .faq_box and .faq_accordion_item.
   #044FC0 is only 2.0:1 against --vs-surface-2 (it is ~7:1 on the light card), so
   the left half of the gradient fades into the card and the bar tapers away. Both
   stops move up one step: --vs-brand-fill clears 3:1 and --vs-brand reaches 4.6:1,
   so it reads across its whole width. */
:root[data-theme="dark"] .offering_box::before,
:root[data-theme="dark"] .faq_box::before,
:root[data-theme="dark"] .faq_accordion_item::before {
  background: linear-gradient(90deg, var(--vs-brand-fill), var(--vs-brand));
}


/* --- FAQ blocks and accordion --------------------------------------------- */

:root[data-theme="dark"] .faq_box {
  background: linear-gradient(135deg, var(--vs-surface-2) 0%, var(--vs-surface) 100%);
  border: 1px solid var(--vs-border);
}

:root[data-theme="dark"] .faq_question {
  color: var(--vs-text);
}

:root[data-theme="dark"] .faq_question i {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .faq_answer {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .faq_accordion_item {
  background: linear-gradient(135deg, var(--vs-surface-2) 0%, var(--vs-surface) 100%);
  border: 1px solid var(--vs-border);
}

:root[data-theme="dark"] .faq_accordion_button {
  color: var(--vs-brand) !important;
}

:root[data-theme="dark"] .faq_accordion_button:not(.collapsed) {
  background: linear-gradient(135deg, var(--vs-brand-soft) 0%, var(--vs-surface) 100%) !important;
  color: var(--vs-brand) !important;
}

/* The chevron is an inline SVG data URI with a hard-coded fill='%23044FC0',
   which cannot read a CSS variable, so the lightened brand (#4d90f0) is
   written into the URL literally. */
:root[data-theme="dark"] .faq_accordion_button::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%234d90f0'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") !important;
}

:root[data-theme="dark"] .faq_accordion_body {
  background: linear-gradient(135deg, var(--vs-surface-2) 0%, var(--vs-surface) 100%);
  color: var(--vs-text-muted) !important;
  border-top: 1px solid var(--vs-border) !important;
}


/* --- course detail header ------------------------------------------------- */

:root[data-theme="dark"] .course_top_background {
  background-color: var(--vs-surface-2);
}

:root[data-theme="dark"] .course_title_wrapper .course_detail_title,
:root[data-theme="dark"] .course_title_wrapper .course_detail_subtitle {
  color: var(--vs-text);
}

/* rgba(26,46,85,.07) is a pale blue-grey lift that disappears on navy */
:root[data-theme="dark"] .course_detail_side_card {
  box-shadow: 0px 10px 50px 0px var(--vs-shadow) !important;
}


/* --- .custom_steps_wrapper ------------------------------------------------ */

:root[data-theme="dark"] .custom_steps_wrapper .step_number {
  background-color: var(--vs-surface-2);
}

/* .custom_steps_wrapper .nav-link.active .step_number (the --primary-color fill
   with #fff on it) is already corrected in 00-tokens.css. */


/* --- .modal-title --------------------------------------------------------- */

:root[data-theme="dark"] .modal-title {
  color: var(--vs-text) !important;
}


/* --- MCQ question boxes and options --------------------------------------- */

:root[data-theme="dark"] .mcq_question_box {
  border: 1px solid var(--vs-border);
  background-color: var(--vs-surface-2);
}

:root[data-theme="dark"] .mcq_question_option {
  border: 1px solid var(--vs-border);
}

:root[data-theme="dark"] .option_alpha {
  background-color: var(--vs-surface-2);
  color: var(--vs-text-muted);
}

/* No dark-green surface token exists; #12321f is the green counterpart of
   --vs-surface-2 and carries --vs-text at 12.6:1. */
:root[data-theme="dark"] .correct_option {
  background-color: #12321f;
  border-color: var(--vs-success);
}

/* .correct_option .option_alpha keeps #fff on #155724 (6.1:1) -- fine on dark. */

:root[data-theme="dark"] .has_user_selected {
  border-color: var(--vs-brand);
}

/* #044FC0 is only 3.1:1 against the darkened question box, so the chip takes the
   filled-brand token; the #fff glyph rules below it stay correct (4.8:1). */
:root[data-theme="dark"] .has_user_selected .option_alpha {
  background-color: var(--vs-brand-fill);
}


/* --- .beautiful_table ------------------------------------------------------ */

:root[data-theme="dark"] .beautiful_table {
  border: 1px solid var(--vs-hairline);
}

:root[data-theme="dark"] .beautiful_table th,
:root[data-theme="dark"] .beautiful_table td {
  border: 1px solid var(--vs-border);
  color: var(--vs-text);
}

/* #4e4edc is 2.6:1 on the dark surface */
:root[data-theme="dark"] .beautiful_table th {
  color: var(--vs-brand);
}

/* Bootstrap striping: the source sets a near-white --bs-table-color-type on a
   near-white stripe, which would land as dark-on-dark here. Both halves are
   repointed so the stripe stays a stripe and stays readable. */
:root[data-theme="dark"] .beautiful_table.table-striped > tbody > tr:nth-of-type(odd) > * {
  --bs-table-color-type: var(--vs-text);
  --bs-table-bg-type: var(--vs-surface-2);
}


/* --- answer-state tints ----------------------------------------------------- */

/* No red/green surface tokens; these are the danger and success hues taken down
   to --vs-surface-2's value. --vs-text clears 13:1 on all three. */
:root[data-theme="dark"] .user_answered_incorrectly {
  border-color: var(--vs-danger);
  background-color: #3a1417;
}

:root[data-theme="dark"] .mcq_question_box.incorrect_answer {
  background-color: #2a181c !important;
}

:root[data-theme="dark"] .mcq_question_box.correct_answer {
  background-color: #12281f !important;
}

:root[data-theme="dark"] .mcq_question_box.unattempted {
  background-color: var(--vs-surface-2) !important;
}


/* ==========================================================================
   11-helpers.css -- dark overrides for assets/website/css/helpers.css

   Additive only. Every selector below is scoped to :root[data-theme="dark"], so
   nothing here can match in light mode. Blocks follow source order and each one
   names the helpers.css rule it shadows, so the two files diff side by side.
   !important is mirrored from the source, never added.

   Deliberately absent: rules that set no colour (layout, sizing, font-size,
   animation timing, select2 geometry), rules whose colour already comes from a
   var(--bs-*) that Bootstrap's data-bs-theme="dark" retargets (.bg-danger-subtle,
   .nav-underline .dropdown-item a, .select2-selection--multiple border), and
   colours that already read correctly on the navy canvas: the purple .bg_primary
   fill, the orange .bg-warning / .btn-warning fills, the four .legend-title
   fills, the saturated .seats-dot indicators and .seats-text.text-* colours, the
   rgba(0,0,0,.6) .form-loader scrim, and .text_white (white on coloured fills).
   ========================================================================== */


/* .border-primary-subtle -- border-color #e4efff */
:root[data-theme="dark"] .border-primary-subtle {
  border-color: var(--vs-border) !important;
}

/* .help-text -- color: red.
   --vs-danger is the palette's only red; on --vs-surface it reaches 4.2:1, a
   little short of the 4.5 body-text target (it clears 3:1). */
:root[data-theme="dark"] .help-text {
  color: var(--vs-danger);
}

/* .page_subheader -- #666 */
:root[data-theme="dark"] .page_subheader {
  color: var(--vs-text-muted);
}

/* .bg-gray-eee / .bg-gray-ddd -- #eee / #ddd.
   The two helpers exist to be one step apart, so ddd takes --vs-border (the
   next value up from --vs-surface-2) rather than collapsing onto the same fill.
   Neither sets a text colour; body copy is --vs-text and clears 12:1 on both. */
:root[data-theme="dark"] .bg-gray-eee {
  background-color: var(--vs-surface-2) !important;
}

:root[data-theme="dark"] .bg-gray-ddd {
  background-color: var(--vs-border) !important;
}

/* .vidsafe_card -- border #ede5ff, box-shadow 0 0 5px 5px #f4efff.
   That shadow is a white lilac glow; on navy it has to become a dark halo or the
   card grows a bright ring. */
:root[data-theme="dark"] .vidsafe_card {
  border-color: var(--vs-border) !important;
  box-shadow: 0px 0px 5px 5px var(--vs-shadow);
}

/* .data_box .data_label / .data_value -- #666 / #555 */
:root[data-theme="dark"] .data_box .data_label {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .data_box .data_value {
  color: var(--vs-text);
}

/* .drop-zone -- 2px dashed #eee on #f8f5ff, label #044FC0.
   The dashed rule was near-invisible by design in light (#eee on #f8f5ff), so it
   takes the strongest line token and stays a soft hint rather than a hard box. */
:root[data-theme="dark"] .drop-zone {
  border-color: var(--vs-border-strong);
  background-color: var(--vs-brand-soft);
  color: var(--vs-brand);
}

/* .drop-zone.dragover -- #e9ecef. Light steps *down* from #f8f5ff, so dark
   recesses too rather than lifting: --vs-surface-3 is a 1.30:1 step off the
   resting --vs-brand-soft (light's own step is only 1.10:1, so this reads more
   clearly, not less) and it carries the --vs-brand label at 5.3:1. Lifting to
   --vs-border instead would drop that label to 3.6:1, under the 4.5 needed for
   1rem body text. */
:root[data-theme="dark"] .drop-zone.dragover {
  background-color: var(--vs-surface-3);
}

/* .video-preview -- border #ddd */
:root[data-theme="dark"] .video-preview {
  border-color: var(--vs-border);
}

/* FILE UPLOAD -- .file_uploaded_div background #faf8ff, border #d7c4ff */
:root[data-theme="dark"] .file_uploaded_div {
  background-color: var(--vs-surface-2);
  border-color: var(--vs-border);
}

/* .inline-thin-progressbar -- border #bbb, background #fff */
:root[data-theme="dark"] .inline-thin-progressbar {
  border-color: var(--vs-border-strong);
  background-color: var(--vs-track);
}

/* .file_uploaded_div .delete + :hover -- #666 / #222 */
:root[data-theme="dark"] .file_uploaded_div .delete {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .file_uploaded_div .delete:hover {
  color: var(--vs-text);
}

/* .thin_scrollbar -- track #fbfbfb, thumb #fff with a #f1f1f1 ring, hover #f0f0f0.
   Light kept the thumb barely above the track; dark keeps the same relationship
   with the two line tokens, hover being the lighter of the pair. */
:root[data-theme="dark"] .thin_scrollbar::-webkit-scrollbar-track {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .thin_scrollbar::-webkit-scrollbar-thumb {
  background-color: var(--vs-border);
  border-color: var(--vs-surface-2);
}

:root[data-theme="dark"] .thin_scrollbar::-webkit-scrollbar-thumb:hover {
  background-color: var(--vs-border-strong);
}

/* Firefox. The source declares a single value, which is invalid and dropped, so
   this is the first declaration that actually applies -- thumb then track. */
:root[data-theme="dark"] .thin_scrollbar * {
  scrollbar-color: var(--vs-border) var(--vs-surface-2);
}

/* .fieldset_box + legend -- borders #ddd, legend #fff on #000 text.
   The legend fill is what masks the border line behind it, so it must stay an
   opaque surface, not a transparent tint. */
:root[data-theme="dark"] .fieldset_box {
  border-color: var(--vs-border);
}

/* The #ddd ring belongs to every legend, the four coloured .legend-title chips
   included -- left alone it stays a pale outline on the navy card. */
:root[data-theme="dark"] .fieldset_box legend {
  border-color: var(--vs-border);
}

/* Fill and text are excluded from .legend-title.primary/.danger/.warning/
   .success. Those four carry their own saturated fill and white text in
   helpers.css at (0,3,0); `.fieldset_box legend` under the theme prefix is
   (0,3,1), so an unqualified rule here would out-specify all four and flatten
   them to the plain surface in dark only. The :not() chain keeps them on their
   source colours (which read correctly on navy) while bare <legend> and the
   bare .legend-title used in the mains-test templates still get the dark
   surface they need. */
:root[data-theme="dark"] .fieldset_box legend:not(.primary):not(.danger):not(.warning):not(.success) {
  background-color: var(--vs-surface);
  color: var(--vs-text);
}

/* .stacked-responsive-table (mobile) -- tr background white, td border #f6f6f6,
   td::before #888. The white row is the headline fix here: without it every
   stacked card renders --vs-text on white. */
@media (max-width: 768px) {
  :root[data-theme="dark"] .stacked-responsive-table tr {
    background-color: var(--vs-surface);
  }

  :root[data-theme="dark"] .stacked-responsive-table td {
    border-bottom-color: var(--vs-hairline);
  }

  :root[data-theme="dark"] .stacked-responsive-table td::before {
    color: var(--vs-text-muted);
  }
}

/* .custom_fieldset + legend -- 1px dashed #044fc0 on #fff, legend #fff on #044fc0.
   The legend keeps its white text: it moves to the darker brand fill, where white
   sits at 4.8:1. */
:root[data-theme="dark"] .custom_fieldset {
  border-color: var(--vs-brand);
  background-color: var(--vs-surface);
}

:root[data-theme="dark"] .custom_fieldset legend {
  background-color: var(--vs-brand-fill);
}

/* .image_upload_preview_box + :after -- border #ddd, placeholder caption #999 */
:root[data-theme="dark"] .image_upload_preview_box {
  border-color: var(--vs-border);
}

:root[data-theme="dark"] .image_upload_preview_box:after {
  color: var(--vs-text-muted);
}

/* textarea.form-control::placeholder -- #ccc.
   --vs-text-muted is the faintest legible token in the palette; it reads a touch
   stronger than #ccc did on white, which is the right trade on a dark field. */
:root[data-theme="dark"] textarea.form-control::placeholder {
  color: var(--vs-text-muted) !important;
}

/* .bg-primary-light -- #edf0f9 */
:root[data-theme="dark"] .bg-primary-light {
  background-color: var(--vs-brand-soft) !important;
}

/* #pricing_calculation_preview .course_price / .payment_gateway / .your_share /
   .our_share -- all #000 */
:root[data-theme="dark"] #pricing_calculation_preview .course_price,
:root[data-theme="dark"] #pricing_calculation_preview .payment_gateway,
:root[data-theme="dark"] #pricing_calculation_preview .your_share,
:root[data-theme="dark"] #pricing_calculation_preview .our_share {
  color: var(--vs-text);
}

/* .input_with_clear_button::after -- #666 X on a #fff pill, border #ddd.
   The pill is a mask over the field it sits on, so it takes the field surface. */
:root[data-theme="dark"] .input_with_clear_button::after {
  color: var(--vs-text-muted);
  background-color: var(--vs-surface-3);
  border-color: var(--vs-border);
}

/* .form_group_with_clear_button .clear_input_button + :hover -- #666 / #000 */
:root[data-theme="dark"] .form_group_with_clear_button .clear_input_button {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .form_group_with_clear_button .clear_input_button:hover {
  color: var(--vs-text);
}

/* select.form-control -- the chevron is an inline SVG with stroke='%23343a40',
   a near-black that disappears on a dark field. A data: URI cannot read a CSS
   variable, so the stroke is the literal value of --vs-text-muted (#94a3b8). */
:root[data-theme="dark"] select.form-control {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2394a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 6 7 7 7-7'/%3e%3c/svg%3e");
}

/* .alert-warning -- #E8750C on #FFF8F2, border #F58F30.
   .alert-info -- #f2f7ff fill, border #B0CFFF. Both fills are near-white. The
   info alert's text is left to Bootstrap's dark --bs-info-text-emphasis. */
:root[data-theme="dark"] .alert-warning {
  border-color: var(--vs-warning) !important;
  color: var(--vs-warning) !important;
  background-color: var(--vs-surface-2) !important;
}

:root[data-theme="dark"] .alert-info {
  border-color: var(--vs-brand) !important;
  background: var(--vs-brand-soft) !important;
}

/* .box_checkbox_label + :hover -- #fff chip, border #ddd, hover tint #f2f7ff.
   The chip sits on a card, so it recesses to --vs-surface-3 and the hover lifts
   to --vs-brand-soft; using --vs-surface-2 for the resting state would have made
   the hover step invisible. */
:root[data-theme="dark"] .box_checkbox_label {
  border-color: var(--vs-border);
  background-color: var(--vs-surface-3);
}

:root[data-theme="dark"] .box_checkbox_label:hover {
  background-color: var(--vs-brand-soft);
}

/* .highlight_flash + @keyframes highlightFlash -- #fff9c4 settling to #fff.
   A @keyframes block cannot be scoped by an attribute selector, so dark gets its
   own uniquely named copy and .highlight_flash is repointed at it; the light
   animation is left exactly as it was and nothing references the dark one
   outside [data-theme="dark"]. The amber glow works on navy and is kept.
   No dark-amber token exists, so the wash is a literal warm navy that holds
   --vs-text at 12:1. */
@keyframes vsDarkHighlightFlash {
  0% {
    box-shadow: 0 0 10px rgba(255, 177, 59, 0.8);
    background-color: #3a2e08 !important;
    border-color: #ff7807 !important;
  }
  50% {
    box-shadow: 0 0 5px rgba(255, 177, 59, 0.4);
    background-color: #3a2e08 !important;
    border-color: #ff7807 !important;
  }
  100% {
    box-shadow: none;
    background-color: var(--vs-surface) !important;
    border-color: #ff7807 !important;
  }
}

:root[data-theme="dark"] .highlight_flash {
  animation-name: vsDarkHighlightFlash;
  background-color: #3a2e08 !important;
}

/* .select2-selection__choice -- #2a62b4 on #e9eef7, border #d4e5ff */
:root[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice {
  background-color: var(--vs-brand-soft);
  border-color: var(--vs-border);
  color: var(--vs-brand);
}

/* ===== Collapsible Slots Accordion ===== */

/* .slots-accordion .accordion-item -- border #e9ecef */
:root[data-theme="dark"] .slots-accordion .accordion-item {
  border-color: var(--vs-border);
}

/* .accordion-button, :not(.collapsed), :focus -- #fff fills, focus border #e9ecef */
:root[data-theme="dark"] .slots-accordion .accordion-button,
:root[data-theme="dark"] .slots-accordion .accordion-button:not(.collapsed) {
  background-color: var(--vs-surface);
}

:root[data-theme="dark"] .slots-accordion .accordion-button:focus {
  border-color: var(--vs-border);
}

/* .accordion-button::after -- inline SVG chevron with fill='%23666'. Same data:
   URI constraint as the select arrow, so the literal --vs-text-muted value. */
:root[data-theme="dark"] .slots-accordion .accordion-button::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2394a3b8'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}

/* .date-header-icon / .date-header-text -- #0d6efd / #333 */
:root[data-theme="dark"] .slots-accordion .date-header-icon {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .slots-accordion .date-header-text {
  color: var(--vs-text);
}

/* .slots-count-badge -- #555 on #e9ecef */
:root[data-theme="dark"] .slots-accordion .slots-count-badge {
  background-color: var(--vs-surface-2);
  color: var(--vs-text);
}

/* .bookings-count-badge -- #c62828 on #fce4ec. The pale pink fill has to go, and
   --vs-danger is the only red in the palette: it lands at 3.7:1 on
   --vs-surface-2, below the 4.5 target for a 0.75rem label but clear of 3:1. */
:root[data-theme="dark"] .slots-accordion .bookings-count-badge {
  background-color: var(--vs-surface-2);
  color: var(--vs-danger);
}

/* ===== Slots Table ===== */

/* .slots-table thead th -- #6c757d on #fff, rules #e9ecef */
:root[data-theme="dark"] .slots-table thead th {
  background-color: var(--vs-surface-2);
  border-bottom-color: var(--vs-border);
  border-top-color: var(--vs-border);
  color: var(--vs-text-muted);
}

/* .slots-table tbody td -- border-bottom #f0f0f0 */
:root[data-theme="dark"] .slots-table tbody td {
  border-bottom-color: var(--vs-hairline);
}

/* .slots-table .mentor-cell-td -- border-right #f0f0f0 */
:root[data-theme="dark"] .slots-table .mentor-cell-td {
  border-right-color: var(--vs-hairline);
}

/* .slots-table .mentor-last-row td -- 2px #e9ecef group separator */
:root[data-theme="dark"] .slots-table .mentor-last-row td {
  border-bottom-color: var(--vs-border);
}

/* .mentor-avatar -- #6c757d glyph on #e9ecef */
:root[data-theme="dark"] .slots-table .mentor-avatar,
:root[data-theme="dark"] .mentor-card-header .mentor-avatar {
  background-color: var(--vs-surface-2);
  color: var(--vs-text-muted);
}

/* .mentor-name / .time-text / .center-text -- #333 / #333 / #555 */
:root[data-theme="dark"] .slots-table .mentor-name,
:root[data-theme="dark"] .mentor-card-header .mentor-name,
:root[data-theme="dark"] .slots-table .time-text,
:root[data-theme="dark"] .slots-table .center-text {
  color: var(--vs-text);
}

/* Mode Badges -- .mode-on is #2e7d32 on #e8f5e9 with a #c8e6c9 edge, .mode-off is
   #757575 on #fff with a #e0e0e0 edge. Both fills are near-white. --vs-success on
   --vs-surface-2 gives 4.3:1, marginally under 4.5 for the 0.75rem label; the
   palette has no lighter green. */
:root[data-theme="dark"] .mode-badge.mode-on {
  background-color: var(--vs-surface-2);
  color: var(--vs-success);
  border-color: var(--vs-success);
}

:root[data-theme="dark"] .mode-badge.mode-off {
  background-color: var(--vs-surface-2);
  color: var(--vs-text-muted);
  border-color: var(--vs-border);
}

/* .action-link -- #0d6efd */
:root[data-theme="dark"] .action-link {
  color: var(--vs-brand);
}

/* ===== Slots mobile cards ===== */

/* .mentor-card -- #fff, border #e9ecef */
:root[data-theme="dark"] .mentor-card {
  background-color: var(--vs-surface);
  border-color: var(--vs-border);
}

/* .mentor-card-header -- #f8f9fa, border-bottom #e9ecef */
:root[data-theme="dark"] .mentor-card-header {
  background-color: var(--vs-surface-2);
  border-bottom-color: var(--vs-border);
}

/* .slot-card / .slot-card-row -- #fff on #f0f0f0, row rule #f8f8f8. Light nested
   white-on-white and let the hairline do the work; dark keeps that reading. */
:root[data-theme="dark"] .slot-card {
  background-color: var(--vs-surface);
  border-color: var(--vs-hairline);
}

:root[data-theme="dark"] .slot-card-row {
  border-bottom-color: var(--vs-hairline);
}

/* .slot-label / .slot-value -- #6c757d / #333 */
:root[data-theme="dark"] .slot-label {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .slot-value {
  color: var(--vs-text);
}

/* .dropdown-item.active, .dropdown-item:active -- #f0f0f0 fill, colour inherits */
:root[data-theme="dark"] .dropdown-item.active,
:root[data-theme="dark"] .dropdown-item:active {
  background-color: var(--vs-surface-2);
}


/* ==========================================================================
   12-navigation.css -- dark overrides for the dashboard chrome.

   Sources covered, in full:
     assets/website/css/navigation.css   sidebar, top bar, treeview menu,
                                         .trigger, .theme_toggle_btn
     assets/website/css/style.css
     assets/website/css/accounts.css

   Additive only. Every selector is scoped to :root[data-theme="dark"], so nothing
   here can match in light mode. Source rules are named in the headings so the two
   files diff against each other.

   --primary-color is already repointed at --vs-brand in 00-tokens.css, so most
   `var(--primary-color)` uses in navigation.css -- the .trigger ring and icon, the
   active nav label and its 4px border, the .theme_toggle_btn hover -- resolve
   correctly on their own and are not repeated here. .trigger:hover (brand fill +
   white icon) is handled there too.

   The exception is the treeview sub-menu: see the specificity note on that block.
   ========================================================================== */


/* --- navigation.css: the sidebar shell -----------------------------------
   .dashboard_left_column, .dashboard_left_column .menu_container, and its
   .light_sidebar counterpart (declared twice in the source: colour at the top of
   the file, background again further down). */

:root[data-theme="dark"] .dashboard_left_column{
  border-right-color: var(--vs-hairline);    /* was #eee -- vanishes otherwise */
}

:root[data-theme="dark"] .dashboard_left_column .menu_container,
:root[data-theme="dark"] .light_sidebar .dashboard_left_column .menu_container{
  background: var(--vs-surface);             /* was #FFF, both copies */
}

/* The .light_sidebar copy is the one that actually ships, and its #444 is the
   sidebar's inherited text colour. The base rule's `color:#fff` is left alone --
   white already reads at ~16:1 on --vs-surface. */
:root[data-theme="dark"] .light_sidebar .dashboard_left_column .menu_container{
  color: var(--vs-text-nav);                 /* was #444 */
}


/* --- navigation.css: .dashboard_right_column ------------------------------
   The content canvas, in both its base and .light_sidebar forms. */

:root[data-theme="dark"] .dashboard_right_column,
:root[data-theme="dark"] .light_sidebar .dashboard_right_column{
  background: var(--vs-app-bg);              /* was #F3F5F9 / #F5F6FB */
}


/* --- navigation.css: .dashboard_top_bar ---------------------------------- */

:root[data-theme="dark"] .dashboard_top_bar{
  background: var(--vs-surface);             /* was #fff */
  border-bottom-color: var(--vs-hairline);   /* was #eee */
}


/* --- navigation.css: .trigger -------------------------------------------
   The sidebar collapse chip. Colour and the 2px ring are var(--primary-color) and
   follow the token remap; only the white fill needs a token here. --vs-surface
   would sink it into the sidebar it sits on, so it takes the next surface up and
   keeps reading as a raised chip. */

:root[data-theme="dark"] .trigger{
  background: var(--vs-surface-2);           /* was #fff */
}


/* --- navigation.css: .user_image_holder ---------------------------------
   Avatar placeholder disc. #ddd would normally map to --vs-border, but this is an
   image placeholder, which is exactly what --vs-thumb is for. It reads slightly
   flatter than the light theme did (1.16:1 off --vs-surface vs #ddd-on-#fff's
   1.36:1), which is why the note is here rather than the round number.
   Currently dead in any case -- the markup is commented out in all three
   dashboard bases -- so it is kept only so the class is covered if it returns. */

:root[data-theme="dark"] .user_image_holder{
  background: var(--vs-thumb);               /* was #ddd */
}


/* --- navigation.css: .sidebar_menu_ul li a ------------------------------- */

:root[data-theme="dark"] .light_sidebar .sidebar_menu_ul li a{
  color: var(--vs-text-nav);                 /* was #444 -- 11:1 on --vs-surface */
}

/* The hover tint is rgba(4,79,192,.06): six per cent of a dark blue over a dark
   blue panel is nothing at all, so it becomes the soft brand tint. */
:root[data-theme="dark"] .sidebar_menu_ul li a:hover{
  background: var(--vs-brand-soft);
}


/* --- navigation.css: li.active a / li.treeview.active a -------------------
   The active pill. Its label colour and 4px left border are var(--primary-color)
   and would come through on their own; they are restated so the whole pair sits
   in one place. --vs-brand on --vs-brand-soft measures 4.41:1 -- a hair UNDER the
   4.5:1 body-text bar (it clears the 3:1 large/UI bar comfortably). Both values are
   fixed by the token contract, so this is a palette-level call, not one this file
   can make: do not darken --vs-brand-soft any further, and see the note at the
   foot of the file. */

:root[data-theme="dark"] .light_sidebar .sidebar_menu_ul li.active a,
:root[data-theme="dark"] .light_sidebar .sidebar_menu_ul li.treeview.active a{
  background: var(--vs-brand-soft);          /* was #E8EFFB */
  color: var(--vs-brand);
  border-left-color: var(--vs-brand);
}


/* --- navigation.css: the treeview sub-menu -------------------------------
   .treeview-menu li a sets its colour !important, so the override must too, or it
   loses. Its `background: transparent !important` is already correct on dark.

   SPECIFICITY TRAP -- the reason the hover and active colours are restated below
   rather than left to the --primary-color remap. Scoping adds two class-level
   components (:root and [data-theme]), so the resting rule outranks the source's
   own state rules even though it is the less specific selector in light mode:

     :root[data-theme="dark"] .treeview-menu li a   (0,3,2)  !important
     .treeview-menu li a:hover                      (0,2,2)  !important
     ul.treeview-menu li.active a                   (0,2,3)  !important

   (0,3,2) beats both, so a bare `color: ... !important` here silently flattens the
   hovered and active sub-items to the muted resting grey -- the active item stops
   being distinguishable at all. Verified in-browser: light gives rgb(4,79,192) for
   both states; without the two restatements below, dark gave rgb(148,163,184) for
   both. Each state therefore restates its colour at its own scoped specificity. */

:root[data-theme="dark"] .treeview-menu li a{
  color: var(--vs-text-subnav) !important;   /* was #4b5563 -- 6.4:1 on --vs-surface */
}

:root[data-theme="dark"] .treeview-menu li a:hover{
  background: var(--vs-brand-soft) !important;   /* was rgba(4,79,192,.06) */
  color: var(--vs-brand) !important;             /* was var(--primary-color) */
}

:root[data-theme="dark"] ul.treeview-menu li.active a{
  background: var(--vs-brand-soft) !important;   /* was #E8EFFB */
  color: var(--vs-brand) !important;             /* was var(--primary-color) */
}

/* The rail down the left of an open group, and the rule between menu groups. */
:root[data-theme="dark"] .treeview.active > .treeview-menu{
  border-left-color: var(--vs-border);       /* was #e3e7ef */
}

:root[data-theme="dark"] .sidebar_menu_ul li.menu_divider{
  background: var(--vs-border);              /* was #e3e7ef */
}


/* --- navigation.css: .theme_toggle_btn -----------------------------------
   The light/dark switch itself. It sits on the top bar (--vs-surface), so it takes
   the next surface up plus the strong border to stay findable. The fill alone is
   only 1.16:1 against the bar (as in light, where #f3f5f9 on #fff is 1.09:1) -- it
   is the border that draws the control, at 1.9:1, up from #c6c7c9-on-#fff's 1.7:1.
   Still short of the 3:1 UI bar, but strictly better than the light theme it
   mirrors. The :hover rule is pure var(--primary-color) and needs nothing. */

:root[data-theme="dark"] .theme_toggle_btn{
  border-color: var(--vs-border-strong);     /* was #c6c7c9 */
  background: var(--vs-surface-2);           /* was #f3f5f9 */
  color: var(--vs-text-nav);                 /* was #444 -- 9.9:1 on --vs-surface-2 */
}


/* --- style.css: alternating landing bands --------------------------------
   In light mode type_2 (#fff) *is* the page and type_1 (#f9fbff) is a faint lift
   off it. Inverting that relationship rather than the hexes keeps the banding:
   type_2 returns to the canvas, type_1 stays one step above it. */

:root[data-theme="dark"] .home_section_type_1{
  background-color: var(--vs-surface);       /* was #f9fbff */
}

:root[data-theme="dark"] .home_section_type_2{
  background-color: var(--vs-app-bg);        /* was #fff */
}

:root[data-theme="dark"] .institute_footer{
  background-color: var(--vs-surface);       /* was #f9fbff */
}


/* style.css's .course_detail_side_card shadow is NOT repeated here -- 10-base.css
   already carries an identical rule under its course-detail section. Two byte-for-byte
   copies in the same assembled file is just drift waiting to happen. */


/* --- style.css: .thin_vidsafe_card -------------------------------------- */

:root[data-theme="dark"] .thin_vidsafe_card{
  border-color: var(--vs-border);            /* was #E6EAF6 */
  background-color: var(--vs-surface);       /* was #fff */
}

:root[data-theme="dark"] .thin_vidsafe_card_title,
:root[data-theme="dark"] .thin_vidsafe_card_price{
  color: var(--vs-text);                     /* was #000 on a now-dark card */
}


/* --- accounts.css: .load_modal_step_link --------------------------------
   Grey step pill. The colour carries !important in the source, the background does
   not, so the override matches each. Hover moved a step darker against white; on
   navy the equivalent move is a step lighter. */

:root[data-theme="dark"] .load_modal_step_link{
  color: var(--vs-text) !important;          /* was #000 !important */
  background: var(--vs-surface-2);           /* was #eee */
}

:root[data-theme="dark"] .load_modal_step_link:hover{
  background: var(--vs-border);              /* was #ddd */
}


/* --- Deliberately not overridden ----------------------------------------
   .login_column (#044FC0) and .auth_context_badge -- a saturated brand panel that
     reads correctly on a dark page, with its white text at 7.3:1 on the blue and
     the badge's rgba(255,255,255,.16) fill still sitting on that unchanged blue.
   .help-text (#eb953a) -- 7.7:1 on --vs-app-bg as it stands.
   .sidebar_menu_ul li a / .menu_container base `color:#fff` -- white on
     --vs-surface, already correct.
   .treeview.active { background: transparent } and .treeview-menu li a's
     transparent background -- no colour to change.
   .thin_badge, .no-break, .force-hidden, .organization_box, the media query, and
     the layout halves of every rule above -- geometry only.

   KNOWN MARGINAL CONTRAST (palette-level, not fixable in this file)
   --vs-brand on --vs-brand-soft is 4.41:1. That pairing is what the token contract
   prescribes for active navigation, and it lands just under the 4.5:1 body-text
   threshold. It affects three states: the active sidebar pill, the active treeview
   sub-item, and the treeview hover. Raising --vs-brand a step (or lifting
   --vs-brand-soft toward --vs-surface-2) would clear it, but both are shared tokens
   and the change belongs in 00-tokens.css after a look at everything else that uses
   them. Flagged rather than patched locally with an off-contract colour.
   ------------------------------------------------------------------------ */


/* ==========================================================================
   13 -- assets/website/css/forms.css  +  assets/website/css/components.css

   Additive dark-mode overrides only. Every selector is prefixed with
   :root[data-theme="dark"], so light mode cannot match a single rule here.
   Headings below name the source rule (and its line) being overridden.
   !important is mirrored from the source, never added.
   ========================================================================== */


/* --------------------------------------------------------------------------
   forms.css -- form controls
   The source's own rules set almost no colour (.form-control is radius/height
   only, line 49), but a text input is the one surface that must never stay
   white, so the resting state is pinned to the control token here rather than
   left to whatever --bs-body-bg happens to be.
   -------------------------------------------------------------------------- */

:root[data-theme="dark"] .form-control,
:root[data-theme="dark"] .form-select,
:root[data-theme="dark"] textarea.form-control {
  background-color: var(--vs-surface-3);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
}

/* forms.css .form-control:focus (line 71) -- source pins bg to --bs-body-bg,
   which is the page canvas; keep focus on the control surface instead so the
   field does not change shade when focused. border-color #86b7fe is left alone:
   a pale blue ring already reads correctly on the navy. */
:root[data-theme="dark"] .form-control:focus,
:root[data-theme="dark"] .form-select:focus {
  background-color: var(--vs-surface-3);
  color: var(--vs-text);
}

/* forms.css input.form-control::placeholder + the four vendor prefixes
   (lines 54-69). #bbb on a dark field is a mid-grey smear.
   textarea is deliberately NOT grouped in here: helpers.css sets
   `textarea.form-control::placeholder { color:#ccc !important }`, which no
   un-!important declaration can beat. That one is handled, with the
   !important mirrored, in 11-helpers.css. */
:root[data-theme="dark"] input.form-control::placeholder {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] input.form-control::-webkit-input-placeholder {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] input.form-control:-moz-placeholder {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] input.form-control::-moz-placeholder {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] input.form-control:-ms-input-placeholder {
  color: var(--vs-text-muted);
}

/* Disabled / readonly: Bootstrap greys these to #e9ecef, i.e. a white field
   with dark text sitting in a dark form. */
:root[data-theme="dark"] .form-control:disabled,
:root[data-theme="dark"] .form-select:disabled,
:root[data-theme="dark"] .form-control[readonly] {
  background-color: var(--vs-surface-2);
  border-color: var(--vs-border);
  color: var(--vs-text-muted);
}

/* The select caret is a data-URI SVG with a hard-coded stroke='%23343a40' --
   near-black on a dark field. A data URI cannot read a CSS variable, so the
   hex is inlined; %2394a3b8 is --vs-text-muted. */
:root[data-theme="dark"] .form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2394a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

/* File input: the button half is a separate light slab. */
:root[data-theme="dark"] .form-control::file-selector-button {
  background-color: var(--vs-surface-2);
  border-color: var(--vs-border);
  color: var(--vs-text);
}

/* Checkboxes and radios: unchecked box is #fff with an rgba(0,0,0,.25) border,
   both invisible on dark. The checked tick/dot stays white on the brand fill. */
:root[data-theme="dark"] .form-check-input {
  background-color: var(--vs-surface-3);
  border-color: var(--vs-border-strong);
}
:root[data-theme="dark"] .form-check-input:checked {
  background-color: var(--vs-brand-fill);
  border-color: var(--vs-brand-fill);
}

/* forms.css .form-label (line 79) sets size/weight only; the colour comes from
   the cascade. Labels and helper text get the muted token. */
:root[data-theme="dark"] .form-label,
:root[data-theme="dark"] .col-form-label {
  color: var(--vs-text);
}
:root[data-theme="dark"] .form-text {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .input-group-text {
  background-color: var(--vs-surface-2);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
}

/* Validation states are deliberately NOT overridden.
   base.html sets data-bs-theme="dark" alongside data-theme="dark", so Bootstrap
   5.3.3's own dark theme is already supplying --bs-form-invalid-color /
   --bs-form-invalid-border-color = #ea868f and --bs-form-valid-* = #75b798.
   Those measure 6.5:1 and 7.1:1 on --vs-surface. Repointing them at --vs-danger
   (#d9534f) would DROP .invalid-feedback to 4.18:1 on a card -- below AA -- and
   --vs-success (#2e9e63) to 4.87:1, so forcing the palette tokens here makes the
   contrast worse, not better. Left to Bootstrap. */

/* Selection highlight: the browser default is tuned for a white page. */
:root[data-theme="dark"] ::selection {
  background-color: var(--vs-brand-fill);
  color: var(--vs-text);
}


/* --------------------------------------------------------------------------
   forms.css .btn-primary (line 90) / .btn-outline-primary (line 107)
   Only the values that break on dark are repointed. --bs-btn-hover-bg and
   --bs-btn-active-bg (#083C8B) are left as-is: a deep saturated blue under
   white text is fine on the navy and keeps the pressed state distinct.
   -------------------------------------------------------------------------- */

:root[data-theme="dark"] .btn-primary {
  --bs-btn-bg: var(--vs-brand-fill);
  --bs-btn-border-color: var(--vs-brand-fill);
  --bs-btn-disabled-bg: var(--vs-brand-fill);
  --bs-btn-disabled-border-color: var(--vs-brand-fill);
}

/* #044fc0 as a *text* colour is ~2.5:1 on the navy -- unreadable. The outline
   variant takes the lightened brand for text/border (5.2:1 on a card) and the
   fill for its hover/active grounds (white on it, 4.8:1).
   Note: 00-tokens.css line 191 declares an identical `.btn-outline-primary`
   block (reasoning from Bootstrap's #0d6efd rather than forms.css's #044fc0).
   Same eight declarations, same values, so nothing breaks and this one wins on
   order -- but one of the two should be retired. forms.css owns the selector, so
   this is the copy to keep. */
:root[data-theme="dark"] .btn-outline-primary {
  --bs-btn-color: var(--vs-brand);
  --bs-btn-border-color: var(--vs-brand);
  --bs-btn-hover-bg: var(--vs-brand-fill);
  --bs-btn-hover-border-color: var(--vs-brand-fill);
  --bs-btn-active-bg: var(--vs-brand-fill);
  --bs-btn-active-border-color: var(--vs-brand-fill);
  --bs-btn-disabled-color: var(--vs-brand);
  --bs-btn-disabled-border-color: var(--vs-brand);
}

/* forms.css .btn-light (line 124) -- brand text on a pale brand slab.
   Hover text goes to --vs-text: brand-on-hover-fill is only 3.4:1, while the
   resting brand-on-soft pair is 4.5:1. Hover/active grounds step *lighter*
   (--vs-border, --vs-border-strong) because on dark "raised" means lighter.

   (An earlier draft of 20-bootstrap-shim.css also declared `.btn-light` and, being
   later in the concatenation, won at equal specificity with Bootstrap's stock
   #f8f9fa/#000 -- values this app never renders, since forms.css line 124 replaces
   all twelve --bs-btn-* with the brand pair #044FC0 on #E6EAF6. That duplicate has
   since been removed from part 20, so the faithful mapping below is the live one.
   Do not reintroduce a second `.btn-light` block.) */
:root[data-theme="dark"] .btn-light {
  --bs-btn-color: var(--vs-brand);
  --bs-btn-bg: var(--vs-brand-soft);
  --bs-btn-border-color: var(--vs-brand-soft);
  --bs-btn-hover-color: var(--vs-text);
  --bs-btn-hover-bg: var(--vs-border);
  --bs-btn-hover-border-color: var(--vs-border);
  --bs-btn-active-color: var(--vs-text);
  --bs-btn-active-bg: var(--vs-border-strong);
  --bs-btn-active-border-color: var(--vs-border-strong);
  --bs-btn-disabled-color: var(--vs-text-muted);
  --bs-btn-disabled-bg: var(--vs-brand-soft);
  --bs-btn-disabled-border-color: var(--vs-brand-soft);
}

/* forms.css .btn-link:hover / :active (lines 141-146) -- near-white wash.
   !important mirrored from the source. The link colour itself already comes
   from --bs-link-color in 00-tokens.css, so it is not repeated here. */
:root[data-theme="dark"] .btn-link:hover {
  background-color: var(--vs-surface-2) !important;
}
:root[data-theme="dark"] .btn-link:active {
  background-color: var(--vs-border) !important;
}


/* --------------------------------------------------------------------------
   forms.css .select2-container (line 149) -- NOT overridden here.
   The source's only select2 declaration is `display: block !important`, which
   carries no colour. Every coloured part of the widget (selection box, dropdown
   panel, search field, result highlight, multi-select pills, clear/arrow glyphs,
   disabled state) is owned by 21-vendor.css, which names forms.css line 149 and
   helpers.css lines 278-295 / 655-667 as its sources and loads after this file.
   Duplicating it here would only produce two blocks at equal specificity where
   the later one silently wins.
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   components.css :: VIEW TOGGLE COMPONENT (lines 66-102)
   A segmented control: a light track with a white "raised" chip. On dark the
   ordering has to invert -- the track becomes the darkest step (a recessed
   well) and the active chip the lightest, or the chip would read as a hole.
   -------------------------------------------------------------------------- */

:root[data-theme="dark"] .view-toggle-container {
  background-color: var(--vs-surface-3);
}

:root[data-theme="dark"] .view-toggle-btn {
  color: var(--vs-text-muted);          /* 6.6:1 on the track */
}

/* Hover has to be a step the eye can actually see against the track. The obvious
   pick, --vs-surface, is only 1.02:1 on --vs-surface-3 -- the hover state would
   simply not exist. --vs-border is 1.47:1 on the track (the light theme's
   #e0e0e0-on-#f0f0f0 is 1.16:1) and carries --vs-text at 10.5:1. */
:root[data-theme="dark"] .view-toggle-btn:hover {
  color: var(--vs-text);
  background-color: var(--vs-border);
}

/* Source shadow is rgba(0,0,0,.1) -- invisible on navy; --vs-shadow restores
   the lift. */
:root[data-theme="dark"] .view-toggle-btn.active {
  background-color: var(--vs-surface-2);
  color: var(--vs-brand);               /* 4.6:1 */
  box-shadow: 0 1px 3px var(--vs-shadow);
}


/* --------------------------------------------------------------------------
   components.css :: FILTER SIDEBAR COMPONENT (lines 109-211)
   -------------------------------------------------------------------------- */

/* .filter-chip-label (line 120) + :hover (line 131). Hover also brightens the
   text: the muted token on the lighter hover ground is only 4.3:1, on
   --vs-text it is ~10:1. */
:root[data-theme="dark"] .filter-chip-label {
  background-color: var(--vs-surface-2);
  border-color: var(--vs-border);
  color: var(--vs-text-muted);          /* 5.7:1 */
}
:root[data-theme="dark"] .filter-chip-label:hover {
  background-color: var(--vs-border);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
}

/* Checked chip (line 136) keeps its white label -- 4.8:1 on the fill. */
:root[data-theme="dark"] .filter-chip input[type="checkbox"]:checked + .filter-chip-label {
  background-color: var(--vs-brand-fill);
  border-color: var(--vs-brand-fill);
}

:root[data-theme="dark"] .filter-section-title {
  color: var(--vs-text);
}

/* border-bottom: 1px solid #eee -- would disappear entirely. */
:root[data-theme="dark"] .filter-section {
  border-bottom-color: var(--vs-hairline);
}

/* The offcanvas panel itself (.offcanvas.filter-offcanvas, line 159) sets only
   a width, but its footer is being darkened below -- leaving the panel white
   would put dark body text on a dark strip. Scoped to the filter offcanvas so
   no other panel is touched. */
:root[data-theme="dark"] .offcanvas.filter-offcanvas {
  background-color: var(--vs-surface);
  color: var(--vs-text);
}
:root[data-theme="dark"] .offcanvas-footer {
  background-color: var(--vs-surface-2);
  color: var(--vs-text);
}

/* .active-filter-pill (line 183). The pale blue ground/border become the soft
   brand tint and the card accent; the label takes the lightened brand (4.5:1).
   .remove-filter is left alone: a saturated blue dot under a white glyph is
   already correct on dark, and its darker :hover shade still passes. */
:root[data-theme="dark"] .active-filter-pill {
  background-color: var(--vs-brand-soft);
  border-color: var(--vs-card-accent);
  color: var(--vs-brand);
}


/* --------------------------------------------------------------------------
   components.css :: COURSE META TAGS COMPONENT (lines 240-283)
   Pastel-ground badges. All three grounds go to --vs-surface-3: on the lighter
   --vs-surface-2 the green label drops to 4.3:1, and these labels are 10-11px.
   Borders use the semantic tokens because the palette has no soft green/amber
   tint to stand in for #a7e9c8 / #ffe082.
   -------------------------------------------------------------------------- */

:root[data-theme="dark"] .meta-tag-center {
  background-color: var(--vs-surface-3);
  color: var(--vs-brand);               /* 5.3:1 */
  border-color: var(--vs-card-accent);
}

:root[data-theme="dark"] .meta-tag-language {
  background-color: var(--vs-surface-3);
  color: var(--vs-success);             /* 5.0:1 */
  border-color: var(--vs-success);
}

:root[data-theme="dark"] .meta-tag-tags-group {
  background-color: var(--vs-surface-3);
  color: var(--vs-warning);             /* 7.5:1 */
  border-color: var(--vs-warning);
}

/* .tag-item (line 260) is skipped on purpose: a saturated #ffc107 chip with
   black text reads correctly on dark and needs no change. */

/* .tag-more-btn / .tag-less-btn (line 273): #b58105 is 3.1:1 here. The hover
   shade darkens in the source, which is backwards on dark and there is no
   lighter amber token, so hover brightens to --vs-text instead. */
:root[data-theme="dark"] .tag-more-btn,
:root[data-theme="dark"] .tag-less-btn {
  color: var(--vs-warning);
}
:root[data-theme="dark"] .tag-more-btn:hover,
:root[data-theme="dark"] .tag-less-btn:hover {
  color: var(--vs-text);
}


/* --------------------------------------------------------------------------
   components.css :: TABLE VIEW COMPONENT (lines 327-436)
   -------------------------------------------------------------------------- */

/* GROUND -- read before changing anything below.
   courses.html renders this table as `table table-bordered table-light
   courses-table`. `.table-light` is one of Bootstrap's FIXED contextual
   variants: unlike .table/.card/.modal it is NOT re-derived under
   data-bs-theme="dark" -- it hard-pins --bs-table-color:#000 and
   --bs-table-bg:#f8f9fa in both themes, and 00-tokens.css only reaches
   --bs-table-color. Left uncorrected, every rule in this section would paint
   --vs-text on a near-white slab (~1.05:1) and the table view would read blank.
   It is corrected in 20-bootstrap-shim.css (`.table-light` -> --bs-table-bg:
   var(--vs-surface-2)), which owns the Bootstrap utilities and loads after this
   file, so the ground for everything below is --vs-surface-2, not --vs-surface.
   Do not duplicate that block here; do keep these rules legible on surface-2. */

/* Image placeholder shown before/instead of the thumbnail. */
:root[data-theme="dark"] .table-thumbnail {
  background-color: var(--vs-thumb);
}

:root[data-theme="dark"] .table-course-title {
  color: var(--vs-text);
}
:root[data-theme="dark"] .table-course-title:hover {
  color: var(--vs-brand);
}
:root[data-theme="dark"] .table-course-subtitle {
  color: var(--vs-text-muted);
}

/* .table-action-btn (line 366) -- `background: white` with a mid-grey icon.
   The row it sits on is --vs-surface-2 (see .table-light above), so the button
   cannot also be --vs-surface-2 or it disappears into the row. --vs-surface is
   1.12:1 off the row, the same separation `background: white` had against the
   source's #f8f9fa table (1.05:1), and the border does the rest. Hover steps
   lighter (1.28:1 off the row) and brightens the icon. */
:root[data-theme="dark"] .table-action-btn {
  background: var(--vs-surface);
  border-color: var(--vs-border);
  color: var(--vs-text-muted);          /* 6.5:1 */
}
:root[data-theme="dark"] .table-action-btn:hover {
  background-color: var(--vs-border);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
}

/* Compact table variants of the meta tags (lines 406-419) -- same mapping. */
:root[data-theme="dark"] .table-meta-tag.center {
  background-color: var(--vs-surface-3);
  color: var(--vs-brand);
}
:root[data-theme="dark"] .table-meta-tag.language {
  background-color: var(--vs-surface-3);
  color: var(--vs-success);
}
:root[data-theme="dark"] .table-meta-tag.tag {
  background-color: var(--vs-surface-3);
  color: var(--vs-warning);
}

:root[data-theme="dark"] .table-price .old-price {
  color: var(--vs-text-muted);
}

/* #198754 is 2.9:1 on the navy. --vs-success is the only green in the palette.
   NOTE for review: the .table-light ground is --vs-surface-2, so this pair
   measures 4.33:1 -- just under AA for normal text. Every other --vs-success use
   in this file sits on a --vs-surface-3 chip (4.99:1) and passes. The real fix
   is a lighter green token in 00-tokens.css, not a one-off hex here. */
:root[data-theme="dark"] .table-price .current-price {
  color: var(--vs-success);
}

/* The @media (max-width: 992px) block (line 448) sets widths and margins only
   -- nothing to override. Likewise the equal-height card grid (lines 11-59) and
   the show/hide utilities. .courses-table th has its own background commented
   out in the source, but it is not colourless in practice: the head inherits
   .table-light from the <table>, which is handled at the top of this section. */


/* ==========================================================================
   PART 20 -- Bootstrap 5.3 compatibility shim + template-local <style> blocks.

   SOURCES COVERED
   1. Bootstrap 5.3.3 (CDN, linked from website/templates/website/base.html) --
      only the colour utilities that name a literal colour and therefore do NOT
      follow data-bs-theme="dark": .bg-white, .bg-light, .text-dark, .text-black,
      .text-black-50, .link-dark, .border-light, .text-bg-light, the contextual
      .table-success/danger/warning/info variants, .btn-outline-secondary.
      Everything Bootstrap themes for itself (cards, modals, tables, dropdowns,
      alerts -- including .alert-light, which routes through --bs-light-bg-subtle
      and flips on its own -- badges, nav-tabs, offcanvas, forms, .text-muted,
      .text-body-*, .bg-body-*) is deliberately absent: see the SKIPPED note at
      the bottom.
      .table-light is here too, and is the ground 13-forms-components.css's whole
      table-view section is measured against -- see the note on its rule.
      NOT repeated here, because an earlier part already owns them:
        .btn-light                   -> 13-forms-components.css
        .fieldset_box                -> 11-helpers.css
        .bg-gray-eee / .bg-gray-ddd  -> 11-helpers.css
        .text-primary, the .link-* utilities, the other .btn-outline-* variants
                                     -> 00-tokens.css
      All parts concatenate into one sheet at equal specificity, so a second copy
      here would silently win on source order and let the two definitions drift.
      The ONE deliberate exception is .btn-outline-secondary, flagged at its rule.
   2. The <style> blocks written inline in these dashboard templates:
        website/templates/website/org_staff_dashboard_base.html
        website/templates/website/announcement_list.html
        website/templates/website/create_edit_announcement.html
        website/templates/website/vidsafe_staff_organization_detail.html
        website/templates/website/staff/vidsafe_raw_videos.html
        website/templates/website/staff/vidsafe_website_configs.html
        accounts/templates/accounts/_org_settings_styles.html
        accounts/templates/accounts/team_members.html
        mentoring_mgmt/.../student/book_mentoring_slot_v2.html
        mentoring_mgmt/.../org_staff/modal_booking_details.html
        mentoring_mgmt/.../org_staff/staff_dashboard_slots_list.html
        mentoring_mgmt/.../org_staff/staff_dashboard_course_list.html
        mcq_test/.../student_mcq_test_report.html
        mcq_test/.../org_staff/staff_student_mcq_test_report.html
        mains_test/.../student_display_evaluated_answer_sheet.html
        mains_test/.../staff_display_evaluated_answer_sheet.html
        mains_test/.../manually_evaluate_answer_sheet.html
        mains_test/.../create_edit_mains_test.html
        mains_test/.../staff_course_mains_test_series_list.html
        mains_test/.../mains_dashboard.html
        course_mgmt/.../course_detail.html
        course_mgmt/.../course_pricing_tab_content.html
        course_mgmt/.../org_staff/create_edit_mains_test_course_item.html
        course_mgmt/.../modal_create_edit_study_material.html
        course_mgmt/.../modal_bulk_cancel_subscription_verify.html
        billing_mgmt/.../includes/billing_page_styles.html

   ONE DELIBERATE EXCEPTION TO THE TOKEN CONTRACT
   Status chips and contextual table rows are a light tint carrying a matching dark
   text colour (#dcfce7 + #166534, #f8d7da + #000 ...). The palette has --vs-success
   / --vs-danger / --vs-warning, but those are single accent colours, not tint/ink
   PAIRS, and there is no --vs- token for "dark green chip background". Bootstrap
   5.3 already computes exactly that pair for dark mode in --bs-*-bg-subtle /
   --bs-*-text-emphasis, and the page carries data-bs-theme="dark", so those are
   live. Using them keeps the two halves of each chip in step; inventing hexes here
   would not. Every such use is flagged inline.
   ========================================================================== */


/* --- Bootstrap utilities: backgrounds ------------------------------------
   .bg-white (58 uses) and .bg-light (108) are
     `background-color: rgba(var(--bs-white-rgb|--bs-light-rgb), var(--bs-bg-opacity)) !important`
   -- literal white / #f8f9fa in every theme. They are overridden one class at a
   time rather than by flipping --bs-white-rgb / --bs-light-rgb, because those same
   triplets also drive .text-white (56 uses, all of them on a coloured or hero
   background that stays light-on-dark) and .text-light; flipping them would put
   dark text on those.
   .bg-white lands on card headers, card footers, panels and tables, i.e. the same
   role --vs-surface plays; .bg-light lands on inset wells and table heads. */
:root[data-theme="dark"] .bg-white {
  background-color: var(--vs-surface) !important;
}
:root[data-theme="dark"] .bg-light {
  /* Also wins over course_mgmt/templates/course_mgmt/coupons.html, which re-declares
     `.bg-light { background-color: #f8f9fa !important }` in a local <style> block:
     that selector is (0,1,0), this one is (0,3,0), and both carry !important.
     Two templates pair this with .bg-opacity-10; a flat token ignores the 10%, which
     on dark reads as the intended faint inset rather than a washed-out one. */
  background-color: var(--vs-surface-2) !important;
}

/* --- Bootstrap utilities: text -------------------------------------------
   .text-dark (98 uses) is rgba(var(--bs-dark-rgb)) = #212529, .text-black (7) is
   #000, .text-black-50 is rgba(0,0,0,.5). All three are near-black ink that
   would sit on a surface this file just darkened. --bs-dark-rgb is left alone so
   .bg-dark (9 uses) stays dark.
   .text-black-50's only live use is in website/dummy_mains_test_series_list.html,
   a mock -- the rule is kept as one line of insurance, not because a real page
   needs it. (The SKIPPED note at the bottom lists it as unreached; both are true.) */
:root[data-theme="dark"] .text-dark,
:root[data-theme="dark"] .text-black {
  color: var(--vs-text) !important;      /* 16.7:1 on --vs-app-bg, 13.3:1 on --vs-surface-2 */
}
:root[data-theme="dark"] .text-black-50 {
  color: var(--vs-text-muted) !important; /* half-strength ink -> muted ink, 7.2:1 on --vs-app-bg */
}

/* .link-dark (2 uses -- the "Refresh plan details" link in both sidebar footers).
   Bootstrap sets colour AND text-decoration-color, both !important, and darkens to
   rgb(26,30,33) on hover/focus; on dark the hover has to move the other way.
   00-tokens.css also carries a .link-dark rule, but it sets `color` only and to the
   same token, so this block is a strict superset and the two cannot disagree. It
   should still be collapsed into one definition. */
:root[data-theme="dark"] .link-dark {
  color: var(--vs-text) !important;
  text-decoration-color: var(--vs-text) !important;
}
:root[data-theme="dark"] .link-dark:hover,
:root[data-theme="dark"] .link-dark:focus {
  color: var(--bs-emphasis-color) !important;
  text-decoration-color: var(--bs-emphasis-color) !important;
}

/* .border-light (1 use) is a near-white rule that would be the brightest thing on
   the page. .border-white has 0 uses in the templates and is not overridden. */
:root[data-theme="dark"] .border-light {
  border-color: var(--vs-border) !important;
}

/* .text-bg-light -- the same #f8f9fa as .bg-light but with `color:#000!important`
   welded on, so overriding .bg-light alone does not reach it:
     .text-bg-light { color:#000!important;
                      background-color:RGBA(var(--bs-light-rgb),...)!important }
   --bs-light-rgb is 248,249,250 in BOTH themes (Bootstrap redefines the -subtle and
   -emphasis variables under data-bs-theme="dark", never the raw *-rgb triplets), so
   this stays a white badge on the navy page. 10 uses, 9 of them on staff dashboard
   pages that do extend base.html: course_mgmt/student_activity.html (7),
   course_mgmt/coupons.html, payment_mgmt/organization_payment_settings.html.
   Both source declarations are !important, so both of these must be.
   Same ground as .bg-light; --vs-text on it is 13.4:1.
   NOTE: two of the uses pair it with .text-muted / .text-primary, which are (0,1,0)
   even with !important, so this (0,3,0) rule takes their colour back to --vs-text.
   That loses a little of the de-emphasis those badges have in light mode; it is the
   only way to get #000 off a dark chip without a per-template rule. */
:root[data-theme="dark"] .text-bg-light {
  color: var(--vs-text) !important;
  background-color: var(--vs-surface-2) !important;
}


/* helpers.css .bg-gray-eee (30 uses) / .bg-gray-ddd (16) are handled in
   11-helpers.css, which owns that stylesheet. The identical pair used to be
   repeated here; it was removed so the two copies cannot drift.
   .bg-gray (40 uses), .bg-gray-dark (8) and .bg-gray-f5f5f5 (2) appear in the
   markup but have no rule anywhere in assets/ or static/ -- inert class names,
   not overridden. */


/* --- Bootstrap contextual table rows -------------------------------------
   This is the one that silently breaks. Part 00 sets --bs-table-color on `.table`
   at (0,3,0), which beats Bootstrap's own `.table-danger` (0,1,0), so the ink is
   already light -- but the matching --bs-table-BG is still #f8d7da, so these rows
   render light-on-light and vanish. Each variant needs its background moved in the
   same direction as the ink.
   Bootstrap emits no !important here, only custom properties, so neither does this.

   .table-light (44 uses, nearly all on <thead>, plus the whole courses.html table
   view) lives HERE and only here. 13-forms-components.css used to carry a second
   copy; it now defers to this one and documents --vs-surface-2 as the ground for
   every rule in its table-view section. Do not move or duplicate this block without
   updating that comment -- with both copies gone the courses table renders --vs-text
   on #f8f9fa at ~1.05:1, i.e. blank.

   Every variant must set the striped / hover / active trio as well as the resting
   pair. Bootstrap's `.table-*` pins ALL NINE custom properties, and applies the
   state ones as `inset 0 0 0 9999px var(--bs-table-bg-type)` over the resting
   background -- so leaving them alone leaves #eccccf / #e5c7ca / #dfc2c4 with
   --bs-table-*-color:#000 live. staff_dashboard_slots_list.html really does render
   `table table-bordered table-hover table-striped` with .table-warning /
   .table-danger rows, so hovering a cancelled slot in dark mode would flash a
   near-white pink row. The three state fills are the same translucent lifts
   00-tokens.css uses on plain `.table`: they composite OVER each variant's own
   tint instead of replacing it, which keeps the semantic colour at every state and
   needs no invented hexes (no --vs- token is a translucent overlay).
   .table-light is the exception: it is a neutral, not a semantic tint, so its states
   are named surfaces rather than overlays -- the same ladder the rest of the file
   uses for neutral chrome. */
:root[data-theme="dark"] .table-light {
  --bs-table-color: var(--vs-text);
  --bs-table-bg: var(--vs-surface-2);
  --bs-table-border-color: var(--vs-border);
  --bs-table-striped-color: var(--vs-text);
  --bs-table-striped-bg: var(--vs-surface);
  --bs-table-hover-color: var(--vs-text);
  --bs-table-hover-bg: var(--vs-border);
  --bs-table-active-color: var(--vs-text);
  --bs-table-active-bg: var(--vs-border-strong);
}
:root[data-theme="dark"] .table-success {
  --bs-table-color: var(--bs-success-text-emphasis);
  --bs-table-bg: var(--bs-success-bg-subtle);
  --bs-table-border-color: var(--bs-success-border-subtle);
  --bs-table-striped-color: var(--bs-success-text-emphasis);
  --bs-table-striped-bg: rgba(148, 163, 184, .06);
  --bs-table-hover-color: var(--bs-success-text-emphasis);
  --bs-table-hover-bg: rgba(148, 163, 184, .10);
  --bs-table-active-color: var(--bs-success-text-emphasis);
  --bs-table-active-bg: rgba(148, 163, 184, .14);
}
:root[data-theme="dark"] .table-danger {
  --bs-table-color: var(--bs-danger-text-emphasis);
  --bs-table-bg: var(--bs-danger-bg-subtle);
  --bs-table-border-color: var(--bs-danger-border-subtle);
  --bs-table-striped-color: var(--bs-danger-text-emphasis);
  --bs-table-striped-bg: rgba(148, 163, 184, .06);
  --bs-table-hover-color: var(--bs-danger-text-emphasis);
  --bs-table-hover-bg: rgba(148, 163, 184, .10);
  --bs-table-active-color: var(--bs-danger-text-emphasis);
  --bs-table-active-bg: rgba(148, 163, 184, .14);
}
:root[data-theme="dark"] .table-warning {
  --bs-table-color: var(--bs-warning-text-emphasis);
  --bs-table-bg: var(--bs-warning-bg-subtle);
  --bs-table-border-color: var(--bs-warning-border-subtle);
  --bs-table-striped-color: var(--bs-warning-text-emphasis);
  --bs-table-striped-bg: rgba(148, 163, 184, .06);
  --bs-table-hover-color: var(--bs-warning-text-emphasis);
  --bs-table-hover-bg: rgba(148, 163, 184, .10);
  --bs-table-active-color: var(--bs-warning-text-emphasis);
  --bs-table-active-bg: rgba(148, 163, 184, .14);
}
:root[data-theme="dark"] .table-info {
  --bs-table-color: var(--bs-info-text-emphasis);
  --bs-table-bg: var(--bs-info-bg-subtle);
  --bs-table-border-color: var(--bs-info-border-subtle);
  --bs-table-striped-color: var(--bs-info-text-emphasis);
  --bs-table-striped-bg: rgba(148, 163, 184, .06);
  --bs-table-hover-color: var(--bs-info-text-emphasis);
  --bs-table-hover-bg: rgba(148, 163, 184, .10);
  --bs-table-active-color: var(--bs-info-text-emphasis);
  --bs-table-active-bg: rgba(148, 163, 184, .14);
}


/* --- Bootstrap buttons that name a literal colour ------------------------
   .btn-light (95 uses) is NOT overridden here. Bootstrap's version is #f8f9fa with
   #000 text, but assets/website/css/forms.css:124 loads after Bootstrap and
   re-declares the whole custom-property block as #044FC0 ink on an #E6EAF6 brand
   slab -- so Bootstrap's grey never renders anywhere in this app.
   13-forms-components.css maps that real source (brand ink on --vs-brand-soft,
   4.5:1) and owns the class. An earlier draft of this file repeated .btn-light
   against Bootstrap's default; because the parts concatenate in order it silently
   beat 13's block and turned all 95 buttons grey. Removed.

   .btn-outline-secondary (153 uses) IS overridden here, deliberately superseding
   the block in 00-tokens.css: that one leaves the outline at --vs-border-strong,
   which measures 2.1:1 on --vs-app-bg and 1.9:1 on --vs-surface -- under the 3:1
   a control boundary needs, on the single most-used button in the console. The
   two definitions should be collapsed into one; until then this is the live one.

   Bootstrap emits no !important here, only custom properties, so neither does this.
   --bs-btn-focus-shadow-rgb is left at Bootstrap's grey: it wants a bare "r, g, b"
   triplet, not a colour, so no token fits, and the grey focus ring is perfectly
   visible against the navy. */
:root[data-theme="dark"] .btn-outline-secondary {
  /* Ink and outline share one colour, exactly as Bootstrap's #6c757d does.
     --vs-text-muted is 7.1:1 on --vs-app-bg and 6.5:1 on --vs-surface; the palette's
     line tokens were all under 2.2:1 as an outline here, which would have erased 153
     buttons. */
  --bs-btn-color: var(--vs-text-muted);
  --bs-btn-border-color: var(--vs-text-muted);
  --bs-btn-hover-color: var(--vs-text);
  --bs-btn-hover-bg: var(--vs-border-strong);
  --bs-btn-hover-border-color: var(--vs-border-strong);
  --bs-btn-active-color: var(--vs-text);
  --bs-btn-active-bg: var(--vs-border-strong);
  --bs-btn-active-border-color: var(--vs-border-strong);
  --bs-btn-disabled-color: var(--vs-text-muted);
  --bs-btn-disabled-border-color: var(--vs-border-strong);
}


/* ==========================================================================
   TEMPLATE-LOCAL <style> BLOCKS
   Overridden here rather than edited in place, so the templates stay one theme.
   Specificity note: every source rule below is a plain class or id selector, so
   the `:root[data-theme="dark"]` prefix already outranks it; none of them carry
   !important except where called out, and neither do the overrides.
   ========================================================================== */

/* --- website/org_staff_dashboard_base.html : WebSocket status -------------
   .websocket-status.disconnected / .error are #721c24 on a transparent chip pinned
   over the page -- 1.7:1 on --vs-app-bg, i.e. gone. .connecting is a #fff3cd chip.
   .connected (#05b52e) is already 6.7:1 on the navy and is left alone. */
:root[data-theme="dark"] .websocket-status.connecting {
  background: var(--vs-surface-2);
  color: var(--vs-warning);              /* 6.4:1 */
}
:root[data-theme="dark"] .websocket-status.disconnected,
:root[data-theme="dark"] .websocket-status.error {
  color: var(--vs-danger);               /* 4.6:1 on --vs-app-bg */
}

/* --- website/org_staff_dashboard_base.html : chat notification -----------
   `background: white` with no colour of its own, so it inherits the now-light body
   ink: white-on-white today, and the #e0e0e0 border plus both rgba(0,0,0,.15/.2)
   shadows are tuned for a white page. .notification-icon keeps its #007bff fill --
   a saturated blue disc with a white glyph reads fine on the navy. */
:root[data-theme="dark"] .chat-notification {
  background: var(--vs-surface);
  border: 1px solid var(--vs-border);
  box-shadow: 0 4px 20px var(--vs-shadow);
}
:root[data-theme="dark"] .chat-notification:hover {
  box-shadow: 0 6px 25px var(--vs-shadow);
}
:root[data-theme="dark"] .notification-title {
  color: var(--vs-text);                 /* was #333 */
}
:root[data-theme="dark"] .notification-text {
  color: var(--vs-text-muted);           /* was #666, 6.4:1 on --vs-surface */
}
:root[data-theme="dark"] .notification-close {
  color: var(--vs-text-muted);           /* was #999 */
}
:root[data-theme="dark"] .notification-close:hover {
  background: var(--vs-surface-2);       /* was #f0f0f0 */
  color: var(--vs-text);
}

/* --- website/announcement_list.html --------------------------------------
   White card, #f8fafc header, #e5e7eb rules, #111827 / #374151 ink, and a
   rgba(15,23,42,.08) drop shadow. The two status badges are tint/ink pairs. */
:root[data-theme="dark"] .announcement-card {
  background: var(--vs-surface);
  border: 1px solid var(--vs-border);
  box-shadow: 0 8px 24px var(--vs-shadow);
}
:root[data-theme="dark"] .announcement-card .card-header {
  background: var(--vs-surface-2);
  border-bottom: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .announcement-title,
:root[data-theme="dark"] .announcement-meta-value {
  color: var(--vs-text);
}
:root[data-theme="dark"] .announcement-details {
  color: var(--vs-text);                 /* body copy, was #374151 */
}
:root[data-theme="dark"] .announcement-details img:first-of-type,
:root[data-theme="dark"] .announcement-meta {
  border-color: var(--vs-border);
}
:root[data-theme="dark"] .announcement-meta-label {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .announcement-status-badge.published {
  /* tint/ink pair -- see file header */
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success-text-emphasis);
}
:root[data-theme="dark"] .announcement-status-badge.unpublished {
  background: var(--bs-danger-bg-subtle);
  color: var(--bs-danger-text-emphasis);
}

/* --- website/create_edit_announcement.html -------------------------------
   #f8fbff well, #dbe3ef rule, inset rgba(15,23,42,.04) that does nothing on navy. */
:root[data-theme="dark"] .announcement-details-box {
  border: 1px solid var(--vs-border);
  background: var(--vs-surface-2);
  box-shadow: inset 0 1px 2px var(--vs-shadow);
}

/* --- website/vidsafe_staff_organization_detail.html ----------------------
   White stat tiles plus a JSON pretty-printer. #0d6efd (3.6:1) and #198754 (3.6:1)
   both fall under 4.5:1 and take the lightened palette instead.
   Ground check: the pretty-printer is `<pre class="... p-2 bg-light rounded
   json-viewer">` (line 239), so its ground is .bg-light -> --vs-surface-2, the
   LIGHTEST surface in the palette -- not --vs-surface. Every ratio below is measured
   there, which is the worst case for these inks. #fd7e14 (.json-number, 5.7:1 there)
   is already fine and is left alone. */
:root[data-theme="dark"] .stat-tile {
  background: var(--vs-surface);
  border: 1px solid var(--vs-border);
  box-shadow: 0 1px 2px var(--vs-shadow);
}
:root[data-theme="dark"] .stat-tile-label,
:root[data-theme="dark"] .stat-tile-sub,
:root[data-theme="dark"] .json-null {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .stat-tile-value {
  color: var(--vs-text);
}
:root[data-theme="dark"] .json-key {
  color: var(--vs-brand);                /* 4.6:1 on --vs-surface-2 */
}
:root[data-theme="dark"] .json-string {
  /* --vs-success is 4.9:1 on --vs-surface but only 4.33:1 on --vs-surface-2, which is
     the ground this block actually has (.bg-light) -- a fail for 0.82rem monospace.
     Bootstrap's dark success emphasis is 6.3:1 there. Same substitution the
     .slot-status.booked rule makes for danger ink on a raised surface, and the same
     one 00-tokens.css makes for .text-success. --vs-success stays correct for green
     ink on --vs-surface and darker; the real fix is a lighter green token in
     00-tokens.css, not a one-off hex here. */
  color: var(--bs-success-text-emphasis);
}
:root[data-theme="dark"] .json-boolean {
  /* #6f42c1 is 2.2:1 on --vs-surface-2 and there is no light-purple token. Syntax
     colours only have to stay mutually distinct, so this borrows the pink Part 00
     already defines for inline code: 5.9:1, and unlike any other token in use here. */
  color: var(--bs-code-color);
}

/* --- website/staff/vidsafe_raw_videos.html -------------------------------
   Everything else in this block already reads --bs-border-color / --bs-body-bg /
   --bs-secondary-color, which Part 00 repoints. Only the hover shadow is a literal,
   and it is the one rule in the whole file that carries !important -- mirrored. */
:root[data-theme="dark"] .rv-tile:hover {
  box-shadow: 0 .25rem .75rem var(--vs-shadow) !important;
}

/* --- website/staff/vidsafe_website_configs.html --------------------------- */
:root[data-theme="dark"] .wc-value {
  background: var(--vs-surface-2);
  border: 1px solid var(--vs-border);
}

/* --- accounts/_org_settings_styles.html ----------------------------------
   The browser-tab mock and the help card. .favicon_shot .cap and .upload_empty are
   #8a93a2, which lands at 5.3:1 on --vs-surface once the mock goes dark, so they
   are left as they are. */
:root[data-theme="dark"] .org_settings_page .preview_frame {
  background-color: var(--vs-surface);
}
:root[data-theme="dark"] .org_settings_page .tab_mock {
  background: var(--vs-surface);
  border: 1px solid var(--vs-border);
  box-shadow: inset 0 -1px 0 var(--vs-shadow);
}
:root[data-theme="dark"] .org_settings_page .tab_mock .tab_title {
  color: var(--vs-text-subnav);          /* was #4b5563, the contract's subnav grey */
}
:root[data-theme="dark"] .org_settings_page .tab_strip {
  border-bottom: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .org_settings_page .swatch {
  /* transparency checkerboard: the light squares have to darken with the page or
     the swatch becomes the brightest patch on screen */
  background-image:
    linear-gradient(45deg, var(--vs-surface-2) 25%, transparent 25%, transparent 75%, var(--vs-surface-2) 75%),
    linear-gradient(45deg, var(--vs-surface-2) 25%, transparent 25%, transparent 75%, var(--vs-surface-2) 75%);
}
:root[data-theme="dark"] .org_settings_page .switch_desc,
:root[data-theme="dark"] .org_settings_page .help_list {
  color: var(--vs-text-muted);           /* was #5b6472, ~2:1 once the page is navy */
}
:root[data-theme="dark"] .org_settings_page .help_card {
  background: var(--vs-surface-2);       /* was #fbfcfe */
}

/* --- accounts/team_members.html ------------------------------------------ */
:root[data-theme="dark"] .team-member-resource-access .team-member-course-heading {
  color: var(--vs-text-subnav);
}
:root[data-theme="dark"] .team-member-resource-access .team-member-course-pill {
  color: var(--vs-text);
  background-color: var(--vs-surface-2);
  border: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .team-member-resource-access a.team-member-course-pill:hover {
  background-color: var(--vs-border);
  color: var(--vs-text);
  border-color: var(--vs-border-strong);
}
:root[data-theme="dark"] .team-members-table .team-member-cm-split-badge {
  /* rgba(0,0,0,.06) hairline ring: invisible on navy, so it becomes a line token */
  box-shadow: 0 0 0 1px var(--vs-border);
}
:root[data-theme="dark"] .team-members-table .team-member-cm-split-badge__limited {
  /* the fill and ink already read --bs-danger-*; only the divider was a literal */
  border-left: 1px solid var(--vs-border);
}

/* --- mentoring_mgmt/student/book_mentoring_slot_v2.html ------------------
   The largest inline block in the dashboards: a full white-card booking board. */
:root[data-theme="dark"] .book-v2 .course-group,
:root[data-theme="dark"] .book-v2 .slots-card {
  background: var(--vs-surface);
  border: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .book-v2 .cg-head {
  border-bottom: 1px solid var(--vs-hairline);
}
:root[data-theme="dark"] .book-v2 .cg-sub,
:root[data-theme="dark"] .book-v2 .noslot,
:root[data-theme="dark"] .book-v2 .slots-empty {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .book-v2 .date-label,
:root[data-theme="dark"] .book-v2 .course-label,
:root[data-theme="dark"] .book-v2 .test-line {
  color: var(--vs-text);
}
:root[data-theme="dark"] .book-v2 .course-label .bi {
  color: var(--vs-brand);
}
/* .v2-center-hint is built by JS into #confirmBody, which lives in .v2-backdrop --
   a SIBLING of .book-v2, not a descendant -- so a .book-v2-prefixed selector never
   matched it. Scoped to the theme only. (The template's own rule already resolves
   this to var(--vs-brand), so this is belt and braces, not the sole fix.) */
:root[data-theme="dark"] .v2-center-hint {
  color: var(--vs-brand);
}
/* Chip tints (tint/ink pairs -- see file header). */
:root[data-theme="dark"] .book-v2 .cg-label.label-course {
  background: var(--vs-brand-soft);
  color: var(--vs-brand);                /* 4.5:1 */
}
:root[data-theme="dark"] .book-v2 .cg-label.label-mains {
  background: var(--bs-warning-bg-subtle);
  color: var(--bs-warning-text-emphasis);
}
:root[data-theme="dark"] .book-v2 .cg-label.label-date {
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success-text-emphasis);
}
:root[data-theme="dark"] .book-v2 .cg-label.label-noslot {
  background: var(--vs-surface-2);
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .book-v2 .course-label .cl-series,
:root[data-theme="dark"] .book-v2 .course-label .cl-series .bi {
  color: var(--bs-warning-text-emphasis);
}
:root[data-theme="dark"] .book-v2 .date-label .bi,
:root[data-theme="dark"] .book-v2 .cg-label.label-date .bi {
  color: var(--bs-success-text-emphasis);   /* 7.0:1 on --vs-surface */
}
/* Filter bar and its modal twin. */
:root[data-theme="dark"] .time-filter-bar .tf-label,
:root[data-theme="dark"] .time-filter-bar .group-by .gb-label,
:root[data-theme="dark"] .time-filter-bar .tf-clear,
:root[data-theme="dark"] .time-filter-bar .tf-range,
:root[data-theme="dark"] #filterModal .fs-title {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .time-filter-bar .tf-chip,
:root[data-theme="dark"] #filterModal .tf-chip,
:root[data-theme="dark"] .btn-filter {
  color: var(--vs-text);
  background: var(--vs-surface-3);       /* control well, was #fff */
  border: 1px solid var(--vs-border-strong);
}
:root[data-theme="dark"] .time-filter-bar .tf-chip:hover,
:root[data-theme="dark"] #filterModal .tf-chip:hover,
:root[data-theme="dark"] .btn-filter:hover {
  border-color: var(--vs-text-muted);     /* was #9ca3af: the outline lifts on hover */
}
:root[data-theme="dark"] .time-filter-bar .tf-chip.active,
:root[data-theme="dark"] #filterModal .tf-chip.active,
:root[data-theme="dark"] .btn-filter .filter-count {
  background: var(--vs-brand-fill);
  border-color: var(--vs-brand-fill);
  /* pure white, not --vs-text: the off-white token is only 4.5:1 on --vs-brand-fill
     and there is no pure-white token in the contract. 4.9:1. */
  color: #fff;
}
:root[data-theme="dark"] .time-filter-bar .tf-sep {
  background: var(--vs-border);
}
:root[data-theme="dark"] .time-filter-bar .group-by .form-select {
  color: var(--vs-text);
  border-color: var(--vs-border-strong);
}
:root[data-theme="dark"] .time-filter-bar .tf-clear:hover {
  color: var(--vs-text);
}
:root[data-theme="dark"] #filterModal .filter-section {
  border-bottom: 1px solid var(--vs-hairline);
}
:root[data-theme="dark"] #filterModal .select2-container--default .select2-selection--multiple {
  border-color: var(--vs-border-strong);
}
:root[data-theme="dark"] #filterModal .select2-container--default .select2-selection__choice {
  background: var(--vs-brand-soft);
  border-color: var(--vs-border);
  color: var(--vs-brand);
}
/* Slot tiles. */
:root[data-theme="dark"] .book-v2 .mentor-avatar {
  background: var(--vs-brand-soft);
  color: var(--vs-brand);
}
:root[data-theme="dark"] .book-v2 .slot {
  border: 1px solid var(--vs-border);
  background: var(--vs-surface);
}
:root[data-theme="dark"] .book-v2 .slot:hover {
  border-color: var(--vs-brand);
  box-shadow: 0 2px 6px var(--vs-shadow);   /* rgba(4,79,192,.12) does nothing on navy */
}
:root[data-theme="dark"] .book-v2 .slot.booked {
  background: var(--vs-surface-2);
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .book-v2 .slot .slot-mentor {
  color: var(--vs-text);
  border-bottom: 1px solid var(--vs-hairline);
}
:root[data-theme="dark"] .book-v2 .slot .slot-date,
:root[data-theme="dark"] .book-v2 .slot .meta,
:root[data-theme="dark"] .book-v2 .slot .slot-status.noslot-status {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .book-v2 .slot .slot-status.available {
  color: var(--vs-success);              /* 4.8:1 on --vs-surface */
}
:root[data-theme="dark"] .book-v2 .slot .slot-status.booked {
  /* --vs-danger is only 4.1:1 on --vs-surface and 3.7:1 on --vs-surface-2, under the
     4.5:1 bar for this 12px label, so danger INK on a raised surface uses Bootstrap's
     dark danger emphasis (6.5:1). --vs-danger is still used for danger ink sitting
     directly on --vs-app-bg, where it clears 4.6:1. */
  color: var(--bs-danger-text-emphasis);
}
:root[data-theme="dark"] .book-v2 .slot.slot-unavailable {
  background: var(--vs-surface-2);
}
:root[data-theme="dark"] .book-v2 .slot.slot-unavailable:hover {
  border-color: var(--vs-border);
}
:root[data-theme="dark"] .book-v2 .slot .deadline {
  color: var(--vs-warning);              /* 7.2:1 on --vs-surface */
}
/* The hand-rolled modal (not a Bootstrap .modal, so Part 00 does not reach it). */
:root[data-theme="dark"] .v2-backdrop {
  background: var(--vs-overlay);
}
:root[data-theme="dark"] .v2-modal {
  background: var(--vs-surface);
  box-shadow: 0 20px 50px var(--vs-shadow);
}
:root[data-theme="dark"] .v2-modal-head {
  border-bottom: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .v2-modal-foot {
  border-top: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .btn-close-x,
:root[data-theme="dark"] .v2-modal-body .kv .k {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .v2-seg .seg-btn {
  border: 1px solid var(--vs-border);
  background: var(--vs-surface-2);
  color: var(--vs-text);
}
:root[data-theme="dark"] .v2-seg .seg-btn.on {
  background: var(--vs-brand-fill);
  border-color: var(--vs-brand-fill);
  color: #fff;                           /* see the .tf-chip.active note above */
}
:root[data-theme="dark"] .type-indicator {
  color: var(--vs-text);
}
:root[data-theme="dark"] .type-choice {
  border: 2px solid var(--vs-border);
  background: var(--vs-surface);
  color: var(--vs-text);
}
:root[data-theme="dark"] .type-choice:hover {
  border-color: var(--vs-brand);
  background: var(--vs-brand-soft);
}
@media (max-width: 575.98px) {
  :root[data-theme="dark"] .time-filter-bar .group-by {
    border-top: 1px solid var(--vs-hairline);
  }
}

/* --- mentoring_mgmt/org_staff/modal_booking_details.html -----------------
   AJAX fragment (handlers.py:601) injected into the dark dashboard, so its <style>
   block ships with it and needs the same treatment as a page-level one. */
:root[data-theme="dark"] .bd-wrap {
  background: var(--vs-surface-2);
}
:root[data-theme="dark"] .bd-header-icon {
  background: var(--vs-border);
  color: var(--vs-text);
}
:root[data-theme="dark"] .bd-header-title,
:root[data-theme="dark"] .bd-name,
:root[data-theme="dark"] .bd-name .bd-sid,
:root[data-theme="dark"] .bd-label,
:root[data-theme="dark"] .bd-value,
:root[data-theme="dark"] .bd-comment-text {
  color: var(--vs-text);
}
/* .bd-count is a pill sitting DIRECTLY on .bd-wrap, which this file just set to
   --vs-surface-2; giving the pill the same token painted it out entirely (1.00:1
   against its own ground). It takes the recessed well instead -- 1.15:1 off the
   wrap, the same separation #e5e7eb had against #f3f4f6, and the muted ink still
   measures 6.6:1 on it. .bd-pill-gray is the one that sits on .bd-card
   (--vs-surface), where --vs-surface-2 is the correct step and the ink is 5.7:1. */
:root[data-theme="dark"] .bd-count {
  background: var(--vs-surface-3);
  color: var(--vs-text-muted);           /* 6.6:1 */
}
:root[data-theme="dark"] .bd-pill-gray {
  background: var(--vs-surface-2);
  color: var(--vs-text-muted);           /* 5.7:1 */
}
:root[data-theme="dark"] .bd-card {
  background: var(--vs-surface);
  border: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .bd-avatar,
:root[data-theme="dark"] .bd-pill-blue {
  background: var(--vs-brand-soft);
  color: var(--vs-brand);
}
:root[data-theme="dark"] .bd-value.bd-muted {
  /* #c0c4cc was a pale grey that reads as DE-emphasis on white but would be 9.4:1
     -- brighter than the surrounding body copy -- on navy. Meaning wins over hue. */
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .bd-pill-cyan {
  background: var(--bs-info-bg-subtle);
  color: var(--bs-info-text-emphasis);
}
:root[data-theme="dark"] .bd-link,
:root[data-theme="dark"] .bd-comment-toggle {
  color: var(--vs-brand);
}
:root[data-theme="dark"] .bd-comment {
  border-top: 1px solid var(--vs-hairline);
}

/* --- mentoring_mgmt/org_staff/staff_dashboard_slots_list.html ------------
   .mentoring-booked-capacity is a 92%-white chip with #212529 ink that floats on top
   of .table-warning / .table-danger rows -- both of which this file just darkened,
   so the chip has to come with them or it is a white blob with invisible text. */
:root[data-theme="dark"] .mentoring-booking-entry + .mentoring-booking-entry {
  border-top: 1px dashed var(--vs-border);
}
:root[data-theme="dark"] .mentoring-booked-capacity {
  color: var(--vs-text);
  background-color: var(--vs-surface);
  border: 1px solid var(--vs-border);
  box-shadow: 0 1px 2px var(--vs-shadow);
}
:root[data-theme="dark"] .table-warning .mentoring-booked-capacity,
:root[data-theme="dark"] .table-danger .mentoring-booked-capacity {
  background-color: var(--vs-surface);
  border-color: var(--vs-border-strong);
}

/* --- mentoring_mgmt/org_staff/staff_dashboard_course_list.html ------------ */
:root[data-theme="dark"] .mentor-chip {
  background: var(--vs-brand-soft);
  color: var(--vs-brand);
}
:root[data-theme="dark"] .mentor-more-chip {
  background: var(--vs-surface-2);
  color: var(--vs-text-muted);
}

/* --- mcq_test student + staff report blocks ------------------------------
   Identical <style> blocks in student_mcq_test_report.html and
   org_staff/staff_student_mcq_test_report.html. The three legend dots
   (#518C6C / #F86C6C / #B0CFFF) are saturated fills that read fine on navy, and
   .data_box_label (#768CAC) is already 4.8:1, so all four are left alone. */
:root[data-theme="dark"] .data_box_icon {
  color: var(--vs-brand);                /* #044FC0 was ~2:1 on the navy */
}
:root[data-theme="dark"] .data_box_value {
  color: var(--vs-text);
}
:root[data-theme="dark"] .report-scope-tabs {
  border-bottom: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .report-scope-tab {
  color: var(--vs-text-muted);
  background: var(--vs-surface-2);
}
:root[data-theme="dark"] .report-scope-tab:hover {
  background: var(--vs-border);
  color: var(--vs-text);
}
:root[data-theme="dark"] .report-scope-tab.active {
  background: var(--vs-surface);
  color: var(--vs-brand);
  border-bottom-color: var(--vs-brand);
}

/* --- mains_test answer-sheet + question-row blocks -----------------------
   Shared by student_display_evaluated_answer_sheet.html,
   staff_display_evaluated_answer_sheet.html, manually_evaluate_answer_sheet.html,
   create_edit_mains_test.html and course_mgmt/org_staff/create_edit_mains_test_course_item.html.
   .active_canvas keeps its #337ab7 glow: a saturated blue ring marking the current
   page, which is exactly as legible on navy as it was on white. */
:root[data-theme="dark"] #pdf_canvas_container {
  border: 1px dashed var(--vs-border);
}
:root[data-theme="dark"] #pdf_canvas_container canvas {
  /* Separator between stacked page renders. The canvases themselves are the scanned
     answer sheet, i.e. still white -- so this rule is cosmetic, not a legibility fix:
     #333 and --vs-border both read clearly between two white pages. It moves only so
     the strip matches the dashed container rule around it. */
  border-bottom: 4px solid var(--vs-border);
}
:root[data-theme="dark"] .question_row {
  border: 1px solid var(--vs-border);
  background: var(--vs-surface-2);
}
/* .fieldset_box is NOT redefined here. helpers.css:297 owns the class app-wide and
   11-helpers.css already recolours it (border-color: var(--vs-border)); the copy that
   used to sit here was a `border:` shorthand at the same specificity, so it would
   have quietly taken over from 11 on source order for no gain.
   .legend-title still needs its own rule: create_edit_mains_test.html declares a
   BARE .legend-title (#333 heading, not a <legend>), which 11's
   `.fieldset_box legend` selector never reaches. The :not() chain mirrors 11's, so
   the four saturated helpers.css chips (.primary #0b4db1, .danger #ff0000,
   .warning #ffa500, .success #008000, all with white text) keep the white ink they
   are designed around instead of being dragged to the off-white body token. */
:root[data-theme="dark"] .legend-title:not(.primary):not(.danger):not(.warning):not(.success) {
  color: var(--vs-text);
}

/* --- mains_test/staff_course_mains_test_series_list.html
       + course_mgmt/course_detail.html : folder browser ---------------------
   Identical blocks. #007bff scrapes 4.6:1 on the navy but drops under it on any
   raised surface, so it moves to the lightened brand for consistency. */
:root[data-theme="dark"] #breadcrumb a,
:root[data-theme="dark"] .folder-item:hover {
  color: var(--vs-brand);
}
:root[data-theme="dark"] .folder-item {
  border: 1px solid var(--vs-border);
  background-color: var(--vs-surface-2);
}
:root[data-theme="dark"] .folder-item .arrow {
  color: var(--vs-text-muted);
}

/* --- mains_test/mains_dashboard.html ------------------------------------- */
:root[data-theme="dark"] .parent-type-filter .filter-label {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .parent-type-filter .filter-select {
  background-color: var(--vs-surface-3);
}

/* --- course_mgmt/course_pricing_tab_content.html -------------------------
   The .border-bottom.border-primary gradient bar and the .offline-billing-notice
   left rule + faint blue wash are brand colour on brand colour and stay. Only the
   grey rules, the light-page shadow and the 3.6:1 title colour move. */
:root[data-theme="dark"] .pricing-breakdown {
  border: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .pricing-breakdown:hover {
  box-shadow: 0 4px 8px var(--vs-shadow);
}
:root[data-theme="dark"] .pricing-breakdown .col-md-3:not(:last-child)::after {
  background: var(--vs-border);          /* vertical column separator */
}
:root[data-theme="dark"] .offline-billing-notice {
  box-shadow: 0 1px 2px var(--vs-shadow);
}
:root[data-theme="dark"] .offline-billing-notice .offline-billing-notice__title {
  color: var(--vs-brand);
}

/* --- course_mgmt/modal_create_edit_study_material.html -------------------
   AJAX fragment (handlers.py:1666). The :hover #86b7fe and :focus-within #0d6efd
   ring are Bootstrap's own focus blues and read fine on navy; only the resting
   control surface, its outline and the two greys change. */
:root[data-theme="dark"] .editable_filenames .file_rename_wrap {
  border: 1px solid var(--vs-border-strong);
  background: var(--vs-surface-3);
}
:root[data-theme="dark"] .editable_filenames .file_ext_label {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .editable_filenames .file_rename_icon {
  color: var(--vs-brand);
}

/* --- course_mgmt/modal_bulk_cancel_subscription_verify.html --------------
   AJAX fragment (handlers.py:5241); the sticky header would stay white otherwise. */
:root[data-theme="dark"] .bulk-cancel-scroll thead th,
:root[data-theme="dark"] .bulk-cancel-scroll-sm thead th {
  background-color: var(--vs-surface-2);
}

/* --- billing_mgmt/includes/billing_page_styles.html ---------------------- */
:root[data-theme="dark"] .summary-card {
  border: 1px solid var(--vs-border);
}
:root[data-theme="dark"] .summary-card .card-text {
  color: var(--vs-text-muted);
}


/* ==========================================================================
   DELIBERATELY NOT OVERRIDDEN
   .text-muted (973), .text-body / .text-body-secondary / .text-body-tertiary (37),
   .bg-body / .bg-body-secondary / .bg-body-tertiary (10): Bootstrap 5.3 already
     routes all of these through --bs-secondary-color / --bs-tertiary-color /
     --bs-body-bg / --bs-secondary-bg / --bs-tertiary-bg, which Part 00 repoints.
   .text-white (56): every use is on bg-primary, bg-success, bg-danger or a dark
     marketing hero -- none of them a background this theme darkens.
   .bg-secondary (103), .bg-dark (9), .bg-transparent (53), .table-striped (64),
   .table-hover (21), .btn-close, .form-control::placeholder: already dark-correct,
     either because the colour is a saturated mid-tone or because Bootstrap 5.3
     derives it from a themed variable.
   .bg-gray / .bg-gray-dark / .bg-gray-f5f5f5 (50 uses between them): no rule exists
     for these class names anywhere in assets/ or static/. Inert.
   .btn-outline-dark, .text-white-50, .text-black-50 in dummy_* templates: their only
     uses are in website/dummy_*.html and staff/modal_dct_image_watermark.html mocks.
   mcq_test/online_mcq_test.html: the biggest inline <style> block in the codebase
     (103 colour declarations) but it is a standalone <!DOCTYPE html> document that
     does NOT extend website/base.html, so it never receives data-theme and never
     loads this file. Editing it would require a template change, not an override.
   website/book_mentor_slot.html, course_mgmt/student/ (store, buy_item, ...),
     payment_mgmt/invoice.html, website/403.html, website/404.html: same reason --
     outside the website/base.html inheritance tree.
   Public marketing / onboarding pages that DO extend base.html (pricing.html,
     customer_support_public.html, create_organization.html, organization_signup.html,
     login_intent.html, find_institute.html): out of scope for a dashboard shim, and
     the last two are already white-on-dark hero cards.
   ========================================================================== */


/* ==========================================================================
   21-vendor.css -- dark skins for the third-party widgets base.html loads.

   SOURCES COVERED (confirmed against website/templates/website/base.html):
     * select2 4.1.0-beta.1 -- cdnjs .../select2/4.1.0-beta.1/css/select2.min.css
       ...plus this app's own select2 tweaks in assets/website/css/helpers.css
       (lines 278-295 and 655-667) and assets/website/css/forms.css line 149.
     * SweetAlert2 11.7.32 -- cdn.jsdelivr.net/npm/sweetalert2@11.7.32/dist/sweetalert2.min.css
     * jquery.datetimepicker -- assets/website/js/datetimepicker-master/build/
       jquery.datetimepicker.min.css (read from the unminified sibling in the same tree)
     * jQuery UI 1.12.1 -- assets/website/js/jquery-ui-1.12.1.custom/jquery-ui.theme.css
       (dormant today -- see the note above that section)
     * lity 2.4.1 -- assets/website/css/lity.min.css (audited, nothing to override; see below)
     * TinyMCE 4.5.1 "lightgray" skin -- deliberately NOT themed; see the note at the
       very bottom of this file for why, and for the one border that is corrected.

   Every selector here is prefixed with :root[data-theme="dark"], so none of it can
   match in light mode. !important is mirrored from the source only where the source
   used it; the extra specificity of the theme prefix carries the rest.
   ========================================================================== */


/* ==========================================================================
   SELECT2 4.1.0-beta.1
   The most-used widget on the site (56 templates, initialised in
   assets/website/js/script.js). Note that select2's own stylesheet uses
   !important on exactly one rule -- .select2-hidden-accessible, which sets no
   colour -- and helpers.css only uses it for radius/height/border-right. So
   every override below is a plain declaration and wins on specificity alone.
   ========================================================================== */

/* .select2-container--default .select2-selection--single { background-color:#fff; border:1px solid #aaa }
   .select2-container--default .select2-selection--multiple { background-color:white; border:1px solid #aaa }
   helpers.css:278/291 only restyle radius and height, so the colours are select2's. */
:root[data-theme="dark"] .select2-container--default .select2-selection--single,
:root[data-theme="dark"] .select2-container--default .select2-selection--multiple {
  background-color: var(--vs-surface-3);
  border-color: var(--vs-border-strong);
}

/* .select2-selection__rendered { color:#444 } -- the chosen value.
   helpers.css:286 only overrides line-height. */
:root[data-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__rendered {
  color: var(--vs-text);
}

/* .select2-selection__placeholder { color:#999 } */
:root[data-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__placeholder {
  color: var(--vs-text-muted);
}

/* The clear ("x") glyph. select2 sets `background-color:transparent; border:none` on it
   but never a colour, so this <button> falls back to the UA button text colour -- black
   on the control we just darkened. Bootstrap's color-scheme:dark would usually rescue it,
   but that is an implicit dependency; pin it. */
:root[data-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__clear,
:root[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__clear {
  color: var(--vs-text-muted);
}

/* The dropdown caret is a CSS triangle, so its "fill" is a border colour:
   .select2-selection__arrow b { border-color:#888 transparent transparent transparent }
   and the open state redraws the opposite edge. */
:root[data-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__arrow b {
  border-color: var(--vs-text-muted) transparent transparent transparent;
}
:root[data-theme="dark"] .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
  border-color: transparent transparent var(--vs-text-muted) transparent;
}

/* .select2-container--focus .select2-selection--multiple { border: solid black 1px }
   Black on a dark control reads as no focus ring at all. */
:root[data-theme="dark"] .select2-container--default.select2-container--focus .select2-selection--multiple {
  border-color: var(--vs-brand);
}

/* .select2-container--disabled .select2-selection--single/--multiple { background-color:#eee }
   The disabled label had no colour of its own, so it kept the enabled #444. */
:root[data-theme="dark"] .select2-container--default.select2-container--disabled .select2-selection--single,
:root[data-theme="dark"] .select2-container--default.select2-container--disabled .select2-selection--multiple {
  background-color: var(--vs-surface-2);
}
:root[data-theme="dark"] .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__rendered {
  color: var(--vs-text-muted);
}

/* .select2-dropdown { background-color:white; border:1px solid #aaa } -- and no colour,
   so the option text was inheriting from whatever the dropdown was appended to (<body>).
   Pinning colour here is what makes .select2-results__option correct: that rule sets only
   padding in the source, so it has nothing of its own to override. */
:root[data-theme="dark"] .select2-dropdown {
  background-color: var(--vs-surface);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
}

/* .select2-container--default .select2-search--dropdown .select2-search__field { border:1px solid #aaa }
   -- background and colour are never set, so the search box fell back to the UA's light
   field colours with black text. */
:root[data-theme="dark"] .select2-container--default .select2-search--dropdown .select2-search__field {
  background-color: var(--vs-surface-3);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
}

/* .select2-container--default .select2-search--inline .select2-search__field { background:transparent }
   -- transparent is still right over the tag field, but the typed text needs a colour. */
:root[data-theme="dark"] .select2-container--default .select2-search--inline .select2-search__field {
  color: var(--vs-text);
}

/* select2 never styles the search placeholder, so it was the UA grey tuned for a white field. */
:root[data-theme="dark"] .select2-search__field::placeholder {
  color: var(--vs-text-muted);
}

/* Results list. Source order matters here: --selected comes before
   --highlighted.--selectable so that a row which is both reads as highlighted.
   .select2-results__option--disabled { color:#999 }
   .select2-results__option--selected { background-color:#ddd }
   .select2-results__option--highlighted.--selectable { background-color:#5897fb; color:white } */
:root[data-theme="dark"] .select2-container--default .select2-results__option--disabled {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .select2-container--default .select2-results__option--selected {
  background-color: var(--vs-surface-2);
}
/* #5897fb only carries its white label at 2.6:1 (a light-mode bug we inherit); the darker
   brand fill takes that same white to 4.8:1, so the source's `color: white` is left alone. */
:root[data-theme="dark"] .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
  background-color: var(--vs-brand-fill);
}

/* Multi-select tags. select2 ships
     .select2-selection__choice { background-color:#e4e4e4; border:1px solid #aaa }
   and helpers.css:655 then repaints it as a brand-tinted pill
     { background-color:#e9eef7; border:1px solid #d4e5ff; color:#2a62b4 }.
   Neither uses !important, so the theme prefix wins. The border stays decorative: #d4e5ff
   on #e9eef7 is ~1.1:1 in light, and --vs-border on --vs-brand-soft is ~1.3:1 here.
   The fill is what delineates the pill; the border is a hairline, not a UI boundary.

   --vs-border, NOT --vs-card-accent: 11-helpers.css and 20-bootstrap-shim.css both paint
   this same pill with --vs-border, and 11-helpers.css writes the identical selector at
   identical specificity. parts/*.css concatenates in name order, so this block is the one
   that survives -- if it disagreed, the other two would be silently dead and the pill
   would change colour for no reason a reader could see. (--vs-card-accent is also the
   dashboard stat-card token, which has nothing to do with a select2 tag.) */
:root[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice {
  background-color: var(--vs-brand-soft);
  border-color: var(--vs-border);
  color: var(--vs-brand);   /* 4.5:1 -- the tightest text pair in this file */
}

/* .select2-selection__choice__remove { color:#999 } and its hover
   { background-color:#f1f1f1; color:#333 }. Its border-right:1px solid #aaa is already
   killed by helpers.css:665 (`border-right: none !important`), so it is not touched. */
:root[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
  color: var(--vs-text-muted);
}
:root[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover,
:root[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:focus {
  background-color: var(--vs-surface-2);
  color: var(--vs-text);
}


/* ==========================================================================
   SWEETALERT2 11.7.32
   v11 wraps almost every selector in :where(), which has zero specificity, so a
   plain class selector already outranks it -- the theme prefix outranks it by a
   mile. The sheet's only colour-bearing !important rules are .swal2-backdrop-hide
   (transparent) and .swal2-inputerror (a saturated red), neither of which needs a
   dark variant, so nothing below carries !important.
   ========================================================================== */

/* div:where(.swal2-container) div:where(.swal2-popup) { background:#fff; color:#545454;
     border:none }
   The border is added, not recoloured, and it is the one place this file departs from the
   source's own shape. Swal gives the popup no border and no shadow -- in light mode it does
   not need either, because #fff against rgba(0,0,0,.4) over the page composites to ~#939497
   and the dialog edge reads at 3.05:1. Darken both halves and that separation collapses:
   --vs-surface against --vs-overlay over --vs-app-bg is 1.16:1, so the popup dissolves into
   its own scrim and the dialog has no perceivable edge at all. Nothing else can rescue it --
   a box-shadow here would be black on near-black. A hairline restores the boundary at
   2.26:1 against the composited backdrop, which is also what every Bootstrap modal in this
   theme already gets from --bs-modal-border-color in 00-tokens.css.
   --vs-border-strong rather than --vs-border because a Bootstrap modal is carrying a real
   shadow as well; this edge is doing the job alone. */
:root[data-theme="dark"] .swal2-popup {
  background: var(--vs-surface);
  color: var(--vs-text);
  border: 1px solid var(--vs-border-strong);
}

/* h2:where(.swal2-title) and .swal2-html-container are both `color: inherit`. Pinned rather
   than left to inherit because the app passes raw markup through Swal's `html:` option, and
   anything in there that resets colour would otherwise land on a light-page assumption. */
:root[data-theme="dark"] .swal2-title,
:root[data-theme="dark"] .swal2-html-container {
  color: var(--vs-text);
}

/* div:where(.swal2-container).swal2-backdrop-show { background: rgba(0,0,0,.4) } -- a 40%
   scrim is tuned to knock back a white page; on the navy canvas it barely registers. */
:root[data-theme="dark"] .swal2-container.swal2-backdrop-show,
:root[data-theme="dark"] .swal2-container.swal2-noanimation {
  background: var(--vs-overlay);
}

/* input:where(.swal2-input) / input:where(.swal2-file) / textarea:where(.swal2-textarea)
     { border:1px solid #d9d9d9; background:transparent; color:inherit }
   select:where(.swal2-select) { background:transparent; color:inherit }
   `transparent` meant "the white of the popup" -- i.e. a form-control surface -- so on dark
   it takes the form-control token rather than staying flush with the popup. */
:root[data-theme="dark"] .swal2-input,
:root[data-theme="dark"] .swal2-file,
:root[data-theme="dark"] .swal2-textarea,
:root[data-theme="dark"] .swal2-select {
  background: var(--vs-surface-3);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
}

/* :focus { border: 1px solid #b4dbed } -- a pale pastel that vanishes against a dark field. */
:root[data-theme="dark"] .swal2-input:focus,
:root[data-theme="dark"] .swal2-file:focus,
:root[data-theme="dark"] .swal2-textarea:focus {
  border-color: var(--vs-brand);
}

/* ::placeholder { color:#ccc } -- near-white, so it survived as the brightest text in the field. */
:root[data-theme="dark"] .swal2-input::placeholder,
:root[data-theme="dark"] .swal2-file::placeholder,
:root[data-theme="dark"] .swal2-textarea::placeholder {
  color: var(--vs-text-muted);
}

/* .swal2-range { background:#fff } and .swal2-radio / .swal2-checkbox { background:#fff } */
:root[data-theme="dark"] .swal2-range,
:root[data-theme="dark"] .swal2-radio,
:root[data-theme="dark"] .swal2-checkbox {
  background: var(--vs-surface);
}

/* div:where(.swal2-validation-message) { background:#f0f0f0; color:#666 }. Its ::before pill
   is a saturated #f27474 with white on it -- already fine on dark, left alone. */
:root[data-theme="dark"] .swal2-validation-message {
  background: var(--vs-surface-2);
  color: var(--vs-text);
}

/* button:where(.swal2-close) { color:#ccc } -- near-white by design on a white popup, which
   made it the brightest thing on the dark popup. Its :hover red is fine on dark, left alone. */
:root[data-theme="dark"] .swal2-close {
  color: var(--vs-text-muted);
}

/* div:where(.swal2-footer) { border-top: 1px solid #eee } */
:root[data-theme="dark"] .swal2-footer {
  border-top-color: var(--vs-hairline);
}

/* The three .swal2-styled fills (#7066e0 confirm / #dc3741 deny / #6e7881 cancel) are all
   saturated and each clears 4.5:1 against its white label on a dark popup, so they stay.
   What does not survive is the interaction wash:
     :hover  { background-image: linear-gradient(rgba(0,0,0,.1), rgba(0,0,0,.1)) }
     :active { background-image: linear-gradient(rgba(0,0,0,.2), rgba(0,0,0,.2)) }
   Darkening an already-dark-ish button drags its white label under 4.5:1, and reads as the
   button receding rather than responding. Lighten instead. No token exists for a hover
   scrim -- these are translucent washes over an arbitrary fill, not surfaces. */
:root[data-theme="dark"] .swal2-actions:not(.swal2-loading) .swal2-styled:hover {
  background-image: linear-gradient(rgba(255, 255, 255, .12), rgba(255, 255, 255, .12));
}
:root[data-theme="dark"] .swal2-actions:not(.swal2-loading) .swal2-styled:active {
  background-image: linear-gradient(rgba(255, 255, 255, .2), rgba(255, 255, 255, .2));
}

/* div:where(.swal2-timer-progress-bar) { background: rgba(0,0,0,.2) } -- black-on-dark is
   invisible. Same reasoning as above: a wash, not a surface, so no token applies. */
:root[data-theme="dark"] .swal2-timer-progress-bar {
  background: rgba(255, 255, 255, .24);
}


/* ==========================================================================
   JQUERY.DATETIMEPICKER  (vendored, xdsoft)
   The library ships an .xdsoft_dark variant, but switching to it needs a JS option
   we do not set (and its palette is pure black/orange, not this one), so the light
   skin is re-tinted here instead. Rules are kept in the source file's order because
   several of them are same-specificity and depend on it -- notably td.xdsoft_today
   must come before td.xdsoft_current, exactly as in the original.
   ========================================================================== */

/* .xdsoft_datetimepicker { background:#fff; border-top/left/right:#ccc; border-bottom:#bbb;
   color:#333; box-shadow: 0 5px 15px -5px rgba(0,0,0,.506) } */
:root[data-theme="dark"] .xdsoft_datetimepicker {
  background: var(--vs-surface);
  border-color: var(--vs-border-strong);
  color: var(--vs-text);
  box-shadow: 0 5px 15px -5px var(--vs-shadow);
}

/* .xdsoft_label { background-color:#fff } -- the month / year button, which sits over the panel. */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_label {
  background-color: var(--vs-surface);
}

/* .xdsoft_label > .xdsoft_select { border:1px solid #ccc; background:#fff } */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select {
  background: var(--vs-surface);
  border-color: var(--vs-border-strong);
}

/* .xdsoft_calendar td, .xdsoft_calendar th { background:#f5f5f5; border:1px solid #ddd; color:#666 } */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_calendar td,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_calendar th {
  background: var(--vs-surface-2);
  border-color: var(--vs-border);
  color: var(--vs-text-muted);
}

/* .xdsoft_calendar th { background:#f1f1f1; color:#999 } -- the weekday header sits one shade
   darker than the day cells in light, so it goes one shade darker here too. Its #999 maps to
   the same muted token as the day cells, so only the background is restated. */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_calendar th {
  background: var(--vs-surface-3);
}

/* .xdsoft_calendar td.xdsoft_today { color:#33aaff } */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today {
  color: var(--vs-brand);
}

/* The selected day / selected time / selected month-year option all share one rule:
     { background:#33aaff; box-shadow:#178fe5 0 1px 3px 0 inset; color:#fff }
   #33aaff carries its white label at only 2.5:1 (a light-mode bug we inherit); the darker
   brand fill takes the same white to 4.8:1, so the source's `color:#fff` is left in place.
   The inset shadow flips from a darker blue to the lighter brand so it still reads as a lip. */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box > div > div.xdsoft_current {
  background: var(--vs-brand-fill);
  box-shadow: var(--vs-brand) 0 1px 3px 0 inset;
}

/* The "selected but disabled" reassertion, which the source writes with !important:
     td.xdsoft_current.xdsoft_disabled:hover { background:#33aaff !important;
       box-shadow:#178fe5 0 1px 3px 0 inset !important; color:#fff !important }
   !important mirrored, or it would win over the rule above and snap back to light blue. */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box > div > div.xdsoft_current.xdsoft_disabled:hover {
  background: var(--vs-brand-fill) !important;
  box-shadow: var(--vs-brand) 0 1px 3px 0 inset !important;
}

/* .xdsoft_timepicker .xdsoft_time_box { border-bottom:1px solid #ddd }
   .xdsoft_time_box { border:1px solid #ccc }  <- lower specificity, so the source's
   border-bottom survives; the same relative specificity is preserved here.
   .xdsoft_time_box > div > div { background:#f5f5f5; border-top:1px solid #ddd; color:#666 } */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box {
  border-bottom-color: var(--vs-border);
}
:root[data-theme="dark"] .xdsoft_time_box {
  border-color: var(--vs-border-strong);
}
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box > div > div {
  background: var(--vs-surface-2);
  border-top-color: var(--vs-border);
  color: var(--vs-text-muted);
}

/* .xdsoft_scrollbar > .xdsoft_scroller { background:#ccc !important } -- !important mirrored.
   ~1.7:1 against the time-column rows; the source manages only ~1.3:1, so this is the light
   theme's own relationship, slightly improved. It is a drag handle, not text. */
:root[data-theme="dark"] .xdsoft_scrollbar > .xdsoft_scroller {
  background: var(--vs-border-strong) !important;
}

/* SILENT KILLER. The credit line is deliberately almost invisible in light mode --
     .xdsoft_copyright        { color:#ccc !important }
     .xdsoft_copyright a      { color:#eee !important }
     .xdsoft_copyright a:hover{ color:#aaa !important }
   -- near-white on a white panel. Left untouched it becomes the brightest text in the whole
   picker. These keep the original intent (barely there) against the dark panel, and are
   therefore intentionally far below 4.5:1. !important mirrored. */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_copyright {
  color: var(--vs-hairline) !important;
}
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_copyright a {
  color: var(--vs-border) !important;
}
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_copyright a:hover {
  color: var(--vs-border-strong) !important;
}

/* SILENT KILLER. .xdsoft_label i, .xdsoft_prev, .xdsoft_next and .xdsoft_today_button all
   paint one shared base64 PNG sprite: the arrows are dark grey pixels baked into the image,
   so no colour property can reach them -- same class of problem as a hard-coded fill= on an
   inline SVG. Inverting the element flips the glyphs to light; the sprite sits on a
   transparent background and the elements' own text is off-canvas (text-indent:100%), so
   nothing else is affected. The resting opacity is nudged from .5 to .65 because a light
   glyph at 50% over navy reads weaker than a dark glyph at 50% over white did (~2.5:1 vs
   ~3.2:1 against the panel). */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_label i,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_prev,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_next,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_today_button {
  filter: invert(1);
  opacity: .65;
}

/* The rule above outranks the source's own hover rules, which would otherwise pin the
   glyphs at .65 and kill the hover feedback. Restated at matching specificity. */
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_label:hover i,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_today_button:hover,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_next:hover,
:root[data-theme="dark"] .xdsoft_datetimepicker .xdsoft_prev:hover {
  opacity: 1;
}


/* ==========================================================================
   JQUERY UI 1.12.1  (vendored, but NOT currently reaching any page)

   Four templates link it, and all four ask for it at a path that does not exist:
     {% static 'website/libraries/jquery-ui-1.12.1.custom/jquery-ui.min.css' %}
   STATICFILES_DIRS is assets/ (vidsafe/setting_files/base.py:281), and the only copy in
   the tree is assets/website/js/jquery-ui-1.12.1.custom/ -- .../libraries/... is a 404.
   The one .datepicker() call that would have needed it is commented out. So nothing below
   paints anything today; it is kept because it is correct for the sheet as vendored, and
   fixing the four <link> paths would otherwise drop a white widget onto the navy canvas
   with no warning. Do not extend it further without first making the sheet actually load.

   Coverage is therefore deliberately partial: only the two rules the autocomplete menu
   needs. .ui-widget-header (#e9e9e9), .ui-state-default (#f6f6f6) and .ui-state-hover
   (#ededed) are all still light and would need doing at that point.

   Nothing in jquery-ui.theme.css uses !important, so plain declarations suffice.
   .ui-autocomplete and .ui-menu-item are intentionally absent: in 1.12 they live in
   jquery-ui.structure.css and carry no colour at all. The autocomplete panel is a
   .ui-menu.ui-widget.ui-widget-content, and each row takes .ui-state-active on focus,
   so the two rules below are what actually paint them.
   ========================================================================== */

/* .ui-widget-content { border:1px solid #ddd; background:#fff; color:#333 }
   .ui-widget-content a { color:#333 } */
:root[data-theme="dark"] .ui-widget-content {
  background: var(--vs-surface);
  border-color: var(--vs-border);
  color: var(--vs-text);
}
:root[data-theme="dark"] .ui-widget-content a {
  color: var(--vs-text);
}

/* .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active
     { border:1px solid #003eff; background:#007fff; color:#fff }
   #007fff carries white at 3.8:1; the brand fill takes the same white to 4.8:1, so the
   source's `color:#ffffff` (and its `.ui-state-active a` sibling) is left in place.
   Both the bare and the .ui-widget-content-descendant forms are restated so the override
   holds at either specificity. */
:root[data-theme="dark"] .ui-state-active,
:root[data-theme="dark"] .ui-widget-content .ui-state-active,
:root[data-theme="dark"] .ui-widget-header .ui-state-active {
  background: var(--vs-brand-fill);
  border-color: var(--vs-brand);
}


/* ==========================================================================
   LITY 2.4.1 -- nothing emitted, on purpose.
   Audited assets/website/css/lity.min.css in full: the lightbox is already dark
   end to end. .lity is rgba(0,0,0,.9), .lity-loader and .lity-close are #fff,
   .lity-iframe-container iframe is #000, and every shadow is rgba(0,0,0,.6).
   There is no light surface in the file, so an override would be noise.
   ========================================================================== */


/* ==========================================================================
   TINYMCE 4.5.1 -- the editor canvas stays light, on purpose.

   This repo vendors TinyMCE 4.5.1 with the "lightgray" skin
   (assets/website/js/tinymce.4.5.1.formula/skins/lightgray/skin.min.css). That
   release predates every official dark skin, and its chrome colours are spread
   across hundreds of minified .mce-* rules plus a separate content.min.css that
   styles the document *inside* the editor iframe -- which CSS from this page
   cannot reach at all. Re-skinning it from the outside would leave the toolbar
   dark and the typing surface white, which is worse than leaving it alone.

   It is also arguably correct: what the author types is going to be rendered on
   a light page elsewhere (emails, printed test papers, the student app webview),
   so a white canvas is a true preview, not a bug.

   The one thing that IS corrected is the frame. .mce-panel draws
   `border: 0 solid rgba(0,0,0,0.2)`, a black-alpha hairline tuned to sit on a
   white page. Around a light editor dropped onto the navy canvas that edge
   disappears and the whole widget reads as an unstyled rectangle -- a rendering
   fault rather than a deliberate light island. A navy border re-attaches it to
   the page. Only the outer container is targeted (.mce-tinymce.mce-panel is the
   element that carries both classes); the inner .mce-panel toolbars, menubar and
   statusbar keep their light dividers so the editor stays internally consistent.
   ========================================================================== */

:root[data-theme="dark"] .mce-tinymce,
:root[data-theme="dark"] .mce-tinymce.mce-panel {
  border-color: var(--vs-border-strong);
}


/* ==========================================================================
   30-chat-tests.css -- dark overrides for:
       assets/chat/chat.css              (2544 lines, the chat product)
       assets/mcq_test/test_interface.css
       assets/mcq_test/custom_popup.css

   HOW THESE SHEETS LOAD
   chat.css is pulled in from chat_mgmt/templates/chat_mgmt/student/chat.html and
   .../org_staff/chat.html, both inside {% block base_top_head %}. Those templates
   extend website/base.html, so <html> does carry data-theme="dark" -- but the block
   renders AFTER dark.css in <head>, which means chat.css wins on source order. Every
   rule below therefore relies on specificity to win instead: the :root[data-theme]
   prefix adds (0,2,0) on top of the source selector it copies, so it always outranks
   the original. Where the source used !important, so does the override.

   custom_popup.css loads from mcq_test/templates/mcq_test/edit_mcq_test_questions.html,
   which extends website/org_staff_dashboard_base.html -> website/base.html, so it is on
   a data-theme page and these rules take effect.

   NOT ON A data-theme PAGE: test_interface.css is only linked from
   mcq_test/templates/mcq_test/dummy_student_mcq_test_interface.html, a standalone
   <html lang="en"> that does not extend website/base.html and never gets data-theme.
   Its overrides are written below and are correct, but they are inert until that page
   is moved onto base.html (or the attribute is added to it).

   THE COST OF WINNING ON SPECIFICITY
   Because the prefix adds (0,2,0), an override on a BASE selector also outranks the
   source's own single-step variants of it -- `.x:hover`, `.x.active`, `.a.state .b` --
   which are only (0,1,0) above the base. Those states are not "left alone" by writing
   nothing: they are silently clobbered by the base override. Every state the source
   defines for a property this file touches therefore has to be re-declared here, even
   when the answer is "keep exactly what the source had". Rules that only restore a
   source value are marked RESTORE.

   TWO CONVENTIONS USED THROUGHOUT
   1. Hover states that need to sit one step ABOVE an already-dark surface use
      var(--vs-border). There is no --vs-surface-4, and --vs-surface-3 is recessed
      (darker), which reads as "pressed" rather than "hovered".
   2. Pastel status chips (#fff3cd, #f8d7da, #d1ecf1 ...) become a low-alpha tint of
      the matching semantic token with near-white --vs-text on top. Keeping the
      original dark-on-pastel text would need a light amber/red/cyan text token that
      does not exist, and the tint carries the semantic just as well.
   ========================================================================== */


/* --- chat.css: attachment preview tray -----------------------------------
   .attachment-preview-area / -item / -name / -size / -remove, .attachment-progress-*,
   .attachment-status.*  (source lines 1-142) */
:root[data-theme="dark"] .attachment-preview-area {
  background: var(--vs-surface-2);
  border-top-color: var(--vs-border);
}

:root[data-theme="dark"] .attachment-preview-item {
  background: var(--vs-surface);
  border-color: var(--vs-border);
  box-shadow: 0 2px 4px var(--vs-shadow);
}

/* #044FC0 only reaches 2.3:1 on the dark card -- below the 3:1 a border needs. */
:root[data-theme="dark"] .attachment-preview-item.uploading {
  border-color: var(--vs-brand);
}

/* These two CANNOT be left to the source: the base .attachment-preview-item rule above
   sets border-color at (0,3,0) and would repaint the uploaded/error cards with the
   neutral border. Re-stated on the semantic tokens (4.9:1 and 4.2:1 on the dark card,
   both well past the 3:1 a border needs). */
:root[data-theme="dark"] .attachment-preview-item.uploaded {
  border-color: var(--vs-success);
}

:root[data-theme="dark"] .attachment-preview-item.error {
  border-color: var(--vs-danger);
}

:root[data-theme="dark"] .attachment-preview-name {
  color: var(--vs-text);
}

:root[data-theme="dark"] .attachment-preview-size {
  color: var(--vs-text-muted);
}

/* #ea868f: every small red label in this file (12px or less) falls under 4.5:1 with
   --vs-danger #d9534f on the dark surfaces, and no lightened-red TOKEN exists -- but
   the theme does already have a lightened red, and this is it. 00-tokens.css paints
   .text-danger, .link-danger and .btn-outline-danger with #ea868f (Bootstrap's own
   --bs-danger-text-emphasis), and those utilities appear inside these very components:
   the retry button in .message-error is a .btn-outline-danger sitting directly beside
   .error-text. Inventing a second light red here would put two of them side by side.
   #ea868f clears 6.5:1 on --vs-surface, 5.8:1 on --vs-surface-2, and 4.5:1 on the
   worst of the red-tinted chips it is used against. */
:root[data-theme="dark"] .attachment-preview-remove {
  color: #ea868f;
}

/* The pale-pink hover chip (#f8d7da) would flash bright on the dark tray. */
:root[data-theme="dark"] .attachment-preview-remove:hover {
  background-color: rgba(217, 83, 79, .18);
}

:root[data-theme="dark"] .attachment-progress-bar {
  background-color: var(--vs-track);
}

:root[data-theme="dark"] .attachment-progress-fill {
  background-color: var(--vs-brand);
}

:root[data-theme="dark"] .attachment-status.uploading {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .attachment-status.error {
  color: #ea868f;
}
/* .attachment-status.uploaded keeps #28a745 -- 5.3:1 on the dark card already.
   .attachment-retry-btn keeps its solid red / grey fills with white text (4.5:1 and
   4.7:1), which are unaffected by the page going dark. */


/* --- chat.css: image / video placeholders --------------------------------
   .video-preview-container, .video-preview-placeholder (declared twice, lines 174 and
   225 -- one override covers both), .video-play-icon, .image-placeholder,
   .video-name, .video-size  (source lines 144-254) */
:root[data-theme="dark"] .video-preview-container {
  background: var(--vs-thumb);
}

:root[data-theme="dark"] .video-preview-placeholder {
  background: linear-gradient(135deg, var(--vs-thumb), var(--vs-surface-3));
}

:root[data-theme="dark"] .video-play-icon {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .image-placeholder {
  background: var(--vs-thumb);
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .video-name {
  color: var(--vs-text);
}

:root[data-theme="dark"] .video-size {
  color: var(--vs-text-muted);
}


/* --- chat.css: link previews ---------------------------------------------
   .link-preview and children  (source lines 256-334) */
:root[data-theme="dark"] .link-preview {
  border-color: var(--vs-border);
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .link-preview:hover {
  box-shadow: 0 4px 12px var(--vs-shadow);
}

:root[data-theme="dark"] .link-preview-image {
  background: var(--vs-thumb);
}

:root[data-theme="dark"] .link-preview-title {
  color: var(--vs-text);
}

:root[data-theme="dark"] .link-preview-description {
  color: var(--vs-text-muted);
}

/* #007bff is 3.7:1 on --vs-surface-2 and this is 11px text. */
:root[data-theme="dark"] .link-preview-url {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .link-preview-loading {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .link-preview-error {
  color: #ea868f;
}


/* --- chat.css: lightbox --------------------------------------------------
   .lightbox-content  (source lines 400-412). The overlay is already rgba(0,0,0,.9)
   and .lightbox-close is white-on-black -- both correct on dark, left alone. */
:root[data-theme="dark"] .lightbox-content {
  background: var(--vs-surface);
}


/* --- chat.css: search box and empty state --------------------------------
   .search-no-results, .chat-search input / :focus / ::placeholder / i
   (source lines 559-582, 715-729) */
:root[data-theme="dark"] .search-no-results {
  color: var(--vs-text-muted);
}

/* The source sets a border but no background, so the field falls back to the UA
   `field` colour -- white unless color-scheme happens to be honoured. Pinning it
   avoids near-white text landing on a white field. */
:root[data-theme="dark"] .chat-search input {
  background: var(--vs-surface-3);
  color: var(--vs-text);
  border-color: var(--vs-border-strong);
}

/* rgba(4,79,192,.25) is invisible against the navy; the ring is re-tinted with the
   lightened brand. No translucent-brand token exists. */
:root[data-theme="dark"] .chat-search input:focus {
  border-color: var(--vs-brand);
  box-shadow: 0 0 0 0.2rem rgba(77, 144, 240, .30);
}

:root[data-theme="dark"] .chat-search input::placeholder {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .chat-search i {
  color: var(--vs-text-muted);
}


/* --- chat.css: load-previous button --------------------------------------
   .load-previous-messages .btn:hover  (source lines 597-601) */
:root[data-theme="dark"] .load-previous-messages .btn:hover {
  background: var(--vs-brand-fill);
  border-color: var(--vs-brand-fill);
  color: #fff;
}


/* --- chat.css: typing indicator ------------------------------------------
   .typing-dots span, .typing-text  (source lines 654-685).
   .chat-connection-status.* are saturated green/amber/red chips with white or
   near-black glyphs -- already correct on dark, skipped. */
:root[data-theme="dark"] .typing-dots span {
  background: var(--vs-text-muted);
}

:root[data-theme="dark"] .typing-text {
  color: var(--vs-text-muted);
}


/* --- chat.css: shell and conversation list -------------------------------
   .chat-container, .chat-list-sidebar, .chat-list-header  (source lines 688-708) */
:root[data-theme="dark"] .chat-container {
  background: var(--vs-app-bg);
}

:root[data-theme="dark"] .chat-list-sidebar {
  background: var(--vs-surface);
  border-right-color: var(--vs-border);
}

:root[data-theme="dark"] .chat-list-header {
  background: var(--vs-surface-2);
  border-bottom-color: var(--vs-border);
}


/* --- chat.css: filter pills ----------------------------------------------
   .chat-filter-pill and states  (source lines 745-771).
   The active pill was green-on-pale-green; on dark it becomes the console's standard
   active pill (brand-soft) with near-white text at 13.3:1 -- "this filter is on" is
   the meaning being preserved, not the hue. */
:root[data-theme="dark"] .chat-filter-pill {
  background: var(--vs-surface-2);
  color: var(--vs-text);
}

:root[data-theme="dark"] .chat-filter-pill:hover {
  background: var(--vs-border);
}

:root[data-theme="dark"] .chat-filter-pill.active {
  background: var(--vs-brand-soft);
  color: var(--vs-text);
}

:root[data-theme="dark"] .chat-filter-pill.active:hover {
  background: var(--vs-card-accent);
}


/* --- chat.css: filter dropdown -------------------------------------------
   .filter-dropdown-btn / -menu / -item and .chat-filter-btn
   (source lines 785-892). Colour is set explicitly on the buttons because the UA
   gives <button> `buttontext`, not the inherited body colour. */
:root[data-theme="dark"] .filter-dropdown-btn {
  border-color: var(--vs-border-strong);
  background: var(--vs-surface-3);
  color: var(--vs-text);
}

:root[data-theme="dark"] .filter-dropdown-btn:hover {
  background: var(--vs-surface-2);
  border-color: var(--vs-brand);
}

:root[data-theme="dark"] .filter-dropdown-btn.active {
  background: var(--vs-brand-fill);
  color: #fff;
  border-color: var(--vs-brand-fill);
}

:root[data-theme="dark"] .filter-dropdown-menu {
  background: var(--vs-surface);
  color: var(--vs-text);
  border-color: var(--vs-border);
  box-shadow: 0 4px 20px var(--vs-shadow);
}

:root[data-theme="dark"] .filter-dropdown-item {
  border-bottom-color: var(--vs-hairline);
}

:root[data-theme="dark"] .filter-dropdown-item:hover {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .filter-dropdown-item.active {
  background: var(--vs-brand-soft);
  color: var(--vs-brand);
}

:root[data-theme="dark"] .chat-filter-btn {
  border-color: var(--vs-border-strong);
  background: var(--vs-surface-3);
  color: var(--vs-text);
}

:root[data-theme="dark"] .chat-filter-btn.active {
  background: var(--vs-brand-fill);
  color: #fff;
  border-color: var(--vs-brand-fill);
}


/* --- chat.css: conversation rows -----------------------------------------
   .chat-item and states, .chat-avatar, .chat-name, .chat-preview, .chat-type.*,
   .chat-time, .chat-item.unread  (source lines 894-1015).
   .chat-arrow / .chat-arrow:hover are skipped: the source sets display:none !important
   on them, so their colours never render.
   .chat-unread (#25d366 with white text) is left as-is -- the badge reads well on the
   dark row, and its white-on-green text is 2.0:1 in light mode too, so it is a
   pre-existing pairing rather than something dark mode broke. */
:root[data-theme="dark"] .chat-item {
  border-bottom-color: var(--vs-hairline);
}

:root[data-theme="dark"] .chat-item:hover {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .chat-item.active {
  background: var(--vs-brand-soft);
}

:root[data-theme="dark"] .chat-avatar {
  background: var(--vs-brand-soft);
  color: var(--vs-brand);
}

:root[data-theme="dark"] .chat-name {
  color: var(--vs-text);
}

:root[data-theme="dark"] .chat-preview {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .chat-type.direct {
  background: var(--vs-brand-soft);
  color: var(--vs-text);
  border-color: var(--vs-border);
}

/* The blue/purple split between direct and group chats is the whole point of these
   two chips, and there is no purple token -- a purple tint is defined inline so the
   distinction survives. Near-white label sits at 11.9:1 on it. */
:root[data-theme="dark"] .chat-type.group {
  background: rgba(155, 102, 220, .20);
  color: var(--vs-text);
  border-color: rgba(155, 102, 220, .42);
}

:root[data-theme="dark"] .chat-time {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .chat-item.unread {
  background-color: var(--vs-surface-2);
}


/* --- chat.css: chat-item dropdown ----------------------------------------
   .chat-item-dropdown, .dropdown-menu-item and children, .dropdown-menu-separator
   (source lines 1039-1091) */
:root[data-theme="dark"] .chat-item-dropdown {
  background: var(--vs-surface);
  color: var(--vs-text);
  border-color: var(--vs-border);
  box-shadow: 0 4px 20px var(--vs-shadow);
}

:root[data-theme="dark"] .dropdown-menu-item {
  border-bottom-color: var(--vs-hairline);
}

:root[data-theme="dark"] .dropdown-menu-item:hover {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .dropdown-menu-item i {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .dropdown-menu-item span {
  color: var(--vs-text);
}

:root[data-theme="dark"] .dropdown-menu-separator {
  background: var(--vs-hairline);
}


/* --- chat.css: new-chat picker (search results, pills, people rows) -------
   .search-results .*, .no-results, .participant-pill, .student/.course/.user-*
   (source lines 1108-1342) */
:root[data-theme="dark"] .search-results .chat-item {
  border-bottom-color: var(--vs-hairline);
}

:root[data-theme="dark"] .search-results .chat-item:hover {
  background: var(--vs-surface-2);
}

/* #007bff carries white initials at only 4.0:1; the fill token gets it to 4.8:1. */
:root[data-theme="dark"] .search-results .chat-avatar {
  background: var(--vs-brand-fill);
  color: #fff;
}

:root[data-theme="dark"] .search-results .chat-name {
  color: var(--vs-text);
}

/* #ccc is a deliberately faint 3rem glyph (1.6:1 on white); --vs-border-strong keeps
   it equally faint (1.9:1) instead of turning it into a bright icon. */
:root[data-theme="dark"] .no-results i {
  color: var(--vs-border-strong);
}

:root[data-theme="dark"] .no-results p {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .participant-pill {
  background: var(--vs-brand-soft);
  border-color: var(--vs-border);
  color: var(--vs-brand);
}

:root[data-theme="dark"] .participant-pill .pill-avatar {
  background: var(--vs-brand-fill);
  color: #fff;
}

:root[data-theme="dark"] .participant-pill .pill-remove {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .participant-pill .pill-remove:hover {
  background: var(--vs-brand-fill);
  color: #fff;
}

:root[data-theme="dark"] .student-avatar,
:root[data-theme="dark"] .course-avatar,
:root[data-theme="dark"] .user-avatar {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .course-avatar i {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .student-name,
:root[data-theme="dark"] .course-name,
:root[data-theme="dark"] .user-name {
  color: var(--vs-text);
}

:root[data-theme="dark"] .student-status,
:root[data-theme="dark"] .course-students,
:root[data-theme="dark"] .user-role {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .status-dot.offline {
  background: var(--vs-text-muted);
}

:root[data-theme="dark"] .course-indicator i {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .overlay-footer {
  border-top-color: var(--vs-border);
}


/* --- chat.css: welcome / empty screen ------------------------------------
   .chat-welcome-screen, .globe-icon, .paper-plane-icon, .welcome-text
   (source lines 1356-1517). The saturated illustration chips (#25d366, #28a745,
   #20c997, #e83e8c) carry white glyphs and read fine on dark -- skipped. Only the two
   plain-white chips are darkened, because a white disc is the one thing in that
   illustration that would glare. They are decorative, so their low separation from the
   panel is acceptable. */
:root[data-theme="dark"] .chat-welcome-screen {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .globe-icon,
:root[data-theme="dark"] .paper-plane-icon {
  background: var(--vs-surface);
}

:root[data-theme="dark"] .globe-icon i,
:root[data-theme="dark"] .paper-plane-icon i {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .welcome-text h2 {
  color: var(--vs-text);
}

:root[data-theme="dark"] .welcome-text p {
  color: var(--vs-text-muted);
}


/* --- chat.css: conversation header ---------------------------------------
   .chat-header, .chat-header-avatar  (source lines 1526-1549).
   .chat-header-info small is skipped -- line 1704 sets display:none on it. */
:root[data-theme="dark"] .chat-header {
  background: var(--vs-surface);
  border-bottom-color: var(--vs-border);
}

:root[data-theme="dark"] .chat-header-avatar {
  background: var(--vs-brand-fill);
  color: #fff;
}


/* --- chat.css: message canvas and bubbles --------------------------------
   .chat-messages, .message.sent/.received .message-bubble, .message-time,
   .message-dropdown-chevron  (source lines 1570-1652)

   THE BUBBLE PAIR. Light mode distinguishes sender from receiver almost entirely by
   hue: #dcf8c6 green against white, only 1.13:1 apart in luminance. That trick does
   not survive on dark, where hue separation collapses at low luminance, so the dark
   pair is rebuilt to separate on BOTH axes:

     canvas   --vs-app-bg  #0a1628
     received --vs-surface #0f1f36   1.09:1 vs canvas (light mode: 1.20:1)
     sent     #16402c                1.55:1 vs canvas, 1.42:1 vs received, plus green

   #16402c is written literally: there is no dark green-tinted surface token, and every
   navy token close enough in luminance (--vs-surface-2 #142943, --vs-brand-soft
   #14294a) sits within 1.01:1 of the others, which would erase the distinction
   entirely. It carries --vs-text at 10.6:1 and --vs-text-muted at 4.5:1.

   colour is stated on both bubbles even though the source omits it, so the bubble text
   can never inherit a dark value onto these dark fills. */
:root[data-theme="dark"] .chat-messages {
  background: var(--vs-app-bg);
}

:root[data-theme="dark"] .message.received .message-bubble {
  background: var(--vs-surface);
  color: var(--vs-text);
}

:root[data-theme="dark"] .message.sent .message-bubble {
  background: #16402c;
  color: var(--vs-text);
}

:root[data-theme="dark"] .message-time {
  color: var(--vs-text-muted);
}

/* rgba(0,0,0,.05) is invisible on a dark bubble; the scrim flips to white-on-dark.
   No translucent-light token exists. */
:root[data-theme="dark"] .message-dropdown-chevron {
  background: rgba(255, 255, 255, .08);
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .message-dropdown-chevron:hover {
  background: rgba(255, 255, 255, .16);
  color: var(--vs-text);
}


/* --- chat.css: offline composer state ------------------------------------
   .chat-input.offline textarea  (source lines 1694-1697). The greyed .send-btn
   (#6c757d, white glyph at 4.7:1) is fine on dark and is skipped. */
:root[data-theme="dark"] .chat-input.offline textarea {
  background-color: var(--vs-surface-2);
  color: var(--vs-text-muted);
}


/* --- chat.css: composer --------------------------------------------------
   .chat-input and children  (source lines 1737-1815).
   The textarea, like the search field, has a border but no background in the source,
   so it is pinned rather than left to the UA field colour. The ::placeholder rule has
   no source counterpart but is added deliberately -- an unset placeholder on a dark
   field is the classic dark-mode miss. */
:root[data-theme="dark"] .chat-input {
  background: var(--vs-surface);
  border-top-color: var(--vs-border);
}

:root[data-theme="dark"] .chat-input input,
:root[data-theme="dark"] .chat-input textarea {
  background: var(--vs-surface-3);
  color: var(--vs-text);
  border-color: var(--vs-border-strong);
}

:root[data-theme="dark"] .chat-input input::placeholder,
:root[data-theme="dark"] .chat-input textarea::placeholder {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .chat-input input:focus,
:root[data-theme="dark"] .chat-input textarea:focus {
  border-color: var(--vs-brand);
}

:root[data-theme="dark"] .chat-input .attachment-btn {
  background: var(--vs-surface-2);
  color: var(--vs-text-muted);
  border-color: var(--vs-border-strong);
}

:root[data-theme="dark"] .chat-input .attachment-btn:hover {
  background: var(--vs-border);
  color: var(--vs-text);
}

:root[data-theme="dark"] .chat-input .send-btn {
  background: var(--vs-brand-fill);
  color: #fff;
}

/* RESTORE. The rule above is (0,4,0) and outranks the source's own (0,3,0) hover and
   offline rules, so both have to be re-stated or the button would keep one flat blue
   in every state. Values are the source's, unchanged: the teal carries its white glyph
   at 4.1:1 and the grey at 4.7:1, neither of which dark mode affects. No token exists
   for either -- they are a brand accent and a disabled fill, not palette colours. */
:root[data-theme="dark"] .chat-input .send-btn:hover {
  background: #128c7e;
}

:root[data-theme="dark"] .chat-input.offline .send-btn {
  background-color: #6c757d;
  border-color: #6c757d;
}


/* --- chat.css: attachment menu -------------------------------------------
   .attachment-menu, .attachment-menu-item, .attachment-menu.below-button::before/after
   (source lines 1818-1872). The ::before/::after are the popover's arrow -- a white
   triangle would be the brightest thing on the panel. */
:root[data-theme="dark"] .attachment-menu {
  background: var(--vs-surface);
  color: var(--vs-text);
  border-color: var(--vs-border);
  box-shadow: 0 4px 20px var(--vs-shadow);
}

:root[data-theme="dark"] .attachment-menu-item:hover {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .attachment-menu-item span {
  color: var(--vs-text);
}

:root[data-theme="dark"] .attachment-menu.below-button::after {
  border-bottom-color: var(--vs-surface);
}

:root[data-theme="dark"] .attachment-menu.below-button::before {
  border-bottom-color: var(--vs-border);
}


/* --- chat.css: mobile back button ----------------------------------------
   .mobile-back-btn, inside @media (max-width: 768px)  (source lines 1909-1924).
   The media query is reproduced so the rule keeps the same breakpoint; the
   :root[data-theme="dark"] prefix is inside it. */
@media (max-width: 768px) {
  :root[data-theme="dark"] .mobile-back-btn {
    color: var(--vs-text-muted);
  }

  :root[data-theme="dark"] .mobile-back-btn:hover {
    background: var(--vs-surface-2);
    color: var(--vs-text);
  }
}


/* --- chat.css: scrollbars ------------------------------------------------
   .chat-list-sidebar / .chat-messages ::-webkit-scrollbar-*  (source lines 1963-1983).
   The hover thumb takes --vs-text-muted: it is the only token light enough to read as
   "brighter than --vs-border-strong", and a scrollbar thumb is neither text nor a
   surface, so borrowing it does not confuse the two roles. */
:root[data-theme="dark"] .chat-list-sidebar::-webkit-scrollbar-track,
:root[data-theme="dark"] .chat-messages::-webkit-scrollbar-track {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .chat-list-sidebar::-webkit-scrollbar-thumb,
:root[data-theme="dark"] .chat-messages::-webkit-scrollbar-thumb {
  background: var(--vs-border-strong);
}

:root[data-theme="dark"] .chat-list-sidebar::-webkit-scrollbar-thumb:hover,
:root[data-theme="dark"] .chat-messages::-webkit-scrollbar-thumb:hover {
  background: var(--vs-text-muted);
}


/* --- chat.css: context menu ----------------------------------------------
   .context-menu and children, .context-menu-submenu  (source lines 1986-2048) */
:root[data-theme="dark"] .context-menu {
  background: var(--vs-surface);
  color: var(--vs-text);
  border-color: var(--vs-border);
  box-shadow: 0 4px 20px var(--vs-shadow);
}

:root[data-theme="dark"] .context-menu-item:hover {
  background: var(--vs-surface-2);
}

:root[data-theme="dark"] .context-menu-item.has-submenu::after {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .context-menu-item.submenu-item:hover {
  background: var(--vs-brand-soft);
}

:root[data-theme="dark"] .context-menu-separator {
  background: var(--vs-hairline);
}

:root[data-theme="dark"] .context-menu-submenu {
  background: var(--vs-surface);
  color: var(--vs-text);
  border-color: var(--vs-border);
  box-shadow: 0 4px 20px var(--vs-shadow);
}


/* --- chat.css: conversation labels and assignment chips ------------------
   .chat-label.pending / .later, .chat-assigned  (source lines 2062-2084).
   All three were pastel-background + dark-text, which inverts to invisible on dark.
   They become semantic tints under near-white text (12:1 and better) per the
   convention noted in the file header. */
:root[data-theme="dark"] .chat-label.pending {
  background: rgba(217, 164, 6, .20);
  color: var(--vs-text);
  border-color: rgba(217, 164, 6, .42);
}

:root[data-theme="dark"] .chat-label.later {
  background: rgba(217, 83, 79, .22);
  color: var(--vs-text);
  border-color: rgba(217, 83, 79, .45);
}

:root[data-theme="dark"] .chat-assigned {
  background: var(--vs-brand-soft);
  color: var(--vs-text);
  border-color: var(--vs-border);
}


/* --- chat.css: in-bubble attachments -------------------------------------
   .attachment-container, .file-attachment / .image-attachment / .video-attachment,
   file icon chips, .file-name, .file-size, .upload-status  (source lines 2098-2213) */

/* rgba(0,0,0,.03) does nothing on a dark bubble. */
:root[data-theme="dark"] .attachment-container {
  background: rgba(255, 255, 255, .05);
}

:root[data-theme="dark"] .file-attachment,
:root[data-theme="dark"] .image-attachment,
:root[data-theme="dark"] .video-attachment {
  background: var(--vs-surface-2);
  border-color: var(--vs-border);
}

:root[data-theme="dark"] .file-attachment i,
:root[data-theme="dark"] .video-attachment i {
  background-color: var(--vs-surface-3);
}

/* The three file-type icon chips were saturated glyphs on pastel squares. The squares
   become tints; only danger and primary need their glyph re-lightened (#dc3545 falls
   to 2.8:1 on the red tint, #007bff to 3.7:1 on the blue). Success needs no colour rule
   at all: chat.css's `.file-attachment i.text-success` is (0,2,1), which LOSES to
   00-tokens.css's `:root[data-theme="dark"] .text-success` at (0,3,0) -- both !important,
   so specificity decides -- and the glyph already renders #75b798 at 4.9:1 on the tint.
   !important mirrors the source, which has it on `color` only -- not on the
   background-color, so these background-colors are plain. */
:root[data-theme="dark"] .file-attachment i.text-danger {
  color: #ea868f !important;
  background-color: rgba(217, 83, 79, .18);
}

:root[data-theme="dark"] .file-attachment i.text-primary {
  color: var(--vs-brand) !important;
  background-color: var(--vs-brand-soft);
}

:root[data-theme="dark"] .file-attachment i.text-success {
  background-color: rgba(46, 158, 99, .18);
}

:root[data-theme="dark"] .file-name,
:root[data-theme="dark"] .video-name {
  color: var(--vs-text);
}

:root[data-theme="dark"] .file-size,
:root[data-theme="dark"] .video-size {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .upload-status {
  background: rgba(255, 255, 255, .07);
}

:root[data-theme="dark"] .upload-status .text-danger {
  color: #ea868f !important;
}

/* THE ONE THAT WOULD HAVE DISAPPEARED: the source paints this label pure #000 because
   it sat on a light chip. On the darkened chip it would be black on near-black.
   #ffda6a, not --vs-warning: this is a .text-warning, and 00-tokens.css already paints
   every .text-warning on the site #ffda6a. Matching it keeps one amber ink across the
   theme and is the brighter of the two here (8.8:1 on the chip against 5.3:1). */
:root[data-theme="dark"] .upload-status .text-warning {
  color: #ffda6a !important;
}
/* .upload-status .text-success needs nothing here for the same reason as the file icon
   above: at (0,2,0) it loses to the global .text-success at (0,3,0), so it renders
   #75b798 at 5.1:1 on the lightened chip rather than the #28a745 the source asked for. */


/* --- chat.css: file actions and upload progress --------------------------
   .file-actions .btn-outline-*, .upload-progress-*  (source lines 2230-2301) */
:root[data-theme="dark"] .file-actions .btn-outline-danger {
  color: #ea868f;              /* #dc3545 is 3.3:1 here and this is 11px text */
  border-color: var(--vs-danger);
}

:root[data-theme="dark"] .file-actions .btn-outline-primary {
  color: var(--vs-brand);
  border-color: var(--vs-brand);
}

:root[data-theme="dark"] .file-actions .btn-outline-primary:hover {
  background-color: var(--vs-brand-fill);
  color: #fff;
}

/* RESTORE, and the worst leak in this group if it is missed: the danger rule above is
   (0,4,0), so it repaints the source's (0,3,0) hover ink too. The hover still fills
   solid #dc3545 from the source, and the lightened red would then sit on it at 2.3:1 --
   a button label that all but vanishes the moment the pointer touches it. Source value
   restored; #fff on #dc3545 is 4.5:1. */
:root[data-theme="dark"] .file-actions .btn-outline-danger:hover {
  color: #fff;
}

:root[data-theme="dark"] .upload-progress-bar {
  background-color: var(--vs-track);
}

/* The light gradient's dark end (#0056b3) all but vanishes on --vs-track. */
:root[data-theme="dark"] .upload-progress-fill {
  background: linear-gradient(90deg, var(--vs-brand), var(--vs-brand-fill));
}

:root[data-theme="dark"] .upload-progress-text {
  color: var(--vs-text-muted);
}


/* --- chat.css: reply quote and reply composer bar ------------------------
   .message-reply-quote, .reply-indicator, .reply-vertical-bar, .reply-sender,
   .reply-message  (source lines 2332-2408).
   The quote's rgba(255,255,255,.3) was a light wash on a light bubble; at that alpha
   on a dark bubble it becomes a near-white slab, so the alpha drops rather than the
   colour changing. .reply-indicator mirrors the source's !important on every
   declaration it touches. */
:root[data-theme="dark"] .message-reply-quote {
  background: rgba(255, 255, 255, .08);
}

:root[data-theme="dark"] .message-reply-quote:hover {
  background: rgba(255, 255, 255, .14);
  box-shadow: 0 2px 8px var(--vs-shadow);
}

:root[data-theme="dark"] .reply-indicator {
  background: var(--vs-surface-2) !important;
  border-left: 3px solid var(--vs-brand) !important;
  color: var(--vs-text-muted) !important;
  box-shadow: 0 2px 8px var(--vs-shadow) !important;
}

:root[data-theme="dark"] .reply-vertical-bar {
  background: var(--vs-danger);
}

:root[data-theme="dark"] .reply-sender {
  color: #ea868f;
}

:root[data-theme="dark"] .reply-message {
  color: var(--vs-text-muted);
}


/* --- chat.css: edit mode -------------------------------------------------
   .message-bubble.editing  (source lines 2416-2435). The source marks the editing
   bubble with a pale amber fill; on dark it becomes an amber tint that composites over
   whichever bubble colour is underneath, carrying --vs-text at 12.8:1.
   !important mirrored from the source. */
:root[data-theme="dark"] .message-bubble.editing {
  background: rgba(217, 164, 6, .16) !important;
  border-color: rgba(217, 164, 6, .40) !important;
}

:root[data-theme="dark"] .message-bubble.editing [contenteditable="true"]:focus {
  background: rgba(255, 255, 255, .10);
}


/* --- chat.css: message error and loading strips --------------------------
   .error-text, .message-loading  (source lines 2438-2490).
   .message-error's own rgba red tint and border already composite correctly on dark
   and are skipped; only the text on top of them needed lifting. */
:root[data-theme="dark"] .error-text {
  color: #ea868f;             /* #dc3545 falls to 2.9:1 on the tint over a sent bubble */
}

/* ANOTHER SILENT KILLER: #856404 is a dark brown chosen for a pale amber strip. The
   strip's rgba(255,193,7,.1) composites dark, so the text vanished. --vs-warning only
   reaches 4.2:1 once the strip composites over a SENT bubble, so this takes the same
   #ffda6a the theme's amber ink uses elsewhere -- 7.0:1 there, 10:1 on a received one. */
:root[data-theme="dark"] .message-loading {
  color: #ffda6a;
}


/* --- chat.css: links inside messages -------------------------------------
   .message-link and states  (source lines 2502-2519).
   Received bubbles take --vs-brand (5.2:1 on --vs-surface). Sent bubbles sit on the
   green fill where --vs-brand only reaches 3.6:1, so they take the lighter blue the
   theme already uses for --bs-link-hover-color (5.2:1), and their hover goes lighter
   still -- on dark, hover has to add luminance, not remove it as the light rules do.
   Neither light blue exists as a --vs- token. */
:root[data-theme="dark"] .message-link {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .message-link:hover {
  color: #7ab0f7;
}

:root[data-theme="dark"] .message.sent .message-link {
  color: #7ab0f7;
}

:root[data-theme="dark"] .message.sent .message-link:hover {
  color: #a9c9fa;
}


/* --- chat.css: deleted messages ------------------------------------------
   .message.deleted .message-bubble / .message-text  (source lines 2536-2544).
   CONTRAST NOTE: this is the one pair that stays under 4.5:1. The bubble carries
   opacity: .6, so the italic "message deleted" placeholder composites down to ~3.0:1
   against the faded bubble. It is deliberately de-emphasised -- and the same pair is
   2.2:1 in light mode, so dark is an improvement rather than a regression. Lifting it
   would need the source's opacity removed, which is out of scope for an override. */
:root[data-theme="dark"] .message.deleted .message-bubble {
  background-color: var(--vs-surface-2);
  border-color: var(--vs-border);
}

:root[data-theme="dark"] .message.deleted .message-text {
  color: var(--vs-text-muted);
}


/* --- test_interface.css --------------------------------------------------
   INERT TODAY. See the file header: the only template linking this sheet
   (mcq_test/dummy_student_mcq_test_interface.html) is a standalone <html lang="en">
   that does not extend website/base.html, so data-theme is never present and none of
   the rules below can match. They are written so the sheet is ready the moment that
   page moves onto base.html.
   .dot-seen (#ffc107) and .dot-answered (#28a745) are saturated status dots that read
   correctly on dark and are skipped. */
:root[data-theme="dark"] .vs-test-card,
:root[data-theme="dark"] .vs-test-side {
  background: var(--vs-surface);
  border-color: var(--vs-border);
}

:root[data-theme="dark"] .vs-test-header {
  border-bottom-color: var(--vs-border);
}

:root[data-theme="dark"] .vs-divider {
  background: var(--vs-border-strong);
}

:root[data-theme="dark"] .vs-timer {
  color: var(--vs-brand);
}

:root[data-theme="dark"] .vs-timer.vs-muted {
  color: var(--vs-text-muted);
}

/* The "unseen" dot is the neutral one; it needs to stay visible as a dot on the dark
   card without reading as an active state, which is what --vs-border-strong gives. */
:root[data-theme="dark"] .dot-unseen {
  background: var(--vs-border-strong);
}

:root[data-theme="dark"] .vs-palette button,
:root[data-theme="dark"] .vs-section-nav .section-pill {
  border-color: var(--vs-border-strong);
  background: var(--vs-surface-2);
  color: var(--vs-text);
}

:root[data-theme="dark"] .vs-palette button.seen {
  background: rgba(217, 164, 6, .20);
  color: var(--vs-text);
}

:root[data-theme="dark"] .vs-palette button.answered {
  background: rgba(46, 158, 99, .20);
  color: var(--vs-text);
}

:root[data-theme="dark"] .vs-question {
  border-color: var(--vs-border);
}

:root[data-theme="dark"] .vs-paragraph {
  background: var(--vs-surface-2);
  border-color: var(--vs-border);
}


/* --- custom_popup.css ----------------------------------------------------
   The hand-rolled popup used by mcq_test/edit_mcq_test_questions.html, which does
   carry data-theme. Bootstrap's dark modal styling never reaches it because it is not
   a .modal-content, so every surface has to be named here.
   The .btn-primary hover has to be re-stated for the same specificity reason as the
   chat send button -- see the header note. */
:root[data-theme="dark"] .custom-popup-overlay {
  background-color: var(--vs-overlay);
}

:root[data-theme="dark"] .custom-popup-container {
  background: var(--vs-surface);
  color: var(--vs-text);
  box-shadow: 0 10px 30px var(--vs-shadow);
}

:root[data-theme="dark"] .custom-popup-header {
  background: var(--vs-surface-2);
  border-bottom-color: var(--vs-border);
}

:root[data-theme="dark"] .custom-popup-title {
  color: var(--vs-text);
}

:root[data-theme="dark"] .custom-popup-close {
  color: var(--vs-text-muted);
}

:root[data-theme="dark"] .custom-popup-close:hover {
  background-color: var(--vs-border);
  color: var(--vs-text);
}

:root[data-theme="dark"] .custom-popup-body .form-control {
  border-color: var(--vs-border-strong);
}

/* Same focus-ring problem as the chat search field: the light ring is invisible here. */
:root[data-theme="dark"] .custom-popup-body .form-control:focus {
  border-color: var(--vs-brand);
  box-shadow: 0 0 0 0.2rem rgba(77, 144, 240, .30);
}

:root[data-theme="dark"] .custom-popup-body .btn-primary {
  background-color: var(--vs-brand-fill);
  border-color: var(--vs-brand-fill);
}

/* RESTORE. Source values kept: #0056b3 is the darker-on-hover step the light design
   intends and white sits on it at 7.0:1, but the (0,4,0) rule above would erase it. */
:root[data-theme="dark"] .custom-popup-body .btn-primary:hover {
  background-color: #0056b3;
  border-color: #004085;
}


/* ==========================================================================
   40-exam.css -- dark refinements for the online exam interface
   (mcq_test/templates/mcq_test/online_mcq_test.html)

   The exam's question palette encodes state in COLOUR, so it needs more than a
   readable label: a student has to tell "answered", "not answered" and "marked for
   review" apart at a glance, under time pressure. Tokenising the literals one for one
   gets the labels legible but flattens the default state, which is the one most
   circles are in when the paper opens.

   Light mode is untouched, as everywhere else in this file.
   ========================================================================== */

/* The "not answered" chip. In light it is #E0ECFE (pale blue) on a white panel --
   clearly a chip, clearly empty. Mapping it to --vs-brand-soft (#14294a) put it at
   1.4:1 against the --vs-surface panel behind it, so the default state all but
   disappeared, and its #4d90f0 number landed at 4.4:1 -- just under AA.

   This fill keeps the blue that already means "not answered" in light mode, while
   clearing 3:1 against the panel (so the chip reads as a chip) and 6:1 for the number
   inside it. It stays clearly distinct from the green "answered" and purple "marked". */
:root[data-theme="dark"] .question-circle.unattempted {
  background-color: #3b6aa3;   /* 3.2:1 vs the --vs-surface panel */
  color: #ffffff;              /* 6.2:1 on the fill                */
}

/* The legend dot has to match the chip it explains, or the key stops being a key. */
:root[data-theme="dark"] .status-dot.unattempted {
  background-color: #3b6aa3;
}

/* "Current question" is signalled by a ring rather than a fill, so it survives the
   theme -- but its halo was rgba(13,110,253,.25), tuned to glow against white. On the
   navy it barely separates the ring from the panel. */
:root[data-theme="dark"] .question-circle.current {
  box-shadow: 0 0 0 2px rgba(77, 144, 240, .45);
}

/* "Marked for review" keeps Bootstrap's #6f42c1, which was 4.4:1 against the white
   sidebar but only 2.5:1 against the dark one -- under the 3:1 bar for a UI shape, so
   the chip edge softens into the panel even though its white number stays readable.
   Lightened just enough to clear 3:1 on the panel (3.65:1) while keeping the number at
   4.56:1 and staying recognisably the same purple as light mode. Circle and legend dot
   move together, or the key stops matching what it explains. */
:root[data-theme="dark"] .question-circle.marked,
:root[data-theme="dark"] .status-dot.marked {
  background-color: #845ddb;
}


/* ==========================================================================
   41-brand-panels.css -- surfaces that are already a saturated brand colour

   Some panels are not a "surface" this theme darkens: they are a solid brand fill
   that looks identical in both modes. .login_column (#044FC0, assets/website/css/
   accounts.css) is the big one -- the blue half of every login, OTP, signup and
   change-password screen.

   The panel itself is correctly left alone, but the CONTROLS sitting on it are not:
   they were picking up the page-level dark tokens, so on the staff login screen the
   mobile and password fields turned navy-on-blue and the white Continue button
   turned into a dark slab. On an unchanged blue ground the light treatment is the
   correct one, so it is restored here. Specificity does the work -- adding the
   .login_column ancestor beats the page-level rules without any !important.
   ========================================================================== */

/* Restore the light Bootstrap context for anything inside the blue panel, so
   components that derive their colours from variables behave as they do in light. */
:root[data-theme="dark"] .login_column {
  --bs-body-bg: #ffffff;
  --bs-body-color: #212529;
  --bs-border-color: #c6c7c9;
  --bs-secondary-color: #6c757d;
  --bs-tertiary-bg: #f8f9fa;
}

/* Form fields: white ground, dark ink -- exactly as in light mode. */
:root[data-theme="dark"] .login_column .form-control,
:root[data-theme="dark"] .login_column .form-select,
:root[data-theme="dark"] .login_column textarea.form-control {
  background-color: #ffffff;
  border-color: #c6c7c9;
  color: #212529;
}
:root[data-theme="dark"] .login_column .form-control::placeholder {
  color: #8a9099;
}
:root[data-theme="dark"] .login_column .form-control:focus,
:root[data-theme="dark"] .login_column .form-select:focus {
  background-color: #ffffff;
  color: #212529;
}

/* The Continue / submit button. forms.css line 124 gives .btn-light the brand pair
   (#044FC0 on #E6EAF6); on the blue panel that pale slab is the whole point of the
   button, so it keeps those values rather than the page-level dark mapping. */
:root[data-theme="dark"] .login_column .btn-light {
  --bs-btn-color: #044FC0;
  --bs-btn-bg: #E6EAF6;
  --bs-btn-border-color: #E6EAF6;
  --bs-btn-hover-color: #044FC0;
  --bs-btn-hover-bg: #dce4ff;
  --bs-btn-hover-border-color: #E6EAF6;
  --bs-btn-active-color: #000;
  --bs-btn-active-bg: #dce4ff;
  --bs-btn-active-border-color: #E6EAF6;
  --bs-btn-disabled-color: #044FC0;
  --bs-btn-disabled-bg: #E6EAF6;
  --bs-btn-disabled-border-color: #E6EAF6;
}

/* The "Change" chip next to the masked number. accounts.css pins its ink to
   #000 !important, so the page-level dark hover (--vs-border) left black text on a
   dark navy chip. On the blue panel it keeps its light grey ground in both modes. */
:root[data-theme="dark"] .login_column .load_modal_step_link {
  background: #eee;
}
:root[data-theme="dark"] .login_column .load_modal_step_link:hover {
  background: #ddd;
}


