/* FILE: site/css/theme.css
   Canonical CSS variables for AiMe OS (Amber/Sapphire design system).
   Theme switching is keyed off the [data-theme] attribute on <html>,
   managed exclusively by js/theme.js. No external theming libraries,
   no Azure/MSAL/AppInsights artifacts. */

:root {
  /* --- Theme-independent constants ---
     Not swapped between light/dark: --color-bg-base is a fixed near-black
     used as text-on-amber-badge contrast (pricing.css .pricing-badge), not
     an adaptive surface color. The --color-xp-start / --color-xp-end pair
     and --color-level-end formalize the XP/level toast gradients
     (toast.css) as named tokens instead of raw hex sitting in a component
     file -- kept theme-independent per that file's own docs: intentional
     semantic accents, distinct from brand palette by design -- status
     notifications, not UI chrome. Values unchanged from what was
     previously hardcoded, so no visual change. */
  --color-bg-base: #0b0e1a;
  /* Fixed near-WHITE partner to --color-bg-base. Added 2026-07-31: these two
     are the only text colours available to a surface that does NOT change
     with the theme (the XP and level toast gradients). --color-bg-primary
     cannot serve, because it swaps. */
  --color-fg-base: #f5f9fd;
  --color-xp-start: #10b981;
  --color-xp-end: #059669;
  /* --color-level-start ADDED 2026-07-31. The level toast gradient ran
     `var(--color-accent-sapphire) -> var(--color-level-end)` -- one
     theme-swapped token and one fixed one. In light theme both stops are deep
     blue and white text worked; in DARK theme sapphire becomes pale (#5c8dff
     then, #8ab0ff since 2026-08-04 — paler still, so this reasoning only got
     stronger) while level-end stays deep navy, so the ramp goes light -> dark
     and NO single text colour clears 4.5:1 at both ends (white 3.13 at the
     start, near-black 1.86 at the end). Pinning the start makes the gradient
     mean the same thing in both themes, which is what the sibling XP pair
     already did. Value is the light theme's sapphire, so light is unchanged. */
  --color-level-start: #1d4ed8;
  --color-level-end: #1e3a8a;

  /* --- Layout tokens (MOVED here 2026-07-26, real bug fix) ---
     These were defined ONLY inside [data-theme='light']. Every page in this
     project ships as <html data-theme="dark">, so in the theme that actually
     renders, all seven of these resolved to nothing.

     Most call sites pass a fallback (`var(--radius-md, 12px)`) and survived
     by accident. 28 do not -- e.g. login.html's card, pricing.html's plan
     cards, gallery/waggle/documents/create-studio panels, and forum.html's
     `margin: 0 auto var(--space-4)`. An undefined custom property with no
     fallback makes the whole declaration invalid at computed-value time, so
     those silently rendered with square corners and zero spacing instead of
     the intended values.

     Spacing and corner radius are not theme-dependent -- a card is the same
     shape in light and dark -- so :root is where they belong. Values are
     copied verbatim from the light block; nothing changes for light mode.
     Same bug family as `--color-text-muted` (2026-07-18) and `--color-error`
     (2026-07-26): a variable that was never defined, quietly doing nothing. */
  --header-height: 64px;
  --radius-md: 12px;
  --radius-sm: 6px;
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
}

