/*
 * Dark theme.
 *
 * WHY THIS IS TOKENS AND NOT `dark:` VARIANTS
 * The app carries roughly 7,000 light-mode color utilities across 443 ERB
 * templates. Tailwind v4 compiles every one of them to a custom-property
 * reference — `.bg-white { background-color: var(--color-white) }` — so
 * redefining the tokens under one attribute flips the whole app at once.
 * Adding `dark:` to every call site would be the same change 7,000 times, and
 * every new view would have to remember to do it again.
 *
 * WHY THESE RULES BEAT TAILWIND'S
 * Tailwind emits its tokens inside `@layer theme { :root, :host { … } }`.
 * The rules here are deliberately UNLAYERED, and an unlayered rule beats any
 * layered one regardless of specificity or source order — so this file wins
 * even though Propshaft happens to serve it after tailwind.css (which it does:
 * `stylesheet_link_tag :app` emits every stylesheet alphabetically, and
 * "theme" sorts after "tailwind"). Do not wrap this file in an `@layer`.
 *
 * HOW THE RAMPS ARE BUILT
 * Neutrals are ordered by ROLE, not mirrored numerically: gray-50…300 are
 * surfaces and borders and stay dark, gray-400…900 are text and go light.
 * That matches how the codebase actually uses them (gray-400 is text 646
 * times and a border 95; gray-200 is a border/background 645 times and text
 * 33), so the dominant role of each token is the one that reads correctly.
 *
 * Accent lightness climbs monotonically with the shade number, which lets one
 * token serve both of its roles: `bg-blue-50 text-blue-800` is a dark tint
 * carrying light text, while `bg-blue-600 text-white` is still a saturated
 * fill under a white label. Chroma tapers as lightness rises so pale tints
 * don't go neon on a dark background.
 *
 * Every pairing below was checked against WCAG AA (4.5:1 body text, 3:1 for
 * large text and UI) by spec/assets/dark_theme_contrast_spec.rb, which parses
 * the token values straight out of this file and recomputes the ratios — so a
 * future palette tweak cannot quietly regress contrast.
 *
 * NOT THEMED HERE: the sales deck. Its layouts (sales_deck, deck_public) never
 * SET data-theme, but that alone was never enough to keep it out — Turbo Drive
 * replaces <body> and leaves <html> alone, so an app page's data-theme rides
 * into the deck editor on a visit. Two guards, both added with that fix:
 *   - every .sales-deck canvas root carries data-theme-static, so the whole
 *     light palette below is restored inside the slide itself even with JS off;
 *   - the deck layouts' <body> carries data-deck-document, and the
 *     turbo:before-render handler in application.js strips the theme from
 *     <html> on the way in and restores it on the way out.
 * That makes the [data-theme-static] block a CLIENT-FACING dependency: changing
 * it changes published slides, not just opted-out app chrome.
 * Org-configurable deck palettes are separate upcoming work — do not extend
 * this file to cover them.
 */

