/* Ossintheus design tokens — the single source of truth for the whole product.

   Every surface loads this file first: the investigation shell, the map
   workspace, and the login entry. Nothing else may declare a colour, radius,
   or font; other stylesheets alias these names instead.

   The system is "an instrument, not a dashboard". Depth comes from surface
   steps and 1px hairlines — there is not one drop shadow in the product.

   Colour is state, never chrome. The product has ONE signal hue, the cyan the
   marketing site is built on, and modules are told apart by a step within that
   hue rather than by a hue of their own. The previous scheme handed each module
   a different colour — gold to Case, amber to Phone, violet to Discord, green
   to Email — which spent four of the semantic colours on decoration: amber is
   how the product says "stale", violet is how it says "inferred", green is how
   it says "verified". A screen where Phone is permanently amber cannot then use
   amber to mean anything. Accent stays under ~8% of any screen.

   Text is dimmed by colour value, never by alpha. --text-2 is the floor for
   anything a person has to read; --text-3 is for labels only and never carries
   a sentence. The old system dimmed nav labels with opacity, which is how
   "Username" ended up unreadable at 40% grey on black. */

:root {
  color-scheme: dark;

  /* ---- Surfaces. The marketing site's ramp, value for value. ---- */
  --void: #08080a;          /* the gutter the shell floats on      */
  --base: #0f0f10;          /* app background                      */
  --surface: #151516;       /* cards                               */
  --surface-2: #1a1a1c;     /* nested panels, inputs, wells        */
  --surface-3: #1c1c1d;     /* source-pool idle cell, raised hover */
  --well: #0b0b0c;          /* recessed — code, logs, empty states */
  --line: #232325;          /* hairlines — 1px, everywhere         */
  --line-2: #2f2f33;        /* hairline at emphasis                */
  --line-soft: #1a1a1c;     /* a channel, not a line               */

  /* ---- Text. Four levels, all solid colours. ---- */
  --text: #f3f3f3;          /* primary                                   */
  --text-1: #c8c5c2;        /* primary, one step back                    */
  --text-2: #a29c99;        /* secondary — the floor for readable text   */
  --text-3: #878180;        /* labels only, never sentences              */

  /* ---- The mark's planet (templates/_mark.html): its body and lit edge. The reticle and
     ring are currentColor, so they take --text; the planet keeps two greys of its own. ---- */
  --mark-planet: #2c2c2f;
  --mark-planet-edge: #6e6e73;

  /* ---- The signal. One hue for the whole product. ---- */
  --signal: #66b8c9;
  --signal-deep: #315d68;

  /* ---- Module accents. One theme for every module (owner, 2026-11-04): each is the
         signal itself. The names stay so a module can be given its own step again. ---- */
  --accent-map: var(--signal);
  --accent-username: var(--signal);
  --accent-email: var(--signal);
  --accent-phone: var(--signal);
  --accent-discord: var(--signal);
  --accent-telegram: var(--signal);
  --accent-social: var(--signal);
  --accent-recon: var(--signal);
  --accent-forensics: var(--signal);
  /* Research lives inside a case rather than beside the modules, and reads what
     they produced — so it takes a quiet step rather than a bright one. */
  --accent-research: var(--signal);
  /* Case is the centre of the product, so it carries the signal itself. */
  --accent-case: var(--signal);
  --accent-breach: #d66b66;

  /* The accent of whatever surface you are in. Panels set this; components
     read it, so one rule paints an icon, a rail bar and a pool cell. */
  --accent: var(--accent-map);

  /* ---- Evidence states. The product's central claim, as colour.
         Never used alone: every state also carries its word. ---- */
  --state-available: var(--accent);
  --state-inferred: #9a7ac7;
  --state-stale: #d6a75c;
  --state-refused: #d66b66;
  --state-missing: #4a4a4e;
  --state-empty: #2c2c2f;
  --state-unavailable: #7a3a32;
  --state-truncated: #d6a75c;

  /* The source pool's cells. Lifted from the state greys above because a cell is
     4px of colour on a well, not a badge beside a word: at --state-missing an
     answered source was one step off an unanswered one. The legend under the
     pool reads these too, so a swatch and the cell it names are one colour. */
  --pool-idle: #34343b;
  --pool-checking: #3f5c6b;
  --pool-missing: #61616c;
  --pool-empty: #4c4c55;
  --pool-unavailable: #8a4a3e;

  /* Semantic colours that are not any one module's. Used for verdicts that mean
     the same thing wherever they appear. */
  --ok: #83b98b;
  --warn: #d6a75c;
  --violet: #9a7ac7;
  --critical: #d66b66;
  --critical-dim: #2a1512;
  /* Weather borrowed --text-2 and, dimmed to 0.55 while switched off, arrived on
     screen as body-text grey — indistinguishable from a disabled control. It
     gets a hue of its own: slate rather than the map's cyan, so the two blue
     layers are still told apart. */
  --sky: #8fa3bf;
  /* The keyless map feeds, one hue each and borrowed by nothing else. Fires are
     orange rather than --critical, which the EONET wildfire layer already owns;
     ALPR is green-teal rather than cyan, which is selection. */
  --layer-fires: #e8743b;
  --layer-disasters: #e39ac0;
  --layer-outages: #c7d36b;
  --layer-events: #a9c7f5;
  --layer-alpr: #5fd1b5;
  /* A camera's square on the map, by what the last check found. The same four
     values are painted into the marker bitmaps in static/map/basemap.js. */
  --camera-online: #3fd0e6;
  --camera-streaming: #4ade80;
  --camera-unchecked: #f2c94c;
  --camera-down: #e5534b;
  /* Infrastructure. Cable lines keep TeleGeography's per-system colours on the
     map, so the cable token is the bone of their landing points. Solar is gold:
     more saturated than --sun, which is the geo tools' own, and lighter than
     --warn, which means stale. */
  --layer-cables: #d9e2ea;
  --layer-solar: #f2c94c;

  /* Vessels. Rose, because every blue on the map is already spoken for and
     the greens and ambers mean verdicts. */
  --vessel: #d08fb4;

  /* The sun on the geo tools' sun and shadow compass (static/geo-sky.js), and
     nothing else. Pale straw rather than warning amber, which means "stale". */
  --sun: #e8d7a0;

  /* The scene page (static/scene/). The past of a trail through time: a warm, desaturated
     gold-brown, neither --warn (stale) nor --sun. The Raw style's void: pure black. The 13
     semantic classes (model.SEMANTIC), for "Colour: Class" only, borrowed by nothing else. */
  --scene-past: #a8814f;
  --scene-raw-void: #000000;
  --scene-class-unlabelled: #6e6a66;
  --scene-class-sky: #8fa8c8;
  --scene-class-person: #e07a5f;
  --scene-class-animal: #d9a441;
  --scene-class-vehicle: #c76fb0;
  --scene-class-vegetation: #6fa96b;
  --scene-class-ground: #9c8b72;
  --scene-class-building: #b9b2a6;
  --scene-class-water: #4f8fbf;
  --scene-class-furniture: #a5765a;
  --scene-class-object: #7fb8a8;
  --scene-class-screen: #d4d35a;
  --scene-class-sign: #e8a0a0;
  /* The world lane (static/scene/world.js): map context, never evidence, in hues of its own */
  --scene-context: #5c5c60;       /* terrain and building particles with no imagery under them */
  --scene-context-dim: #3a3a3e;   /* a viewshed sample the camera cannot see */
  --scene-viewshed: #8fb3a0;      /* a viewshed sample it can: its own hue, not the signal's */
  /* The view modes (Layers, Colour), each a rendering of the data and never a sensor reading. Depth
     borrows --text to --signal-deep; a missing reading is --state-missing. */
  --scene-height-low: #2f4858;    /* Height: the fitted floor (0) ... */
  --scene-height-high: #f2dfb4;   /* ... to the top of the place */
  --scene-false-0: #1a1030;       /* Brightness, false colour: darkest ... */
  --scene-false-1: #7a1f5c;
  --scene-false-2: #e0603a;
  --scene-false-3: #f8e7a0;       /* ... to brightest. The picture's brightness, not heat */
  --scene-lowlight: #a6f0a0;      /* Low light: brightness in one green ramp, a stylisation */
  --scene-edge: #f0ead6;          /* Edges: the photo's strongest edge, from near black */
  --scene-change-first: #9c6580;  /* Changed: no point of the other visit near it, of the first visit ... */
  --scene-change-later: #e2b9cb;  /* ... of a later one: two steps of one quiet rose, the rest --text-3 */

  /* ---- 3D map models (static/map/globe3d.js) ----
     Bodies are warm bone, the same family as the text, so a sky full of
     aircraft reads as objects rather than as accent. Shape, not colour, tells
     a military jet from an airliner; the military tone is only a quieter
     neutral beside it. Solar panels are a deep neutral slate. A satellite
     draws a long white light streak behind it, like a long-exposure photo
     of the ISS crossing the night sky, and glows white at the craft (the
     --thruster-* names are the streak and its glare: pure white core, a
     neutral glow, only the faintest cool edge); its orbit is a near-white
     hairline. On the globe a satellite reads as a small bright object, not
     as accent colour. Selection is still signal cyan. */
  --model-hull: #e9e5dc;
  --model-military: #a3aa9c;
  --model-panel: #353c47;
  --thruster-hot: #ffffff;
  --thruster-glow: #eef2fb;
  --thruster-edge: #cdd8ee;
  --orbit-line: #d8dde3;
  /* Daylight, for the map and nothing else: the sky a pitched camera looks up
     into, the paler air at its horizon, and the haze where the two meet the
     ground. The flight deck's sky (static/map/flight-cockpit.js) is the first
     two; the globe's (static/map/globe-polish.js) and the backdrop behind the
     map canvas (static/map-workspace.css) are all three. The shell around the
     map stays dark; this is what is past the planet's edge once the camera is
     low enough to be inside the air. */
  --daylight-sky: #3f79b8;
  --daylight-horizon: #b9d3ea;
  --daylight-haze: #d9e6f0;
  --model-vessel-hull: #5e6873;
  --model-outline: #06080b;

  /* ---- Paper. The printed case report only (static/case-report.css): ink
         on white, because a dark page wastes toner and photocopies to mud.
         The same meanings as on screen, each a step deep enough to read in
         print — signal, estimate (violet), approximate (amber), refused. ---- */
  --paper: #ffffff;
  --paper-ink: #17191c;
  --paper-ink-2: #4b5057;
  --paper-line: #d3d6da;
  --paper-well: #f3f4f5;
  --paper-signal: #1d6474;
  --paper-warn: #7d520f;
  --paper-violet: #5a3d88;
  --paper-critical: #93302b;

  /* ---- Shape ----
     The brand sheet (DESIGN.md) has always specified 10 / 14 / 22. This file
     capped everything at 4px, and the whole product came out square — panels,
     cards, floating overlays and the map inspector all with the same near-hard
     corner, which is what made a dense screen read as a stack of boxes rather
     than as surfaces. The scale now matches the brand, and it is a scale rather
     than one number: a chip, a control, a panel and a sheet are different sizes
     of thing and should not share a corner.

     Data still gets the hard edge. A state chip or an evidence tag is a label,
     not a surface, and rounding those was never the problem. */
  --radius: 10px;          /* controls, cards, inputs   */
  --radius-panel: 14px;    /* panels and floating boxes */
  --radius-sheet: 22px;    /* sheets and dialogs        */
  --radius-sm: 6px;        /* small controls, chips     */
  --radius-none: 0;

  /* ---- Type. Three faces, three jobs. ----
     Sans for reading, mono for anything exact, Martian for eyebrows and state
     tags. Fallbacks are metric-adjacent so the swap is quiet. */
  --font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Consolas, monospace;
  --label: "Martian Mono", "IBM Plex Mono", ui-monospace, monospace;
  /* The display face, shared with the marketing site. Spent on page and panel
     titles only — the sizes where a blocky face is legible and where a product
     is allowed to have a voice. Falls back to the reading face, not to a system
     pixel font, so a failed download is quiet rather than wrong. */
  --display: "Pixelify Sans", "IBM Plex Sans", ui-sans-serif, sans-serif;

  /* Scale: 11 / 13 / 15 / 20 / 32 / 48. Nothing between 15 and 20 — the gap is
     what makes the hierarchy read as decided rather than interpolated. */
  --size-11: 0.6875rem;
  --size-13: 0.8125rem;
  --size-15: 0.9375rem;
  --size-20: 1.25rem;
  --size-32: 2rem;
  --size-48: 3rem;

  /* ---- Layout ---- */
  --rail-width: 178px;
  /* The shell and the sidebar read --sidebar-width, so it defaults to the rail
     token; a dragged width is written to :root inline and still wins. */
  --sidebar-width: var(--rail-width);
  --topbar-height: 64px;

  /* ---- Motion ----
     Instruments answer immediately. Hover and press are effectively instant;
     only the run sequence is choreographed, and only once per investigation. */
  --instant: 60ms;
  --t-color: 60ms;
  --t-press: 80ms;
  --t-panel: 220ms;
  --t-enter: 380ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-color: linear;
  --spring: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* a move that starts from rest and comes to rest: the scene's pull-back */
  /* The scene page's moments (static/scene/renderer.js), each once per action, never looping. */
  --t-scene-birth: 3600ms;    /* cameras arrive, then the place pours out of their pictures */
  --t-scene-run: 1500ms;      /* time runs up to the moment worth seeing after the birth */
  --t-scene-fly: 840ms;       /* the view flies into a camera, or home */
  --t-scene-trace: 520ms;     /* a picked point's spark, one way along its ray */
  --t-scene-settle: 1140ms;   /* the scene settles to its anchored size */
  --t-scene-dissolve: 1600ms; /* playing across the fold between two visits */
  --t-scene-manifest: 5200ms; /* the place is built from its cameras outward: a card, its edges lit, a scan plane, colour, the pull-back */
  --t-scene-flash: 1000ms;    /* each class of the built place flashes in turn, all of them in this */
  --t-scene-chrome: 3200ms;   /* idle this long, the scene's panels and labels recede */

  /* ---- Elevation. There is none. Depth is surface steps and hairlines. ---- */
  --focus-ring: 2px;

  /* ---- Stacking ---- */
  --z-base: 0;
  --z-map: 2;
  --z-map-control: 8;
  --z-nav: 20;
  --z-sheet: 30;
  --z-dialog: 40;

  font-family: var(--font);
  font-synthesis: none;
  text-rendering: optimizeLegibility;

  /* Numbers never jitter while they count. */
  font-variant-numeric: tabular-nums;
}