[data-theme='light'] {
  /* --- Surfaces & text --- */
  --color-bg-primary: #f5f9fd;
  --color-bg-surface: #ffffff;
  --color-text-primary: #10131a;
  --color-text-secondary: #4a5568;
  /* FIX 2026-07-18: was undefined -- pricing.css referenced
     var(--color-text-muted, #9a9a9a), which meant the fallback hex rendered
     literally, in BOTH themes, never actually adapting to light/dark. */
  --color-text-muted: var(--color-text-secondary);

  /* --- Brand: Amber (primary) & Sapphire (accent) ---

     DARKENED 2026-07-30, owner-approved. Was #b8621b, which measured
     4.13:1 on --color-bg-primary and 4.37:1 on --color-bg-surface — under
     the 4.5:1 WCAG AA minimum on BOTH light surfaces.

     This is the highest-impact value in the file: it paints every
     `.site-hero h1`, the `.cs-tab.is-active` state, and the `.btn-primary`
     background on every page. It failed quietly because a 4.13:1 heading
     does not look broken — it looks like a design choice — which is exactly
     why nothing caught it for as long as light-first has existed.

     #a85a18 is the same hue, one step darker: 4.79:1 / 5.07:1. It also
     improves .btn-primary, where --color-bg-primary text sits ON this
     colour: that pairing was 4.13:1 and is now 4.79:1.

     -hover is unchanged at #8a4a14 and stays darker than the new base, so
     the hover direction still reads correctly.

     Enforced now, not just documented: audit-css-tokens.js measures this. */
  --color-primary-amber: #a85a18;
  --color-primary-amber-hover: #8a4a14;

  /* TEXT THAT SITS ON AN AMBER FILL. Added 2026-07-31 after measuring the
     LIVE page rather than the stylesheet.

     .btn-primary and .pricing-badge both used `color: var(--color-bg-base)`,
     and --color-bg-base is a FIXED near-black (#0b0e1a) that does NOT swap
     between themes. That was verified once, in dark theme, at 8.92:1 -- and
     nobody ever measured it in light theme, where it is dark-on-dark:

       #0b0e1a on light amber #b8621b = 4.40  (marginal)
       #0b0e1a on light amber #a85a18 = 3.79  FAIL   <- after I darkened amber

     Darkening the amber for the sake of amber-on-white made this pairing
     WORSE, and the prediction recorded at the time ("improves .btn-primary
     from the other direction ... now 4.79:1") was computed against
     --color-bg-primary, which is NOT the token the rule actually uses.

     No single fixed colour clears 4.5:1 on BOTH ambers -- that is the whole
     reason this token has to exist:

       #ffffff on light #a85a18 = 5.07   |  #ffffff on dark #e8a33d = 2.16
       #0b0e1a on light #a85a18 = 3.79   |  #0b0e1a on dark #e8a33d = 8.92

     Hover states are checked too and both improve:
       light  #ffffff on #8a4a14 =  6.84
       dark   #0b0e1a on #f5b85c = 10.89 */
  --color-on-fill: #ffffff;
  --color-accent-sapphire: #1d4ed8;
  --color-accent-gold: #d9971f; /* decorative only, not for text */

  /* RECOLORED 2026-08-16, owner-directed: "shouldn't the chat bubble be blue
     and gold - not purple?" Yes. The token was named --gradient-brand and was
     100% purple -- the SAME hex as --world-purple / --world-purple-strong
     below. So the token called "brand" was literally the Swag Studio feature
     world, and the chat widget (the persona's face on every page) wore it.

     History, because the previous pass was not wrong so much as aimed
     elsewhere: on 2026-07-26 this was pink->purple (#c9459a -> #7c4dff) and
     the pink was removed as the one element belonging to no part of the
     Amber/Sapphire system. Correct fix, different question. Nobody asked
     whether the PURPLE belonged either.

     Now built from the brand tokens themselves rather than repeating their
     hex, so if amber or sapphire ever moves again this follows instead of
     silently diverging -- which is exactly how The Honey Patch ended up on an
     amber that existed nowhere else (see the note at --color-primary-amber).

     MEASURED, both ends, both themes, against --color-on-fill:
       light  amber #a85a18 on #ffffff  5.07:1     sapphire #1d4ed8  6.70:1
       dark   amber #e8a33d on #0b0e1a  8.92:1     sapphire #8ab0ff  8.91:1
     Every value clears AA at both ends. The purple did not: in DARK theme,
     against the hardcoded white the chat widget was using, #a78bfa measured
     2.72:1 and #7c5cff measured 4.35:1. Both under AA, in the DEFAULT theme,
     on the chat header, the send button, and the user's own message bubbles.

     BLAST RADIUS -- this token also paints .feature-card::before, the 3px top
     border on the homepage cards (styles.css). That moves from purple to
     gold->blue with this commit. On-brand and intended; revert that one line
     if the homepage reads worse. */
  --gradient-brand: linear-gradient(
    90deg,
    var(--color-primary-amber) 0%,
    var(--color-accent-sapphire) 100%
  );

  /* --- Feature worlds: purple (Swag Studio) & teal (Bring Your Voice) ---
     ADDED 2026-07-30. NOT new colors — these are the --gradient-brand
     endpoints above, promoted from anonymous hex inside a gradient into
     named, theme-aware tokens. The site already had exactly one purple; it
     just had no name, so create-studio.html hardcoded its own copy of it
     and that copy never followed the light-first sweep.

     DECLARED IN BOTH THEME BLOCKS, NEVER IN :root. That rule is not style —
     the 2026-07-26 bug was seven layout tokens defined only under
     [data-theme='light'] while every page shipped dark, silently collapsing
     ~28 call sites.

     Every value below is MEASURED, not chosen by eye, and the measurement is
     enforced: audit-css-tokens.js now fails any token used as `color:` that
     drops under 4.5:1 against this theme's bg-primary and bg-surface. */
  --world-purple: #7c4dff; /* 4.55:1 on bg-primary · 4.81:1 on bg-surface */
  --world-purple-strong: #5b34d6; /* 6.83:1 · 7.22:1 */
  --world-purple-wash: rgba(124, 77, 255, 0.07);
  --world-teal: #0f766e; /* 5.17:1 · 5.47:1 */
  --world-teal-wash: rgba(15, 118, 110, 0.07);

  /* --- Structure --- */
  --color-border: #e2e8f0;
  --color-success: #15803d;
  --color-primary-amber-wash: rgba(168, 90, 24, 0.04);
  --color-tier-freemium-wash: rgba(95, 107, 125, 0.1);
  --color-success-wash: rgba(21, 128, 61, 0.03);
  --color-danger: #b91c1c;

  /* --- Forum-specific accents --- */
  --forum-bg-overlay: radial-gradient(
    ellipse at top,
    var(--color-primary-amber-wash),
    transparent 60%
  );
  /* Tracks --color-primary-amber. Moved with it 2026-07-30 (#b8621b ->
     #a85a18) — it was the same hex before and would otherwise have silently
     diverged, leaving The Honey Patch on an amber that exists nowhere else.

     The contrast audit correctly did NOT flag this one: its only use is a
     `border-left` on .post-card (styles.css:970), never a `color:`. Borders
     answer to WCAG 1.4.11 non-text contrast at 3:1, which both the old and
     new value clear. Changed for consistency, not because it was failing. */
  --forum-accent-syrup: #a85a18;
  --forum-card-bg: #fffbf5;

  /* --- Tier colors (Freemium / Creator / Pro / Team) --- */
  /* RECOLOURED 2026-07-30, owner-approved — all three were under WCAG AA as
     badge TEXT on the light theme, measured by audit-css-tokens.js:

       freemium  #718096  3.80:1 / 4.02:1  ->  #5f6b7d  5.11:1 / 5.40:1
       creator   #b8621b  4.13:1 / 4.37:1  ->  #a85a18  4.79:1 / 5.07:1
       team      #d9971f  2.36:1 / 2.50:1  ->  #8a5a00  5.60:1 / 5.93:1

     `pro` (#1d4ed8) already passed at 6.70:1 and is untouched.

     freemium is the one that mattered most in practice — it is the badge the
     largest number of people ever see, and it was the second-worst value on
     the page.

     creator tracks --color-primary-amber deliberately; it was the same hex
     before and is the same hex now. If the amber moves again, move this with
     it or the Creator badge silently stops matching the brand.

     TEAM IS STILL OPEN. The tier was RETIRED 2026-07-19 and cannot be
     purchased, so the honest end state is probably deleting this token and
     `.badge--team` outright. Not done here: _shared_limits.js still carries a
     `team` entry for what its comment calls 2 possible legacy rows, and
     deleting a badge that a real account might still render is a bigger call
     than recolouring it. Fixed the contrast now; the deletion is a scope
     decision recorded in FINAL-BUILD-PLAN. */
  --color-tier-freemium: #5f6b7d;
  --color-tier-creator: #a85a18;
  --color-tier-pro: #1d4ed8;
  --color-tier-team: #8a5a00;

  /* --- Page texture (ADDED 2026-08-02) ---
     The honey-pool artwork, laid site-wide behind all content by
     `body::before` in styles.css. Declared here so each image has exactly
     ONE declaration point per theme; a page never names the file.

     THE OPACITY IS NOT A TASTE VALUE. It was computed against every pixel
     of the source PNG, worst case, not the mean. The binding constraint is
     --color-primary-amber (#a85a18) as TEXT on --color-bg-primary: it
     measures 4.79:1, only 0.29 above the WCAG AA floor, because it was
     already darkened once (2026-07-30) to clear 4.5. A gold wash over the
     page eats that margin immediately:

       opacity  amber-as-normal-text   amber-as-large-text (>=24px, AA 3.0)
       0.06     4.36  FAIL             PASS
       0.10     4.08  FAIL             PASS
       0.15     3.74  FAIL             PASS

     0.15 is shippable ONLY because every amber-on-page-background rule is
     large text (.site-hero h1, .pricing-hero h1 32-56px; .addons-section h2,
     .faq-section h2 24px; .stat-box h3 32px) or sits inside an OPAQUE card
     the wash never reaches (.price-amount, .plan-name, .balance-val,
     .lb-row__rank). .brand-strip p is 14.4px but uses amber-HOVER (#8a4a14),
     which measures 5.05 at this opacity.

     If you raise this number, or make any amber text smaller than 24px sit
     on the page background, re-run scripts/audit-css-tokens.js and expect
     it to fail. Do not raise it to "see how it looks." */
  /* OFF IN LIGHT THEME, 2026-08-05. Owner verdict after two attempts: "the
     light setting specifically looks plastic wrapped," then "still masked"
     after the radial mask shipped and was confirmed live.

     Both attempts were the wrong shape of fix, and the reason is physical
     rather than aesthetic: ON A LIGHT PAGE A TEXTURE CAN ONLY DARKEN. There
     is no light left to add. Gold over near-black #0b0e1a ADDS luminance and
     reads as depth — something glowing behind the content. The same gold over
     near-white #f5f9fd can only SUBTRACT, and a uniform subtraction laid
     across every pixel is the definition of a film. Opacity changes how much
     film. A mask changes where the film is. Neither can make it stop being
     film.

     So the honey wash is a DARK-THEME effect. Light theme gets the clean
     white page the jar artwork was chosen for in the first place — which is
     the same reasoning that made this project light-first on 2026-07-29: the
     mark is luminous, and luminous things need somewhere bright to sit.

     TO BRING IT BACK: restore the two lines below to
       --page-texture: url('...honey-pool-light.png&w=1024');
       --page-texture-opacity: 0.15;
     and set --page-texture-mask back to the radial-gradient in git history
     at b2740a4. Nothing else changes; body::before already handles `none`
     because `background-image: none` is valid and paints nothing.

     The contrast note that used to live here is now moot for light — with no
     wash, --color-primary-amber is back to its full 4.79:1 on the page
     background and the 0.29 of headroom is no longer being spent on
     decoration. That is a real accessibility gain, not just a visual one. */
  --page-texture: none;
  --page-texture-opacity: 0;

  /* --- The light-theme plastic-wrap fix, 2026-08-05 ------------------------
     LIGHT AND DARK ARE NOT THE SAME PROBLEM, and shipping one number for both
     is what made this look wrong.

     Gold texture over the near-black dark page ADDS light. It reads as depth,
     like something glowing behind the content. Over the near-white light page
     (#f5f9fd) the identical texture SUBTRACTS — it is a uniform gold tint
     laid across every pixel, which is the literal definition of a film. Same
     opacity, same image, opposite effect.

     The tell was uniformity. An even wash edge to edge has no reason to be
     there, so the eye reads it as a sheet ON the page rather than as part of
     it. This masks the texture out of the middle, where the content column
     sits, and leaves it in the corners and edges — so it behaves like light
     falling into the page instead of cling film over it.

     Mask alpha semantics: TRANSPARENT hides the layer, OPAQUE shows it. So
     the transparent centre is the clean area and the black edges are where
     the honey shows. Reads backwards the first time; it is correct.

     Dark sets this to `none` — full bleed genuinely works there, and masking
     it would throw away the depth that is the whole point. */
  --page-texture-mask: none;

  /* Hero band art. inside-jar-*.png is a 2752x1536 LANDSCAPE crop of the jar
     interior — the only landscape source in site/images, which is why it is
     the hero and honey-pool (1024 square) is the page wash. w=1600 here, not
     2752: the band is never wider than a viewport, and asking the Image CDN
     for the full source would ship megabytes to render a strip. Requesting
     BELOW the source width is safe; upscaling is what cost bandwidth on
     .site-hero--honey (see its comment in styles.css). */
  --hero-jar: url('/.netlify/images?url=/images/inside-jar-light.png&w=1600');
  /* ADDED 2026-08-04. The jar watermark inside every text input was a raw
     url() pinned to aime-jar-icon-LIGHT.png in styles.css, which made it the
     one brand mark on the site that could not follow the theme. It is a token
     now for the same reason --hero-jar is one: a CSS background cannot use the
     .jar-light / .jar-dark display swap, so the theme has to hand it the right
     file. w=96 matches the old call site; the mark renders at 35px. */
  --input-jar-mark: url('/.netlify/images?url=/images/aime-jar-icon-light.png&w=96');
  /* --- Hero scrim, REBUILT 2026-08-05: the plastic-wrap fix ---------------
     Was a FLAT ramp, rgba(255,255,255,0.88) -> rgba(245,249,253,0.94). Nearly
     solid, edge to edge. That is heavier than the rgba(11,14,26,0.55) this
     codebase already recorded as reading like plastic film, and it produced
     exactly that: a hero where the jar photo was washed to almost nothing and
     the band looked like a shrink-wrapped panel rather than a photograph.

     The mistake was treating the scrim as one number. Text does not sit
     everywhere — .site-hero centres its content, so the text occupies roughly
     the middle 44% and the top and bottom edges carry nothing at all. A flat
     scrim pays the contrast cost across the WHOLE image to protect a band.

     So this varies. Measured against every pixel of inside-jar-*.png inside
     the text band, worst case:

       α      light: secondary / amber(24px+)      dark: secondary / amber
       0.72   4.56 / 3.07  ok                      3.45 FAIL / 3.74
       0.80   5.22 / 3.52  ok                      4.62 / 5.00  ok
       0.86   5.72 / 3.86  ok                      5.75 / 6.23  ok

     Dark is the binding constraint and its floor is 0.80. 0.86 through the
     band gives real margin; 0.38 at the edges is well under any legibility
     threshold and is precisely where the photograph gets to be a photograph.

     Stops are 22%/78%, wider than the measured 28-72% text band, so a longer
     headline that wraps to three lines does not slide out from under cover.

     ONE COLOUR, varying alpha only — not surface->page as before. Two
     colours AND two alphas makes the contrast impossible to re-derive when
     someone changes it. If you change these numbers, re-run the measurement
     against the text band, not against the mean of the image. */
  --hero-jar-scrim: linear-gradient(
    180deg,
    rgba(245, 249, 253, 0.38) 0%,
    rgba(245, 249, 253, 0.86) 22%,
    rgba(245, 249, 253, 0.86) 78%,
    rgba(245, 249, 253, 0.38) 100%
  );

  /* Layout tokens moved to :root 2026-07-26 -- they were only ever defined
     here, in the light theme, while the site ships dark-first. See the
     comment on the :root block above for the full finding. */
}

