/* Liquid & Larder — core styles shared by every page (guest + staff). */
/* v5 (30 Jul 2026): light AND dark. The :root block is the light theme, unchanged from
   v4 apart from two new tokens (--accent-ink, --chart-accent) that resolve to values
   already in use, so light renders exactly as it did. The [data-theme="dark"] block at
   the foot is the confirmed "Deep forest + Linen" palette.
   Which theme applies is decided by an inline script in each page's <head>, BEFORE these
   stylesheets load, so there is never a light frame in front of a dark page. That script
   resolves the stored preference (system | light | dark) to a concrete data-theme on
   <html>; the user's choice itself lives in localStorage under "ll-theme".
   The guest page has no such script and no data-theme, so it stays light on purpose —
   it is a payment-trust surface seen once, and its terms snapshot pairs with a known look. */
/* v4 (30 Jul 2026): colour tokens. Every colour in the hub now resolves through the
   :root block below — there is no raw hex or rgba() anywhere else in core.css, hub.css
   or any page's <style> block. Values are byte-for-byte what v3 rendered, so v4 is a
   pure refactor with no visual change; it exists so a second (dark) set of these tokens
   can be dropped in later without touching a single page.
   HOUSE RULE: a raw colour literal outside this block is a bug. Need an opacity?
   Use the channel triples — rgb(var(--green-rgb) / .08) — never rgba(29,62,59,.08). */
:root {
  /* --- base palette ----------------------------------------------------- */
  --bg:#faf8f4; --card:#ffffff; --ink:#242c2a; --dim:#646c68;
  --green:#1d3e3b; --green-hi:#28524d; --sand:#dcd1c0; --sand-soft:#f2ecdf;
  --err:#a44e33; --ok:#42704a; --info:#3e6a8a;

  /* --- the same palette as space-separated channels, so any opacity can be
         written as rgb(var(--x-rgb) / .08) --------------------------------- */
  --bg-rgb:250 248 244; --card-rgb:255 255 255; --ink-rgb:36 44 42; --dim-rgb:100 108 104;
  --green-rgb:29 62 59; --sand-rgb:220 209 192;
  --err-rgb:164 78 51; --ok-rgb:66 112 74; --info-rgb:62 106 138;

  /* --- surfaces and lines ----------------------------------------------- */
  --surface:#fbfaf7;        /* inset surface: inputs, textareas, sub-panels, data-table row hover */
  --surface-hover:#faf7f0;  /* generic table row hover (hub.css) */
  --sand-hi:#eae2cf;        /* hover state of --sand-soft: ghost buttons, back pill, app icon */
  --sand-cream:#faf6ec;     /* warm second stop of the sand gradient on amount / total boxes */
  --rule:#f0ece3;           /* light divider inside data tables, and the skeleton base */
  --rule-hi:#f8f5ee;        /* skeleton shimmer highlight */
  --border:#e6dfd2;         /* solid control border, stronger than --hairline */
  --hairline:rgb(var(--green-rgb) / .08);

  /* --- painted ON the green brand surface: buttons, active nav, avatars.
         These must stay light whatever the theme, so they are NOT --bg. ---- */
  --on-accent:#faf8f4; --on-accent-soft:#dcd1c0; --on-accent-warm:#e9dfce;

  /* --- overlays and elevation ------------------------------------------- */
  --overlay:rgb(22 48 46 / .40);   /* full-screen modal scrim */
  --shadow-card:0 1px 2px rgb(var(--green-rgb) / .04), 0 12px 40px -8px rgb(var(--green-rgb) / .10);
  --shadow-btn:0 2px 6px rgb(var(--green-rgb) / .30), inset 0 1px 0 rgb(var(--card-rgb) / .12);
  --shadow-modal:0 14px 44px rgb(0 0 0 / .22);
  --ring:0 0 0 3px rgb(var(--green-rgb) / .14);

  /* --- the brand colour as TEXT or an ICON rather than as a surface ------
         Card titles, tile names, app-icon strokes, the wordmark mask, the back
         pill. In light this is --green's value; in dark the two must diverge,
         because the surface stays a deep green while the text has to lift. ---- */
  --accent-ink:#1d3e3b;

  /* --- the channels used for INTERACTIVE TINTS: hover washes, track fills,
         selected rows, chip hover borders. Identical to --green-rgb in light,
         so nothing changes; in dark it becomes the warm sand, because a 7%
         wash of the dark green over a dark card is invisible. -------------- */
  --tint-rgb:29 62 59;

  /* --- the two ambient corner glows, as whole colours so dark can dial them
         right down rather than inherit a bright sand haze. ----------------- */
  --glow-a:rgb(var(--sand-rgb) / .5);
  --glow-b:rgb(var(--green-rgb) / .09);

  /* --- the primary data series. Deliberately NOT --accent-ink: dark's brand
         text is a warm off-white, and a warm off-white data line would wash
         out against the card. ------------------------------------------------ */
  --chart-accent:#1d3e3b;

  --r-card:16px; --r-input:10px;
  color-scheme: light;
}