/* ---- Light, chosen in Account (static/app/shell.js, applyTheme).

   The same ramp turned over: surfaces step down from paper instead of up from
   black, text is ink, and the signal and its module steps go a shade deeper so an
   accent still clears 4.5:1 on a pale surface. The evidence-state hues keep their
   meanings and darken for the same reason. Nothing else changes: every other
   stylesheet reads these names, which is why a second theme is one block.

   The shell reads `data-theme-resolved`, set from the choice and, for "Same as my
   computer", from prefers-color-scheme; so the palette is written once. */
:root[data-theme-resolved="light"] {
  color-scheme: light;

  --void: #e9e8e5;
  --base: #f4f3f1;
  --surface: #fbfbfa;
  --surface-2: #f0efec;
  --surface-3: #e6e5e1;
  --well: #ecebe8;
  --line: #d6d4cf;
  --line-2: #c2bfb9;
  --line-soft: #e6e5e1;

  --text: #16171a;
  --text-1: #2e2f33;
  --text-2: #55534f;
  --text-3: #6b6864;

  --mark-planet: #c2bfb9;
  --mark-planet-edge: #e6e5e1;

  --signal: #1d6474;
  --signal-deep: #a9d3dc;

  --accent-map: var(--signal);
  --accent-username: var(--signal);
  --accent-email: var(--signal);
  --accent-phone: var(--signal);
  --accent-discord: var(--signal);
  --accent-telegram: var(--signal);
  --accent-social: var(--signal);
  --accent-recon: var(--signal);
  --accent-forensics: var(--signal);
  --accent-research: var(--signal);
  --accent-case: var(--signal);
  --accent-breach: #93302b;

  --state-inferred: #5a3d88;
  --state-stale: #7d520f;
  --state-refused: #93302b;
  --state-missing: #b4b2ad;
  --state-empty: #d6d4cf;
  --state-unavailable: #93302b;
  --state-truncated: #7d520f;
  --scene-context: #8c8a86;
  --scene-context-dim: #c4c2bd;
  --scene-viewshed: #4f7f66;

  --pool-idle: #dcdad5;
  --pool-checking: #a9c6cf;
  --pool-missing: #a6a39d;
  --pool-empty: #c6c3bd;
  --pool-unavailable: #b0574a;

  --ok: #2f6b39;
  --warn: #7d520f;
  --violet: #5a3d88;
  --critical: #93302b;
  --critical-dim: #f6e3e1;
}