[data-theme='dark'] {
  /* --- Surfaces & text --- */
  --color-bg-primary: #0b0e1a;
  --color-bg-surface: #151926;
  --color-text-primary: #f1f3f7;
  --color-text-secondary: #a3aab8;
  --color-text-muted: var(--color-text-secondary);

  /* --- Brand: Amber (primary) & Sapphire (accent) --- */
  --color-primary-amber: #e8a33d;
  --color-primary-amber-hover: #f5b85c;
  /* See the light-theme note. Dark theme's amber is LIGHT, so text on it must
     be dark -- the opposite of light theme. 8.92:1 rest, 10.89:1 hover. */
  --color-on-fill: #0b0e1a;
  /* LIGHTENED 2026-08-04, #5c8dff -> #8ab0ff. Sapphire was the DIMMEST token
     in this theme by a clear step: 6.14:1 on bg-primary, where amber measures
     8.92 and even --color-text-secondary manages 8.24. #8ab0ff lands at
     8.91 / 8.12 — level with amber, so the accent no longer reads as the one
     washed-out colour on a dark page.

     Lightening a colour is only free when nothing FILLS with it, because a
     lighter fill hurts whatever text sits on top. Checked all three fills
     before changing it, and all three improve:

       .aime-toast.info (toast.css)  text is var(--color-on-fill) = #0b0e1a
                                     -> 6.14 becomes 8.91
       admin.html TEST badge         text is var(--color-bg-primary) = #0b0e1a
                                     -> 6.14 becomes 8.91
       .imp-bar__fill (import.html)  4px progress bar, NO text on it at all;
                                     only has to be visible against the
                                     --color-border track -> 4.49 becomes 6.51

     That result is not luck, it is structural: this theme's --color-on-fill IS
     #0b0e1a, the same value as --color-bg-primary, so "sapphire as text on the
     page" and "dark text on a sapphire fill" are the SAME contrast pair and
     move together. The direction that would have been unsafe is white-on-
     sapphire (3.13 -> 2.16), and no dark rule does that — white on a status
     fill was the 2026-07-31 bug that --color-on-fill exists to prevent.

     Light theme is untouched: its sapphire is #1d4ed8 with a WHITE on-fill,
     a separate value in a separate block. */
  --color-accent-sapphire: #8ab0ff;
  --color-accent-gold: #f0b429;

  /* RECOLORED 2026-08-16, owner-directed -- see the light-theme note above for
     the full reasoning and the measurements. Amber -> Sapphire, built from the
     tokens rather than repeating their hex.

     This theme is where the old value actually hurt. Against the chat widget's
     hardcoded white, #a78bfa measured 2.72:1 and #7c5cff 4.35:1 -- both under
     AA, in the DEFAULT theme, on the chat header, the send button and the
     user's own message bubbles. With --color-on-fill (#0b0e1a) the new ends
     measure 8.92:1 and 8.91:1.

     Purple has not been deleted, it has been put back where it belongs: the
     --world-purple tokens directly below still carry Swag Studio's identity.
     What changed is that the SITE-WIDE token no longer wears a single
     feature's colour. */
  --gradient-brand: linear-gradient(
    90deg,
    var(--color-primary-amber) 0%,
    var(--color-accent-sapphire) 100%
  );

  /* --- Feature worlds --- see the light-theme note above. Same two
     --gradient-brand endpoints, named. #a78bfa is the violet that was already
     hardcoded in create-studio.html; it is perfectly legible here (6.44:1)
     and was 2.72:1 on the light theme, which is the entire bug. */
  --world-purple: #a78bfa; /* 7.07:1 on bg-primary · 6.44:1 on bg-surface */
  --world-purple-strong: #7c5cff; /* gradient endpoint ONLY — 4.03:1 on
                                     bg-surface, do not use as text */
  --world-purple-wash: rgba(167, 139, 250, 0.12);
  --world-teal: #2dd4bf; /* 10.33:1 · 9.41:1 */
  --world-teal-wash: rgba(45, 212, 191, 0.12);

  /* --- Structure --- */
  --color-border: #262b3d;
  --color-success: #34d399;
  --color-primary-amber-wash: rgba(232, 163, 61, 0.12);
  --color-tier-freemium-wash: rgba(160, 174, 192, 0.12);
  --color-success-wash: rgba(52, 211, 153, 0.12);
  --color-danger: #f87171;

  /* --- Forum-specific accents --- */
  --forum-bg-overlay: radial-gradient(
    ellipse at top,
    var(--color-primary-amber-wash),
    transparent 60%
  );
  --forum-accent-syrup: #e8a33d;
  --forum-card-bg: #1a1712;

  /* --- Tier colors (Freemium / Creator / Pro / Team) --- */
  --color-tier-freemium: #a0aec0;
  --color-tier-creator: #e8a33d;
  /* Tracks --color-accent-sapphire, lightened with it 2026-08-04. These are
     two tokens that have always held the SAME hex, and .badge--pro uses this
     one as border + text on a translucent wash, never as a fill — so the same
     audit applies and the numbers only go up: 5.19 -> 7.53 on that wash over
     bg-surface, 5.70 -> 8.28 over bg-primary. Leaving it at #5c8dff would have
     moved the "dimmest token in dark theme" title onto the Pro badge and put
     two visibly different blues on the same page. */
  --color-tier-pro: #8ab0ff;
  --color-tier-team: #f0b429;

  /* --- Page texture (ADDED 2026-08-02) ---
     Dark has far more headroom than light: amber (#e8a33d) measures 8.92:1
     on this page background, so the wash costs it almost nothing. Measured
     worst case over every pixel of honey-pool-dark.png:

       opacity  amber   text-secondary   text-primary
       0.14     6.14    5.68             11.93
       0.20     4.97    4.59              9.64

     0.14 is chosen to match the light theme's visual weight, not because
     0.20 fails — it does not. Room to go up here if the light value ever
     changes; see the light block for why light cannot. */
  --page-texture: url('/.netlify/images?url=/images/honey-pool-dark.png&w=1024');
  --page-texture-opacity: 0.14;

  /* NONE, deliberately — see the long note in the light block. Gold over a
     near-black page adds light and reads as depth, so a full-bleed wash is
     right here. Masking the centre out would remove exactly the effect that
     makes dark work. The light theme is the one that needed rescuing. */
  --page-texture-mask: none;

  /* Dark partner of the hero band. inside-jar-dark.png is ALSO the og:image
     on all 29 pages — same file, different transform (og asks for
     w=1200&h=630&fit=cover). Reusing one source for both is deliberate:
     the social card and the hero then show the same artwork. */
  --hero-jar: url('/.netlify/images?url=/images/inside-jar-dark.png&w=1600');
  /* Dark partner of the input watermark. See the light block for why this is a
     token at all. */
  --input-jar-mark: url('/.netlify/images?url=/images/aime-jar-icon-dark.png&w=96');
  /* Dark partner of the varying hero scrim. See the light block for the full
     reasoning and the measured table — this is the theme that SET the floor.
     Dark's --color-text-secondary (#a3aab8) fails at α=0.72 (3.45:1) and
     passes at 0.80 (4.62:1), so 0.86 through the text band is the number both
     themes share, with dark carrying the tighter margin.

     Do not lower 0.86 for dark without re-measuring secondary text against the
     inside-jar-dark.png text band specifically. It is the first thing to
     break, and it breaks quietly. */
  --hero-jar-scrim: linear-gradient(
    180deg,
    rgba(11, 14, 26, 0.38) 0%,
    rgba(11, 14, 26, 0.86) 22%,
    rgba(11, 14, 26, 0.86) 78%,
    rgba(11, 14, 26, 0.38) 100%
  );
}

body {
  background: var(--color-bg-primary);
  color: var(--color-text-primary);
  transition:
    background 0.2s ease,
    color 0.2s ease;
}
