/*
 * Dark theme.
 *
 * Everything here is scoped under [data-bs-theme="dark"] on <html>. Nothing sets that attribute
 * except the boot script in head.html, which reads localStorage - there is deliberately no
 * prefers-color-scheme rule anywhere in this file. Light is the default and stays the default for
 * a visitor who has never touched the switch, including one whose operating system is dark. See
 * "Dark theme" in docs/page-shell.md.
 *
 * WHY THIS FILE EXISTS AT ALL, rather than a handful of overrides next to the rules they change:
 * the library ships Bootstrap 5.1.3, and Bootstrap's own dark mode is a 5.3 feature. In 5.1 the
 * attribute styles nothing - .card, .modal-content, .dropdown-menu, .table, .form-control and the
 * rest carry literal hex in bootstrap.min.css. So the dark half of every Bootstrap component we
 * use has to be written by hand, and it is written here, in one place, instead of being scattered
 * through the files that style our own components. When the theme moves to Bootstrap 5.3 the
 * "Bootstrap components" section below can be deleted wholesale; the token sections stay.
 *
 * HOW IT WORKS, in three layers, cheapest first:
 *
 *   1. The --sl-* scale is mirrored. In light it runs light (--sl-50) to dark (--sl-900); in dark
 *      it runs dark to light. Every existing `color: var(--sl-800)` and `background: var(--sl-100)`
 *      therefore keeps its meaning - body text stays the high-contrast end, a hover fill stays a
 *      slight lift off the ground - and needs no edit. This is why the scale is mirrored rather
 *      than replaced by differently-named tokens.
 *
 *   2. Bootstrap's *variables* are defined. 5.1 publishes --bs-gray-100..900 at :root and never
 *      reads them itself (verified: zero var(--bs-gray-*) usages in bootstrap.min.css), and it
 *      does not define --bs-border-color, --bs-secondary-bg or --bs-secondary-color at all. Our
 *      code writes `var(--bs-border-color, #dee2e6)` with a literal fallback precisely because
 *      those are undefined in 5.1 - an undefined custom property drops the whole declaration.
 *      Defining them here, under the dark attribute only, flips every one of those call sites at
 *      once and changes nothing in light mode, where they stay undefined and the fallback wins.
 *      searchui-datasheet's styles.css gets its borders, muted text and table grounds this way
 *      without a single edit in that repository.
 *
 *   3. What is left - Bootstrap's hard-coded component colours, and our own surfaces - is written
 *      out below.
 *
 * WHAT IS NOT WHITE. `#fff` in the light rules means two different things and they are not
 * interchangeable: a *surface* (a card, the topbar, a menu) and *text on a brand-coloured ground*
 * (the label on an orange button, a badge). Only the first becomes dark. Brand, danger and success
 * grounds keep their colour in dark mode and their labels stay white, so nothing in this file
 * touches `color: #fff` where it sits on one of them.
 */

/* ── tokens ─────────────────────────────────────────────────────────────
 *
 * The selector list is two entries and both are needed. shell.css declares the whole --sl-* scale
 * on `body.shell`, which is (0,1,1); `[data-bs-theme="dark"]` on <html> is (0,1,0), so on its own
 * it loses on the body element and the light values inherit into the entire page. The symptom is
 * worth recording because it does not look like a specificity problem: the page ground goes dark
 * (it reads --bs-gray-100, which body.shell does not redeclare) while every surface and every
 * piece of text stays light - dark grey text on a near-black ground. `[data-bs-theme="dark"]
 * body.shell` is (0,2,1) and wins.
 *
 * The bare attribute stays in the list so the scale is also defined for a page that is not the
 * shell and for anything rendered outside body.shell.
 * -------------------------------------------------------------------- */