html[data-theme="dark"] {
  /* ── neutral surfaces: page is deepest, each layer above it lighter ── */
  --color-white: oklch(21.5% 0.007 265.0);
  --color-gray-50: oklch(26.2% 0.008 265.0);
  --color-gray-100: oklch(30.2% 0.009 265.0);
  --color-gray-200: oklch(34.5% 0.010 265.0);
  --color-gray-300: oklch(40.5% 0.012 265.0);
  --color-gray-400: oklch(64.0% 0.017 265.0);
  --color-gray-500: oklch(73.0% 0.018 265.0);
  --color-gray-600: oklch(81.0% 0.015 265.0);
  --color-gray-700: oklch(88.0% 0.012 265.0);
  --color-gray-800: oklch(93.0% 0.008 265.0);
  --color-gray-900: oklch(97.0% 0.004 265.0);

  /* ── accents: lightness climbs with the shade number ── */
  /* -50 and -100 are surface washes rather than fills — they sit under whole
     columns, rows and panels, and the ramp's own curve made them two to three
     times heavier than their light-mode counterparts, so a tinted column read
     as a slab. Both steps are flattened for every hue; -200 and up carry the
     curve.

     Matched by CONTRAST against the CARD surface rather than by lightness
     against the page: these tints sit on cards as often as on the page, and
     measuring against the page alone lands -50 exactly on the card surface,
     where it vanishes entirely (dark_theme_contrast_spec pins this). Light
     mode reads 1.09 for -50 and 1.22 for -100 on a white card; these come to
     1.10 and 1.23. */
  --color-amber-50: oklch(25.5% 0.020 70.1);
  --color-amber-100: oklch(29.0% 0.035 70.1);
  --color-amber-200: oklch(38.0% 0.080 70.1);
  --color-amber-300: oklch(47.0% 0.115 70.1);
  --color-amber-400: oklch(56.0% 0.150 70.1);
  --color-amber-500: oklch(61.0% 0.170 70.1);
  --color-amber-600: oklch(64.0% 0.175 70.1);
  --color-amber-700: oklch(71.0% 0.140 70.1);
  --color-amber-800: oklch(80.0% 0.095 70.1);
  --color-amber-900: oklch(88.0% 0.055 70.1);
  --color-blue-50: oklch(25.5% 0.020 259.8);
  --color-blue-100: oklch(29.0% 0.035 259.8);
  --color-blue-200: oklch(38.0% 0.080 259.8);
  --color-blue-300: oklch(47.0% 0.115 259.8);
  --color-blue-400: oklch(56.0% 0.150 259.8);
  --color-blue-500: oklch(61.0% 0.170 259.8);
  --color-blue-600: oklch(64.0% 0.175 259.8);
  --color-blue-700: oklch(71.0% 0.140 259.8);
  --color-blue-800: oklch(80.0% 0.095 259.8);
  --color-blue-900: oklch(88.0% 0.055 259.8);
  --color-emerald-50: oklch(25.5% 0.020 162.5);
  --color-emerald-100: oklch(29.0% 0.035 162.5);
  --color-emerald-200: oklch(38.0% 0.080 162.5);
  --color-emerald-400: oklch(56.0% 0.150 162.5);
  --color-emerald-500: oklch(61.0% 0.170 162.5);
  --color-emerald-600: oklch(64.0% 0.175 162.5);
  --color-emerald-700: oklch(71.0% 0.140 162.5);
  --color-emerald-900: oklch(88.0% 0.055 162.5);
  --color-green-50: oklch(25.5% 0.020 149.6);
  --color-green-100: oklch(29.0% 0.035 149.6);
  --color-green-200: oklch(38.0% 0.080 149.6);
  --color-green-300: oklch(47.0% 0.115 149.6);
  --color-green-400: oklch(56.0% 0.150 149.6);
  --color-green-500: oklch(61.0% 0.170 149.6);
  --color-green-600: oklch(64.0% 0.175 149.6);
  --color-green-700: oklch(71.0% 0.140 149.6);
  --color-green-800: oklch(80.0% 0.095 149.6);
  --color-green-900: oklch(88.0% 0.055 149.6);
  --color-indigo-50: oklch(25.5% 0.020 277.1);
  --color-indigo-100: oklch(29.0% 0.035 277.1);
  --color-indigo-200: oklch(38.0% 0.080 277.1);
  --color-indigo-300: oklch(47.0% 0.115 277.1);
  --color-indigo-400: oklch(56.0% 0.150 277.1);
  --color-indigo-500: oklch(61.0% 0.170 277.1);
  --color-indigo-600: oklch(64.0% 0.175 277.1);
  --color-indigo-700: oklch(71.0% 0.140 277.1);
  --color-indigo-800: oklch(80.0% 0.095 277.1);
  --color-indigo-900: oklch(88.0% 0.055 277.1);
  --color-lime-50: oklch(25.5% 0.020 125.0);
  --color-lime-700: oklch(71.0% 0.140 125.0);
  --color-orange-50: oklch(25.5% 0.020 47.6);
  --color-orange-100: oklch(29.0% 0.035 47.6);
  --color-orange-300: oklch(47.0% 0.115 47.6);
  --color-orange-400: oklch(56.0% 0.150 47.6);
  --color-orange-500: oklch(61.0% 0.170 47.6);
  --color-orange-600: oklch(64.0% 0.175 47.6);
  --color-orange-700: oklch(71.0% 0.140 47.6);
  --color-orange-800: oklch(80.0% 0.095 47.6);
  --color-pink-500: oklch(61.0% 0.170 354.3);
  --color-purple-50: oklch(25.5% 0.020 303.9);
  --color-purple-100: oklch(29.0% 0.035 303.9);
  --color-purple-200: oklch(38.0% 0.080 303.9);
  --color-purple-300: oklch(47.0% 0.115 303.9);
  --color-purple-400: oklch(56.0% 0.150 303.9);
  --color-purple-500: oklch(61.0% 0.170 303.9);
  --color-purple-600: oklch(64.0% 0.175 303.9);
  --color-purple-700: oklch(71.0% 0.140 303.9);
  --color-purple-800: oklch(80.0% 0.095 303.9);
  --color-purple-900: oklch(88.0% 0.055 303.9);
  --color-red-50: oklch(25.5% 0.020 25.3);
  --color-red-100: oklch(29.0% 0.035 25.3);
  --color-red-200: oklch(38.0% 0.080 25.3);
  --color-red-300: oklch(47.0% 0.115 25.3);
  --color-red-400: oklch(56.0% 0.150 25.3);
  --color-red-500: oklch(61.0% 0.170 25.3);
  --color-red-600: oklch(64.0% 0.175 25.3);
  --color-red-700: oklch(71.0% 0.140 25.3);
  --color-red-800: oklch(80.0% 0.095 25.3);
  --color-red-900: oklch(88.0% 0.055 25.3);
  --color-rose-100: oklch(29.0% 0.035 16.4);
  --color-rose-500: oklch(61.0% 0.170 16.4);
  --color-rose-800: oklch(80.0% 0.095 16.4);
  --color-sky-50: oklch(25.5% 0.020 237.3);
  --color-sky-100: oklch(29.0% 0.035 237.3);
  --color-sky-200: oklch(38.0% 0.080 237.3);
  --color-sky-300: oklch(47.0% 0.115 237.3);
  --color-sky-400: oklch(56.0% 0.150 237.3);
  --color-sky-500: oklch(61.0% 0.170 237.3);
  --color-sky-600: oklch(64.0% 0.175 237.3);
  --color-sky-700: oklch(71.0% 0.140 237.3);
  --color-sky-800: oklch(80.0% 0.095 237.3);
  --color-sky-900: oklch(88.0% 0.055 237.3);
  --color-slate-100: oklch(31.0% 0.015 257.4);
  --color-slate-400: oklch(64.0% 0.020 257.4);
  --color-slate-500: oklch(73.0% 0.022 257.4);
  --color-teal-50: oklch(25.5% 0.020 182.5);
  --color-teal-100: oklch(29.0% 0.035 182.5);
  --color-teal-300: oklch(45.0% 0.100 182.5);
  --color-teal-500: oklch(61.0% 0.170 182.5);
  --color-teal-600: oklch(64.0% 0.175 182.5);
  --color-teal-700: oklch(71.0% 0.140 182.5);
  --color-teal-900: oklch(88.0% 0.055 182.5);
  --color-violet-50: oklch(25.5% 0.020 292.7);
  --color-violet-100: oklch(29.0% 0.035 292.7);
  --color-violet-200: oklch(38.0% 0.080 292.7);
  --color-violet-300: oklch(47.0% 0.115 292.7);
  --color-violet-400: oklch(56.0% 0.150 292.7);
  --color-violet-500: oklch(61.0% 0.170 292.7);
  --color-violet-600: oklch(64.0% 0.175 292.7);
  --color-violet-700: oklch(71.0% 0.140 292.7);
  --color-violet-800: oklch(80.0% 0.095 292.7);
  --color-yellow-50: oklch(25.5% 0.020 85.0);
  --color-yellow-100: oklch(29.0% 0.035 85.0);
  --color-yellow-200: oklch(38.0% 0.080 85.0);
  --color-yellow-600: oklch(64.0% 0.175 85.0);
  --color-yellow-700: oklch(71.0% 0.140 85.0);
  --color-yellow-800: oklch(80.0% 0.095 85.0);
  --color-yellow-900: oklch(88.0% 0.055 85.0);

  /* Black stays black so `bg-black/50` modal backdrops still read as a scrim.
     `text-black` is corrected below instead. */
}