/* ==========================================================================
   DARK — "Deep forest" surfaces with "Linen" brand text. Confirmed 30 Jul 2026
   after reviewing three candidates on the hub's own components. Every pairing
   below clears WCAG 2.1: text and chip inks at 4.5:1, decorative at 3:1.
   Only the values change; not one selector in the hub knows this block exists.
   ========================================================================== */
:root[data-theme="dark"] {
  /* --- base palette ----------------------------------------------------- */
  --bg:#0f1c1a; --card:#162723; --ink:#e9eae0; --dim:#9dada3;
  --green:#2a6055;          /* the brand SURFACE: button gradients, active nav, row anchor */
  --green-hi:#38776a;       /* the lighter stop of that gradient */
  --sand:#c6b59a; --sand-soft:#1f312c;
  --err:#e88d70; --ok:#71c98c; --info:#82b3d9;

  /* --- channel triples --------------------------------------------------- */
  --bg-rgb:15 28 26; --card-rgb:22 39 35; --ink-rgb:233 234 224; --dim-rgb:157 173 163;
  --green-rgb:42 96 85; --sand-rgb:198 181 154;
  --err-rgb:232 141 112; --ok-rgb:113 201 140; --info-rgb:130 179 217;

  /* --- surfaces and lines ----------------------------------------------- */
  --surface:#1c2f2b; --surface-hover:#213631; --sand-hi:#2a4139; --sand-cream:#23342c;
  --rule:#233830; --rule-hi:#2f473e; --border:#2f473e;
  --hairline:rgb(var(--sand-rgb) / .15);   /* warm, not green — a green hairline vanishes here */

  /* --- painted ON the green brand surface. Still light, because the button
         surface is still a deep green: nothing flips. ----------------------- */
  --on-accent:#f3f2e9; --on-accent-soft:#d5c9b0; --on-accent-warm:#e8dec8;

  /* --- brand text / icons: LINEN. 11.4:1 on a card. Note it sits close in
         lightness to --ink, so headings read as a warmer white rather than as
         a distinct hue — the hierarchy comes from Gilda Display and the size.
         That is the intended look, chosen over a lifted sage which read teal. */
  --accent-ink:#e4dccb;

  /* --- interactive tints go warm, so a hover wash is actually visible ---- */
  --tint-rgb:198 181 154;

  /* --- the ambient glows, dialled down: a 50% sand blur would fog the page */
  --glow-a:rgb(var(--sand-rgb) / .06);
  --glow-b:rgb(var(--green-rgb) / .38);

  /* --- overlays and elevation. Shadows do almost nothing on a dark surface,
         so elevation is carried by --hairline and the surface ladder; these
         remain only so cards and modals still have an edge. ----------------- */
  --overlay:rgb(5 14 13 / .68);
  --shadow-card:0 1px 2px rgb(0 0 0 / .30), 0 14px 40px -12px rgb(0 0 0 / .58);
  --shadow-btn:0 2px 6px rgb(0 0 0 / .42), inset 0 1px 0 rgb(255 255 255 / .07);
  --shadow-modal:0 18px 54px rgb(0 0 0 / .64);
  --ring:0 0 0 3px rgb(var(--sand-rgb) / .22);

  color-scheme: dark;
}
* { box-sizing:border-box; margin:0; padding:0; }
body { background:var(--bg); color:var(--ink); font-family:'Montserrat',sans-serif;
       -webkit-font-smoothing:antialiased; position:relative; overflow-x:hidden; }
