/* cos - shared design language. One file, not a token block copied into each page - the
 * same reasoning as lib/agents.js being the fleet's single declaration: two copies of a
 * palette drift the first time one page gets edited and the other doesn't.
 *
 * Minimalist, outline-first: components are defined by a single hairline border, not a
 * filled surface competing with the page background. Color is reserved for meaning - the
 * accent marks the one primary action on a screen, danger marks something destructive -
 * never used decoratively. One border color, one radius scale, one type scale, so nothing
 * on the page is “close but not quite” the same size as something else.
 */

:root {
  color-scheme: light dark;

  --bg:     #ffffff;
  --text:   #0a0a0c;
  --muted:  #6b6b70;
  --border: #dcdce0;   /* the one hairline used everywhere - cards, tabs, inputs, dividers */
  --line:   #0a0a0c;   /* text-strength emphasis for an active/focused border, not a second border colour */

  --accent: #0a6cff;
  --danger: #d92c20;
  --overdue: var(--danger);

  /* The app header's own fill (2026-09-17) - a filled navy bar rather than this design
     language's usual bare outline, deliberately: the header is chrome, not content, and reads
     as a native title bar rather than another card competing with the page. Scoped to the
     header/menu only - every card, tab and button below it stays the existing outline-first
     language untouched. */
  --navy: #0b2a3f;

  /* A pale three-tier scale for a "how good a fit" ladder (opportunities.screen_verdict:
     push, strongest > pass, solid > possible, marginal - all three are shades of yes, not a
     literal yes/no/maybe, graduated by strength). The one deliberate place in this design
     language that uses a filled tint rather than a bare outline - a status chip's whole job
     is to read as a colour at a glance, which a 1px border alone doesn't do as well. */
  --good-bg: #e3f4e9; --good-fg: #1f7a43;
  --mid-bg:  #fdf3d9; --mid-fg:  #8a6d1f;
  --low-bg:  #fbe7d9; --low-fg:  #a15a1f;

  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* Type scale - four sizes, nothing in between. Every text element on the page maps onto
     one of these exactly, rather than each rule picking its own close-but-different rem value. */
  --fs-label: 0.72rem;
  --fs-small: 0.8125rem;
  --fs-body:  0.9375rem;
  --fs-h1:    1.375rem;

  /* Spacing scale - five steps. */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;

  /* Radius scale - two steps: small controls, larger containers. */
  --r-sm: 6px; --r-md: 14px;

  /* A minor shadow for the two "container" surfaces (.card, form) - low enough not to
     contradict the outline-first language, just enough to lift a card off the background
     pattern a shade further than the border alone does. */
  --shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0a0a0c; --text: #f2f2f4; --muted: #8b8b90; --border: #2a2a2e; --line: #f2f2f4;
    --good-bg: #123321; --good-fg: #5fd08a;
    --mid-bg:  #332b12; --mid-fg:  #e0c157;
    --low-bg:  #3a2414; --low-fg:  #e8935a;
    /* A flat rgba(0,0,0,...) shadow barely registers against an already-dark card - needs more
       opacity than the light version to read as depth rather than disappear. */
    --shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
  }
}
/* The explicit half of the toggle: data-theme="dark" wins even when the OS is in light mode.
   Without this block, picking "Dark" on a light-OS phone would silently do nothing - the media
   query above only ever fires when the OS itself prefers dark. Same values as that block,
   repeated rather than shared, because a media query and a plain selector can't be combined
   into one rule. */