/* Two of Tailwind's internals are declared `@property … inherits: false`, so
 * setting them on <html> reaches nothing — they have to be set on every
 * element. Both are in @layer base rather than unlayered so a per-element
 * utility (shadow-<color>, ring-offset-[#101114]) still wins.
 *
 *   --tw-ring-offset-color has `initial-value: #fff`. Left alone, all 41
 *   ring-offset-1/2 call sites draw a WHITE gap around their focus ring on a
 *   dark surface. It wants the color of whatever sits behind the element, and
 *   the card surface is the best single answer.
 *
 *   --tw-shadow-color is the alpha-black in every shadow-* utility, supplied
 *   as a var() FALLBACK, so token inversion cannot reach it. Deepened, because
 *   a shadow tuned for white needs more weight to register against dark.
 */
@layer base {
  html[data-theme="dark"] * {
    --tw-ring-offset-color: oklch(21.5% 0.007 265);
    --tw-shadow-color: oklch(0% 0 0 / 0.5);
  }
}

/* The page is the deepest layer, below every card. `bg-gray-50` is the token
   for a subtle fill sitting ON a card, so it is lighter than the card and
   cannot double as the page background — <body class="bg-gray-50"> gets the
   deeper value here rather than a rewrite of the layout.

   <html> is painted the same color as the body (both modes): under page zoom
   Chrome fills the canvas below a short <body> with the UA default instead of
   the propagated body background, splitting short pages into two tones at the
   body's bottom edge. Scoped by :has to app chrome so the deck layouts (whose
   body carries data-deck-document, not data-app-themed) keep their own
   canvas propagation. */
