/* ========================================================================
   SBV SURFACES — the shared MATERIAL layer
   ========================================================================

   Established 2026-08-30. Load AFTER colors.css:

     <link rel="stylesheet" href="../branding/colors.css">
     <link rel="stylesheet" href="../branding/surfaces.css">

   ------------------------------------------------------------------------
   WHY THIS FILE EXISTS
   ------------------------------------------------------------------------
   colors.css standardised the palette. Nothing standardised the MATERIAL —
   what a surface, an edge, an elevation or a corner is — so every app
   invented its own answer. Measured on three live screens (2026-08-30):

     screen                    page bg    dominant radius   distinct shadows
     brew_suite/index.php      #1a1a1a    8.8px             5
     opsdash/daily.php         #111111    4px               11
     brew_suite/wbs/           #141414    8/12px            7

   Three different page backgrounds means the canvas differs before anything
   is drawn on it, so the apps can never read as one product. Those three
   files alone carry TWELVE different border-radius values.

   It got worse silently: new_project/BOOTSTRAP_GUIDE.md tells every new app
   to write `background: var(--sbv-surface)` — and that variable had never
   been defined anywhere. Every app that followed the guide hit an undefined
   value and hardcoded a colour instead. It is defined here now.

   ------------------------------------------------------------------------
   WHY THIS IS NOT THE 2025 layouts.css MISTAKE
   ------------------------------------------------------------------------
   Read branding/KISS_POLICY.md: an attempt to force one HEADER LAYOUT onto
   every app failed and broke things. That was the LAYOUT layer, and the
   conclusion there still stands — opsdash's calendar grid must not be
   forced into a card list.

   This file is the MATERIAL layer, and the difference is not cosmetic:
   this file contains ONLY custom properties. No selectors, no component
   classes, no layout, no resets. A custom property that nothing references
   has no effect whatsoever, so adding this file cannot change how any app
   renders. Apps opt in one declaration at a time.

   KEEP IT THAT WAY. The moment a component class lands in here, this file
   becomes layouts.css and will fail the same way.

   ------------------------------------------------------------------------
   ADOPTING THIS FILE: ONE TRAP, AND IT IS SILENT
   ------------------------------------------------------------------------
   The tokens below only exist under [data-theme="dark"], so adopting this
   file means adding that attribute to <html>. Doing so ALSO activates the
   "Compatibility aliases (legacy apps)" block at the end of colors.css's dark
   theme, which defines the SHORT names --bg, --text, --border, --card,
   --muted, --primary and --brand on `:root[data-theme="dark"]`.

   That selector is specificity 0,1,1 and beats an app's own bare `:root`
   (0,1,0) REGARDLESS of load order. So an app that already defines --bg or
   --border for itself silently loses them the moment it opts in: nothing
   errors, nothing logs, the app's own values simply stop applying. It cost a
   debugging round in opsdash (page #1a1a1a instead of #141414, borders
   #3a3a3a instead of --sbv-surface-edge).

   Fix: write your own token block as
     `:root, :root[data-theme="dark"], .sbv-theme-dark { … }`.
   Equal specificity, and your stylesheet loads after colors.css.

   ⛔ THE CLASS IS A SEPARATE GUARD AND IT IS THE ONE PEOPLE MISS. colors.css
   declares those aliases on the attribute selector AND on `.sbv-theme-dark`.
   Many apps put that class on <body>, so colors.css re-declares --bg, --text
   and --border ON THE BODY ELEMENT — which shadows whatever :root passes down,
   no matter how specific the :root rule is. Guarding only the attribute makes
   the value correct when you inspect <html> and wrong on every element that
   actually uses it. Measured in brew_track and maintenance on 2026-09-08:
   --bg was the app's own value at <html> and colors.css's at <body>.
   Check both: getComputedStyle(document.body).getPropertyValue('--bg').

   ------------------------------------------------------------------------
   THE HOUSE LOOK
   ------------------------------------------------------------------------
   Glass + meaningful glow, proven in brew_suite/wbs/ (the champion app).
   Two rules travel with it, and both are load-bearing:

   1. GLOW MEANS SOMETHING. Yellow = running / focused, blue = in progress,
      red = blocked or full, green = just completed. A decorative hover glow
      must never sit on an element that already carries a meaningful one, or
      the meaning is gone.

   2. TRANSLUCENCY IS A CONTRAST HAZARD. A glass surface lifts the
      background under every label while the CSS `color` stays identical, so
      readability drops without a single line looking wrong. Glass alpha is
      held high (0.62–0.78) for exactly this reason. Any app adopting these
      tokens must verify contrast against the COMPOSITED ancestor chain —
      see apps/local/tests/wbs_ui/verify.mjs for a working check. It found
      real debt on its first run.
   ======================================================================== */

