/* ============================================================================
   DARK — the app's dark adaptation, in one layer.
   ============================================================================
   Linked LAST in every layout's `stylesheet_link_tag` — after application,
   default/mobile and Halo — so it refines the whole cascade rather than
   fighting a piece of it. Everything here is scoped to `html.dark`, which
   ThemeHelper stamps server-side and layouts/_theme_boot re-resolves before
   first paint (the mode is three-way — auto | light | dark — and "auto"
   follows the OS). No rule in any sheet reads `prefers-color-scheme`
   directly: the class on <html> is the single switch, so CSS never has an
   opinion of its own about the OS.

   Two parts of the app are NOT covered by this file and do not need to be,
   because each declares its own ramp and flips it in its own dark block:

     • The signed-in top bar — shared/_in_app_nav_styles, `--mv-*`. That sheet
       is inlined in <head> and this one is a linked asset.
     • The messaging + notifications UI — notification.css, `--msg-*`. That
       sheet travels inside the `default` / `mobile` bundles rather than being
       linked alongside this one, and it is ~5,600 lines of semantic component
       classes (`.messages-sidebar`, `.message-row`) rather than utilities, so
       nothing in the utility map below could reach it anyway.

   Three sheets, one switch — `html.dark`.

   ── Why this is a layer and not 842 edits ──────────────────────────────────

   Move's views are written in raw Tailwind utilities: 1,243 `bg-white`s
   across 304 files, and essentially no `dark:` variants anywhere. Converting
   them one file at a time is a quarter of work and an unreviewable diff, and
   it would leave the app half-dark for every week of it. So the utilities are
   remapped centrally instead — the same technique
   dashboard/calendar_app/_dark_mode_styles already uses for the calendar, now
   applied app-wide and built to stay out of the way:

     • Every utility selector is wrapped in `:where()`, which contributes ZERO
       specificity. A rule here therefore weighs `html.dark` — (0,1,1) — which
       beats a bare utility (0,1,0) and LOSES to a hand-written `dark:`
       variant, since Tailwind compiles those to `.dark .dark\:bg-x` (0,2,0).
       So a view that opts into dark properly always wins, and this layer
       quietly steps aside for it. Convert views over time; nothing here has
       to be removed first.

     • `!important` appears nowhere. It is what makes the calendar's version
       impossible to override, and the `:where()` trick above is what makes it
       unnecessary.

   ── The one judgement call ────────────────────────────────────────────────

   Only the LIGHT end of the neutral ramp is remapped as a surface: white
   through 400. The dark end (500–950, black) is left exactly as authored.

   The reason is intent. A `bg-gray-50` panel is page furniture — a well, a
   card, a hairline — and furniture has to flip or the screen is unreadable.
   A `bg-gray-900` element is a filled, high-contrast thing: a primary button,
   a dark banner. Flipping it to white would be a guess about what the author
   meant, and would strand the `text-white` sitting on it. Left alone it is a
   dark chip on a dark page: lower contrast than ideal, never broken.

   Text is the exception, and it inverts the rule: the DARK end of the text
   ramp is what must move, because dark ink on a dark page is invisible. So
   text-black through text-gray-500 flip to light inks, and text-gray-400 and
   lighter are left alone — those are already light-on-dark by intent.

   The five neutral families (gray · slate · zinc · neutral · stone) collapse
   onto one ramp here. They differ only in temperature, a distinction that
   does not survive a 5% white wash on a near-black page, and keeping five
   ramps would be five times the file for a difference nobody can see.

   ── Opting out ────────────────────────────────────────────────────────────

   Some surfaces must stay light in a dark app: a QR code has to scan, a
   ticket has to print, an email preview has to look like the email. Put
   `.keep-light` on the wrapper. Every rule below resolves through a custom
   property, so redeclaring those properties inside the wrapper restores the
   light values for the whole subtree at once — see the last section.
   ============================================================================ */

html.dark {
  /* Surfaces, lightest-to-darkest as the ramp climbs. The values are Move's
     own dark pairs from mobile/shared/_app_styles, extended past the three
     steps that partial needed. */
  --d-canvas:    #191919;
  --d-surface:   #1c1c1e;
  --d-raise:     #232326;
  --d-raise-2:   #2a2a2e;
  --d-raise-3:   #33333a;
  --d-raise-4:   #3d3d45;
  --d-raise-5:   #4a4a53;

  --d-ink:       rgba(255, 255, 255, 0.86);
  --d-ink-2:     rgba(255, 255, 255, 0.72);
  --d-muted:     #9b9a97;
  --d-faint:     #75746f;
  /* Full-strength ink, used only where a hover deliberately pushes PAST the
     resting ink (`hover:text-black`, `hover:text-[#37352F]`). It has to be a
     token rather than a literal `#fff` so that `.keep-light` (section 10) can
     take it back — a subtree that opted out still has to darken on hover. */
  --d-ink-hi:    #ffffff;

  --d-hairline:  rgba(255, 255, 255, 0.08);
  --d-line:      rgba(255, 255, 255, 0.14);
  --d-line-2:    rgba(255, 255, 255, 0.20);
  /* The edge that means SELECTED rather than merely present. In light the
     app draws it as full ink — a 12:1 step off the page — so a 20% white
     hairline is not the same message. */
  --d-line-3:    rgba(255, 255, 255, 0.38);

  /* The filled, high-contrast control: the ink-black primary button. See
     section 8 for why this one flips when `bg-gray-900` does not. */
  --d-fill:       #35353c;
  --d-fill-hover: #41414a;

  /* Accents, lifted only as far as legibility on near-black requires. Hue is
     preserved — these carry meaning (link, money, danger) and a hue shift
     would be a different message. */
  --d-blue:       #5ba8f5;
  --d-green:      #45bda9;
  --d-red:        #f07a7a;
  --d-gold:       #d6b72f;
  --d-purple:     #a98ae8;
  --d-orange:     #e89b52;

  /* Tint washes. In light these are a 4% dip of the hue into white; the dark
     analogue is a 14% lift of the same hue off the canvas. */
  --d-wash-blue:  rgba(91, 168, 245, 0.14);
  --d-wash-green: rgba(69, 189, 169, 0.14);
  --d-wash-red:   rgba(240, 122, 122, 0.14);
  --d-wash-gold:  rgba(214, 183, 47, 0.14);

  /* Elevation on a dark page is carried by the shadow being BLACKER than the
     surface, not softer — the navy tint that works on paper reads as haze
     here. */
  --d-sh-sm:     0 1px 2px rgba(0, 0, 0, 0.40);
  --d-sh:        0 2px 8px rgba(0, 0, 0, 0.45);
  --d-sh-md:     0 4px 14px rgba(0, 0, 0, 0.50);
  --d-sh-lg:     0 10px 28px rgba(0, 0, 0, 0.55);
  --d-sh-xl:     0 18px 44px rgba(0, 0, 0, 0.62);

  /* Tells the engine to paint scrollbars, date/colour pickers, spinners and
     autofill in their dark forms. One line, and it fixes a dozen native
     controls no stylesheet can reach. */
  color-scheme: dark;
  background-color: var(--d-canvas);
}