html[data-theme="dark"]:has(> body[data-app-themed]),
html[data-theme="dark"] body {
  background-color: oklch(17.5% 0.006 265);
}

/* And that ground is a gradient, not a flat fill, for the reason `.ground-fill`
   gives in application.css: Chrome draws a raster tile that comes out one flat
   color from the compositor, skipping the color conversion the rastered tiles
   get, so the empty space below a page's content reads a few values darker than
   the ground beside it — with a seam along the bottom edge of the last thing
   that painted. The second stop is one value of green. Dark mode only: in light
   mode both paths land on the same near-white. Scoped to app chrome, unlike the
   color above, so a deck document's own body background is left alone. */
html[data-theme="dark"]:has(> body[data-app-themed]),
html[data-theme="dark"] body[data-app-themed] {
  background-image: linear-gradient(
    to bottom,
    oklch(17.5% 0.006 265),
    oklch(17.6% 0.006 265)
  );
}

html:not([data-theme="dark"]):has(> body[data-app-themed]) {
  background-color: var(--color-gray-50);
}

/* Belt to the html rules above: the app body always reaches the viewport
   bottom, so its own background covers the page even in an engine that
   ignores the :has rule or paints the below-body canvas its own way
   (the Electron shell showed the seam after the html fix). Both modes. */
body[data-app-themed] {
  min-height: 100dvh;
}

/* ── Elevation ────────────────────────────────────────────────────────────
 * On white, a drop shadow is what separates a popover from the card behind it.
 * On a dark ground a black shadow is invisible, so a `bg-white` popover sitting
 * on a `bg-white` card became the SAME color with nothing between them — the
 * assign-team-member popover had no edge at all.
 *
 * Dark UIs signal elevation with a rim light instead. Tailwind composes
 * box-shadow from five slots and `--tw-inset-ring-shadow` is unused here (no
 * `inset-ring-*` utility appears anywhere in the app), so borrowing that slot
 * adds a hairline rim to every floating element WITHOUT clobbering the shadow
 * Tailwind already put in `--tw-shadow`.
 *
 * Scoped to the floating tiers — md and above, 166 call sites. shadow-sm is
 * left alone: it is mostly cards, which already carry `border-gray-200`, and a
 * rim on all 164 of them would read as noise.
 *
 * @property declares this slot `inherits: false`, so it has to be set on the
 * elevated element itself, which is what these selectors do.
 */
html[data-theme="dark"] .shadow-md,
html[data-theme="dark"] .shadow-lg,
html[data-theme="dark"] .shadow-xl,
html[data-theme="dark"] .shadow-2xl {
  --tw-inset-ring-shadow: inset 0 0 0 1px oklch(100% 0 0 / 0.12);
}

/* The heaviest tiers are modals and detached panels, usually over a backdrop —
   they can carry a slightly brighter rim without looking outlined. */
html[data-theme="dark"] .shadow-xl,
html[data-theme="dark"] .shadow-2xl {
  --tw-inset-ring-shadow: inset 0 0 0 1px oklch(100% 0 0 / 0.16);
}

/* The staffing timeline's own layout variables, declared at :root in
   application.css and read by both CSS and fullscreen_toggle_controller.js.
   They paint the frozen columns and grid lines across the whole week-by-week
   grid, so leaving them light left that view on a light ground while its text
   went light — the single biggest dark-mode gap outside the token palette. */