:root {
  /* ---- Structural: theme-independent -------------------------------- */
  --sbv-radius-sm: 8px;
  --sbv-radius-md: 12px;
  --sbv-radius-lg: 16px;
  --sbv-radius-pill: 999px;

  --sbv-blur: blur(18px) saturate(135%);

  /* One vocabulary for motion, so hovers feel like the same product. */
  --sbv-transition-fast: 0.16s ease;
  --sbv-transition-base: 0.22s ease;
  --sbv-ease-out: cubic-bezier(0.22, 0.9, 0.32, 1);
}

/* ========================================================================
   DARK — the proven path. Every internal app is dark.
   ======================================================================== */

:root[data-theme="dark"],
.sbv-theme-dark {
  /* ---- Surfaces ------------------------------------------------------
     surface-0 is the page canvas. Deliberately darker than the older
     --sbv-bg-primary (#1a1a1a): the ambient gradient below lightens the
     corners, so the base has to sit lower for the effect to read. The two
     are NOT unified yet — changing --sbv-bg-primary touches 80 files and
     deserves its own deliberate pass. Adopt --sbv-surface-0 for the page.  */
  --sbv-surface-0: #141414;

  /* SOLID IS THE DEFAULT, AND TRANSLUCENT IS THE UPGRADE — see the three
     tiers at the end of this file. A translucent surface is only a material
     when something blurs behind it; without `backdrop-filter` the same value
     is just a washed-out grey that drops contrast for nothing. These four
     therefore start solid and are re-declared inside an @supports block
     below. An app that writes `var(--sbv-surface-1)` gets the right one for
     the machine it is on, without asking and without any JavaScript. */
  --sbv-surface-1: #282828;                  /* panels, zones            */
  --sbv-surface-2: #3a3a3a;                  /* cards, raised            */
  --sbv-surface-3: #202020;                  /* rows, sunken             */
  --sbv-surface-strong: #1c1c1c;             /* header, modal            */

  /* The alias BOOTSTRAP_GUIDE.md has always referenced. */
  --sbv-surface: var(--sbv-surface-1);

  --sbv-surface-edge: rgba(255, 255, 255, 0.09);
  --sbv-surface-sheen: inset 0 1px 0 rgba(255, 255, 255, 0.05);

  /* Solid ON PURPOSE — for a dense screen that should stay opaque even where
     blur is available, because translucency costs more legibility than it
     buys atmosphere. A calendar grid is allowed to be opaque; it is still the
     same product if the radii and elevation match. Different from the tiering
     above: these never become translucent, whatever the machine can do. */
  --sbv-surface-1-solid: #282828;
  --sbv-surface-2-solid: #3a3a3a;
  --sbv-surface-3-solid: #202020;

  /* ---- Ambient light -------------------------------------------------
     Put on a fixed, pointer-events:none ::before behind everything. This is
     what makes backdrop-filter worth its cost — blur with nothing behind it
     is only expensive. Static on purpose: it is a light source, not an
     effect that asks for attention.                                       */
  --sbv-ambient:
    radial-gradient(1000px 560px at 12% -8%, rgba(255, 217, 0, 0.11), transparent 62%),
    radial-gradient(820px 460px at 96% 4%, rgba(59, 130, 246, 0.09), transparent 60%),
    radial-gradient(900px 620px at 55% 108%, rgba(34, 197, 94, 0.07), transparent 62%);

  /* ---- Elevation ------------------------------------------------------ */
  --sbv-elev-1: 0 2px 8px rgba(0, 0, 0, 0.35);
  --sbv-elev-2: 0 10px 30px rgba(0, 0, 0, 0.45);
  --sbv-elev-3: 0 24px 60px rgba(0, 0, 0, 0.65);

  /* ---- Glow — ring + soft halo + depth, per meaning ------------------- */
  --sbv-glow-brand: 0 0 0 1.5px rgba(255, 217, 0, 0.6), 0 0 26px rgba(255, 217, 0, 0.42), 0 0 64px rgba(255, 217, 0, 0.16), 0 10px 28px rgba(0, 0, 0, 0.5);
  --sbv-glow-brand-soft: 0 0 0 1px rgba(255, 217, 0, 0.35), 0 0 20px rgba(255, 217, 0, 0.24), 0 0 48px rgba(255, 217, 0, 0.08);
  --sbv-glow-danger: 0 0 0 1px rgba(239, 68, 68, 0.55), 0 0 20px rgba(239, 68, 68, 0.32), 0 0 44px rgba(239, 68, 68, 0.12);
  --sbv-glow-success: 0 0 0 2px rgba(34, 197, 94, 0.65), 0 0 26px rgba(34, 197, 94, 0.5), 0 0 60px rgba(34, 197, 94, 0.2);
  --sbv-glow-info: 0 0 0 1px rgba(59, 130, 246, 0.5), 0 0 18px rgba(59, 130, 246, 0.28);

  /* Chip/tint strength. A tint over a DARK surface composites dark, so it
     carries LIGHT text — a "yellow" chip at 16% renders olive, not yellow. */
  --sbv-tint-alpha: 0.16;

  /* Which tier is active. An app can read this to label its own toggle; it is
     NOT a switch — writing to it changes nothing. Tier 2 is set by the app,
     because only the app knows whether it mounted its canvas. */
  --sbv-fx-tier: 0;
}