html.dark body { background-color: var(--d-canvas); color: var(--d-ink); }
html.dark ::selection { background: rgba(184, 154, 35, 0.35); }

/* The full-page spinner veil the layout paints during a Turbo visit is a
   white flash on every navigation otherwise. */
html.dark[data-turbo-busy] body::after { background: rgba(0, 0, 0, 0.6); }

/* ════════════════════════════════════════════════════════════════════════
   1 · TOKEN LAYERS — the parts of the app that are already tokenised
   ════════════════════════════════════════════════════════════════════════
   Three design systems in this codebase declare their whole palette as
   custom properties. For those, dark mode is a list of variables and
   nothing else — no component rule is touched, and any component added to
   them later is dark for free. This is what the rest of the app should
   look like eventually; the utility map below is the interim.

   The messaging system (notification.css) was converted to this shape too —
   it just keeps its own flip, for the linking reason in the header.
   ──────────────────────────────────────────────────────────────────────── */

/* — Halo (halo_desktop.css / halo_mobile.css), on :root ——————————————— */
html.dark {
  --halo-ink:           rgba(255, 255, 255, 0.88);
  --halo-text:          rgba(255, 255, 255, 0.86);
  --halo-text-subtle:   #9b9a97;
  --halo-text-faint:    #75746f;
  /* Gold survives the flip; it is the brand, and it reads on both. The soft
     tint does not — a pale cream on near-black is a glare, so it becomes a
     wash of the gold instead. */
  --halo-gold-soft:     rgba(214, 183, 47, 0.22);
  --halo-surface:       #1c1c1e;
  --halo-canvas:        #191919;
  --halo-hairline:      rgba(255, 255, 255, 0.08);
  --halo-hairline-soft: rgba(255, 255, 255, 0.04);
  --halo-hover:         rgba(255, 255, 255, 0.06);
  --halo-active:        rgba(255, 255, 255, 0.11);
  /* The icon tile is a RAISE, not a surface: on light it lightens to near
     white, so on dark it lightens too — just enough to sit above the pill's
     own 0.06 hover fill. Flipping it to a dark value would sink the tile
     into the pill and lose the lift the hover exists to give. */
  --halo-ic-raise:      rgba(255, 255, 255, 0.14);
  --halo-shadow-xs:     var(--d-sh-sm);
  --halo-shadow-sm:     var(--d-sh);
  --halo-shadow:        var(--d-sh-md);
  --halo-shadow-lg:     var(--d-sh-lg);
}

/* Halo's own literals. Several rules in the two Halo sheets reach past the
   token scale for a hover fill or a filled-button ink — `#fff`, `#262626`,
   `#ecdfae`, `#4a3f10`. They are the hover states of the dashboard's most
   used controls, so a stale white one is the first thing anybody notices.
   (`.halo-wave-btn` was listed here too; it is declared in
   shared/_dash_sidebar_menu and is now flipped there, so there is one owner
   per control instead of two rules racing on source order.) */