/* The map's controls used to keep the dark look in the light theme too, and under a
   pale bar and rail the dark Layers button, tool strip and camera counter read as a
   page half-themed (round 8). They follow the theme now; they read every colour from
   these tokens, and were checked at 1440x900 and 390x844 over the imagery. The two
   cockpits, drawn as instruments over the picture itself, keep the dark ramp from
   :root above, restated for their own subtree. */
:root[data-theme-resolved="light"] #panel-map :is(.flight-cockpit, .orbit-cockpit) {
  color-scheme: dark;
  --void: #08080a;
  --base: #0f0f10;
  --surface: #151516;
  --surface-2: #1a1a1c;
  --surface-3: #1c1c1d;
  --well: #0b0b0c;
  --line: #232325;
  --line-2: #2f2f33;
  --line-soft: #1a1a1c;
  --text: #f3f3f3;
  --text-1: #c8c5c2;
  --text-2: #a29c99;
  --text-3: #878180;
  --signal: #66b8c9;
  --signal-deep: #315d68;
  --accent-map: var(--signal);
  --accent: var(--accent-map);
  --state-inferred: #9a7ac7;
  --state-stale: #d6a75c;
  --state-refused: #d66b66;
  --state-missing: #4a4a4e;
  --state-empty: #2c2c2f;
  --state-unavailable: #7a3a32;
  --state-truncated: #d6a75c;
  --ok: #83b98b;
  --warn: #d6a75c;
  --violet: #9a7ac7;
  --critical: #d66b66;
  --critical-dim: #2a1512;

  /* Inherited colour is the value <body> computed, not the token, so text with no
     colour of its own came through as dark ink on the dark controls. */
  color: var(--text);
}