/* ========================================================================
   THE THREE TIERS — good without a GPU, excellent with one
   ========================================================================

   The mistake this replaces: processdocumenter put its ENTIRE look behind one
   WebGL probe. A machine without WebGL lost the particle field (fair) but
   ALSO lost card depth, the hover lift and every glow — none of which need a
   GPU at all. Those are ordinary box-shadows the compositor draws once. A
   client on office hardware therefore saw a flat app, and the conclusion drawn
   from it was "the branding needs a GPU". It does not. One probe was asked to
   answer three different questions.

   So: three tiers, each gated on the thing it actually needs, and the first
   two need no JavaScript whatsoever.

     TIER 0 — always, every machine, no gate.
       Radii, edges, the sheen, elevation, GLOW, hover lift, and the static
       ambient wash (--sbv-ambient on a fixed ::before). All of it is paint
       -once box-shadow and gradient work. This tier alone already reads as
       the product. If an app looks flat here, that is a bug in the app, not a
       limit of the hardware.

     TIER 1 — @supports (backdrop-filter: blur(1px)).
       Translucent surfaces and blur. Gated on the FEATURE, not on a GPU:
       a browser that reports backdrop-filter can composite it. The four
       --sbv-surface-* tokens flip to their translucent values below, so an
       app written against them moves up a tier by doing nothing.

     TIER 2 — the app's own WebGL probe.
       Per-FRAME work: a particle canvas, a cursor glow that follows the
       pointer, drop-shadow on moving connectors, a breathing glow. This is
       the only tier that needs a probe, the only one that costs per frame,
       and the only one that needs a user-facing off switch. Reference
       implementation: apps/*/processdocumenter/ (body.pd-fx + `pd_fx_tier`
       in localStorage, which always beats auto-detection).

   PUTTING A TIER-2 EFFECT IN TIER 0 IS THE FAILURE MODE. Ask: does it repaint
   every frame? Then it is tier 2. Does it need something blurred behind it?
   Tier 1. Otherwise it is tier 0 and everybody gets it.
   ======================================================================== */

   The @supports block that does the flip sits at the END of this file, after
   both themes, so it can raise either one. Putting it here would have let the
   light block below overwrite it.
   ======================================================================== */