html[data-theme="dark"] {
  --layout-page-bg: oklch(17.5% 0.006 265);
  --layout-border-color: oklch(34.5% 0.010 265);
  /* Proportionate to the light value: that one sits ~1% off white, so this sits
     a few percent off the card surface (21.5%) with low chroma. Scaled from a
     badge tint like yellow-50 it read as a solid olive block, far louder than
     the hint it is meant to be. */
  --layout-holiday-bg: oklch(24.5% 0.021 85);
  /* gray-200 sits ~7 points off white in light mode but ~13 points off the
     21.5% week background in dark, so the ruler read as bright stripes rather
     than hairlines. Matched to the light contrast instead. */
  --gantt-grid-line: oklch(27.5% 0.008 265);
}

/* `white` flipped to the card surface above, which is right for the 509 places
   it is a background, border, ring or gradient stop — but `text-white` is the
   label on a saturated fill (`bg-blue-600 text-white`), and those fills stay
   saturated in dark mode, so its text has to stay light. Overriding the
   utility is safe here specifically because `text-white` never shares an
   element with a variant `text-*` utility in this codebase (verified: one
   `placeholder:text-gray-600`, a different pseudo-element) — so this cannot
   swallow a hover color the way an unlayered `.bg-white` override would.
   Same reasoning for `text-black`. */
html[data-theme="dark"] .text-white,
html[data-theme="dark"] .hover\:text-white:hover,
html[data-theme="dark"] .focus\:text-white:focus,
html[data-theme="dark"] .active\:text-white:active,
html[data-theme="dark"] .file\:text-white::file-selector-button,
html[data-theme="dark"] .aria-pressed\:text-white[aria-pressed="true"],
html[data-theme="dark"] .data-selected\:text-white[data-selected],
html[data-theme="dark"] .text-black,
html[data-theme="dark"] .hover\:text-black:hover {
  color: oklch(97% 0.004 265);
}

html[data-theme="dark"] .caret-white {
  caret-color: oklch(97% 0.004 265);
}

/* ── the <select> chevron ─────────────────────────────────────────────────
 * The one color in the app that token inversion structurally CANNOT reach: it
 * lives inside a data: URI (app/assets/tailwind/application.css), and a data
 * URI resolves no custom properties. `appearance: none` removes the native
 * arrow, so this is the only chevron on every <select> in the app; left alone
 * it stays gray-500's LIGHT value (#6b7280) on the dark card, ~3.6:1 instead
 * of the ~7.3:1 the token would give.
 *
 * Masking is NOT an option here — `mask` clips the whole element, which would
 * take the select's own label with it. So the URI is restated with the dark
 * ramp's gray-500 baked in: oklch(73% 0.018 265) = #a2a8b3. The two must move
 * together; the only difference between them is that stroke value.
 */
html[data-theme="dark"] select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23a2a8b3' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
}

/* ── Lexxy ────────────────────────────────────────────────────────────────
 * Lexxy ships its own UNLAYERED stylesheet, which beats every Tailwind
 * utility regardless of specificity (see CLAUDE.md), so `.lexxy-content`
 * paints its text from --lexxy-color-ink no matter what `text-gray-900` says
 * on the wrapper. Left alone, every rendered description, comment and PRD body
 * is near-black text on a dark card.
 *
 * Lexxy is built on one "ink" ramp declared at :root, so inverting those six
 * variables carries the editor chrome, rendered content, tables, code blocks
 * and borders together. Mapped onto this theme's neutrals rather than pure
 * greys so Lexxy content sits in the same color family as everything else.
 */