[data-bs-theme="dark"],
[data-bs-theme="dark"] body.shell {
    /*
     * The mirrored neutral scale, on the corporate greys.
     *
     * Every entry reads --shell-sl-dark-* first, exactly as the light scale in shell.css reads
     * --shell-sl-*. That is not decoration: buildBootstrap compiles _shell-theme.scss with the
     * application's $shell-neutral and writes both scales into its bootstrap.min.css, so an
     * application that brands its greys keeps them in BOTH themes. An earlier version of this file
     * hard-coded the values, which quietly threw a product's branding away the moment the switch
     * was pressed - corporate greys in light, generic ones in dark.
     *
     * The fallbacks are the corporate neutral family, not Tailwind's slate: they are built from
     * $neutral-500 #5C717E and land on $neutral-200 #C9D3D8 at --sl-700, the two greys the Applus
     * palette actually defines (products/searchui .../custom.scss). Slate is a blue-violet and read
     * as a generic "developer dark theme" next to the orange; this family is the same hue the
     * brand's own neutrals are.
     *
     * --sl-400 is the corporate neutral itself and does not move between themes: a mid grey is a
     * mid grey in both, and shifting it would make muted text jump on the switch.
     */
    --sl-50: var(--shell-sl-dark-50, #0f1214);
    --sl-100: var(--shell-sl-dark-100, #14191c);
    --sl-200: var(--shell-sl-dark-200, #1f262b);
    --sl-300: var(--shell-sl-dark-300, #2e383f);
    --sl-400: var(--shell-sl-dark-400, #5c717e);
    --sl-500: var(--shell-sl-dark-500, #83939d);
    --sl-600: var(--shell-sl-dark-600, #a4afb7);
    --sl-700: var(--shell-sl-dark-700, #c9d3d8);
    --sl-800: var(--shell-sl-dark-800, #dbe0e3);
    --sl-900: var(--shell-sl-dark-900, #ebeeef);

    /*
     * Surfaces. These are new in both themes - the light values are in shell.css next to the rest
     * of the scale - and they exist because `background: #fff` appeared fourteen times in
     * shell.css with two different meanings. A named surface token makes which one was meant
     * explicit at the call site, and gives this file one place to darken.
     *
     * --sl-ground is the page behind everything; --sl-surface is a card, the topbar, a menu;
     * --sl-surface-2 is a surface on a surface (a table head, an inset panel, a hovered row).
     *
     * They sit on the scale rather than beside it, so a branded --shell-sl-dark-* reaches them too.
     */
    --sl-ground: var(--shell-sl-dark-ground, #0a0d0f);
    --sl-surface: var(--shell-sl-dark-surface, #141a1e);
    --sl-surface-2: var(--shell-sl-dark-surface-2, #1c242a);
    --sl-border: var(--shell-sl-dark-border, #263138);
    --sl-border-strong: var(--shell-sl-dark-border-strong, #35434c);

    /* Shadows have to be much heavier than in light mode: a soft black shadow is invisible on a
       dark ground, and what reads as elevation there is a combination of a lighter surface and a
       hard shadow. */
    --sl-shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
    --sl-shadow: 0 1px 3px rgba(0, 0, 0, .55);
    --sl-shadow-lg: 0 10px 30px rgba(0, 0, 0, .6);

    /*
     * The brand scale.
     *
     * --brand-600 is the application's primary and is deliberately NOT redefined here: the brand
     * colour is the brand colour in both themes. Which colour that is differs per application and
     * this file must not assume - it is Applus orange #FF6900 in the library's own build and in
     * testdataanalyzer, and the corporate brown #746660 in searchui, whose $secondary is the pale
     * orange. Everything below derives from --bs-primary for that reason; a literal orange here
     * would silently off-brand every product that is not orange.
     *
     * What does change is the two tints. In light mode --brand-50 and --brand-100 are the brand
     * mixed towards white, which on a dark ground is a bright block rather than a hint; here they
     * are the brand mixed towards the ground. --brand-500 and --brand-700/800 are left to
     * shell.css: a tint and two shades of the brand read correctly on either ground.
     */
    --brand-50: var(--shell-brand-dark-50, #24170c);
    --brand-100: var(--shell-brand-dark-100, #331f10);
    --brand-200: var(--shell-brand-dark-200, #6e4015);
}

@supports (color: color-mix(in srgb, red 50%, white)) {
    [data-bs-theme="dark"],
    [data-bs-theme="dark"] body.shell {
        --brand-50: var(--shell-brand-dark-50, color-mix(in srgb, var(--bs-primary, #ff6900) 12%, var(--sl-ground)));
        --brand-100: var(--shell-brand-dark-100, color-mix(in srgb, var(--bs-primary, #ff6900) 20%, var(--sl-ground)));
        --brand-200: var(--shell-brand-dark-200, color-mix(in srgb, var(--bs-primary, #ff6900) 42%, var(--sl-ground)));
    }
}

/* ── the page stylesheets' tokens ───────────────────────────────────────
 *
 * These exist ONLY here. Nothing defines them in light mode, and that is the whole point.
 *
 * A page stylesheet is themed by turning `background: #fff` into `background: var(--t-surface,
 * #fff)`. In light mode --t-surface is undefined, so the fallback paints and the rule is byte-for-
 * byte what it was; in dark mode it is defined and the rule follows. Light cannot regress, because
 * no light rule has a value any more - it has the same value written as a fallback.
 *
 * That property is why these are separate from --sl-*. Substituting an --sl-* token looks
 * equivalent and is not: --sl-900 is #0f172a, so `color: var(--sl-900, #313a46)` silently repaints
 * a #313a46 heading in LIGHT mode too. The same trap sits in --bs-body-color (#746660 in this
 * branded build, not near-black) and in --bs-table-striped-bg (#f1f5f7 on .table, not rgba black).
 * A substitution is only safe when the token is undefined in light, or defined to exactly the
 * literal it replaces. Measured, not assumed: a computed-style diff of every element against
 * origin/dev caught 47 colour changes from the first attempt at this.
 */
[data-bs-theme="dark"],
[data-bs-theme="dark"] body.shell {
    /* Each one points at the scale above rather than repeating a hex, so a product's branded
       --shell-sl-dark-* reaches the page stylesheets too and not just the shell's own chrome. */
    --t-surface: var(--sl-surface);     /* a card, a panel, a menu - what was white */
    --t-surface-2: var(--sl-surface-2); /* a surface on a surface - a header strip, an inset */
    --t-fill: var(--sl-200);            /* a hover or selected fill */
    --t-ink: var(--sl-800);             /* body text - what was near-black */
    --t-ink-strong: var(--sl-900);      /* headings */
    --t-ink-muted: var(--sl-500);       /* secondary text - what was a mid grey */
    --t-line: var(--sl-border);         /* a border or rule */
    /* A zebra stripe painted by a stylesheet's own rule rather than by Bootstrap's .table-striped.
       Translucent, so it composes over whatever surface the table is on - and so it follows a
       branded surface instead of pinning one grey. */
    --t-stripe: rgba(255, 255, 255, .035);
    /* The wash a loading overlay lays over the content it is covering - the JEXL editor while
       Monaco boots, the admin console while a job runs. In light mode these are white at 55-92%,
       which over a dark page is a white flash rather than a veil. */
    --t-scrim: rgba(10, 13, 15, .74);
}

/* ── Bootstrap's variables ──────────────────────────────────────────────
 *
 * See layer 2 in the file header. Defining these is the cheapest theming in the whole file: it
 * reaches every `var(--bs-...)` call site in this repository and in searchui-datasheet without
 * touching either of them.
 *
 * body.shell is in the selector list for the same reason as the block above - a product's
 * buildBootstrap output may put --bs-* on the body rather than on :root.
 * -------------------------------------------------------------------- */
[data-bs-theme="dark"],
[data-bs-theme="dark"] body.shell {
    /* All of these read the scale rather than repeating a hex, so a branded --shell-sl-dark-*
       reaches Bootstrap's variables too. The -rgb pairs cannot: rgba(var(--x-rgb), .5) needs three
       numbers, not a colour, and there is no way to decompose a custom property in CSS. They are
       the corporate defaults, and a product that brands its neutral and uses an -rgb form at a low
       alpha is the one case where it has to set --shell-sl-dark-*-rgb itself. */
    --bs-body-bg: var(--sl-ground);
    --bs-body-bg-rgb: var(--shell-sl-dark-ground-rgb, 10, 13, 15);
    --bs-body-color: var(--sl-800);
    --bs-body-color-rgb: var(--shell-sl-dark-800-rgb, 219, 224, 227);

    /* Undefined in 5.1, so these only ever add. Every `var(--bs-border-color, #dee2e6)` in the
       codebase switches on this one line. */
    --bs-border-color: var(--sl-border);
    --bs-border-color-translucent: rgba(255, 255, 255, .12);
    --bs-secondary-bg: var(--sl-surface-2);
    --bs-secondary-color: var(--sl-500);
    --bs-tertiary-bg: var(--sl-100);
    --bs-tertiary-color: var(--sl-400);
    --bs-emphasis-color: var(--sl-900);
    --bs-heading-color: var(--sl-900);

    /* The grey ramp, mirrored on the same reasoning as --sl-*. Bootstrap 5.1 publishes these and
       never reads them, so this is safe: it reaches our code only. */
    --bs-gray-100: var(--sl-100);
    --bs-gray-200: var(--sl-200);
    --bs-gray-300: var(--sl-300);
    --bs-gray-400: var(--sl-400);
    --bs-gray-500: var(--sl-500);
    --bs-gray-600: var(--sl-600);
    --bs-gray-700: var(--sl-700);
    --bs-gray-800: var(--sl-800);
    --bs-gray-900: var(--sl-900);

    /*
     * --bs-light IS redefined; --bs-dark is NOT. They look symmetric and are not.
     *
     * --bs-light is read as a *surface* all over this codebase - `background: var(--bs-light,
     * #f8f9fa)` on the users page's licence header, for one - and every such rule is a near-white
     * slab on a dark page. So it moves. Its one ink utility, .text-light, is put back by a rule
     * below.
     *
     * --bs-dark is read as a *dark thing* - .bg-dark is a deliberately dark block, and it is still
     * one here - so it stays where it is. Its ink utility, .text-dark, is the one that has to move,
     * and that is also a rule below rather than a variable, because .text-dark means two different
     * things depending on what it sits on. See the comment there.
     *
     * Getting this pair wrong is cheap to do and expensive to see: flipping both turned
     * `.badge.bg-warning.text-dark` into near-white on amber at 1.4:1, and flipping neither left a
     * white licence header with white text on it at 1.26:1. Both were found by sweeping the
     * rendered pages, not by reading.
     */
    --bs-light: var(--sl-surface-2);
    --bs-light-rgb: var(--shell-sl-dark-surface-2-rgb, 28, 36, 42);

    /*
     * Links, and the one place a literal would have been an off-brand bug. The link colour in light
     * mode is the application's - $orange-800 #A13A0B in searchui - and the brand differs per
     * product, so a fixed orange here would repaint a brown-primary product's links orange the
     * moment the switch was pressed. Both derive from --bs-primary instead: the brand lifted
     * towards the light end of the scale far enough to carry on a dark ground.
     */
    --bs-link-color: var(--brand-500, var(--bs-primary, #ff6900));
    --bs-link-hover-color: var(--brand-200, #6e4015);

    --bs-code-color: var(--brand-500, var(--bs-primary, #ff6900));
    --bs-highlight-bg: var(--brand-100, #331f10);

    color-scheme: dark;
}

@supports (color: color-mix(in srgb, red 50%, white)) {
    [data-bs-theme="dark"],
    [data-bs-theme="dark"] body.shell {
        /*
         * A link has to clear the ground it sits on, and the brand alone does not: searchui's
         * $link-color is $orange-800 #A13A0B, which measures 2.6:1 against a dark card. Mixing it
         * towards the scale's light end keeps the hue and buys the contrast, whatever the brand.
         *
         * 62% is measured, not picked. Against --sl-surface, at 72% the three brands in the
         * estate give 7.5:1 (#FF6900), 5.3:1 (#746660) and 4.4:1 (#A13A0B) - the last one just
         * under AA. 62% gives 8.2 / 6.3 / 5.3, so every brand clears 4.5:1 with room, and the hue
         * is still unmistakably the brand's.
         */
        --bs-link-color: color-mix(in srgb, var(--bs-primary, #ff6900) 62%, var(--sl-900));
        --bs-link-hover-color: color-mix(in srgb, var(--bs-primary, #ff6900) 40%, var(--sl-900));
        --bs-code-color: color-mix(in srgb, var(--bs-primary, #ff6900) 62%, var(--sl-900));
    }
}

/* ── the page ───────────────────────────────────────────────────────── */
[data-bs-theme="dark"] body {
    background-color: var(--sl-ground);
    color: var(--bs-body-color);
}

/* body.shell paints its own ground and text; both are tokens in light mode already, so only the
   ground has to be re-pointed at the dark one - shell.css reads --bs-gray-100 there, which is now
   a near-black and slightly lighter than --sl-ground. Being explicit costs one rule and removes
   the coupling. */
[data-bs-theme="dark"] body.shell {
    background: var(--sl-ground);
    color: var(--sl-800);
}

[data-bs-theme="dark"] hr {
    border-top-color: var(--sl-border);
    opacity: 1;
}

/*
 * Body links.
 *
 * --bs-link-color above does not do this on its own: Bootstrap only reads it for `a` from 5.3, and
 * every application here compiles 5.1.3 (the version is in BuildBootstrapTask and no product
 * overrides it), so in 5.1 the anchor colour is a literal compiled from the application's
 * $link-color. Setting the variable is still right - it is what makes this correct for free on a
 * 5.3 upgrade - but on 5.1 it is inert, so the colour has to be applied here as well.
 *
 * It matters most for the product that is not orange: searchui's $link-color is $orange-800
 * #A13A0B, which measures 2.6:1 against a dark card - unreadable. The library's own #FF6900 gets
 * 6.1:1 and would have been fine, which is exactly why this needed measuring rather than looking.
 *
 * `a:not([class])` and nothing wider. shell.css deliberately has no link colour of its own, and
 * its header records why: a rule on `body.shell a` outranked Bootstrap's component colours, and
 * links drawn as badges (roles and licences on the user page) or as buttons (System alerts) came
 * out orange on an orange or red ground. An anchor with no class at all cannot be one of those,
 * and every component anchor - .btn, .badge, .nav-link, .page-link, .dropdown-item,
 * .list-group-item - is already given its colour explicitly further down this file.
 */
[data-bs-theme="dark"] a:not([class]) {
    color: var(--bs-link-color);
}

[data-bs-theme="dark"] a:not([class]):hover,
[data-bs-theme="dark"] a:not([class]):focus {
    color: var(--bs-link-hover-color);
}

/* These formatter classes otherwise retain custom.css's hard-coded corporate brown. */
[data-bs-theme="dark"] .datasheet-link,
[data-bs-theme="dark"] .element-header {
    color: var(--bs-link-color) !important;
}

[data-bs-theme="dark"] .datasheet-link:hover,
[data-bs-theme="dark"] .datasheet-link:focus {
    color: var(--bs-link-hover-color) !important;
}

/*
 * Logos are NOT transformed, and this rule exists to say so rather than to do anything.
 *
 * An earlier version inverted them - `invert(1) brightness(1.6)` - on the stated assumption that
 * the Applus mark is monochrome, so an inversion would be faithful. It is not monochrome. Both
 * assets are built from the two corporate colours: applus-laboratories-logo.png is the brown
 * #746661 wordmark with the orange #FF6900 mark, and the product marks (for example
 * materials-workspace-navbar-logo.svg) carry #FF6900, #746660 and #FFE1CC. Inverting maps #FF6900
 * to #0096FF, so the topbar rendered the corporate orange as cyan. A brand mark is the one thing
 * on the page that may not be recoloured to suit a theme.
 *
 * Left alone, both read acceptably on the dark ground: the orange measures 6.1:1 against
 * --sl-surface and the brown 3.1:1, which clears the 3:1 WCAG minimum for a graphical object. The
 * brown wordmark is nonetheless dimmer than it is on white, and the proper fix for that is a
 * light-variant asset from design, not a filter invented here.
 *
 * The hook stays for a product that does ship one - set --shell-logo-filter, or override the src -
 * but the default is now no transformation at all.
 */
[data-bs-theme="dark"] body.shell .shell-brand-applus img,
[data-bs-theme="dark"] body.shell img.shell-brand-icon {
    filter: var(--shell-logo-filter, none);
}

/* ── the shell chrome ───────────────────────────────────────────────────
 *
 * shell.css writes these surfaces as literal #fff in light mode. That half is being tokenised in
 * shell.css itself (--sl-surface), so most of the chrome needs nothing here; what remains are the
 * places where the light rule is a tint or a translucent fill that has no dark equivalent.
 * -------------------------------------------------------------------- */

/* The sidebar footer's translucent fill: rgba(248,250,252,.7) in shell.css is a white veil over
   the sidebar, which on a dark sidebar is a bright stripe. Same veil, dark. */
[data-bs-theme="dark"] body.shell .shell-sidebar-footer {
    background: rgba(23, 32, 51, .7);
}

/*
 * The active navigation entry. shell.css paints it `background: var(--brand-50); color:
 * var(--brand-600)`, and both tokens are re-pointed in the token section above, so the entry
 * already follows the theme without a rule here. What it does need is a lighter ink: --brand-600
 * is the full-strength brand orange, which carries on a white sidebar but sits too close to the
 * dark wash behind it to read comfortably.
 */
[data-bs-theme="dark"] body.shell .shell-nav-item.active,
[data-bs-theme="dark"] body.shell .shell-nav-sub.active,
[data-bs-theme="dark"] body.shell .shell-nav-subsub.active {
    color: var(--brand-500, #ff7b1f);
}

/* Backdrops are already black in light mode; over a dark page they need to be heavier to read as
   a veil at all. */
[data-bs-theme="dark"] body.shell .shell-sidebar-backdrop,
[data-bs-theme="dark"] .modal-backdrop,
[data-bs-theme="dark"] .offcanvas-backdrop {
    background: rgba(0, 0, 0, .65);
}

/* ── Bootstrap components ───────────────────────────────────────────────
 *
 * The hand-written half of Bootstrap 5.1's dark mode. Each rule below exists because the matching
 * rule in bootstrap.min.css carries a literal, not a variable, and so cannot be reached by the
 * token sections above. Delete this whole section on the move to 5.3.
 *
 * Specificity: Bootstrap's component rules are a single class, (0,1,0). An attribute selector on
 * an ancestor plus the class is (0,2,0) and wins without !important. Where !important appears
 * below it is because the light rule has it too.
 * -------------------------------------------------------------------- */

[data-bs-theme="dark"] .card,
[data-bs-theme="dark"] .accordion-item,
[data-bs-theme="dark"] .list-group-item {
    background-color: var(--sl-surface);
    border-color: var(--sl-border);
    color: var(--sl-800);
}

[data-bs-theme="dark"] .card-header,
[data-bs-theme="dark"] .card-footer {
    background-color: var(--sl-surface-2);
    border-color: var(--sl-border);
    color: var(--sl-800);
}

[data-bs-theme="dark"] .list-group-item-action:hover,
[data-bs-theme="dark"] .list-group-item-action:focus {
    background-color: var(--sl-surface-2);
    color: var(--sl-900);
}

/* Modals, offcanvas and popovers: the floating surfaces. They are one step lighter than a card,
   because on a dark theme elevation reads as light, not as shadow. */
[data-bs-theme="dark"] .modal-content,
[data-bs-theme="dark"] .offcanvas,
[data-bs-theme="dark"] .popover,
[data-bs-theme="dark"] .toast {
    background-color: var(--sl-surface-2);
    border-color: var(--sl-border);
    color: var(--sl-800);
}

[data-bs-theme="dark"] .modal-header,
[data-bs-theme="dark"] .modal-footer,
[data-bs-theme="dark"] .offcanvas-header,
[data-bs-theme="dark"] .toast-header,
[data-bs-theme="dark"] .popover-header {
    border-color: var(--sl-border);
    background-color: transparent;
    color: var(--sl-900);
}

/* The dropdown menu. --sl-surface-2 rather than --sl-surface for the same elevation reason, and
   the divider has to be re-coloured because Bootstrap's is a literal #e9ecef. */
[data-bs-theme="dark"] .dropdown-menu {
    background-color: var(--sl-surface-2);
    border-color: var(--sl-border);
    color: var(--sl-800);
    box-shadow: var(--sl-shadow-lg);
}

[data-bs-theme="dark"] .dropdown-item {
    color: var(--sl-800);
}

[data-bs-theme="dark"] .dropdown-item:hover,
[data-bs-theme="dark"] .dropdown-item:focus {
    background-color: rgba(255, 255, 255, .07);
    color: var(--sl-900);
}

[data-bs-theme="dark"] .dropdown-item.active,
[data-bs-theme="dark"] .dropdown-item:active {
    background-color: var(--brand-600, #ff6900);
    color: #fff;
}

[data-bs-theme="dark"] .dropdown-item.disabled,
[data-bs-theme="dark"] .dropdown-item:disabled {
    color: var(--sl-400);
}

[data-bs-theme="dark"] .dropdown-divider,
[data-bs-theme="dark"] .dropdown-header {
    border-color: var(--sl-border);
    color: var(--sl-500);
}

/*
 * Tables, and the one place where defining a --bs-* variable on the root does NOT work.
 *
 * The other --bs-* families can be themed from the root because Bootstrap 5.1 either publishes
 * them on :root or does not define them at all. The table family is different: the library's
 * branded Bootstrap build declares all eight of them on `.table` itself -
 *
 *     .table { --bs-table-striped-bg: #f1f5f7; --bs-table-hover-bg: #f0f8ff;
 *              --bs-table-striped-color: #746660; ... }
 *
 * - and a declaration on the element always beats an inherited value, whatever the selector on the
 * ancestor. So they have to be re-declared here, on `.table`, or striping paints an opaque light
 * grey under light text. That was the failure this rule was written for: every odd row in the
 * datasheet came out near-white with #e2e8f0 text on it, while the unstriped table beside it was
 * correct. Note the values are branded, not stock Bootstrap - #f1f5f7 and #f0f8ff are the theme's,
 * which is also why grepping bootstrap's documented defaults does not find them.
 *
 * The accent colours stay translucent white so they compose over whatever surface the table sits
 * on - a card, the page, a modal - instead of pinning one ground.
 */
[data-bs-theme="dark"] .table {
    color: var(--sl-800);
    border-color: var(--sl-border);
    --bs-table-color: var(--sl-800);
    --bs-table-bg: transparent;
    --bs-table-striped-bg: rgba(255, 255, 255, .035);
    --bs-table-striped-color: var(--sl-800);
    --bs-table-hover-bg: rgba(255, 255, 255, .07);
    --bs-table-hover-color: var(--sl-900);
    --bs-table-active-bg: rgba(255, 255, 255, .1);
    --bs-table-active-color: var(--sl-900);
    --bs-table-border-color: var(--sl-border);
}

[data-bs-theme="dark"] .table > :not(caption) > * > * {
    border-bottom-color: var(--sl-border);
}

[data-bs-theme="dark"] .table > thead {
    color: var(--sl-900);
}

[data-bs-theme="dark"] .table-light,
[data-bs-theme="dark"] .table-light > th,
[data-bs-theme="dark"] .table-light > td,
[data-bs-theme="dark"] .table > thead.table-light th {
    --bs-table-bg: var(--sl-surface-2);
    background-color: var(--sl-surface-2);
    color: var(--sl-800);
    border-color: var(--sl-border);
}

[data-bs-theme="dark"] .table-bordered > :not(caption) > * {
    border-color: var(--sl-border);
}

/*
 * The contextual table classes - .table-secondary and its family - and the second instance of the
 * trap the .table rule above documents. Each of them declares, ON THE ELEMENT:
 *
 *     .table-secondary { --bs-table-bg: #e3e0df; color: #000; border-color: #cccac9; ... }
 *
 * so neither the colour nor the border can be reached from an ancestor, and they are not part of
 * the --bs-table-* set re-declared on .table either. The datasheet draws its table headers with
 * .table-secondary, which is why its tables came out with black text on a dark header and light
 * grid lines around it - one root cause behind both "the documents table" and "the normal table".
 *
 * The neutral two become dark surfaces. The semantic five keep their hue as a translucent wash, on
 * the same reasoning as the alerts and the badges: a reader identifies a warning row by colour.
 * .table-dark is left alone - it is already dark and still means what it says.
 */
[data-bs-theme="dark"] .table-secondary,
[data-bs-theme="dark"] .table-light {
    --bs-table-bg: var(--sl-surface-2);
    --bs-table-color: var(--sl-800);
    --bs-table-striped-bg: rgba(255, 255, 255, .035);
    --bs-table-striped-color: var(--sl-800);
    --bs-table-hover-bg: rgba(255, 255, 255, .07);
    --bs-table-hover-color: var(--sl-900);
    --bs-table-active-bg: rgba(255, 255, 255, .1);
    --bs-table-active-color: var(--sl-900);
    color: var(--sl-800);
    border-color: var(--sl-border);
}

[data-bs-theme="dark"] .table-primary,
[data-bs-theme="dark"] .table-success,
[data-bs-theme="dark"] .table-danger,
[data-bs-theme="dark"] .table-warning,
[data-bs-theme="dark"] .table-info {
    --bs-table-color: var(--sl-800);
    --bs-table-striped-color: var(--sl-800);
    --bs-table-hover-color: var(--sl-900);
    --bs-table-active-color: var(--sl-900);
    color: var(--sl-800);
    border-color: var(--sl-border);
}

[data-bs-theme="dark"] .table-primary  { --bs-table-bg: rgba(var(--bs-primary-rgb, 255, 105, 0), .16); }
[data-bs-theme="dark"] .table-success  { --bs-table-bg: rgba(25, 135, 84, .20); }
[data-bs-theme="dark"] .table-danger   { --bs-table-bg: rgba(220, 53, 69, .20); }
[data-bs-theme="dark"] .table-warning  { --bs-table-bg: rgba(255, 193, 7, .18); }
[data-bs-theme="dark"] .table-info     { --bs-table-bg: rgba(13, 202, 240, .18); }

/* Form controls. The disabled and readonly grounds are literals in 5.1, as is the focus shadow. */
[data-bs-theme="dark"] .form-control,
[data-bs-theme="dark"] .form-select,
[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .form-select:focus {
    background-color: var(--sl-surface-2);
    border-color: var(--sl-border-strong);
    color: var(--sl-800);
}

[data-bs-theme="dark"] .form-control::placeholder {
    color: var(--sl-400);
}

[data-bs-theme="dark"] .form-control:disabled,
[data-bs-theme="dark"] .form-control[readonly],
[data-bs-theme="dark"] .form-select:disabled {
    background-color: var(--sl-100);
    color: var(--sl-500);
}

/* The select arrow and the date picker indicator are dark SVGs baked into the stylesheet as data
   URIs. There is no variable for them, so they are inverted. */
[data-bs-theme="dark"] .form-select {
    filter: none;
    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 5l6 6 6-6'/%3e%3c/svg%3e");
}

[data-bs-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator,
[data-bs-theme="dark"] input[type="time"]::-webkit-calendar-picker-indicator,
[data-bs-theme="dark"] input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    filter: invert(1) brightness(.85);
}

[data-bs-theme="dark"] .input-group-text {
    background-color: var(--sl-100);
    border-color: var(--sl-border-strong);
    color: var(--sl-600);
}

[data-bs-theme="dark"] .form-check-input {
    background-color: var(--sl-surface-2);
    border-color: var(--sl-border-strong);
}

[data-bs-theme="dark"] .form-check-input:checked {
    background-color: var(--brand-600, #ff6900);
    border-color: var(--brand-600, #ff6900);
}

[data-bs-theme="dark"] .form-label,
[data-bs-theme="dark"] .col-form-label {
    color: var(--sl-700);
}

[data-bs-theme="dark"] .form-text {
    color: var(--sl-500);
}

/*
 * btn-close is a black SVG data URI. 5.1 has no .btn-close-white variable, only a separate class,
 * so the button is inverted wherever it sits on one of our dark surfaces. This is the one place
 * an invert filter is applied to a control rather than to an image.
 */
[data-bs-theme="dark"] .btn-close {
    filter: invert(1) grayscale(100%) brightness(200%);
}

/* The outline and light buttons: their idle state is a literal in 5.1. The solid brand buttons
   need nothing - an orange ground with white text is correct in both themes. */
[data-bs-theme="dark"] .btn-light,
[data-bs-theme="dark"] .btn-outline-secondary {
    background-color: var(--sl-surface-2);
    border-color: var(--sl-border-strong);
    color: var(--sl-800);
}

[data-bs-theme="dark"] .btn-light:hover,
[data-bs-theme="dark"] .btn-outline-secondary:hover {
    background-color: var(--sl-300);
    border-color: var(--sl-border-strong);
    color: var(--sl-900);
}

[data-bs-theme="dark"] .btn-outline-dark {
    color: var(--sl-800);
    border-color: var(--sl-border-strong);
}

[data-bs-theme="dark"] .btn-outline-dark:hover {
    background-color: var(--sl-300);
    color: var(--sl-900);
}

/*
 * The shell's own outline buttons (shell.css, "components") read --shell-outline, which each
 * variant sets from a Bootstrap semantic colour. Those colours are chosen to carry as ink on
 * white: #198754 and #dc3545 are dark greens and reds, and on a near-black card they are the
 * lowest-contrast thing on the page - "Outline success" was barely legible in the first render.
 *
 * Lightened here, per variant, rather than by redefining --bs-success and --bs-danger globally:
 * those two also paint the solid badges and alerts, where the colour is a *ground* carrying white
 * text and lightening it would make that pair worse. Ink and ground pull in opposite directions,
 * so they are moved separately.
 */
[data-bs-theme="dark"] :where(body.shell) .btn-outline-secondary {
    --shell-outline: var(--sl-600);
    --shell-outline-rgb: 182, 194, 210;
    --shell-outline-pressed: var(--sl-500);
}

[data-bs-theme="dark"] :where(body.shell) .btn-outline-danger {
    --shell-outline: #ff8d98;
    --shell-outline-rgb: 255, 141, 152;
    --shell-outline-pressed: #ff6b79;
}

[data-bs-theme="dark"] :where(body.shell) .btn-outline-success {
    --shell-outline: #6ee7a8;
    --shell-outline-rgb: 110, 231, 168;
    --shell-outline-pressed: #45d98d;
}

/* The hover and pressed states of those buttons fill with --shell-outline and put `color: #fff`
   on top, which was right when the fill was a dark green or red and is not now that it is a pale
   one. The label takes the ground colour instead. */
[data-bs-theme="dark"] :where(body.shell) .btn:is(.btn-outline-secondary, .btn-outline-danger, .btn-outline-success):hover,
[data-bs-theme="dark"] :where(body.shell) .btn:is(.btn-outline-secondary, .btn-outline-danger, .btn-outline-success):is(:active, .active, .show),
[data-bs-theme="dark"] :where(body.shell) .btn-check:checked + .btn:is(.btn-outline-secondary, .btn-outline-danger, .btn-outline-success) {
    color: var(--sl-50);
}

/* Tabs and pills. */
[data-bs-theme="dark"] .nav-tabs {
    border-bottom-color: var(--sl-border);
}

[data-bs-theme="dark"] .nav-tabs .nav-link {
    color: var(--sl-600);
}

[data-bs-theme="dark"] .nav-tabs .nav-link:hover {
    border-color: var(--sl-300) var(--sl-300) var(--sl-border);
    color: var(--sl-900);
}

[data-bs-theme="dark"] .nav-tabs .nav-link.active {
    background-color: var(--sl-surface);
    border-color: var(--sl-border) var(--sl-border) var(--sl-surface);
    color: var(--sl-900);
}

[data-bs-theme="dark"] .nav-link {
    color: var(--sl-700);
}

/* Pagination, breadcrumbs, progress. */
[data-bs-theme="dark"] .page-link {
    background-color: var(--sl-surface-2);
    border-color: var(--sl-border);
    color: var(--sl-700);
}

[data-bs-theme="dark"] .page-link:hover {
    background-color: var(--sl-300);
    border-color: var(--sl-border-strong);
    color: var(--sl-900);
}

[data-bs-theme="dark"] .page-item.disabled .page-link {
    background-color: var(--sl-100);
    border-color: var(--sl-border);
    color: var(--sl-400);
}

[data-bs-theme="dark"] .breadcrumb-item,
[data-bs-theme="dark"] .breadcrumb-item.active {
    color: var(--sl-500);
}

[data-bs-theme="dark"] .progress {
    background-color: var(--sl-200);
}

/* Tooltips keep a near-black ground in light mode; on a dark theme that vanishes into the page, so
   the inner is lifted to a surface and given a border. */
[data-bs-theme="dark"] .tooltip-inner {
    background-color: var(--sl-300);
    color: var(--sl-900);
}

[data-bs-theme="dark"] .tooltip .tooltip-arrow::before {
    border-top-color: var(--sl-300);
    border-bottom-color: var(--sl-300);
}

/*
 * Contextual alerts and badges. 5.1 computes each variant from a literal pair (a very pale ground
 * and a dark ink). On a dark theme the pair has to swap roles: a translucent tint of the variant
 * colour as the ground, and the variant colour itself, lightened, as the ink.
 */
[data-bs-theme="dark"] .alert {
    border-color: transparent;
}

[data-bs-theme="dark"] .alert-primary {
    background-color: rgba(var(--bs-primary-rgb, 255, 105, 0), .14);
    color: #ffa063;
}

[data-bs-theme="dark"] .alert-secondary {
    background-color: rgba(148, 163, 184, .14);
    color: var(--sl-700);
}

[data-bs-theme="dark"] .alert-success {
    background-color: rgba(25, 135, 84, .18);
    color: #6ee7a8;
}

[data-bs-theme="dark"] .alert-danger {
    background-color: rgba(220, 53, 69, .18);
    color: #ff8d98;
}

[data-bs-theme="dark"] .alert-warning {
    background-color: rgba(255, 193, 7, .16);
    color: #ffd666;
}

[data-bs-theme="dark"] .alert-info {
    background-color: rgba(13, 202, 240, .16);
    color: #7fdcf5;
}

[data-bs-theme="dark"] .alert-light {
    background-color: var(--sl-surface-2);
    color: var(--sl-800);
}

[data-bs-theme="dark"] .alert-dark {
    background-color: var(--sl-200);
    color: var(--sl-800);
}

/*
 * Utility classes. These are the ones that appear in our markup as a literal instruction to be
 * white or near-white, which is exactly what a dark theme has to reinterpret. bg-white and bg-light
 * carry !important in Bootstrap, so the overrides do too.
 */
[data-bs-theme="dark"] .bg-white {
    background-color: var(--sl-surface) !important;
}

[data-bs-theme="dark"] .bg-light {
    background-color: var(--sl-surface-2) !important;
}

/* .bg-dark is left alone: it already says "a dark block", and it is still one here. */

[data-bs-theme="dark"] .bg-body {
    background-color: var(--sl-ground) !important;
}

/*
 * .text-dark, and the one utility in Bootstrap that genuinely means two different things.
 *
 * Most of its uses are emphasis on the page: `.fw-bold.text-dark` for a company name, a headline
 * figure. Those must follow the theme, or they are dark ink on a dark page - measured at 1.14:1 on
 * the company chooser and the statistics tiles.
 *
 * The rest are an author forcing readable ink onto a *light semantic ground* - the amber and cyan
 * badges, where the ground carries meaning and stays light in both themes. Those must NOT follow,
 * or the badge ends up near-white on amber, which is worse than it was before the theme existed
 * (1.4:1 on the users page).
 *
 * So: flipped by default, and put back for the two grounds that stay light. Bootstrap's other
 * grounds - primary, success, danger, secondary, dark - are dark enough to carry white text and
 * are not paired with .text-dark. .bg-white, .bg-light and .bg-body are reinterpreted as dark
 * surfaces further up, so the default flip is already right for them.
 */
[data-bs-theme="dark"] .text-dark {
    color: var(--sl-900) !important;
}

[data-bs-theme="dark"] .bg-warning.text-dark,
[data-bs-theme="dark"] .bg-warning .text-dark,
[data-bs-theme="dark"] .bg-info.text-dark,
[data-bs-theme="dark"] .bg-info .text-dark {
    color: #212529 !important;
}

/*
 * .text-light means "light ink", and it still does. --bs-light moved to a dark surface above so
 * that `background: var(--bs-light)` stops painting white slabs, which would otherwise drag this
 * utility down with it and make light ink dark - the mirror of the .text-dark problem.
 */
[data-bs-theme="dark"] .text-light {
    color: var(--sl-900) !important;
}

[data-bs-theme="dark"] .text-muted {
    color: var(--sl-500) !important;
}

[data-bs-theme="dark"] .text-black-50 {
    color: rgba(255, 255, 255, .55) !important;
}

[data-bs-theme="dark"] .text-white-50 {
    color: rgba(255, 255, 255, .6) !important;
}

[data-bs-theme="dark"] .border,
[data-bs-theme="dark"] .border-top,
[data-bs-theme="dark"] .border-end,
[data-bs-theme="dark"] .border-bottom,
[data-bs-theme="dark"] .border-start {
    border-color: var(--sl-border) !important;
}

[data-bs-theme="dark"] .border-light {
    border-color: var(--sl-300) !important;
}

[data-bs-theme="dark"] .shadow-sm {
    box-shadow: var(--sl-shadow-sm) !important;
}

[data-bs-theme="dark"] .shadow {
    box-shadow: var(--sl-shadow) !important;
}

[data-bs-theme="dark"] .shadow-lg {
    box-shadow: var(--sl-shadow-lg) !important;
}

/* ── vue-select ─────────────────────────────────────────────────────────
 *
 * The one vendor widget in the bundle that publishes a complete token set of its own and then
 * pins the light values into it. vue-select.min.css declares --vs-dropdown-bg: #fff,
 * --vs-dropdown-color: inherit, --vs-selected-color: #333 and a #5897fb highlight on :root, and
 * nothing in this repository or in any product had ever overridden one of them.
 *
 * What that produced on a dark page is worth recording, because it is not the failure the name
 * "dark mode bug" suggests. The menu stayed WHITE - --vs-dropdown-bg is a literal - while the
 * option ink resolved through `inherit` to the dark theme's light body colour, so the list came
 * out #dbe0e3 on #ffffff: 1.3:1, invisible. The only readable row was the highlighted one,
 * because its pair (#fff on #5897fb) is self-contained and never looks at the page. Measured on
 * the Baum dropdown of /tree-manager and the exporter dropdown of the "Werkstoffe exportieren"
 * modal, which are the two places the widget is used with a long option list.
 *
 * So this is a token block first: every --vs-* that carries a colour is re-pointed at the shell
 * scale, exactly as the Bootstrap variables are in the section near the top of this file, and the
 * widget then themes itself. `:root` in the vendor sheet is (0,1,0) and so is the bare attribute
 * here, which would leave the outcome to source order - theme-dark.css is linked after the bundle,
 * so it would win - but `[data-bs-theme="dark"] body.shell` is (0,2,1) and settles it outright.
 * That is the same two-selector list every token block in this file uses, and for the same reason.
 *
 * The three rules after the block are not tokens and cannot be: they exist because other
 * stylesheets write literals into these elements, and a literal beats a variable the vendor never
 * reads. An attribute on an ancestor plus the class is (0,2,0) and beats all three whatever the
 * link order happens to be:
 *
 *   - search.css sets .vs__dropdown-option--highlight and .vs__selected to --bs-soft-primary
 *     (#FFB47F, a pale orange) with --bs-body-color as the ink - a light ground carrying light
 *     text once this file redefines --bs-body-color. That file is a page stylesheet, so it is
 *     linked AFTER this one and order would not have saved it. It reaches the exporter modal,
 *     which is rendered on the v2 search pages.
 *   - searchui-datasheet's styles.css has `.v-select { background-color: white }`, a literal white
 *     block behind the whole control. It is fixed from here rather than there so the dark theme
 *     stays in one file and no datasheet release is needed; the light rule is untouched.
 *
 * Colours are derived, never literal. The highlight is --brand-200 - the application's own primary
 * mixed towards the ground, which is what the token section above defines - so a product whose
 * brand is the corporate brown gets a brown highlight and not somebody else's orange.
 * -------------------------------------------------------------------- */
[data-bs-theme="dark"],
[data-bs-theme="dark"] body.shell {
    /* The menu. --sl-surface-2 and a heavy shadow, the same pairing .dropdown-menu gets above:
       on a dark theme a floating surface reads as elevated because it is lighter, not because it
       casts a shadow. */
    --vs-dropdown-bg: var(--sl-surface-2);
    --vs-dropdown-color: var(--sl-800);
    --vs-dropdown-option-color: var(--sl-800);
    --vs-dropdown-box-shadow: var(--sl-shadow-lg);

    /* The highlighted (keyboard-active) row. #fff on the vendor's #5897fb measures 2.9:1 and
       Bootstrap's own dark .dropdown-item.active pairing - white on the full-strength brand - is
       2.9:1 too, so neither was worth copying. The brand mixed towards the ground carries the dark
       theme's brightest ink instead: 8.2:1 on the orange build, and it stays the brand's hue. */
    --vs-dropdown-option--active-bg: var(--brand-200, #6e4015);
    --vs-dropdown-option--active-color: var(--sl-900);

    /* The control. --vs-border-color is what the toggle and the menu both read. */
    --vs-border-color: var(--sl-border-strong);
    --vs-controls-color: var(--sl-500);
    /* The deselect cross is lifted off its ground by a white text-shadow, which on a dark chip is
       a halo. */
    --vs-controls--deselect-text-shadow: none;

    /* The selected value. In a single select the chip's ground is transparent and only the ink
       shows - the vendor's #333 on a near-black card, faded to 40% while the menu is open, which
       is the "placeholder is barely there" half of the report. */
    --vs-selected-bg: var(--sl-300);
    --vs-selected-color: var(--sl-900);
    --vs-selected-border-color: var(--sl-border-strong);

    /* The search input. Its colour is `inherit` in the vendor sheet and so was already right; the
       placeholder is not, and it gets --sl-500 rather than the --sl-400 that .form-control's
       placeholder uses. It sits on the same line as the 40%-faded selection, where the dimmer grey
       reads as a second faded thing; --sl-500 measures 5.5:1 on a card and is unambiguously live
       text. */
    --vs-search-input-color: var(--sl-800);
    --vs-search-input-placeholder-color: var(--sl-500);

    /* Disabled options and a disabled control: the vendor's rgb(248,248,248) is a white block. */
    --vs-state-disabled-bg: var(--sl-100);
    --vs-state-disabled-color: var(--sl-500);
    --vs-state-disabled-controls-color: var(--sl-400);
}

/* The three rules the token block cannot reach. See the header above for which stylesheet writes
   the literal each one is answering. */
[data-bs-theme="dark"] .vs__dropdown-option--highlight {
    background: var(--brand-200, #6e4015);
    color: var(--sl-900);
}

[data-bs-theme="dark"] .vs__selected {
    background-color: var(--sl-300);
    color: var(--sl-900);
}

/* A single select draws no chip - the vendor makes the ground transparent and the value simply
   sits in the control. Restated here because the rule above outranks nothing but has to be
   outranked in turn, exactly as vue-select's own .vs--single rule does it. */
[data-bs-theme="dark"] .vs--single .vs__selected {
    background-color: transparent;
    border-color: transparent;
}

[data-bs-theme="dark"] .v-select {
    background-color: transparent;
}

/* ── the theme switch itself ────────────────────────────────────────────
 *
 * The control lives in the user menu (layout/user-menu.html) and is styled here rather than in
 * user-menu.css because both halves of it - the sun and the moon - only make sense as a pair with
 * the theme they name. The light rules are here too, not under the dark attribute, for the same
 * reason: one place to read.
 * -------------------------------------------------------------------- */
.sui-usermenu-theme-icon {
    position: relative;
    display: inline-flex;
    width: 1em;
    justify-content: center;
}

/* Only one of the two icons is ever shown, and which one is decided by the theme rather than by a
   class the script has to keep in sync. The script sets the attribute; the icons follow. */
.sui-usermenu-theme .sui-usermenu-theme-dark-icon,
[data-bs-theme="dark"] .sui-usermenu-theme .sui-usermenu-theme-light-icon {
    display: inline-flex;
}

.sui-usermenu-theme .sui-usermenu-theme-light-icon,
[data-bs-theme="dark"] .sui-usermenu-theme .sui-usermenu-theme-dark-icon {
    display: none;
}

/* Likewise the label: "Dark theme" while light is on, "Light theme" while dark is on. */
.sui-usermenu-theme .sui-usermenu-theme-to-light,
[data-bs-theme="dark"] .sui-usermenu-theme .sui-usermenu-theme-to-dark {
    display: none;
}

.sui-usermenu-theme .sui-usermenu-theme-to-dark,
[data-bs-theme="dark"] .sui-usermenu-theme .sui-usermenu-theme-to-light {
    display: inline;
}

/* ── print ──────────────────────────────────────────────────────────────
 *
 * A dark page must not be printed dark: it would empty a toner cartridge and read badly. The
 * attribute is on <html> and cannot be removed for the print job, so the theme is switched off
 * here instead. `revert` is not enough - the custom properties have to go back to the light
 * values, which is what unsetting them on the same element achieves, because the light values are
 * declared on body.shell and :root in shell.css.
 * -------------------------------------------------------------------- */
@media print {
    [data-bs-theme="dark"],
    [data-bs-theme="dark"] body.shell {
        --sl-ground: #fff;
        --sl-surface: #fff;
        --sl-surface-2: #fff;
        --sl-border: #e2e8f0;
        --sl-border-strong: #cbd5e1;
        --sl-50: #f8fafc;
        --sl-100: #f1f5f9;
        --sl-200: #e2e8f0;
        --sl-300: #cbd5e1;
        --sl-400: #94a3b8;
        --sl-500: #64748b;
        --sl-600: #475569;
        --sl-700: #334155;
        --sl-800: #1e293b;
        --sl-900: #0f172a;
        --bs-body-bg: #fff;
        --bs-body-color: #1e293b;
        --bs-border-color: #dee2e6;
        color-scheme: light;
    }

    [data-bs-theme="dark"] body,
    [data-bs-theme="dark"] body.shell {
        background: #fff;
        color: #1e293b;
    }

    /* The two things this file does invert: the close button's black SVG and the select arrow.
       The logos are not listed - they carry no filter to undo. */
    [data-bs-theme="dark"] .btn-close,
    [data-bs-theme="dark"] .form-select {
        filter: none;
    }
}