/* ========================================================================
   LIGHT — provided so this file is not dark-only. NOT yet proven in any
   app: glass over a light background is a different design problem (the
   blur has far less to work with, and glow reads as haze). Verify contrast
   before shipping a light-mode app on these.
   ======================================================================== */

:root[data-theme="light"],
.sbv-theme-light {
  --sbv-surface-0: #f4f4f5;
  /* Solid first, like dark — the @supports block at the end raises these. */
  --sbv-surface-1: #ffffff;
  --sbv-surface-2: #ffffff;
  --sbv-surface-3: #f5f5f5;
  --sbv-surface-strong: #ffffff;

  --sbv-surface: var(--sbv-surface-1);

  --sbv-surface-edge: rgba(0, 0, 0, 0.08);
  --sbv-surface-sheen: inset 0 1px 0 rgba(255, 255, 255, 0.7);

  --sbv-surface-1-solid: #ffffff;
  --sbv-surface-2-solid: #ffffff;
  --sbv-surface-3-solid: #f5f5f5;

  --sbv-ambient:
    radial-gradient(1000px 560px at 12% -8%, rgba(255, 217, 0, 0.16), transparent 62%),
    radial-gradient(820px 460px at 96% 4%, rgba(59, 130, 246, 0.08), transparent 60%);

  --sbv-elev-1: 0 1px 3px rgba(0, 0, 0, 0.08);
  --sbv-elev-2: 0 8px 24px rgba(0, 0, 0, 0.1);
  --sbv-elev-3: 0 20px 48px rgba(0, 0, 0, 0.16);

  --sbv-glow-brand: 0 0 0 1.5px rgba(202, 138, 4, 0.5), 0 0 22px rgba(255, 217, 0, 0.4);
  --sbv-glow-brand-soft: 0 0 0 1px rgba(202, 138, 4, 0.3), 0 0 16px rgba(255, 217, 0, 0.24);
  --sbv-glow-danger: 0 0 0 1px rgba(220, 38, 38, 0.45), 0 0 16px rgba(239, 68, 68, 0.22);
  --sbv-glow-success: 0 0 0 1.5px rgba(22, 163, 74, 0.5), 0 0 20px rgba(34, 197, 94, 0.28);
  --sbv-glow-info: 0 0 0 1px rgba(37, 99, 235, 0.45), 0 0 16px rgba(59, 130, 246, 0.22);

  --sbv-tint-alpha: 0.14;

  --sbv-fx-tier: 0;
}

/* ========================================================================
   TIER 1 — the upgrade, gated on the feature it needs
   ========================================================================
   Last in the file on purpose: it must be able to raise BOTH themes, and a
   theme block declared later would otherwise overwrite it. @supports has no
   specificity of its own, so ordering is the whole mechanism here.

   Gated on `backdrop-filter`, not on a GPU. A browser that reports the
   property can composite it; one that does not would render these values as
   flat washed-out grey over nothing, which reads as a broken theme rather
   than a cheaper one. Everything else in this file — radii, edges, sheen,
   elevation, glow, the ambient wash — stays on in tier 0 for everybody.
   ======================================================================== */

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  :root[data-theme="dark"],
  .sbv-theme-dark {
    --sbv-surface-1: rgba(40, 40, 40, 0.62);
    --sbv-surface-2: rgba(58, 58, 58, 0.66);
    --sbv-surface-3: rgba(32, 32, 32, 0.62);
    --sbv-surface-strong: rgba(28, 28, 28, 0.78);
    --sbv-fx-tier: 1;
  }

  :root[data-theme="light"],
  .sbv-theme-light {
    --sbv-surface-1: rgba(255, 255, 255, 0.72);
    --sbv-surface-2: rgba(255, 255, 255, 0.86);
    --sbv-surface-3: rgba(245, 245, 245, 0.8);
    --sbv-surface-strong: rgba(255, 255, 255, 0.9);
    --sbv-fx-tier: 1;
  }
}