html.dark .halo-action-btn:hover,
html.dark .halo-rail-toggle:hover { background: rgba(255, 255, 255, 0.10); }
html.dark .halo-action-btn--primary { background: rgba(255, 255, 255, 0.92); color: #1b1b1e; }
html.dark .halo-action-btn--primary:hover { background: #ffffff; box-shadow: var(--d-sh-md); }
html.dark .halo-action-btn--primary svg,
html.dark .halo-action-btn--primary i,
html.dark .halo-action-btn--primary:hover svg,
html.dark .halo-action-btn--primary:hover i { color: rgba(0, 0, 0, 0.78); }
/* Gold on a cream chip becomes gold on a gold wash — same message, and the
   `#4a3f10` ink it used would be unreadable on it. */
html.dark .halo-action-btn--gold { color: #e6cf72; }
html.dark .halo-action-btn--gold:hover { background: rgba(214, 183, 47, 0.32); }

/* The dashboard sidebar's hover ladder.
   `.halo-action-btn:hover` above is (0,3,1); every variant hover in the Halo
   sheets is (0,2,0), so on its own that flat fill wins over all of them and
   the sidebar's three deliberately different weights — ghost 0.05, soft 0.08,
   filled a lift to #fff — collapse into one identical grey. These restate the
   variants at matching specificity, declared after the base exactly as the
   light sheet orders them. Ghost simply hands the weight back to the token. */
html.dark .halo-action-btn--ghost:hover { background: var(--halo-hover); }
/* Soft rests on an INK wash in both Halo sheets (0.045 desktop, 0.05 mobile)
   — dark-on-dark, so the two quick-action pills had no shape at all at rest
   and then jumped straight to the flat fill above. Restoring the rest state
   is what makes their hover read as a step rather than an appearance. */
html.dark .halo-action-btn--soft { background: rgba(255, 255, 255, 0.05); }
html.dark .halo-action-btn--soft:hover { background: rgba(255, 255, 255, 0.09); }

/* — The calendar app ————————————————————————————————————————————————————
   dashboard/calendar_app/_dark_mode_styles already defines every dark value
   this needs, behind a `.dark-mode` class that only the calendar's own
   toggle sets. Pointing the account-level theme at the same ramp means the
   calendar stops being the one screen that ignores the setting. Values are
   restated rather than referenced because that partial is per-calendar ERB
   (it interpolates the calendar's colour) and this file cannot be. */
html.dark .calendar-app {
  --cal-bg:            #1c1c1e;
  --cal-bg-secondary:  #232326;
  --cal-bg-tertiary:   #2a2a2e;
  --cal-bg-hover:      #33333a;
  --cal-bg-active:     rgba(35, 131, 226, 0.18);
  --cal-border:        rgba(255, 255, 255, 0.12);
  --cal-border-light:  rgba(255, 255, 255, 0.07);
  --cal-text:          rgba(255, 255, 255, 0.86);
  --cal-text-secondary:#b7b6b2;
  --cal-text-muted:    #86857f;
  --cal-text-inverse:  #1b1b1e;
  --cal-shadow:        rgba(0, 0, 0, 0.45);
  --cal-shadow-lg:     rgba(0, 0, 0, 0.6);
  --cal-overlay:       rgba(0, 0, 0, 0.7);
  --cal-weekend-bg:    #202024;
  --cal-past-bg:       #1a1a1d;
}

/* — daisyUI ————————————————————————————————————————————————————————————
   daisyUI 2 already ships a dark theme and the layouts already set
   `data-theme="dark"`, so every .btn, .modal-box, .dropdown-content, .card
   and .menu in the app flips on its own — that part needed no work. What it
   flips TO is daisyUI's stock palette, a blue-grey #1d232a that sits a
   noticeable step off Move's #1c1c1e. Two surfaces of slightly different
   temperature next to each other is worse than either alone, so the three
   base steps and the ink are pointed at the same ramp everything else here
   uses.

   daisyUI 2 stores these as bare HSL components (H S% L%), which is why they
   look like that. The semantic colours — primary, accent, warning — are left
   alone: those are the brand's, not the surface's. */
html.dark {
  --b1:  240 3.5%  11.4%;   /* base-100 — cards, modals, menus            */
  --b2:  240 4.1%  14.3%;   /* base-200 — the step above a card           */
  --b3:  240 4.5%  17.3%;   /* base-300 — hovers, wells, track fills      */
  --bc:  0   0%    87%;     /* base-content — the ink that sits on them   */
}

/* — The Move design system (mobile/shared/_app_styles) ————————————————
   That partial already flips `.app-page` under `.dark`. What it does not
   cover is the page BEHIND the component kit — `.app-page` sets
   `--app-bg: transparent` by design, so the canvas is whatever the layout
   painted, and the layout painted #f2f2f2. */
html.dark .app-page,
html.dark .app-page--paper { background-color: var(--d-canvas); }

/* The FORMS family is the part of that kit written in literals rather than
   tokens — `.app-form-input`, the collapsible card, the radio-as-card all
   name #ffffff and #37352F directly, so the partial's own `.dark .app-page`
   block cannot reach them. They are also the controls a person spends the
   longest looking at, which is the worst place to leave a white box. */
html.dark :where(.app-form-input, .app-form-select, .app-form-textarea,
                 .app-form-details, .app-form-radio-card) {
  background: var(--d-raise);
  border-color: var(--d-line);
  color: var(--d-ink);
}
html.dark :where(.app-form-label) { color: var(--d-ink-2); }
html.dark :where(.app-form-label .app-form-label__note, .app-form-hint) { color: var(--d-muted); }
html.dark :where(.app-form-radio-card):hover { background: var(--d-raise-2); }
html.dark :where(.app-form-dropzone) { border-color: var(--d-line-2); background: var(--d-raise); }

/* ════════════════════════════════════════════════════════════════════════
   2 · SURFACES — the light end of the neutral ramp
   ════════════════════════════════════════════════════════════════════════ */

html.dark :where(.bg-white) { background-color: var(--d-surface); }

/* Translucent whites are glass: headers over a cover image, scrims, sheets.
   They keep their alpha and change their colour, so whatever is behind them
   still shows through by the same amount. */
html.dark :where(.bg-white\/95) { background-color: rgba(28, 28, 30, 0.95); }
html.dark :where(.bg-white\/90) { background-color: rgba(28, 28, 30, 0.90); }
html.dark :where(.bg-white\/80) { background-color: rgba(28, 28, 30, 0.80); }
html.dark :where(.bg-white\/75) { background-color: rgba(28, 28, 30, 0.75); }
html.dark :where(.bg-white\/70) { background-color: rgba(28, 28, 30, 0.70); }
html.dark :where(.bg-white\/60) { background-color: rgba(28, 28, 30, 0.60); }
html.dark :where(.bg-white\/50) { background-color: rgba(28, 28, 30, 0.50); }
html.dark :where(.bg-white\/40) { background-color: rgba(255, 255, 255, 0.12); }
html.dark :where(.bg-white\/30) { background-color: rgba(255, 255, 255, 0.10); }
html.dark :where(.bg-white\/20) { background-color: rgba(255, 255, 255, 0.08); }
html.dark :where(.bg-white\/10) { background-color: rgba(255, 255, 255, 0.06); }
html.dark :where(.bg-white\/5)  { background-color: rgba(255, 255, 255, 0.04); }

html.dark :where(.bg-gray-50,  .bg-slate-50,  .bg-zinc-50,  .bg-neutral-50,  .bg-stone-50)  { background-color: var(--d-raise); }
html.dark :where(.bg-gray-100, .bg-slate-100, .bg-zinc-100, .bg-neutral-100, .bg-stone-100) { background-color: var(--d-raise-2); }
html.dark :where(.bg-gray-200, .bg-slate-200, .bg-zinc-200, .bg-neutral-200, .bg-stone-200) { background-color: var(--d-raise-3); }
html.dark :where(.bg-gray-300, .bg-slate-300, .bg-zinc-300, .bg-neutral-300, .bg-stone-300) { background-color: var(--d-raise-4); }
html.dark :where(.bg-gray-400, .bg-slate-400, .bg-zinc-400, .bg-neutral-400, .bg-stone-400) { background-color: var(--d-raise-5); }

/* The alpha steps the views actually reach for on a well. */
html.dark :where(.bg-gray-50\/70,  .bg-gray-50\/50,  .bg-gray-50\/40,  .bg-gray-50\/30)  { background-color: rgba(255, 255, 255, 0.03); }
html.dark :where(.bg-gray-100\/70, .bg-gray-100\/50, .bg-gray-100\/40, .bg-gray-100\/30) { background-color: rgba(255, 255, 255, 0.05); }

/* Gradients that fade into the page — scrims over a cover, the fade under a
   sticky header. A stop left as #fff is a white band across a dark card.

   `from-*` and `via-*` each write TWO declarations in Tailwind, and both
   have to be restated or the fix is half-applied:

     • `from-white` also sets `--tw-gradient-to: rgb(255 255 255 / 0)` — a
       TRANSPARENT WHITE, which is what a one-stop fade actually fades to.
       Override only the `from` and the gradient still runs to white-zero,
       which reads as a grey haze on near-black.
     • `via-white` has no `--tw-gradient-via` property at all: the colour is
       written straight into `--tw-gradient-stops`, so that whole list is
       what has to be replaced. */
html.dark :where(.from-white) {
  --tw-gradient-from: #1c1c1e var(--tw-gradient-from-position);
  --tw-gradient-to: rgb(28 28 30 / 0) var(--tw-gradient-to-position);
}
html.dark :where(.via-white) {
  --tw-gradient-stops: var(--tw-gradient-from), #1c1c1e var(--tw-gradient-via-position), var(--tw-gradient-to);
}
html.dark :where(.to-white)      { --tw-gradient-to: #1c1c1e var(--tw-gradient-to-position); }
html.dark :where(.from-gray-50)  {
  --tw-gradient-from: #232326 var(--tw-gradient-from-position);
  --tw-gradient-to: rgb(35 35 38 / 0) var(--tw-gradient-to-position);
}
html.dark :where(.to-gray-50)    { --tw-gradient-to: #232326 var(--tw-gradient-to-position); }
html.dark :where(.from-gray-100) {
  --tw-gradient-from: #2a2a2e var(--tw-gradient-from-position);
  --tw-gradient-to: rgb(42 42 46 / 0) var(--tw-gradient-to-position);
}
html.dark :where(.to-gray-100)   { --tw-gradient-to: #2a2a2e var(--tw-gradient-to-position); }

/* ════════════════════════════════════════════════════════════════════════
   3 · TEXT — the dark end of the ramp, which is the end that must move
   ════════════════════════════════════════════════════════════════════════ */

html.dark :where(.text-black) { color: var(--d-ink); }
html.dark :where(.text-gray-950, .text-slate-950, .text-zinc-950, .text-neutral-950, .text-stone-950,
                 .text-gray-900, .text-slate-900, .text-zinc-900, .text-neutral-900, .text-stone-900,
                 .text-gray-800, .text-slate-800, .text-zinc-800, .text-neutral-800, .text-stone-800) { color: var(--d-ink); }
html.dark :where(.text-gray-700, .text-slate-700, .text-zinc-700, .text-neutral-700, .text-stone-700,
                 .text-gray-600, .text-slate-600, .text-zinc-600, .text-neutral-600, .text-stone-600) { color: var(--d-ink-2); }
html.dark :where(.text-gray-500, .text-slate-500, .text-zinc-500, .text-neutral-500, .text-stone-500) { color: var(--d-muted); }

/* text-gray-400 and lighter are deliberately untouched: on a light page they
   are already the faintest thing on it, and on a dark one they read as the
   muted tier without help. Repainting them would flatten two tiers into one. */

/* Prose (the typography plugin) resolves through its own variables, so the
   whole long-form surface flips from here. */
html.dark :where(.prose) {
  --tw-prose-body: var(--d-ink-2);
  --tw-prose-headings: var(--d-ink);
  --tw-prose-lead: var(--d-muted);
  --tw-prose-links: #7ab4ef;
  --tw-prose-bold: var(--d-ink);
  --tw-prose-counters: var(--d-muted);
  --tw-prose-bullets: var(--d-faint);
  --tw-prose-hr: var(--d-hairline);
  --tw-prose-quotes: var(--d-ink);
  --tw-prose-quote-borders: var(--d-line);
  --tw-prose-captions: var(--d-muted);
  --tw-prose-code: var(--d-ink);
  --tw-prose-pre-code: var(--d-ink);
  --tw-prose-pre-bg: var(--d-raise-2);
  --tw-prose-th-borders: var(--d-line);
  --tw-prose-td-borders: var(--d-hairline);
}

/* ════════════════════════════════════════════════════════════════════════
   4 · EDGES — borders, dividers, rings
   ════════════════════════════════════════════════════════════════════════
   A hairline in light is ink at 6%; in dark it is white at 8%. Both are
   "barely there", which is the point of a hairline — so these map by ROLE,
   not by lightness.
   ──────────────────────────────────────────────────────────────────────── */

html.dark :where(.border-white) { border-color: var(--d-line-2); }
html.dark :where(.border-gray-50,  .border-slate-50,  .border-zinc-50,  .border-neutral-50,  .border-stone-50,
                 .border-gray-100, .border-slate-100, .border-zinc-100, .border-neutral-100, .border-stone-100) { border-color: var(--d-hairline); }
html.dark :where(.border-gray-200, .border-slate-200, .border-zinc-200, .border-neutral-200, .border-stone-200) { border-color: var(--d-line); }
html.dark :where(.border-gray-300, .border-slate-300, .border-zinc-300, .border-neutral-300, .border-stone-300,
                 .border-gray-400, .border-slate-400, .border-zinc-400, .border-neutral-400, .border-stone-400) { border-color: var(--d-line-2); }

html.dark :where(.divide-gray-100, .divide-slate-100, .divide-zinc-100, .divide-neutral-100, .divide-stone-100) > :not([hidden]) ~ :not([hidden]) { border-color: var(--d-hairline); }
html.dark :where(.divide-gray-200, .divide-slate-200, .divide-zinc-200, .divide-neutral-200, .divide-stone-200) > :not([hidden]) ~ :not([hidden]) { border-color: var(--d-line); }
html.dark :where(.divide-gray-300, .divide-slate-300, .divide-zinc-300, .divide-neutral-300, .divide-stone-300) > :not([hidden]) ~ :not([hidden]) { border-color: var(--d-line-2); }

html.dark :where(.ring-white)    { --tw-ring-color: var(--d-line-2); }
html.dark :where(.ring-gray-100, .ring-slate-100, .ring-zinc-100, .ring-neutral-100, .ring-stone-100) { --tw-ring-color: var(--d-hairline); }
html.dark :where(.ring-gray-200, .ring-slate-200, .ring-zinc-200, .ring-neutral-200, .ring-stone-200) { --tw-ring-color: var(--d-line); }
html.dark :where(.ring-gray-300, .ring-slate-300, .ring-zinc-300, .ring-neutral-300, .ring-stone-300) { --tw-ring-color: var(--d-line-2); }
/* A black ring at 5% is invisible on near-black; the hairline it stands for
   is not. */
html.dark :where(.ring-black\/5, .ring-black\/10) { --tw-ring-color: var(--d-hairline); }

/* ════════════════════════════════════════════════════════════════════════
   5 · STATES — hover, focus, group-hover
   ════════════════════════════════════════════════════════════════════════
   Written as `html.dark :where(.hover\:x):hover` rather than wrapping the
   `:hover` inside the `:where()`. The pseudo-class has to stay OUTSIDE to
   count: inside, the whole rule weighs (0,1,1) and loses to Tailwind's own
   `.hover\:bg-gray-50:hover` at (0,2,0). Outside, it is (0,2,1) and wins by
   one — which is exactly as much as it should win by.
   ──────────────────────────────────────────────────────────────────────── */

html.dark :where(.hover\:bg-white):hover      { background-color: var(--d-raise); }
html.dark :where(.hover\:bg-gray-50,  .hover\:bg-slate-50,  .hover\:bg-zinc-50,  .hover\:bg-neutral-50,  .hover\:bg-stone-50):hover  { background-color: var(--d-raise-2); }
html.dark :where(.hover\:bg-gray-100, .hover\:bg-slate-100, .hover\:bg-zinc-100, .hover\:bg-neutral-100, .hover\:bg-stone-100):hover { background-color: var(--d-raise-3); }
html.dark :where(.hover\:bg-gray-200, .hover\:bg-slate-200, .hover\:bg-zinc-200, .hover\:bg-neutral-200, .hover\:bg-stone-200):hover { background-color: var(--d-raise-4); }

html.dark :where(.hover\:text-black):hover { color: var(--d-ink-hi); }
html.dark :where(.hover\:text-gray-900, .hover\:text-slate-900, .hover\:text-zinc-900, .hover\:text-neutral-900, .hover\:text-stone-900,
                 .hover\:text-gray-800, .hover\:text-slate-800, .hover\:text-zinc-800, .hover\:text-neutral-800, .hover\:text-stone-800,
                 .hover\:text-gray-700, .hover\:text-slate-700, .hover\:text-zinc-700, .hover\:text-neutral-700, .hover\:text-stone-700):hover { color: var(--d-ink-hi); }
html.dark :where(.hover\:text-gray-600, .hover\:text-slate-600, .hover\:text-zinc-600, .hover\:text-neutral-600, .hover\:text-stone-600):hover { color: var(--d-ink); }

html.dark :where(.hover\:border-gray-200, .hover\:border-slate-200, .hover\:border-zinc-200, .hover\:border-neutral-200, .hover\:border-stone-200):hover { border-color: var(--d-line); }
html.dark :where(.hover\:border-gray-300, .hover\:border-slate-300, .hover\:border-zinc-300, .hover\:border-neutral-300, .hover\:border-stone-300):hover { border-color: var(--d-line-2); }

html.dark .group:hover :where(.group-hover\:bg-gray-50)  { background-color: var(--d-raise-2); }
html.dark .group:hover :where(.group-hover\:bg-gray-100) { background-color: var(--d-raise-3); }
html.dark .group:hover :where(.group-hover\:text-gray-900, .group-hover\:text-gray-700) { color: var(--d-ink-hi); }

html.dark :where(.focus\:bg-white):focus { background-color: var(--d-raise); }
html.dark :where(.focus\:border-gray-300):focus { border-color: var(--d-line-2); }

/* ════════════════════════════════════════════════════════════════════════
   6 · FORM CONTROLS
   ════════════════════════════════════════════════════════════════════════
   @tailwindcss/forms sets a white fill and a #6b7280 border on every input
   in its BASE layer, which no utility in the markup overrides — so without
   this the whole app's forms stay white in dark mode even where the card
   around them has flipped. The selector list mirrors the plugin's own.
   ──────────────────────────────────────────────────────────────────────── */

/* The `:not()` guards are what keep this from trampling a borderless or
   transparent field. An input that already carries `bg-transparent` or
   `border-gray-300` has been given a surface by its author, and the utility
   map above has already translated it — so this rule stands down for it and
   only dresses the inputs nothing else speaks for. (`:not()` takes its
   argument's weight, which also lifts these past the plugin's base rule
   without needing `!important`.) */
html.dark :where(
  [type='text'], [type='email'], [type='url'], [type='password'], [type='number'],
  [type='date'], [type='datetime-local'], [type='month'], [type='search'],
  [type='tel'], [type='time'], [type='week'], [multiple], textarea, select
):not([class*="bg-"]) { background-color: var(--d-raise); }

html.dark :where(
  [type='text'], [type='email'], [type='url'], [type='password'], [type='number'],
  [type='date'], [type='datetime-local'], [type='month'], [type='search'],
  [type='tel'], [type='time'], [type='week'], [multiple], textarea, select
):not([class*="border-"]) { border-color: var(--d-line); }

html.dark :where(
  [type='text'], [type='email'], [type='url'], [type='password'], [type='number'],
  [type='date'], [type='datetime-local'], [type='month'], [type='search'],
  [type='tel'], [type='time'], [type='week'], [multiple], textarea, select
):not([class*="text-"]) { color: var(--d-ink); }
/* The blanket placeholder rule stands down for any field that names its own
   placeholder colour. `html.dark` already out-weighs Tailwind's
   `.placeholder-gray-400::placeholder`, so without this guard a deliberate
   `placeholder-white` on a hero field would silently lose to the default. */
html.dark :where(input, textarea):not([class*="placeholder"])::placeholder { color: var(--d-faint); }
html.dark :where(.placeholder-gray-300, .placeholder-gray-400, .placeholder-gray-500)::placeholder { color: var(--d-faint); }
html.dark :where(.placeholder\:text-gray-300, .placeholder\:text-gray-400, .placeholder\:text-gray-500)::placeholder { color: var(--d-faint); }

/* The plugin draws the select caret as a data: URL, which cannot read a
   custom property — so the arrow stays #6b7280 charcoal on a dark field
   unless the whole image is restated.

   `:where()` here is load-bearing, not housekeeping. This rule must weigh
   exactly what the plugin's own `select` rule weighs — (0,0,1) — so all it
   can ever do is RECOLOUR an arrow the plugin is still drawing. Written as
   `html.dark select:not([multiple])` it weighed (0,2,2), which outranked
   every field that opts out of the native caret to draw its own glyph
   (`.cb-cal-select select { background: transparent }`, (0,1,1)) and handed
   the plugin's arrow back in dark mode only — two chevrons in one control,
   one of them sitting in the 2.5rem gutter the plugin reserves. At (0,0,1)
   it still beats the plugin on source order (dark is linked last) and still
   loses to any local opt-out, so dark never shows a caret light doesn't. */
:where(html.dark) select:where(:not([multiple])) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M6 8l4 4 4-4' stroke='%239b9a97' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* The mobile kits draw their own caret, at their own size and offset, so a
   class already beat the rule above even at its old weight — and now always
   will. They are the one group that genuinely wanted recolouring, so state it
   for them by name instead of by blanket. Same 16-unit geometry as theirs, so
   their background-position / background-size still line up. */
html.dark :where(.app-select, .app-form-select, .ios-select) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6l4 4 4-4' stroke='%239b9a97' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* A native option list is painted by the OS, and on some engines it only
   takes the dark form if the element says so itself. */
html.dark option { background-color: var(--d-raise-2); color: var(--d-ink); }

/* Chrome paints autofilled fields a fixed pale yellow and ignores
   background-color. An inset shadow the size of the field is the only thing
   that covers it. */
html.dark :where(input, textarea, select):-webkit-autofill,
html.dark :where(input, textarea, select):-webkit-autofill:focus {
  -webkit-text-fill-color: var(--d-ink);
  -webkit-box-shadow: 0 0 0 1000px var(--d-raise-2) inset;
  caret-color: var(--d-ink);
}

/* ════════════════════════════════════════════════════════════════════════
   7 · ELEVATION
   ════════════════════════════════════════════════════════════════════════
   Tailwind's shadows are black at 5–10%, tuned to read against white. On a
   #191919 page they are invisible, so every card loses its lift at once.
   ──────────────────────────────────────────────────────────────────────── */

/* Only the two custom properties are rewritten, never `box-shadow` itself.
   Tailwind's own rule is
     box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)
   and custom properties resolve at used-value time — so changing the last
   term recomposes the whole thing, while an element that is ALSO carrying a
   `ring-*` keeps its ring. Writing `box-shadow` here would erase it. */
html.dark :where(.shadow-sm)  { --tw-shadow: var(--d-sh-sm); --tw-shadow-colored: var(--d-sh-sm); }
html.dark :where(.shadow)     { --tw-shadow: var(--d-sh);    --tw-shadow-colored: var(--d-sh); }
html.dark :where(.shadow-md)  { --tw-shadow: var(--d-sh-md); --tw-shadow-colored: var(--d-sh-md); }
html.dark :where(.shadow-lg)  { --tw-shadow: var(--d-sh-lg); --tw-shadow-colored: var(--d-sh-lg); }
html.dark :where(.shadow-xl, .shadow-2xl) { --tw-shadow: var(--d-sh-xl); --tw-shadow-colored: var(--d-sh-xl); }

/* ════════════════════════════════════════════════════════════════════════
   8 · THE NOTION PALETTE — arbitrary-value utilities
   ════════════════════════════════════════════════════════════════════════
   Everything above this point remaps NAMED utilities: `bg-white`,
   `text-gray-900`, `border-gray-200`. That covers the framework half of the
   app and misses the other half entirely, because Move's newer screens are
   not written in Tailwind's ramp at all — they are written in a hand-picked
   palette passed through arbitrary values:

     text-[#37352F]   878×     bg-[#F7F6F3]    89×
     text-[#787774]   824×     bg-[#37352F]    88×
     border-[#37352F] 424×     border-[#E3E2E0] 96×

   3,900 occurrences of ~140 literals across 136 files. `.text-gray-900` and
   `.text-\[\#37352F\]` are unrelated class names to CSS, so not one of the
   rules above touches any of it: those screens stayed pure light on a black
   page — black ink on black — which is most of what "dark mode doesn't look
   good" was.

   Hand-editing 136 files to add `dark:` variants is the same quarter of work
   the file header rejects, so the same technique applies one level down. The
   escaping is Tailwind's own: `text-[#37352F]` compiles to the literal class
   `.text-\[\#37352F\]`, and the hex is CASE-SENSITIVE as a class name —
   `text-[#37352f]` is a DIFFERENT selector, so both cases are spelled out
   wherever the repo writes both.

   Three literals are deliberately absent from every list below, and that is
   the whole judgement of this section:

     • The dark end stays put. `bg-[#191919]`, `bg-[#1c1c1e]`, `bg-[#232326]`,
       `text-[#F0EBD8]` and friends come from the iOS-styled mobile screens,
       which are ALREADY dark surfaces. They are not light furniture to be
       flipped; touching them would break the one part of the app that was
       right to begin with. Note this cuts by utility, not by colour:
       `bg-[#18181b]` is a dark surface and is left alone, while
       `text-[#18181b]` is dark ink and must flip.

     • The mid-grey inks stay put. `text-[#9B9A97]` and lighter are already
       the faintest thing on a light page and read as the muted tier on a
       dark one without help — the same call section 3 makes for
       `text-gray-400`.

     • Saturated fills stay put. `bg-[#2383E2]`, `bg-[#EB5757]`,
       `bg-[#0F7B6C]`, `bg-[#d6b72f]` are chips and buttons whose colour IS
       the message; they sit on near-black perfectly well.
   ──────────────────────────────────────────────────────────────────────── */

/* — Ink ——————————————————————————————————————————————————————————————— */

html.dark :where(
  .text-\[\#37352F\], .text-\[\#37352f\], .text-\[\#1C1A14\], .text-\[\#1F2328\],
  .text-\[\#18181b\], .text-\[\#1a1a1a\], .text-\[\#1f1f1f\], .text-\[\#111827\],
  .text-\[\#0f172a\], .text-\[\#0F1419\], .text-\[\#27272a\], .text-\[\#1F2937\],
  .text-\[\#3C3C43\]
) { color: var(--d-ink); }

html.dark :where(
  .text-\[\#787774\], .text-\[\#75746f\], .text-\[\#4b4845\], .text-\[\#3f3f46\],
  .text-\[\#374151\], .text-\[\#52525b\], .text-\[\#5F5E5A\], .text-\[\#6b6a66\],
  .text-\[\#5C5845\], .text-\[\#7A7260\], .text-\[\#7A7560\], .text-\[\#6B7280\],
  .text-\[\#6b7280\], .text-\[\#71717a\]
) { color: var(--d-ink-2); }

/* The warm gold inks. In light these are dark gold ON a cream wash; both
   halves of that pair move, so the ink has to end up light or it lands on
   its own flipped background. */
html.dark :where(
  .text-\[\#8B7A2B\], .text-\[\#A69032\], .text-\[\#b89a23\], .text-\[\#A67100\],
  .text-\[\#6c5c1c\], .text-\[\#B7791F\], .text-\[\#b45309\], .text-\[\#92400e\]
) { color: var(--d-gold); }

html.dark :where(.text-\[\#2383E2\], .text-\[\#1B6EC2\], .text-\[\#1E72C7\], .text-\[\#5B5FC7\], .text-\[\#2563eb\]) { color: var(--d-blue); }
html.dark :where(.text-\[\#0F7B6C\], .text-\[\#0F7B3E\], .text-\[\#137A45\], .text-\[\#448361\], .text-\[\#27AE60\]) { color: var(--d-green); }
html.dark :where(.text-\[\#EB5757\], .text-\[\#E03E3E\], .text-\[\#B23A3A\], .text-\[\#B91C1C\], .text-\[\#dc2626\], .text-\[\#d84a4a\]) { color: var(--d-red); }
html.dark :where(.text-\[\#6940CE\]) { color: var(--d-purple); }
html.dark :where(.text-\[\#D9730D\]) { color: var(--d-orange); }

html.dark :where(.fill-\[\#37352F\]) { fill: var(--d-ink); }

/* Placeholders. `#C4C4C0` is a pale grey on paper and a shout on near-black —
   brighter than the value the field will hold, which inverts the hierarchy
   the placeholder exists to sit below. */
html.dark :where(.placeholder-\[\#9B9A97\], .placeholder-\[\#B4B4B0\], .placeholder-\[\#C4C4C0\], .placeholder-\[\#B4B3AE\])::placeholder { color: var(--d-faint); }

/* — Surfaces —————————————————————————————————————————————————————————— */

html.dark :where(.bg-\[\#FBFBFA\], .bg-\[\#fbfbfa\], .bg-\[\#FAFAF9\], .bg-\[\#FCFCFD\], .bg-\[\#F8FAFC\], .bg-\[\#F7F7F5\], .bg-\[\#F5F7FA\]) { background-color: var(--d-raise); }
html.dark :where(.bg-\[\#F7F6F3\], .bg-\[\#f7f6f3\], .bg-\[\#F4F3EF\], .bg-\[\#EFEEEB\], .bg-\[\#F2F2F7\], .bg-\[\#f2f2f2\], .bg-\[\#f1f1f5\], .bg-\[\#F1F2F6\]) { background-color: var(--d-raise-2); }
html.dark :where(.bg-\[\#EBEAE6\], .bg-\[\#E3E2E0\], .bg-\[\#E9E9E7\], .bg-\[\#D9D9D8\]) { background-color: var(--d-raise-3); }
html.dark :where(.bg-\[\#B4B4B0\]) { background-color: var(--d-raise-4); }
html.dark :where(.bg-\[\#787774\]) { background-color: var(--d-raise-5); }

/* The ink-filled primary button, and the one place this section departs from
   the file header's "dark end stays as authored" rule.

   `bg-gray-900` is left alone up in section 2 because it is one element among
   many and merely looks flat. `bg-[#37352F]` is different in kind: it is the
   ONE primary action on every Notion-styled screen, and #37352F on the
   #191919 canvas is a 1.5:1 step — the button stops being a shape at all,
   and the screen loses its call to action rather than its polish. Lifting it
   to #35353c restores the shape (2.1:1 off the canvas) while keeping the
   `text-white` it is always written with at 11.4:1. */
html.dark :where(.bg-\[\#37352F\], .bg-\[\#37352f\]) { background-color: var(--d-fill); }
html.dark :where(.bg-\[\#2F2D28\]) { background-color: var(--d-fill-hover); }

/* Tint washes — a status strip, an info callout, a selected row. */
html.dark :where(.bg-\[\#FDECEC\]) { background-color: var(--d-wash-red); }
html.dark :where(.bg-\[\#E7F4EC\]) { background-color: var(--d-wash-green); }
html.dark :where(.bg-\[\#E8F0FE\], .bg-\[\#EAF2FD\], .bg-\[\#E8EBFD\]) { background-color: var(--d-wash-blue); }
html.dark :where(.bg-\[\#FFF6E5\]) { background-color: var(--d-wash-gold); }

/* The one decorative gradient in the set: a lavender-to-peach card wash that
   is otherwise a lit rectangle on a black page. Both stops are named because
   a gradient with one stop flipped is worse than one with neither. */
html.dark :where(.from-\[\#E8EBFD\]) {
  --tw-gradient-from: rgba(91, 168, 245, 0.13) var(--tw-gradient-from-position);
  --tw-gradient-to: rgba(91, 168, 245, 0) var(--tw-gradient-to-position);
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
html.dark :where(.to-\[\#F6E9E2\]) { --tw-gradient-to: rgba(232, 155, 82, 0.13) var(--tw-gradient-to-position); }

/* — Edges ————————————————————————————————————————————————————————————— */

html.dark :where(.border-\[\#F0EFED\], .border-\[\#FBFBFA\], .border-\[\#EBEAE6\], .border-\[\#EAE9E5\], .border-\[\#F1F2F6\]) { border-color: var(--d-hairline); }
html.dark :where(.border-\[\#E3E2E0\], .border-\[\#E9E9E7\], .border-\[\#E0DED9\], .border-\[\#D3D1CB\], .border-\[\#d4d4d8\]) { border-color: var(--d-line); }
/* Full-ink borders are never decoration in this palette — they are the
   selected card, the focused field, the active tab. */
html.dark :where(.border-\[\#37352F\], .border-\[\#37352f\], .border-\[\#1C1A14\]) { border-color: var(--d-line-3); }

html.dark :where(.divide-\[\#37352F\]) > :not([hidden]) ~ :not([hidden]) { border-color: var(--d-line); }
html.dark :where(.divide-\[\#E3E2E0\], .divide-\[\#F0EFED\]) > :not([hidden]) ~ :not([hidden]) { border-color: var(--d-hairline); }

html.dark :where(.ring-\[\#37352F\]) { --tw-ring-color: var(--d-line-3); }
html.dark :where(.ring-\[\#E3E2E0\], .ring-\[\#E9E4DD\], .ring-\[\#fbfbfa\]) { --tw-ring-color: var(--d-line); }

/* — States ———————————————————————————————————————————————————————————— */
/* Same specificity arithmetic as section 5: the pseudo-class stays OUTSIDE
   the `:where()` so the rule weighs (0,2,1) and clears Tailwind's own
   (0,2,0) variant by exactly one. */

html.dark :where(.hover\:bg-\[\#F7F6F3\], .hover\:bg-\[\#f7f6f3\], .hover\:bg-\[\#EFEEEB\], .hover\:bg-\[\#F4F3EF\], .hover\:bg-\[\#EBEAE6\]):hover,
html.dark :where(.active\:bg-\[\#F7F6F3\]):active { background-color: var(--d-raise-3); }
html.dark :where(.hover\:bg-\[\#37352F\], .hover\:bg-\[\#2F2D28\]):hover { background-color: var(--d-fill-hover); }
html.dark :where(.active\:bg-\[\#787774\]):active { background-color: var(--d-raise-5); }
html.dark :where(.hover\:bg-\[\#FDECEC\]):hover { background-color: var(--d-wash-red); }
html.dark :where(.hover\:bg-\[\#EAF2FD\]):hover { background-color: var(--d-wash-blue); }

html.dark :where(.hover\:text-\[\#37352F\], .hover\:text-\[\#37352f\], .hover\:text-\[\#1C1A14\],
                 .hover\:text-\[\#111827\], .hover\:text-\[\#1F2328\], .hover\:text-\[\#18181b\]):hover,
html.dark :where(.active\:text-\[\#37352F\], .active\:text-\[\#1F2328\]):active { color: var(--d-ink-hi); }
html.dark :where(.hover\:text-\[\#787774\]):hover { color: var(--d-ink-2); }
html.dark :where(.hover\:text-\[\#EB5757\], .hover\:text-\[\#d84a4a\]):hover { color: var(--d-red); }
html.dark :where(.hover\:text-\[\#1B6EC2\]):hover { color: var(--d-blue); }
html.dark :where(.hover\:text-\[\#0F7B6C\]):hover { color: var(--d-green); }

html.dark .group:hover :where(.group-hover\:text-\[\#37352F\]) { color: var(--d-ink-hi); }
html.dark .group:hover :where(.group-hover\:text-\[\#787774\]) { color: var(--d-ink-2); }

html.dark :where(.focus\:border-\[\#37352F\]):focus,
html.dark :where(.hover\:border-\[\#37352F\]):hover { border-color: var(--d-line-3); }
html.dark :where(.focus-within\:border-\[\#37352F\]):focus-within { border-color: var(--d-line-3); }
html.dark :where(.hover\:border-\[\#D3D1CB\], .hover\:border-\[\#E9E9E7\]):hover { border-color: var(--d-line-2); }
html.dark :where(.focus\:ring-\[\#37352F\]):focus { --tw-ring-color: var(--d-line-3); }
html.dark :where(.focus\:ring-\[\#E9E4DD\]):focus { --tw-ring-color: var(--d-line); }

/* `peer-checked:` compiles to `.peer:checked ~ .peer-checked\:x` at (0,3,0),
   so the combinator has to be restated outside the `:where()` to reach
   (0,3,1). Selection is the one state that must never be subtle. */
html.dark .peer:checked ~ :where(.peer-checked\:bg-\[\#37352F\]) { background-color: var(--d-fill); }
html.dark .peer:checked ~ :where(.peer-checked\:border-\[\#37352F\]) { border-color: var(--d-line-3); }

/* — The guard ————————————————————————————————————————————————————————— */
/* A saturated fill stays as authored (see the section head), so the ink
   sitting ON one must stay dark too — otherwise `text-[#37352F]` on a
   `bg-[#d6b72f]` badge flips to white-on-gold at 2.1:1 and the badge becomes
   unreadable in dark when it was fine in light. These rules weigh the same
   (0,1,1) as the ink rules above and win on order alone, which is why they
   are last in the section rather than first.

   Both shapes are covered: the pair on ONE element (a filled chip whose own
   class list carries both), and the pair across a wrapper and a descendant
   (a chip with its label in a span).

   The list is exactly the four literals the repo actually uses as a LIGHT
   background — `bg-[#d6b72f]` 23×, `bg-[#34C759]` 7×, `bg-[#bda028]` 2×,
   `bg-[#FF9500]` 2×. It is kept that tight on purpose: the descendant form
   reaches the whole subtree, so every colour added here is another wrapper
   that could pin a nested card's ink dark. All four are small badges with a
   label inside, which is the shape this is safe on. */
html.dark :where(.bg-\[\#d6b72f\], .bg-\[\#bda028\], .bg-\[\#34C759\], .bg-\[\#FF9500\]):where(.text-\[\#37352F\], .text-\[\#37352f\], .text-\[\#1C1A14\]),
html.dark :where(.bg-\[\#d6b72f\], .bg-\[\#bda028\], .bg-\[\#34C759\], .bg-\[\#FF9500\]) :where(.text-\[\#37352F\], .text-\[\#37352f\], .text-\[\#1C1A14\]) { color: #1f1f1f; }

/* ════════════════════════════════════════════════════════════════════════
   9 · CONTENT THAT IS NOT CHROME
   ════════════════════════════════════════════════════════════════════════ */

/* Rich text written in the editor carries its own inline colours and its own
   white paper. The editor surface flips; the author's colours do not, which
   is right — a colour somebody chose is content. */
html.dark :where(.trix-content, .richer-text-content) { color: var(--d-ink-2); }
html.dark :where(trix-editor, .trix-content pre) { background-color: var(--d-raise); color: var(--d-ink); }
html.dark :where(.trix-content hr) { border-color: var(--d-line); }

/* Tables authored with hairlines rather than utilities. */
html.dark :where(table) { border-color: var(--d-hairline); }
html.dark :where(hr) { border-color: var(--d-hairline); }

/* Anything that draws itself — QR codes, signature pads, map tiles, embedded
   documents — is content and stays as authored. `filter: invert()` on an
   image is the single most common dark-mode mistake and it makes a QR code
   unscannable. */

/* Skeletons. A pale grey pulse is the loading state's whole message, and on
   a dark page it is a white flash instead. Both shapes are covered, because
   the repo writes it both ways: `animate-pulse` on the bar itself, and on a
   wrapper around several bars. */
html.dark :where(.animate-pulse.bg-gray-200, .animate-pulse.bg-gray-100, .animate-pulse.bg-slate-200),
html.dark :where(.animate-pulse) :where(.bg-gray-200, .bg-gray-100, .bg-slate-200) { background-color: rgba(255, 255, 255, 0.07); }

/* ════════════════════════════════════════════════════════════════════════
   10 · OPT-OUT
   ════════════════════════════════════════════════════════════════════════
   `.keep-light` on a wrapper restores the light ramp for its whole subtree.

   It works because the solid surface, text, edge and elevation rules above
   all resolve through a custom property, and custom properties inherit:
   redeclaring the tokens below reverts every one of those rules for that
   subtree without repeating a single selector. Use it for anything that has
   to survive being photographed, scanned or printed — a QR code, a ticket
   pass, an email preview.

   What it does NOT revert, because those rules name a colour directly rather
   than a token: the translucent `bg-white/NN` glass steps, and the three
   framework sections above (daisyUI's base variables, Halo's literals, the
   calendar shell). None of them has come up inside something that needs to
   stay light; if one does, give it a token here rather than a second
   opt-out class.
   ──────────────────────────────────────────────────────────────────────── */

html.dark .keep-light {
  --d-canvas:    #f2f2f2;
  --d-surface:   #ffffff;
  --d-raise:     #f9fafb;
  --d-raise-2:   #f3f4f6;
  --d-raise-3:   #e5e7eb;
  --d-raise-4:   #d1d5db;
  --d-raise-5:   #9ca3af;
  --d-ink:       #111827;
  --d-ink-2:     #374151;
  --d-muted:     #6b7280;
  --d-faint:     #9ca3af;
  --d-hairline:  rgba(0, 0, 0, 0.06);
  --d-line:      rgba(0, 0, 0, 0.10);
  --d-line-2:    rgba(0, 0, 0, 0.16);
  --d-line-3:    #37352f;
  --d-ink-hi:    #000000;
  --d-fill:       #37352f;
  --d-fill-hover: #2f2d28;
  --d-blue:       #2383e2;
  --d-green:      #0f7b6c;
  --d-red:        #eb5757;
  --d-gold:       #b89a23;
  --d-purple:     #6940ce;
  --d-orange:     #d9730d;
  --d-wash-blue:  #e8f0fe;
  --d-wash-green: #e7f4ec;
  --d-wash-red:   #fdecec;
  --d-wash-gold:  #fff6e5;
  --d-sh-sm:     0 1px 2px rgba(0, 25, 53, 0.06);
  --d-sh:        0 1px 3px rgba(0, 25, 53, 0.10);
  --d-sh-md:     0 4px 6px rgba(0, 25, 53, 0.10);
  --d-sh-lg:     0 10px 15px rgba(0, 25, 53, 0.10);
  --d-sh-xl:     0 20px 25px rgba(0, 25, 53, 0.10);
  color-scheme: light;
  background-color: #ffffff;
  color: #111827;
}