html[data-theme="dark"] {
  --lexxy-color-ink: oklch(93% 0.008 265);
  --lexxy-color-ink-medium: oklch(81% 0.015 265);
  --lexxy-color-ink-light: oklch(64% 0.017 265);
  --lexxy-color-ink-lighter: oklch(40.5% 0.012 265);
  --lexxy-color-ink-lightest: oklch(30.2% 0.009 265);
  --lexxy-color-ink-inverted: oklch(21.5% 0.007 265);

  /* Accents: the light pair are selection/hover washes, so they have to become
     dark washes rather than staying pale. */
  --lexxy-color-accent-dark: oklch(71% 0.14 259.8);
  --lexxy-color-accent-medium: oklch(61% 0.17 259.8);
  --lexxy-color-accent-light: oklch(38% 0.08 259.8);
  --lexxy-color-accent-lightest: oklch(32% 0.055 259.8);

  --lexxy-color-red: oklch(70% 0.16 27);
  --lexxy-color-green: oklch(72% 0.16 145);
  --lexxy-color-blue: oklch(72% 0.17 258);
  --lexxy-color-purple: oklch(72% 0.16 305);

  /* Lexxy's own code tokens are GitHub-light hexes on a pale code background;
     the background is now dark, so they need the brighter equivalents. */
  --lexxy-color-code-token-att: oklch(72% 0.17 22);
  --lexxy-color-code-token-comment: oklch(64% 0.017 265);
  --lexxy-color-code-token-function: oklch(76% 0.14 305);
  --lexxy-color-code-token-operator: oklch(72% 0.17 22);
  --lexxy-color-code-token-property: oklch(74% 0.14 245);
  --lexxy-color-code-token-punctuation: oklch(88% 0.012 265);
  --lexxy-color-code-token-selector: oklch(76% 0.16 150);
  --lexxy-color-code-token-variable: oklch(76% 0.15 60);

  /* Text-color highlights (slash menu / bucket picker). Saved content keeps
     the var() reference (`color: var(--highlight-N)`), so overriding here
     retints existing documents too. Same hues as the light palette, lifted
     to stay legible on the dark canvas — "Gray" in particular was 102-gray
     on a near-black page. */
  --highlight-1: oklch(76% 0.11 95);
  --highlight-2: oklch(72% 0.14 55);
  --highlight-3: oklch(66% 0.21 27);
  --highlight-4: oklch(70% 0.20 345);
  --highlight-5: oklch(68% 0.20 300);
  --highlight-6: oklch(70% 0.13 250);
  --highlight-7: oklch(72% 0.17 145);
  --highlight-8: oklch(66% 0.09 60);
  --highlight-9: oklch(72% 0.01 265);
}

/* ── Opt-out: components authored DARK on purpose ─────────────────────────
 * A few pieces of chrome are deliberately dark IN LIGHT MODE — the gantt
 * estimate popover is `bg-gray-900 text-white` with `bg-blue-900/60` controls,
 * tuned for a dark ground. Token inversion flips those utilities and turns the
 * component LIGHT, which is the opposite of the intent and lands its accents
 * (text-blue-300 on bg-blue-900/50) at poor contrast.
 *
 * `data-theme-static` restores the light-mode palette inside a subtree, so
 * `bg-gray-900` keeps meaning "nearly black" and the component's internal
 * contrast survives untouched — no re-authoring of its utilities.
 *
 * This is the utility-built counterpart to the literal-valued dark chrome the
 * stylesheet tokenizer skips by selector (Lexxy's floating toolbar, markdown
 * code blocks). Reach for it only when a component is SUPPOSED to look the
 * same in both themes; everything else should follow the theme.
 *
 * Values are Tailwind's own, generated from the compiled theme block.
 */
