/* ==========================================================
   DeltaVisor · universal tokens
   cdn.deltavisor.com/css/v1/universal.css

   THE ONE HOME FOR ANYTHING THAT MUST NOT DIVERGE. Colour, the type
   scale, the spacing scale, radii, shadows, easing, durations, and the
   dark colour-scheme declaration. Nothing else belongs here: if a rule
   describes a COMPONENT rather than a VALUE, it goes in the surface
   that uses it.

   EVERY surface loads this first: the marketing site, the region
   landing pages, the admin console, the dashboard, and the app. That
   is not a convention, it is enforced. `server/services/eventGroups.js`
   PARSES the --eg-* block out of this file at require time and throws
   at boot if an event tag has no colour token here, so a colour cannot
   exist in the product without existing in this file, and cannot be
   changed in one surface without changing all of them.

   SPLIT OUT OF design-system.css 2026-08-28. That file had carried the
   tokens and every component atom together, which meant the marketing
   site pulled 40KB of form fields, switches and chips to get at the
   colours, and used 38% of it. The tokens are universal; the component
   atoms are not. design-system.css keeps the atoms and now REQUIRES
   this file to be loaded before it.

   If a page loses every colour at once, it is missing this link. That
   failure is deliberate and loud, in preference to each surface
   carrying its own drifting copy.
========================================================== */