/* ambient brand-tinted glows */
body::before, body::after { content:''; position:fixed; border-radius:50%; filter:blur(90px); z-index:-1; pointer-events:none; }
body::before { width:620px; height:620px; top:-260px; right:-160px; background:var(--glow-a); }
body::after { width:520px; height:520px; bottom:-240px; left:-180px; background:var(--glow-b); }
.card { background:var(--card); border:1px solid var(--hairline); border-radius:var(--r-card); box-shadow:var(--shadow-card); }
button { border:none; border-radius:999px; cursor:pointer;
  background:linear-gradient(180deg, var(--green-hi), var(--green)); color:var(--on-accent);
  font-family:'Montserrat',sans-serif; font-weight:600; letter-spacing:.18em;
  text-transform:uppercase; box-shadow:var(--shadow-btn);
  transition:transform .15s, filter .15s, opacity .15s; }
button:hover { transform:translateY(-1px); filter:brightness(1.06); }
button:disabled { opacity:.4; cursor:not-allowed; transform:none; filter:none; }
/* The one utility class the whole hub relies on. It used to be redeclared in
   twelve page <style> blocks and — the reason it lives here now — was MISSING
   from both pre-auth pages, so their "Copy link" button, marked class="hidden",
   was permanently visible and overflowed the viewport at 430px. Declared once,
   here, so a page that marks something hidden gets what it asked for. */
.hidden { display:none !important; }
.msg { font-size:13px; line-height:1.6; }
.msg.err { color:var(--err); }
.msg.ok { color:var(--ok); }
/* On phones the ambient corner glows are fixed-position and sit partly beyond the
   screen edge, so they can't be clipped by body overflow — hide them so the page
   never extends past the viewport (no horizontal pan / pinch-out reveal). */
@media (max-width:640px) { body::before, body::after { display:none; } }
/* Standalone launch (Home Screen / apple-mobile-web-app-capable): there is no
   Safari chrome any more, so our own content sits directly under the notch /
   Dynamic Island and above the home indicator unless we pad for it. Guarded to
   display-mode:standalone so a normal browser tab — where Safari already
   reserves that space itself — is never affected. env() resolves to 0 on
   browsers/devices without a safe area, so this is a no-op there too.
   Targeting <html>, NOT <body>: several pages (and hub.css itself) declare
   their own unconditional body{padding:...} for unrelated layout reasons.
   That's the same specificity as this rule and loads later in the cascade,
   so it silently won every time and the safe-area padding never rendered —
   the top of the page sat directly under the status bar on every standalone
   launch. Nothing anywhere sets padding on <html>, so this rule has no
   competition and always applies, on every current and future page. */
@media (display-mode:standalone) {
  html { padding-top:env(safe-area-inset-top); padding-bottom:env(safe-area-inset-bottom); }
  /* ...EXCEPT where a sticky .topbar is present, which is every staff page.
     Padding <html> pushes the document down, but a position:sticky bar pins to
     the VIEWPORT once you scroll, so it slid straight under the status bar the
     moment the page moved - which is exactly what a phone screenshot showed on
     15 Aug. The bar carries the inset itself now (hub.css), so html must not
     also carry it or the two stack. Pages with no topbar - the 404 and the
     guest page - keep the original behaviour. */
  html:has(.topbar) { padding-top:0; }
}