html[data-theme="dark"] [data-theme-static] {
  --color-red-50: oklch(97.1% .013 17.38);
  --color-red-100: oklch(93.6% .032 17.717);
  --color-red-200: oklch(88.5% .062 18.334);
  --color-red-300: oklch(80.8% .114 19.571);
  --color-red-400: oklch(70.4% .191 22.216);
  --color-red-500: oklch(63.7% .237 25.331);
  --color-red-600: oklch(57.7% .245 27.325);
  --color-red-700: oklch(50.5% .213 27.518);
  --color-red-800: oklch(44.4% .177 26.899);
  --color-red-900: oklch(39.6% .141 25.723);
  --color-orange-50: oklch(98% .016 73.684);
  --color-orange-100: oklch(95.4% .038 75.164);
  --color-orange-300: oklch(83.7% .128 66.29);
  --color-orange-400: oklch(75% .183 55.934);
  --color-orange-500: oklch(70.5% .213 47.604);
  --color-orange-600: oklch(64.6% .222 41.116);
  --color-orange-700: oklch(55.3% .195 38.402);
  --color-orange-800: oklch(47% .157 37.304);
  --color-amber-50: oklch(98.7% .022 95.277);
  --color-amber-100: oklch(96.2% .059 95.617);
  --color-amber-200: oklch(92.4% .12 95.746);
  --color-amber-300: oklch(87.9% .169 91.605);
  --color-amber-400: oklch(82.8% .189 84.429);
  --color-amber-500: oklch(76.9% .188 70.08);
  --color-amber-600: oklch(66.6% .179 58.318);
  --color-amber-700: oklch(55.5% .163 48.998);
  --color-amber-800: oklch(47.3% .137 46.201);
  --color-amber-900: oklch(41.4% .112 45.904);
  --color-yellow-50: oklch(98.7% .026 102.212);
  --color-yellow-100: oklch(97.3% .071 103.193);
  --color-yellow-200: oklch(94.5% .129 101.54);
  --color-yellow-600: oklch(68.1% .162 75.834);
  --color-yellow-700: oklch(55.4% .135 66.442);
  --color-yellow-800: oklch(47.6% .114 61.907);
  --color-yellow-900: oklch(42.1% .095 57.708);
  --color-lime-50: oklch(98.6% .031 120.757);
  --color-lime-700: oklch(53.2% .157 131.589);
  --color-green-50: oklch(98.2% .018 155.826);
  --color-green-100: oklch(96.2% .044 156.743);
  --color-green-200: oklch(92.5% .084 155.995);
  --color-green-300: oklch(87.1% .15 154.449);
  --color-green-400: oklch(79.2% .209 151.711);
  --color-green-500: oklch(72.3% .219 149.579);
  --color-green-600: oklch(62.7% .194 149.214);
  --color-green-700: oklch(52.7% .154 150.069);
  --color-green-800: oklch(44.8% .119 151.328);
  --color-green-900: oklch(39.3% .095 152.535);
  --color-emerald-50: oklch(97.9% .021 166.113);
  --color-emerald-100: oklch(95% .052 163.051);
  --color-emerald-200: oklch(90.5% .093 164.15);
  --color-emerald-400: oklch(76.5% .177 163.223);
  --color-emerald-500: oklch(69.6% .17 162.48);
  --color-emerald-600: oklch(59.6% .145 163.225);
  --color-emerald-700: oklch(50.8% .118 165.612);
  --color-emerald-900: oklch(37.8% .077 168.94);
  --color-teal-50: oklch(98.4% .014 180.72);
  --color-teal-100: oklch(95.3% .051 180.801);
  --color-teal-300: oklch(85.5% .138 181.071);
  --color-teal-500: oklch(70.4% .14 182.503);
  --color-teal-600: oklch(60% .118 184.704);
  --color-teal-700: oklch(51.1% .096 186.391);
  --color-teal-900: oklch(38.6% .063 188.416);
  --color-sky-50: oklch(97.7% .013 236.62);
  --color-sky-100: oklch(95.1% .026 236.824);
  --color-sky-200: oklch(90.1% .058 230.902);
  --color-sky-300: oklch(82.8% .111 230.318);
  --color-sky-400: oklch(74.6% .16 232.661);
  --color-sky-500: oklch(68.5% .169 237.323);
  --color-sky-600: oklch(58.8% .158 241.966);
  --color-sky-700: oklch(50% .134 242.749);
  --color-sky-800: oklch(44.3% .11 240.79);
  --color-sky-900: oklch(39.1% .09 240.876);
  --color-blue-50: oklch(97% .014 254.604);
  --color-blue-100: oklch(93.2% .032 255.585);
  --color-blue-200: oklch(88.2% .059 254.128);
  --color-blue-300: oklch(80.9% .105 251.813);
  --color-blue-400: oklch(70.7% .165 254.624);
  --color-blue-500: oklch(62.3% .214 259.815);
  --color-blue-600: oklch(54.6% .245 262.881);
  --color-blue-700: oklch(48.8% .243 264.376);
  --color-blue-800: oklch(42.4% .199 265.638);
  --color-blue-900: oklch(37.9% .146 265.522);
  --color-indigo-50: oklch(96.2% .018 272.314);
  --color-indigo-100: oklch(93% .034 272.788);
  --color-indigo-200: oklch(87% .065 274.039);
  --color-indigo-300: oklch(78.5% .115 274.713);
  --color-indigo-400: oklch(67.3% .182 276.935);
  --color-indigo-500: oklch(58.5% .233 277.117);
  --color-indigo-600: oklch(51.1% .262 276.966);
  --color-indigo-700: oklch(45.7% .24 277.023);
  --color-indigo-800: oklch(39.8% .195 277.366);
  --color-indigo-900: oklch(35.9% .144 278.697);
  --color-violet-50: oklch(96.9% .016 293.756);
  --color-violet-100: oklch(94.3% .029 294.588);
  --color-violet-200: oklch(89.4% .057 293.283);
  --color-violet-300: oklch(81.1% .111 293.571);
  --color-violet-400: oklch(70.2% .183 293.541);
  --color-violet-500: oklch(60.6% .25 292.717);
  --color-violet-600: oklch(54.1% .281 293.009);
  --color-violet-700: oklch(49.1% .27 292.581);
  --color-violet-800: oklch(43.2% .232 292.759);
  --color-purple-50: oklch(97.7% .014 308.299);
  --color-purple-100: oklch(94.6% .033 307.174);
  --color-purple-200: oklch(90.2% .063 306.703);
  --color-purple-300: oklch(82.7% .119 306.383);
  --color-purple-400: oklch(71.4% .203 305.504);
  --color-purple-500: oklch(62.7% .265 303.9);
  --color-purple-600: oklch(55.8% .288 302.321);
  --color-purple-700: oklch(49.6% .265 301.924);
  --color-purple-800: oklch(43.8% .218 303.724);
  --color-purple-900: oklch(38.1% .176 304.987);
  --color-pink-500: oklch(65.6% .241 354.308);
  --color-rose-100: oklch(94.1% .03 12.58);
  --color-rose-500: oklch(64.5% .246 16.439);
  --color-rose-800: oklch(45.5% .188 13.697);
  --color-slate-100: oklch(96.8% .007 247.896);
  --color-slate-400: oklch(70.4% .04 256.788);
  --color-slate-500: oklch(55.4% .046 257.417);
  --color-gray-50: oklch(98.5% .002 247.839);
  --color-gray-100: oklch(96.7% .003 264.542);
  --color-gray-200: oklch(92.8% .006 264.531);
  --color-gray-300: oklch(87.2% .01 258.338);
  --color-gray-400: oklch(70.7% .022 261.325);
  --color-gray-500: oklch(55.1% .027 264.364);
  --color-gray-600: oklch(44.6% .03 256.802);
  --color-gray-700: oklch(37.3% .034 259.733);
  --color-gray-800: oklch(27.8% .033 256.848);
  --color-gray-900: oklch(21% .034 264.665);
  --color-black: #000;
  --color-white: #fff;
}