:root {
  /* Tells the browser the page is dark-themed. Without this,
     Chrome/Edge/Safari render form controls with their light-mode
     UA chrome, which can override author background-color on
     <input>, scrollbars draw light, and autofill-yellow flashes
     bright. One line, fixes a class of "why is my input white?"
     bugs across the design system. */
  color-scheme: dark;

  /* ---------- Surfaces · elevation contract ----------
     OLED near-black ground with neutral-cool surfaces lifted via
     tint. Numbered elevation levels, higher index = brighter, so
     cards-on-panels-on-page reads as a coherent z-stack rather than
     guessing at colour names. Equal +0x0F (≈+15) RGB step per level
     gives a perceptually uniform climb from L0 to L6.

       --paper-0   L0  page background                       (darkest)
       --paper-1   L1  panels, primary surfaces, sticky alerts
       --paper-2   L2  raised items inside panels
                       (e.g. .summary-cell, .report-card,
                        .image-thumb, .preview boxes, base
                        chrome for form controls)
       --paper-3   L3  floating UI / hover-lift target for L2
                       (modals, dropdowns, popovers, form-control
                        hover state, controls raised inside an L2
                        card)
       --paper-4   L4  hover-lift target for L3
                       (e.g. a select inside a card, lifting on
                        hover from L3 base to L4)
       --paper-5   L5  reserved, hover-lift for L4 or rare deeper
                       nesting
       --paper-6   L6  reserved, top of the contract; use only
                       when there's a real surface stacking case
                                                            (brightest)

     Hover-lift convention: an interactive element at level N
     hovers up to N+1. Most elements never need to go above L3, 
     L4-L6 exist for elements that already start raised inside a
     card and still want a hover affordance.

     The `--bar` value below is a semi-transparent variant of L1
     (rgba(13, 13, 16, 0.72)) used by translucent backdrop-blurred
     bars (toolbars, sticky alerts). Kept as a literal because CSS
     can't read a token's components into rgba(). */
  --paper-0:       #060607;
  --paper-1:       #131316;
  --paper-2:       #26262C;
  --paper-3:       #35353D;
  --paper-4:       #44444C;
  --paper-5:       #53535B;
  --paper-6:       #62626A;
  --bar:           rgba(13, 13, 16, 0.72);

  /* ---------- Ink ----------
     Pure white at the top, neutral grays down. Body text uses
     --ink-soft; primary titles and big numerals use --ink. */
  --ink:           #FFFFFF;
  --ink-soft:      #E5E5EA;
  --ink-mid:       #A1A1A8;
  --ink-ghost:     #80808b;

  /* ---------- Hairlines ----------
     Paper at low opacity. Used sparingly, most structure comes
     from surface tinting, not borders. */
  --rule:          rgba(255, 255, 255, 0.07);
  --rule-soft:     rgba(255, 255, 255, 0.03);

  /* ---------- Contrast layers ----------
     Translucent black used as a halo / scrim under bright content
     (e.g. paint-order stroke on text drawn over the heatmap, or
     a backdrop scrim under floating UI). Theme-flips if we ever
     ship light mode. */
  --shade:         rgba(0, 0, 0, 0.65);

  /* ---------- Shadows ----------
     Elevation scale for floating surfaces (tooltips, popovers,
     modals). Token bundles offset + blur + color so a refactor
     ripples consistently. */
  --shadow-sm:     0 0.25rem 0.625rem rgba(0, 0, 0, 0.32);
  --shadow-md:     0 0.5rem 1.5rem rgba(0, 0, 0, 0.45);
  --shadow-lg:     0 1rem 2.5rem rgba(0, 0, 0, 0.55);

  /* ---------- Accent ----------
     iOS system blue (dark-mode variant). The single chromatic
     signal across CTAs, active states, brand mark fill, focus
     rings. */
  --accent:        #0A84FF;
  --accent-deep:   #3A9CFF;
  --accent-tint:   rgba(10, 132, 255, 0.16);

  /* Forest carry-over, maps to OK in the status scale */
  --forest:        #32D17A;

  /* ---------- Status palette ----------
     Saturated iOS system colors. `info` is purple to differentiate
     from the blue accent on the wheel. */
  --status-success:        #32D17A;
  --status-success-bg:     rgba(50, 209, 122, 0.16);
  --status-success-border: rgba(50, 209, 122, 0.30);
  --status-warning:        #FFC83D;
  --status-warning-bg:     rgba(255, 200, 61, 0.16);
  --status-warning-border: rgba(255, 200, 61, 0.30);
  --status-error:          #FF6058;
  --status-error-bg:       rgba(255, 96, 88, 0.16);
  --status-error-border:   rgba(255, 96, 88, 0.30);
  --status-info:           #0A84FF;
  --status-info-bg:        rgba(10, 132, 255, 0.16);
  --status-info-border:    rgba(10, 132, 255, 0.30);

  /* ---------- Signal colors (--eg-*) ----------
     FOUR GROUPS OF TOKENS LIVE HERE, and they are not interchangeable. Before deleting one
     that looks unused, check which group it is in:

       1. TAG NAMESPACE VALUES, read by services/eventGroups (which parses this file, so a
          missing token FAILS STARTUP): --eg-work/ownership/money/condition (the filter bar,
          `domain`), --eg-positive/negative/neutral (`polarity`), --eg-opened/advanced/closed
          (`stage`).
       2. LANDING PAGE CATEGORIES, read by services/landingRenderService for the
          server-rendered SEO pages, which inline their styles and so need literal hex:
          --eg-permits/construction/landuse/violations/tax/foreclosure/vacancy/sales/
          inspections/demolition/other.
       3. THE DIGEST EMAIL uses group 1's values as literal hex, since email has no CSS vars.
       4. MARKETING DECORATION, purely for colored swatches in prose on site/index.html:
          --eg-cofo, --eg-demolitions.

     The old eight-chip bar is gone (the bar is now the `domain` namespace), which is why some
     of group 2's names look like former chips. They are the landing page's own named
     intersections and are still live.
  ---------- original notes ----------
     THE single source for the color of every change-event signal, so the map dots,
     the filter chips, the feed, the histograms, and the landing pages all speak one
     palette. Anything that shows a signal color reads a var below (CSS uses var()
     directly; the map/canvas layers resolve it in JS by name, see egColor()). Permits
     is indigo, deliberately off the brand accent blue. The first six are the canonical
     event GROUPS; the rest are the landing pages' finer distress/opportunity categories.
     Organized by polarity: POSITIVE / opportunity signals are cool (green, indigo, cyan);
     NEGATIVE / distress signals are warm (amber, orange, red). None use the brand blue. */
  --eg-permits:      #32D17A; /* positive: green */
  --eg-sales:        #2D6BF5; /* positive: royal blue (clear of purple and the brand azure) */
  --eg-cofo:         #64D2FF; /* positive: cyan */
  --eg-construction: #9B7BFF; /* positive: violet (owner-declared work start, distinct from the permits green) */
  --eg-violations:   #FF7A2E; /* negative: red-orange (between demolitions and distress) */
  --eg-demolitions:  #FFD633; /* negative: yellow-amber */
  --eg-distress:     #FF3B30; /* negative: pure red (was coral) */
  --eg-landuse:      #00C2A8; /* teal: was cyan, which collided with --eg-cofo in the bar */
  --eg-tax:          #FFC83D;
  --eg-foreclosure:  #FF375F;
  --eg-vacancy:      #D9730D;
  --eg-inspections:  #BF5AF2;
  --eg-demolition:   #8C6D62; /* warm brown: was grey, which collided with --eg-other */
  --eg-condemnation: #8E1B16; /* deepest red: the most severe condition signal */
  --eg-other:        #80808b;

  /* Domain facet values. A domain is a complete classification of the event
     vocabulary rather than a curated grouping, so these read as neutral areas of a
     property's life, not as good/bad polarity like the groupings above. */
  --eg-work:         #32D17A; /* the build-and-alter lifecycle: matches permits green */
  --eg-ownership:    #2D6BF5; /* who owns it: matches the sales royal blue */
  --eg-money:        #FFC83D; /* financial obligations, financing AND distress: neutral gold */
  --eg-condition:    #D9730D; /* state of the structure and land: matches vacancy orange */

  /* Polarity: whether an event is good or bad FOR THE PROPERTY. The landing page has
     carried this dimension as `polarity: up|down` since it existed; these are the same
     idea promoted to a tag. Polarity IS meaningful colour, unlike domain or stage. */
  --eg-positive:     #32D17A;
  --eg-negative:     #FF3B30;
  --eg-neutral:      #8E8E93;

  /* Stage values: where an event sits in its subject's lifecycle. Deliberately
     NEUTRAL greys, because a stage carries no good/bad meaning on its own and
     coloring it would imply one. */
  --eg-opened:       #64D2FF;
  --eg-advanced:     #8E8E93;
  --eg-closed:       #48484A;

  /* Indicator families (services/regionIndicators, the region EMA charts). A fifth group,
     declared rather than borrowed, because these are tag INTERSECTIONS and not a namespace:
     growth is domain:work AND polarity:positive, recovery is polarity:positive that is neither
     work nor ownership. Three of the four reuse an existing token because the intersection has
     the same meaning as its dominant tag, and only `recovery` needs its own: it is positive, so
     --eg-positive would make it indistinguishable from growth on the same chart.

       growth   -> --eg-work / --eg-positive  (#32D17A)
       distress -> --eg-negative              (#FF3B30)
       turnover -> --eg-ownership             (#2D6BF5)
       recovery -> --eg-recovery              below

     Teal reads positive without reading as growth. It is --eg-landuse's value, reused as a HEX
     rather than as a name: land use and recovery are unrelated, and aliasing them would make a
     future change to one silently move the other. */
  --eg-recovery:     #00C2A8;
  --status-pending:        #A1A1A8;
  --status-pending-bg:     rgba(255, 255, 255, 0.06);
  --status-pending-border: rgba(255, 255, 255, 0.12);

  /* ---------- Badge backgrounds ----------
     Inline pill tints. Slightly more saturated than the panel
     `--status-*-bg` set since they sit on the body ground rather
     than under a headline. */
  --badge-neutral-bg: rgba(255, 255, 255, 0.06);
  --badge-success-bg: rgba(50, 209, 122, 0.18);
  --badge-warning-bg: rgba(255, 200, 61, 0.18);
  --badge-danger-bg:  rgba(255, 96, 88, 0.18);
  --badge-info-bg:    rgba(10, 132, 255, 0.18);
  --badge-pending-bg: rgba(255, 255, 255, 0.08);
  --badge-unknown-bg: rgba(255, 255, 255, 0.06);

  /* ---------- Radius ----------
     Rounded scale. Capsule pills use --radius-full; cards use
     --radius-lg; buttons use --radius-md; inputs --radius-md. */
  --radius-sm:   0.375rem;
  --radius-md:   0.625rem;
  --radius-lg:   0.875rem;
  --radius-full: 9999px;

  /* ---------- Contracts overridden by a surface or an ancestor ----------
     These are DEFAULTS, not constants: a surface or a raised container sets its own value on
     an ancestor and every consumer below inherits it (admin widens the content cap; a card
     already sitting at L3 lifts its fields to L3/L4). The default used to live in a `var()`
     fallback, which meant the token was undefined everywhere and the fallback was doing the
     work. Same behaviour, declared instead of implied. */
  --app-main-max:   68.75rem;
  --field-bg:       var(--paper-2);
  --field-bg-hover: var(--paper-3);

  /* ---------- Type ----------
     Geist does all the work. --font-mono is reserved for IDs,
     tabular numerals, and small caption-style chips. --font-wordmark
     is the brand mark face and nothing else, referenced only by
     .app__mark, so the wordmark family can move without touching
     display or body. Currently Geist, at a heavier weight than the
     UI uses. */
  --font-display:  "Geist", -apple-system, "SF Pro Display", BlinkMacSystemFont, "Helvetica Neue", sans-serif;
  --font-body:     "Geist", -apple-system, "SF Pro Text", BlinkMacSystemFont, "Helvetica Neue", sans-serif;
  --font-mono:     "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --font-wordmark: "Geist", -apple-system, BlinkMacSystemFont, sans-serif;

  /* ---------- Type scale ---------- */
  --t-3xs: 0.625rem;
  --t-2xs: 0.6875rem;
  --t-xs:  0.75rem;
  --t-sm:  0.8125rem;
  --t-base:0.875rem;
  --t-md:  0.9375rem;
  --t-lg:  1.0625rem;
  --t-xl:  1.25rem;
  --t-2xl: 1.625rem;
  --t-3xl: 2.125rem;
  --t-4xl: 2.75rem;
  --t-5xl: 3.5rem;
  --t-6xl: 4.5rem;

  /* Badge typography, sans, not mono caps */
  --badge-font-size:      0.6875rem;
  --badge-padding:        0.1875rem 0.625rem;
  --badge-letter-spacing: 0;

  /* ---------- Space ---------- */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4.5rem;
  --s-9: 6.5rem;
  --s-10: 9rem;
  --s-11: 12rem;

  /* ---------- Structure ---------- */
  --container: 82.5rem;
  --gutter:    clamp(1.25rem, 4vw, 3rem);
  --rule-w:    1px;

  /* ---------- Motion ---------- */
  --ease:        cubic-bezier(0.2, 0, 0, 1);
  --ease-out:    cubic-bezier(0.2, 0, 0, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --dur-fast: 120ms;
  --dur:      200ms;
  --dur-slow: 380ms;
  --dur-1: 200ms;
  --dur-2: 380ms;
  --dur-3: 600ms;
  --dur-4: 900ms;
}