:root[data-theme="dark"] {
  --bg: #0a0a0c; --text: #f2f2f4; --muted: #8b8b90; --border: #2a2a2e; --line: #f2f2f4;
  --good-bg: #123321; --good-fg: #5fd08a;
  --mid-bg:  #332b12; --mid-fg:  #e0c157;
  --low-bg:  #3a2414; --low-fg:  #e8935a;
  --shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* A Bauhaus-flavoured flourish, not wallpaper: eighteen primitives (a nested pair of rings, a
   nested pair of arcs, three lines, three triangles at descending scale, two rotated squares -
   one outline, one filled - a 3x3 dot grid and an outlined bar), fixed in the top-right so it
   never scrolls with the lists, at 3.5-11% opacity so it reads as a real composition without
   competing with content. Denser than the previous 9-shape version (2026-09-04) by direct
   request - the repeated-at-scale rings/arcs/triangles are a real Bauhaus device, not just
   more shapes for its own sake. Baked as two separate data URIs rather than one relying on
   `currentColor` - background-image SVGs don't reliably inherit CSS custom properties across
   browsers, so light/dark each get their own ink colour matching --text (dark mode runs every
   opacity a shade higher, same reasoning the shadow token already does), swapped the same way
   the token overrides are - now by an explicit [data-theme] too, not just the media query. */
body {
  margin: 0; color: var(--text);
  background-color: var(--bg);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='860' height='860' viewBox='0 0 860 860'%3E%3Ccircle cx='700' cy='130' r='175' fill='none' stroke='%230a0a0c' stroke-width='2' opacity='0.055'/%3E%3Ccircle cx='700' cy='130' r='110' fill='none' stroke='%230a0a0c' stroke-width='1' opacity='0.035'/%3E%3Ccircle cx='580' cy='50' r='36' fill='%230a6cff' opacity='0.09'/%3E%3Ccircle cx='520' cy='180' r='6' fill='%230a0a0c' opacity='0.06'/%3E%3Cpath d='M 300 30 A 130 130 0 0 1 300 290' fill='none' stroke='%230a0a0c' stroke-width='1.5' opacity='0.05'/%3E%3Cpath d='M 340 70 A 85 85 0 0 1 340 240' fill='none' stroke='%230a6cff' stroke-width='1.2' opacity='0.07'/%3E%3Cline x1='380' y1='0' x2='820' y2='440' stroke='%230a0a0c' stroke-width='1.5' opacity='0.045'/%3E%3Cline x1='640' y1='0' x2='850' y2='210' stroke='%230a6cff' stroke-width='1' opacity='0.06'/%3E%3Cline x1='700' y1='40' x2='560' y2='180' stroke='%230a0a0c' stroke-width='1' opacity='0.04'/%3E%3Cpolygon points='20,580 190,580 20,750' fill='%230a6cff' opacity='0.065'/%3E%3Cpolygon points='40,780 120,780 40,860' fill='none' stroke='%230a0a0c' stroke-width='1.5' opacity='0.05'/%3E%3Cpolygon points='160,620 210,620 160,670' fill='none' stroke='%230a6cff' stroke-width='1' opacity='0.05'/%3E%3Crect x='30' y='390' width='72' height='72' fill='none' stroke='%230a0a0c' stroke-width='1.5' opacity='0.045' transform='rotate(8 66 426)'/%3E%3Crect x='140' y='430' width='26' height='26' fill='%230a6cff' opacity='0.08' transform='rotate(-12 153 443)'/%3E%3Ccircle cx='150' cy='120' r='5' fill='%230a0a0c' opacity='0.09'/%3E%3Ccircle cx='178' cy='120' r='5' fill='%230a0a0c' opacity='0.075'/%3E%3Ccircle cx='206' cy='120' r='5' fill='%230a0a0c' opacity='0.06'/%3E%3Ccircle cx='150' cy='148' r='5' fill='%230a0a0c' opacity='0.07'/%3E%3Ccircle cx='178' cy='148' r='5' fill='%230a0a0c' opacity='0.055'/%3E%3Ccircle cx='206' cy='148' r='5' fill='%230a0a0c' opacity='0.045'/%3E%3Ccircle cx='150' cy='176' r='5' fill='%230a0a0c' opacity='0.055'/%3E%3Ccircle cx='178' cy='176' r='5' fill='%230a0a0c' opacity='0.045'/%3E%3Ccircle cx='206' cy='176' r='5' fill='%230a0a0c' opacity='0.035'/%3E%3Crect x='460' y='520' width='110' height='34' fill='none' stroke='%230a0a0c' stroke-width='1.5' opacity='0.045'/%3E%3Ccircle cx='430' cy='630' r='9' fill='%230a6cff' opacity='0.08'/%3E%3Ccircle cx='380' cy='700' r='22' fill='none' stroke='%230a0a0c' stroke-width='1.2' opacity='0.045'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: top right;
  background-attachment: fixed;
  font: var(--fs-body)/1.4 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
/* The dark version of the same composition applies two ways: automatically, when the OS is in
   dark mode and nothing has been chosen explicitly (the media query, guarded off if an explicit
   light choice is stamped); and explicitly, when data-theme="dark" is stamped regardless of what
   the OS prefers (the toggle in the app header). Both need the identical image - a toggle that
   only flipped the colour tokens and left the OS-driven background behind would be half a
   theme switch. */
@media (prefers-color-scheme: dark) {
  body:not([data-theme="light"]) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='860' height='860' viewBox='0 0 860 860'%3E%3Ccircle cx='700' cy='130' r='175' fill='none' stroke='%23f2f2f4' stroke-width='2' opacity='0.065'/%3E%3Ccircle cx='700' cy='130' r='110' fill='none' stroke='%23f2f2f4' stroke-width='1' opacity='0.045'/%3E%3Ccircle cx='580' cy='50' r='36' fill='%230a6cff' opacity='0.11'/%3E%3Ccircle cx='520' cy='180' r='6' fill='%23f2f2f4' opacity='0.07'/%3E%3Cpath d='M 300 30 A 130 130 0 0 1 300 290' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.06'/%3E%3Cpath d='M 340 70 A 85 85 0 0 1 340 240' fill='none' stroke='%230a6cff' stroke-width='1.2' opacity='0.09'/%3E%3Cline x1='380' y1='0' x2='820' y2='440' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.055'/%3E%3Cline x1='640' y1='0' x2='850' y2='210' stroke='%230a6cff' stroke-width='1' opacity='0.08'/%3E%3Cline x1='700' y1='40' x2='560' y2='180' stroke='%23f2f2f4' stroke-width='1' opacity='0.05'/%3E%3Cpolygon points='20,580 190,580 20,750' fill='%230a6cff' opacity='0.085'/%3E%3Cpolygon points='40,780 120,780 40,860' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.06'/%3E%3Cpolygon points='160,620 210,620 160,670' fill='none' stroke='%230a6cff' stroke-width='1' opacity='0.07'/%3E%3Crect x='30' y='390' width='72' height='72' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.055' transform='rotate(8 66 426)'/%3E%3Crect x='140' y='430' width='26' height='26' fill='%230a6cff' opacity='0.1' transform='rotate(-12 153 443)'/%3E%3Ccircle cx='150' cy='120' r='5' fill='%23f2f2f4' opacity='0.1'/%3E%3Ccircle cx='178' cy='120' r='5' fill='%23f2f2f4' opacity='0.08'/%3E%3Ccircle cx='206' cy='120' r='5' fill='%23f2f2f4' opacity='0.06'/%3E%3Ccircle cx='150' cy='148' r='5' fill='%23f2f2f4' opacity='0.08'/%3E%3Ccircle cx='178' cy='148' r='5' fill='%23f2f2f4' opacity='0.06'/%3E%3Ccircle cx='206' cy='148' r='5' fill='%23f2f2f4' opacity='0.045'/%3E%3Ccircle cx='150' cy='176' r='5' fill='%23f2f2f4' opacity='0.06'/%3E%3Ccircle cx='178' cy='176' r='5' fill='%23f2f2f4' opacity='0.045'/%3E%3Ccircle cx='206' cy='176' r='5' fill='%23f2f2f4' opacity='0.035'/%3E%3Crect x='460' y='520' width='110' height='34' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.055'/%3E%3Ccircle cx='430' cy='630' r='9' fill='%230a6cff' opacity='0.1'/%3E%3Ccircle cx='380' cy='700' r='22' fill='none' stroke='%23f2f2f4' stroke-width='1.2' opacity='0.055'/%3E%3C/svg%3E");
  }
}
body[data-theme="dark"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='860' height='860' viewBox='0 0 860 860'%3E%3Ccircle cx='700' cy='130' r='175' fill='none' stroke='%23f2f2f4' stroke-width='2' opacity='0.065'/%3E%3Ccircle cx='700' cy='130' r='110' fill='none' stroke='%23f2f2f4' stroke-width='1' opacity='0.045'/%3E%3Ccircle cx='580' cy='50' r='36' fill='%230a6cff' opacity='0.11'/%3E%3Ccircle cx='520' cy='180' r='6' fill='%23f2f2f4' opacity='0.07'/%3E%3Cpath d='M 300 30 A 130 130 0 0 1 300 290' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.06'/%3E%3Cpath d='M 340 70 A 85 85 0 0 1 340 240' fill='none' stroke='%230a6cff' stroke-width='1.2' opacity='0.09'/%3E%3Cline x1='380' y1='0' x2='820' y2='440' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.055'/%3E%3Cline x1='640' y1='0' x2='850' y2='210' stroke='%230a6cff' stroke-width='1' opacity='0.08'/%3E%3Cline x1='700' y1='40' x2='560' y2='180' stroke='%23f2f2f4' stroke-width='1' opacity='0.05'/%3E%3Cpolygon points='20,580 190,580 20,750' fill='%230a6cff' opacity='0.085'/%3E%3Cpolygon points='40,780 120,780 40,860' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.06'/%3E%3Cpolygon points='160,620 210,620 160,670' fill='none' stroke='%230a6cff' stroke-width='1' opacity='0.07'/%3E%3Crect x='30' y='390' width='72' height='72' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.055' transform='rotate(8 66 426)'/%3E%3Crect x='140' y='430' width='26' height='26' fill='%230a6cff' opacity='0.1' transform='rotate(-12 153 443)'/%3E%3Ccircle cx='150' cy='120' r='5' fill='%23f2f2f4' opacity='0.1'/%3E%3Ccircle cx='178' cy='120' r='5' fill='%23f2f2f4' opacity='0.08'/%3E%3Ccircle cx='206' cy='120' r='5' fill='%23f2f2f4' opacity='0.06'/%3E%3Ccircle cx='150' cy='148' r='5' fill='%23f2f2f4' opacity='0.08'/%3E%3Ccircle cx='178' cy='148' r='5' fill='%23f2f2f4' opacity='0.06'/%3E%3Ccircle cx='206' cy='148' r='5' fill='%23f2f2f4' opacity='0.045'/%3E%3Ccircle cx='150' cy='176' r='5' fill='%23f2f2f4' opacity='0.06'/%3E%3Ccircle cx='178' cy='176' r='5' fill='%23f2f2f4' opacity='0.045'/%3E%3Ccircle cx='206' cy='176' r='5' fill='%23f2f2f4' opacity='0.035'/%3E%3Crect x='460' y='520' width='110' height='34' fill='none' stroke='%23f2f2f4' stroke-width='1.5' opacity='0.055'/%3E%3Ccircle cx='430' cy='630' r='9' fill='%230a6cff' opacity='0.1'/%3E%3Ccircle cx='380' cy='700' r='22' fill='none' stroke='%23f2f2f4' stroke-width='1.2' opacity='0.055'/%3E%3C/svg%3E");
}

h1 { font-size: var(--fs-h1); font-weight: 700; margin: 0; }
h2 { font-size: var(--fs-label); font-weight: 700; text-transform: uppercase;
     letter-spacing: 0.04em; color: var(--muted); margin: var(--sp-5) 4px var(--sp-2); }

a { color: inherit; }

/* Buttons: outline-first, never a solid fill. Meaning lives in the border+text colour,
   not in a background - the same visual language a card or an input uses. */
button {
  font: inherit; font-size: var(--fs-small); font-weight: 600;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  background: var(--bg); color: var(--text);
}
button:active { opacity: 0.55; }
button:disabled { opacity: 0.4; }
/* Only for a device that actually has a mouse - guards against a tap leaving a button visibly
   "stuck" in its hover state on touch, which has no equivalent un-hover event to clear it. */
@media (hover: hover) {
  button:hover { opacity: 0.75; }
}
button.primary { border-color: var(--accent); color: var(--accent); }
button.negative { border-color: var(--danger); color: var(--danger); }
/* The same good/low tier tokens the verdict tag uses (see the ladder comment on those tokens
   above) - Pursue and Dismiss on a Roles card are the yes/no ends of that same ladder, not a
   generic accept/destroy pair, so they share its colour language rather than the app's
   general blue/red. Genuinely destructive actions (Pass, dropping a task) stay on
   button.negative - a role you're actively chasing is a different kind of "no" than one you
   never pursued. */
button.good { border-color: var(--good-fg); color: var(--good-fg); }
button.low  { border-color: var(--low-fg);  color: var(--low-fg); }

input, select {
  width: 100%; font: inherit; font-size: var(--fs-body);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
}
input:focus, select:focus { outline: none; border-color: var(--line); }
/* The shared input style above removes a select's own arrow in some browsers, so draw one: a
   mid-grey chevron that reads on both the light and the dark background. */
select {
  appearance: none; -webkit-appearance: none; padding-right: calc(var(--sp-3) * 2 + 10px);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238b8b90' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--sp-3) center;
}
/* A class that sets display (grid, flex) beats the browser's own rule for the hidden
   attribute, so an element hidden from script would still show. */
[hidden] { display: none !important; }