/* Tailwind's dark greys carry a deliberate blue cast (gray-900 is
   `oklch(21% 0.034 264)`). Against a white page that reads simply as "dark",
   which is why these components look right in light mode — but on a near-black
   page the cast is the most obvious thing about them, and the estimate popover
   read as a blue box rather than a raised panel.
   So within the opt-out, dark mode gets a NEUTRAL grey ramp, lifted clear of
   the 21.5% card surface so the panel still reads as elevated. The components'
   own accents (bg-blue-900/60 controls, the red unschedule button) are
   untouched and keep the contrast they were designed with. */
html[data-theme="dark"] [data-theme-static] {
  --color-gray-600: oklch(45% 0.007 265);
  --color-gray-700: oklch(38% 0.006 265);
  --color-gray-800: oklch(31% 0.005 265);
  --color-gray-900: oklch(26% 0.004 265);
}

/* The two utility overrides further up have to be undone here too, or white
   text stays light against a now-light-again surface. */
html[data-theme="dark"] [data-theme-static] .text-white,
html[data-theme="dark"] [data-theme-static] .hover\:text-white:hover {
  color: #fff;
}

html[data-theme="dark"] [data-theme-static] .text-black,
html[data-theme="dark"] [data-theme-static] .hover\:text-black:hover {
  color: #000;
}

/* A logo plate, for the client-facing entry pages.
 *
 * The invitation and sign-in pages co-brand with the client's mark × the
 * organization's, and ask deck_ground_logo_tag for the "light" variant because
 * the card below is white. The lockup does not sit ON that card though — it
 * sits above it on the page ground, which in dark mode is nearly black, so the
 * dark-ink mark meant for a white card disappeared into it.
 *
 * Rather than pick a different variant per theme — the marks are uploads and an
 * organization may only have supplied one — the plate gives the light mark the
 * white it was drawn for. The subtree carries data-theme-static so the lockup's
 * grey "×" separator, a text token that goes LIGHT in dark mode, does not
 * vanish into the plate it now sits on.
 *
 * :has() so a project with no marks at all shows no empty white tile, and
 * scoped to dark mode so light mode keeps a plain centred lockup on its own
 * ground. */
html[data-theme="dark"] [data-logo-plate]:has(img, svg) {
  background-color: #fff;
  border-radius: 0.75rem;
  padding: 0.75rem 1.25rem;
}
