/* Hexmark base theme.
 *
 * Every color routes through a :root variable so /theme.css (served per tenant,
 * loaded AFTER this file) can override the palette without a build step. Only the
 * eight keys in db.THEME_KEYS are tenant-writable; everything else here DERIVES
 * from them with color-mix(), so a tenant override reaches the whole system
 * instead of stopping at the eight. A literal hex outside :root is a bug — it is
 * a color a tenant cannot theme. The exceptions are the shadow tint and the
 * status hues, which are deliberately fixed: shadows are neutral by definition,
 * and a "restrictive" band must not be recolorable into looking favourable. */

@font-face {
  font-family: 'Instrument Sans';
  src: url('fonts/InstrumentSans-latin-var.woff2') format('woff2');
  font-weight: 400 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Titillium Web';
  src: url('fonts/TitilliumWeb-latin-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Titillium Web';
  src: url('fonts/TitilliumWeb-latin-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}

:root {
  /* ---- tenant-writable (db.THEME_KEYS) — keep in lockstep with settings.js */
  --ink: #0d0f12;           /* primary text */
  --text-soft: #697180;     /* secondary text */
  --line: #e4e7ec;          /* borders */
  --surface: #f4f6f8;       /* page / muted backgrounds */
  --accent: #0d0f12;        /* buttons, selected states */
  --accent-hover: #333b45;  /* hover on accent */
  --accent-soft: #eceff3;   /* light accent background */
  --bad: #b42318;           /* errors, blocking warnings */

  /* ---- derived: these follow a tenant override automatically */
  --paper: #fff;
  --ink-2: color-mix(in oklab, var(--ink) 66%, var(--surface));
  /* --ink-3 is the SECONDARY TEXT step and has to clear WCAG AA 4.5:1, because
     everything wearing it (.lbl, .hint, .footnote, placeholders, table heads) is
     10.5-12.5px. At 42% it computed to #888b8d = 3.43:1 and failed. 55% is
     #6a6c6f = 5.27:1. --ink-4 keeps the lighter value for the things that are
     not text: decorative rules, and disabled fills that should look disabled. */
  --ink-3: color-mix(in oklab, var(--ink) 55%, var(--surface));
  --ink-4: color-mix(in oklab, var(--ink) 40%, var(--surface));
  --muted: var(--text-soft);          /* was undefined; every var(--muted,#666)
                                         silently fell back and ignored the theme */
  --hairline: color-mix(in oklab, var(--line) 62%, var(--surface));
  --line-strong: color-mix(in oklab, var(--line) 70%, var(--ink));
  --accent-ghost: color-mix(in srgb, var(--accent) 7%, transparent);
  --accent-on: #fff;                  /* text on an accent fill */
  --bad-soft: color-mix(in srgb, var(--bad) 8%, var(--paper));
  --focus: color-mix(in srgb, var(--accent) 26%, transparent);

  /* ---- fixed: status hues are reserved and must not be themeable */
  --good: #15683c;
  --warn: #8a5d00;
  --warn-line: #e0be6b;
  --warn-soft: #fdf8ec;

  /* ---- type */
  --font-display: 'Titillium Web', 'Geist', -apple-system, 'Segoe UI',
    'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Instrument Sans', 'Inter', -apple-system, 'Segoe UI',
    'Helvetica Neue', Arial, sans-serif;
  --tracked: 0.14em;

  /* ---- shape, depth, motion */
  --r-xs: 4px; --r-sm: 7px; --r-md: 10px; --r-lg: 14px; --r-pill: 999px;
  --shade: 13 15 18;
  --e1: 0 1px 2px rgb(var(--shade) / .05), 0 0 0 1px var(--hairline);
  --e2: 0 1px 2px rgb(var(--shade) / .05), 0 6px 16px -6px rgb(var(--shade) / .12),
        0 0 0 1px var(--hairline);
  --e3: 0 2px 6px rgb(var(--shade) / .08), 0 24px 48px -16px rgb(var(--shade) / .28);
  --ease: cubic-bezier(.2, .7, .3, 1);
  --t1: 110ms; --t2: 180ms;

  /* ---- layout. Referenced by body.map-page and by the 700px breakpoint's prose; it
     was never actually declared, so the grid ran on var()'s fallback and the number
     lived in one place that read like it didn't. */
  --rail-w: 340px;
}

* { box-sizing: border-box; }
/* ONE of these, at the top, instead of a counter-rule per offender. Any class that sets
   an author `display` beats the UA's [hidden] rule, so .chk, #clinic-row, #clinic-note
   and #cap-cover each needed a hand-written [hidden] of their own, written in three
   different parts of this file as each feature hit the trap. The next one would have hit
   it too, and silently: a box you can tick with nothing behind it. */
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; font-family: var(--font-body); color: var(--ink);
  font-size: 13px; line-height: 1.5; -webkit-font-smoothing: antialiased;
  font-variant-numeric: tabular-nums; }
body.map-page { display: grid; grid-template-columns: var(--rail-w, 340px) 1fr; }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }

/* One focus treatment everywhere, on :focus-visible only so a mouse click never
   paints a ring. outline (not box-shadow) so it survives overflow clipping. */
:where(a, button, input, select, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-xs);
}

/* position:relative so #tooltip and .map-note, which are positioned from
   map-canvas coordinates, resolve against the map and not the viewport.
   min-height:0 lets a grid item scroll instead of stretching the row. */
#map { position: relative; min-height: 0; }

#panel {
  min-height: 0; overflow-y: auto;
  background: var(--paper); border-right: 1px solid var(--line);
  padding: 0 18px 16px;
}

/* ---------------------------------------------------------------- brand */
.brand { font-family: var(--font-display); font-size: 12px; letter-spacing: var(--tracked);
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 12px;
  display: flex; align-items: center; gap: 9px; }
/* the rail scrolls, so its brand pins to the top; settings and login share .brand
   and must keep the plain in-flow version */
#panel .brand { margin-bottom: 0; position: sticky; top: 0; z-index: 2;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  padding: 15px 0 11px; border-bottom: 1px solid var(--hairline); }
/* The mark, not the wordmark: at 18px the outlined HEXMARK lettering is a
   hairline and its diagonals step, so the brand line sets the name as type and
   uses the icon-tier mark — the one drawn to hold at this size — as the glyph. */
.brand-mark { width: 18px; height: 18px; display: block; flex: none; }
.brand span { color: var(--ink); font-weight: 600; }
.brand .tenant { letter-spacing: 0.06em; }
.brand .spacer { flex: 1; }
.brand a.gear { color: var(--ink-3); text-decoration: none; font-size: 14px;
  letter-spacing: 0; transition: color var(--t1) var(--ease); }
.brand a.gear:hover { color: var(--ink); }

.section { padding: 13px 0; }

/* Bands, not per-section rules. Eight stacked hairlines down a 340px column read as a
   form; the rail has three groups, so it gets two rules. Read the pair as: every
   answer/setup section opens a band, EXCEPT one preceded by a section of the same band
   that is not [hidden]. Only the first VISIBLE section of a band keeps the divider, and
   a band whose sections are all hidden — which is most of them before a lookup — draws
   nothing over the gap where they aren't. `work` needs no rule: it is first, and the
   sticky brand already closes it off. Depends on document order matching band order;
   index.html says so where the bands are declared. */
.section[data-band="answer"], .section[data-band="setup"] {
  border-top: 1px solid var(--hairline); margin-top: 10px; padding-top: 20px; }
.section[data-band="answer"]:not([hidden]) ~ .section[data-band="answer"],
.section[data-band="setup"]:not([hidden]) ~ .section[data-band="setup"] {
  border-top: none; margin-top: 0; padding-top: 13px; }

/* ONE heading tier for one level of hierarchy. There were three, all --ink-3, all
   --tracked, differing only in size: summary at 11px, .lbl at 10.5px, .cap-state at 11px
   — and the sections that were not <details> had no heading at all, so their first .lbl
   doubled as one. "Site lookup" and "Data & layers" were peers in the IA and different
   type on screen. .lbl is now only ever a label for a control group. */
.sec-head, details.section > summary { font-family: var(--font-display); font-size: 11px;
  letter-spacing: var(--tracked); text-transform: uppercase; color: var(--ink-3);
  display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.sec-head { margin-bottom: 8px; }
/* collapsible sections — native <details>, so open/close needs no JS */
details.section > summary { cursor: pointer; list-style: none; margin-bottom: 0;
  transition: color var(--t1) var(--ease); }
details.section[open] > summary { margin-bottom: 9px; }
details.section > summary:hover { color: var(--ink); }
details.section > summary::-webkit-details-marker { display: none; }
details.section > summary::after { content: '\25BE'; margin-left: auto; font-size: 9px;
  transition: transform var(--t2) var(--ease); opacity: .5; }
details.section[open] > summary::after { transform: rotate(180deg); }
/* The pill is the collapsed-state readout for the ICP section, so it sits in the summary
   and must not inherit the heading's tracking on top of its own. */
details.section > summary .pill { letter-spacing: .08em; }
.sub-block + .sub-block { margin-top: 14px; }
.sub-block:first-of-type { margin-top: 12px; }
.lbl { font-family: var(--font-display); font-size: 10.5px; letter-spacing: var(--tracked);
  text-transform: uppercase; color: var(--ink-3); display: block; margin-bottom: 7px; }
.row { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
.row .lbl { margin-bottom: 0; min-width: 58px; }

/* ---------------------------------------------------------------- fields */
.addr-row { display: flex; gap: 6px; }
#addr { flex: 1; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-sm);
  font-family: var(--font-body); font-size: 13px; background: var(--paper);
  transition: border-color var(--t1) var(--ease), box-shadow var(--t1) var(--ease); }
#addr::placeholder { color: var(--ink-3); }
#addr:hover { border-color: var(--line-strong); }
#addr:focus-visible { outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus); }
#addr-go { padding: 8px 14px; border: none; border-radius: var(--r-sm); background: var(--accent);
  color: var(--accent-on); font-family: var(--font-display); font-weight: 600;
  letter-spacing: .02em; transition: background var(--t1) var(--ease); }
#addr-go:hover { background: var(--accent-hover); }
.cand { padding: 7px 9px; font-size: 12.5px; border: 1px solid var(--hairline);
  border-radius: var(--r-sm); margin-top: 5px; cursor: pointer;
  transition: background var(--t1) var(--ease), border-color var(--t1) var(--ease); }
.cand:hover { background: var(--accent-ghost); border-color: var(--line-strong); }
/* Worn by an exact geocoder match in #candidates, and by settings.js. It sat inside the
   login block, which shares neither. */
.exact-chip { display: inline-block; font-family: var(--font-display); font-size: 9.5px;
  letter-spacing: .08em; text-transform: uppercase; padding: 2px 7px; margin-right: 7px;
  border-radius: var(--r-pill); background: var(--accent); color: var(--accent-on);
  font-weight: 600; vertical-align: 1px; }

/* Segmented control: one inset track, the active segment lifted out of it. */
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r-sm);
  overflow: hidden; background: var(--surface); padding: 2px; gap: 2px; }
.seg button { border: none; background: transparent; padding: 4px 10px; font-size: 12px;
  font-family: var(--font-display); font-weight: 600; letter-spacing: .01em;
  color: var(--ink-3); border-radius: 5px;
  transition: background var(--t1) var(--ease), color var(--t1) var(--ease); }
.seg button:hover:not(.on):not(:disabled) { color: var(--ink); }
.seg button.on { background: var(--paper); color: var(--ink); box-shadow: var(--e1); }
.seg button:disabled { opacity: .38; cursor: not-allowed; }
/* .seg sets overflow:hidden, so the global 2px-offset ring would be painted into
   the track's padding and clipped away — a keyboard user could not see which
   segment had focus. Inset it instead. */
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.radios label, .chk { display: block; font-size: 13px; padding: 4px 0; cursor: pointer; }
.chk.is-off { cursor: default; color: var(--ink-3); }
.chk.is-off input { cursor: default; }
/* The affordance for a native title. A CSS bubble would be clipped by the rail's
   overflow-y:auto, and title is what every other explained control in that panel uses. */
.chk .info { display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 6px; border-radius: 50%; cursor: help;
  border: 1px solid var(--line-strong); color: var(--ink-3);
  font-family: var(--font-display); font-size: 9.5px; font-style: italic;
  font-weight: 700; line-height: 1; vertical-align: middle;
  transition: color var(--t1) var(--ease), border-color var(--t1) var(--ease); }
.chk .info:hover, .chk .info:focus-visible { color: var(--ink); border-color: var(--ink); }
.chk .why { font-size: 12px; }
.radios input, .chk input { accent-color: var(--accent); margin-right: 7px; }
.hint { font-size: 11.5px; color: var(--ink-3); margin-top: 5px; line-height: 1.45; }
.dim { color: var(--ink-3); font-size: 12.5px; }

/* ---------------------------------------------------------------- report */
.report-head { font-family: var(--font-display); font-size: 13px; font-weight: 600;
  margin-bottom: 9px; display: flex; align-items: center; gap: 8px; }
.method-tag { font-size: 10px; padding: 2px 8px; border-radius: var(--r-pill);
  text-transform: uppercase; letter-spacing: .08em; font-weight: 600; }
.method-tag.polygon { background: var(--accent); color: var(--accent-on); }
.method-tag.hex { background: var(--accent-soft); color: var(--ink-2); }

/* Hero number: the one figure the panel exists to deliver, so it gets the size
   and the label sits under it rather than competing beside it. */
.big-stat { margin: 10px 0 12px; }
.big-stat .v { font-family: var(--font-display); font-size: 34px; font-weight: 600;
  line-height: 1.05; letter-spacing: -0.02em; display: block; }
.big-stat .k { font-size: 10.5px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: var(--tracked); margin-top: 3px; }
.report-tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
/* The competitor block's takeaway sentence. NOT .ctx-prov, which was the convenient reuse
   and is the amber provisional treatment /context uses for "not enough to grade" -- it
   rendered the one line this layer exists to deliver as though something were wrong with
   it, in the smallest type in the report. Plain sentence at table size; the <b> on each
   number carries the emphasis. */
.join-line { font-size: 13px; line-height: 1.5; margin: 9px 0 11px; color: var(--ink); }
.report-tbl td { padding: 5px 0; border-bottom: 1px solid var(--hairline); }
.report-tbl td:last-child { text-align: right; font-weight: 600; }
.report-tbl tr:last-child td { border-bottom: none; }

.legend-title { font-size: 11.5px; color: var(--ink-3); margin-bottom: 6px; }
.legend-row { display: flex; align-items: center; gap: 9px; font-size: 12px;
  padding: 2px 0; }
/* 2px surface ring so adjacent ramp steps never touch — the low end of a
   sequential ramp is close enough that abutting swatches read as one block. */
.sw { width: 18px; height: 12px; border-radius: 3px; display: inline-block;
  flex: none; box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink) 12%, transparent); }

.footnote { font-size: 10.5px; color: var(--ink-3); padding-top: 12px; }

#tooltip { position: absolute; z-index: 20; display: none; pointer-events: none;
  background: color-mix(in srgb, var(--paper) 96%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border-radius: var(--r-sm); padding: 8px 11px; font-size: 12.5px;
  box-shadow: var(--e2); }

/* ---------------------------------------------------------------- login
   login.html does NOT link theme.css — no tenant is known before sign-in — so
   this is the one surface with a fixed palette, and the only one that can go
   dark without a second tenant contract. It re-points the same tokens the rest
   of the app uses, so every shared control inherits the dark treatment. */
body.login-page {
  --ink: #f4f5f7;
  --ink-2: #b7bdc7;
  --ink-3: #7e8794;
  --ink-4: #666e7a;
  --muted: #7e8794;
  --paper: #131519;
  --surface: #0a0b0d;
  --line: #262a31;
  --hairline: #1d2027;
  --line-strong: #39404a;
  --accent: #f4f5f7;
  --accent-hover: #ffffff;
  --accent-on: #0a0b0d;
  --focus: rgb(244 245 247 / .18);
  --bad: #ff6b5e;
  background: var(--surface); color: var(--ink);
}
.login-wrap { position: relative; min-height: 100%; display: flex; align-items: center;
  justify-content: center; padding: 32px 24px; overflow: hidden; }
/* The lattice is the mark's own geometry at wall scale — generated by
   brand/build_logo.py, not redrawn — and sits far enough back to be texture. */
.login-field { position: absolute; inset: -10% -10% -10% -10%; z-index: 0;
  background: url('login-field.svg') center / cover no-repeat;
  opacity: .34; pointer-events: none;
  -webkit-mask-image: radial-gradient(ellipse 78% 68% at 50% 44%, #000 30%, transparent 100%);
  mask-image: radial-gradient(ellipse 78% 68% at 50% 44%, #000 30%, transparent 100%);
  animation: field-in 1400ms var(--ease) both;
}
@keyframes field-in { from { opacity: 0; transform: scale(1.035); } to { opacity: .34; } }
.login-card { position: relative; z-index: 1; width: 100%; max-width: 396px;
  padding: 0; background: none; border: none; box-shadow: none; }
.login-lockup { display: block; width: 330px; max-width: 100%; height: auto;
  margin-bottom: 44px; }
.login-card h1 { font-family: var(--font-display); font-size: 30px; font-weight: 600;
  margin: 0 0 8px; letter-spacing: -0.02em; line-height: 1.1; }
.login-card .sub { font-size: 13.5px; color: var(--ink-2); margin-bottom: 22px;
  line-height: 1.55; max-width: 34ch; }
.login-card input { width: 100%; padding: 13px 15px; margin: 0 0 11px;
  border: 1px solid var(--line); border-radius: var(--r-md); font-size: 14.5px;
  font-family: var(--font-body); background: var(--paper); color: var(--ink);
  transition: border-color var(--t1) var(--ease), box-shadow var(--t1) var(--ease); }
.login-card input::placeholder { color: var(--ink-3); }
.login-card input:hover { border-color: var(--line-strong); }
.login-card input:focus-visible { outline: none; border-color: var(--ink-2);
  box-shadow: 0 0 0 3px var(--focus); }
/* :not(.linkbtn) is load-bearing, not tidiness. This palette is inverted, so --accent
   and --ink are the SAME near-white, and an unscoped `.login-card button` painted the
   "Try again" link button's own --ink text onto an --accent fill: 1.00:1, invisible, and
   a full-width 13px-padded slab dropped mid-sentence. Out-specifying it with
   `.login-card .linkbtn` fixes only the color -- the hover background still comes from
   (0,3,1) below. Scoping the submit button is what stops the collision existing. */
.login-card button:not(.linkbtn) { width: 100%; padding: 13px; border: none;
  border-radius: var(--r-md);
  background: var(--accent); color: var(--accent-on); font-family: var(--font-display);
  font-weight: 600; font-size: 14.5px; letter-spacing: .01em;
  transition: background var(--t1) var(--ease), transform var(--t1) var(--ease); }
.login-card button:not(.linkbtn):hover:not(:disabled) { background: var(--accent-hover); }
.login-card button:not(.linkbtn):active:not(:disabled) { transform: translateY(1px); }
/* Not decorative: this button's label reads "Sending…" for the length of the
   request, so it has to stay legible while disabled. --ink-4 put it at 2.03:1. */
.login-card button:not(.linkbtn):disabled { background: var(--line-strong);
  color: var(--ink-2); cursor: default; }
.login-err { color: var(--bad); font-size: 12.5px; min-height: 18px; }
.login-sent { font-size: 14px; line-height: 1.6; color: var(--ink-2); }
.login-sent b { color: var(--ink); font-weight: 600; }
/* inherit, not 11.5px: this one is a word inside a 13.5px .sub sentence, not a
   standalone action the way every other .linkbtn in the app is. */
.login-page .linkbtn { color: var(--ink); font-size: inherit; }
.login-page .linkbtn:hover { color: var(--ink-2); }
.login-foot { margin-top: 40px; font-size: 11px; letter-spacing: var(--tracked);
  text-transform: uppercase; color: var(--ink-3); font-family: var(--font-display); }

/* ---------------------------------------------------------------- TAM builder */
/* .builder-head went with the bespoke disclosure it spaced: the heading row is now the
   section's own <summary>, styled with every other one. */
.pill { font-family: var(--font-display); font-size: 9.5px; letter-spacing: .08em;
  text-transform: uppercase; font-weight: 600; padding: 3px 9px;
  border-radius: var(--r-pill); background: var(--surface); color: var(--ink-3);
  box-shadow: inset 0 0 0 1px var(--hairline); }
.pill.custom { background: var(--accent); color: var(--accent-on); box-shadow: none; }
.linkbtn { border: none; background: none; padding: 0; font-family: var(--font-display);
  font-size: 11.5px; font-weight: 600; color: var(--ink); text-decoration: underline;
  text-underline-offset: 3px; text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--ink) 30%, transparent);
  transition: color var(--t1) var(--ease), text-decoration-color var(--t1) var(--ease); }
.linkbtn:hover { color: var(--ink-2); text-decoration-color: currentColor; }
#def-summary { margin-bottom: 3px; line-height: 1.4; }

.bgroup { padding: 11px 0 2px; }
.chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chips button { border: 1px solid var(--line); background: var(--paper);
  border-radius: var(--r-pill); padding: 4px 10px; font-size: 11.5px;
  color: var(--ink-3);
  transition: background var(--t1) var(--ease), color var(--t1) var(--ease),
              border-color var(--t1) var(--ease); }
.chips button:hover:not(.on) { border-color: var(--line-strong); color: var(--ink); }
.chips button.on { background: var(--accent); border-color: var(--accent);
  color: var(--accent-on); }
.presets { margin-top: 8px; display: flex; gap: 12px; }
.presets button { border: none; background: none; padding: 0; font-size: 11px;
  color: var(--ink-3); text-decoration: underline; text-underline-offset: 3px;
  transition: color var(--t1) var(--ease); }
.presets button:hover { color: var(--ink); }

.slider-row { margin-top: 10px; }
.slider-row input[type=range] { width: 100%; accent-color: var(--accent); }
#bar-label { margin-top: 4px; line-height: 1.3; }
#bracket-row select, .addfilter select, #clinic-pick { width: 100%; margin-top: 8px; padding: 7px 9px;
  border: 1px solid var(--line); border-radius: var(--r-sm); font-family: var(--font-body);
  font-size: 12.5px; background: var(--paper);
  transition: border-color var(--t1) var(--ease); }
#bracket-row select:hover, .addfilter select:hover, #clinic-pick:hover {
  border-color: var(--line-strong); }
/* one row per optional feature: name, state, an i for the reason, and — when it is
   the client's to fix and they can fix it — somewhere to go */
.cap-row { display: flex; align-items: center; gap: 6px; padding: 3px 0; font-size: 13px; }
.cap-name { flex: 1; }
.cap-state { font-size: 11px; letter-spacing: var(--tracked); text-transform: uppercase;
  font-family: var(--font-display); }
.cap-state.is-on { color: var(--ink-2); }
.cap-state.is-off { color: var(--ink-3); }
#cap-cover { margin-top: 8px; }
#clinic-row { margin-top: 2px; }

.filter-card { border: 1px solid var(--hairline); border-radius: var(--r-sm);
  padding: 9px 10px; margin-bottom: 7px; background: var(--paper); }
.filter-card .fc-head { display: flex; align-items: center; gap: 7px; margin-bottom: 6px; }
.filter-card .fc-name { font-family: var(--font-display); font-size: 12px;
  font-weight: 600; flex: 1; }
.filter-card .fc-opts label { display: block; font-size: 12px; padding: 2px 0;
  cursor: pointer; }
.tier { font-family: var(--font-display); font-size: 9px; letter-spacing: .06em;
  text-transform: uppercase; font-weight: 600; padding: 2px 7px;
  border-radius: var(--r-pill); cursor: help; }
.tier.exact { background: var(--accent); color: var(--accent-on); }
.tier.sex_matched { background: var(--accent-soft); color: var(--ink-2); }
.tier.independent { background: var(--paper); color: var(--ink-3);
  box-shadow: inset 0 0 0 1px var(--line); }
.fc-x { border: none; background: none; color: var(--ink-3);
  font-size: 15px; line-height: 1; padding: 0 2px;
  transition: color var(--t1) var(--ease); }
.fc-x:hover { color: var(--bad); }

#assumptions { margin-top: 6px; }
.warn { border-left: 2px solid var(--ink-4); background: var(--surface);
  padding: 8px 11px; font-size: 11.5px; line-height: 1.45; color: var(--ink-2);
  margin-top: 7px; border-radius: 0 var(--r-xs) var(--r-xs) 0; }
.warn.stop { border-left-color: var(--bad); background: var(--bad-soft);
  color: color-mix(in oklab, var(--bad) 78%, var(--ink)); }
.caveat { font-size: 11px; line-height: 1.45; color: var(--ink-3); margin-top: 6px; }
.builder-actions { display: flex; gap: 14px; border-top: 1px solid var(--hairline);
  margin-top: 10px; padding-top: 11px; align-items: center; }
.btn-solid { border: none; border-radius: var(--r-pill); background: var(--accent);
  color: var(--accent-on); font-family: var(--font-display); font-weight: 600;
  font-size: 11.5px; padding: 7px 16px; letter-spacing: .01em;
  transition: background var(--t1) var(--ease), transform var(--t1) var(--ease); }
.btn-solid:hover:not(:disabled) { background: var(--accent-hover); }
.btn-solid:active:not(:disabled) { transform: translateY(1px); }
.btn-solid:disabled { background: var(--ink-3); cursor: default; }

.audit { margin-top: 10px; font-size: 12px; }
.audit summary { cursor: pointer; color: var(--ink-2); font-size: 11.5px;
  text-decoration: underline; text-underline-offset: 3px; }
.audit table { width: 100%; border-collapse: collapse; margin-top: 6px; }
.audit td { padding: 4px 0; border-bottom: 1px solid var(--hairline); font-size: 12px; }
.audit td:last-child { text-align: right; }
.audit tr.total td { font-weight: 600; border-bottom: none; }

.map-note { position: absolute; top: 16px; left: 50%; transform: translateX(-50%);
  z-index: 15; background: color-mix(in srgb, var(--paper) 96%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border-left: 3px solid var(--ink-4); border-radius: var(--r-sm);
  padding: 10px 15px; font-size: 12.5px; max-width: 460px; box-shadow: var(--e2); }
.map-note.stop { border-left-color: var(--bad); }


/* ---------------------------------------------------------------- settings page */
.settings-wrap { min-height: 100%; background: var(--surface); padding: 34px 16px 48px; }
.settings-card { background: var(--paper); border-radius: var(--r-lg);
  max-width: 640px; margin: 0 auto 16px; padding: 26px 28px; box-shadow: var(--e1); }
.settings-card h2 { font-family: var(--font-display); font-size: 15px; font-weight: 600;
  margin: 0 0 5px; letter-spacing: -0.01em; }
.settings-card .sub { font-size: 12.5px; color: var(--ink-2); margin-bottom: 16px;
  line-height: 1.55; }
.settings-card label.fld { display: block; font-size: 11px; color: var(--ink-3);
  margin: 12px 0 4px; text-transform: uppercase; letter-spacing: .08em;
  font-family: var(--font-display); font-weight: 600; }
.settings-card input[type=text], .settings-card input[type=email],
.settings-card select {
  width: 100%; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-sm);
  font-size: 13px; font-family: var(--font-body); background: var(--paper);
  transition: border-color var(--t1) var(--ease), box-shadow var(--t1) var(--ease); }
.settings-card input[type=text]:hover, .settings-card input[type=email]:hover,
.settings-card select:hover { border-color: var(--line-strong); }
.settings-card input:focus-visible, .settings-card select:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus); }
.color-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; }
.color-row { display: flex; align-items: center; gap: 9px; font-size: 12.5px; }
.color-row input[type=color] { width: 30px; height: 24px; border: 1px solid var(--line);
  border-radius: var(--r-xs); padding: 0 2px; background: var(--paper); cursor: pointer; }
.ramp-row { display: flex; gap: 6px; margin-top: 7px; }
.ramp-row input[type=color] { width: 38px; height: 28px; border: 1px solid var(--line);
  border-radius: var(--r-xs); padding: 0 2px; background: var(--paper); cursor: pointer; }
.settings-actions { margin-top: 18px; display: flex; gap: 14px; align-items: center; }
.settings-note { font-size: 12px; min-height: 16px; }
.settings-note.ok { color: var(--good); }
.settings-note.err { color: var(--bad); }
/* the honest middle: a locations sync whose pins landed but whose mirror did not. Reading
   it as success overstates it; reading it as failure invites a pointless re-sync. */
.settings-note.warn { color: var(--warn); }
.tbl { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 10px; }
.tbl th { text-align: left; font-family: var(--font-display); font-size: 10px;
  letter-spacing: var(--tracked); text-transform: uppercase; color: var(--ink-3);
  padding: 5px 8px 5px 0; border-bottom: 1px solid var(--line); font-weight: 600; }
.tbl td { padding: 7px 8px 7px 0; border-bottom: 1px solid var(--hairline); }
.tbl tr:last-child td { border-bottom: none; }
.tbl .x { border: none; background: none; color: var(--ink-3);
  transition: color var(--t1) var(--ease); }
.tbl .x:hover { color: var(--bad); }
.wh-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px;
  padding: 5px 0; }

/* ---- the binding editor.
 *
 * .field-grid, not .color-grid: the column mapper used to borrow the appearance card's
 * colour-swatch grid, which is sized for a 30px swatch and a caption and gives a text
 * control nothing to sit in. Separate class, so changing one stops changing the other. */
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px; }
.field-grid label.fld { margin-top: 0; }
@media (max-width: 560px) { .field-grid { grid-template-columns: 1fr; } }

/* A step is a numbered stage of one flow, not a section: the rule is a spine down the
 * left rather than a divider across the top. */
.step { border-left: 2px solid var(--hairline); padding: 2px 0 2px 16px;
  margin: 18px 0 0; }
.step > label.fld:first-child { margin-top: 0; color: var(--ink-2); }
.wh-editor-head { border-top: 1px solid var(--line); margin-top: 18px; padding-top: 16px; }
.wh-editor-head h2 { font-size: 14px; margin-bottom: 2px; }

.notice { font-size: 12.5px; line-height: 1.55; color: var(--ink-2);
  background: var(--accent-ghost); border-radius: var(--r-sm); padding: 10px 12px;
  margin: 4px 0 12px; }
.notice b { color: var(--ink); }
.disclose { margin: 0 0 14px; }
.disclose > summary { font-size: 12.5px; color: var(--ink-3); cursor: pointer;
  padding: 6px 0; font-family: var(--font-display); }
.disclose > summary:hover { color: var(--ink-2); }
.disclose[open] > summary { color: var(--ink-2); margin-bottom: 6px; }

/* Wide content scrolls inside its own box; the card must never scroll sideways. */
.preview-wrap { overflow-x: auto; margin-top: 10px; border: 1px solid var(--hairline);
  border-radius: var(--r-sm); padding: 0 10px; }
.preview-tbl { font-size: 12px; margin: 0; }
.preview-tbl td, .preview-tbl th { white-space: nowrap; padding-right: 16px; }

/* Fixed status hues, deliberately not themeable: "3 unreadable values" must not be
 * recolorable into looking like good news. */
.warn-line { font-size: 12px; color: var(--warn); background: var(--warn-soft);
  border: 1px solid var(--warn-line); border-radius: var(--r-sm);
  padding: 7px 10px; margin-top: 8px; }
.topbar { max-width: 640px; margin: 0 auto 16px; display: flex; align-items: center; }
.topbar .brand { margin: 0; }
.topbar a.back { margin-left: auto; font-size: 12.5px; color: var(--ink-2);
  text-decoration: none; transition: color var(--t1) var(--ease); }
.topbar a.back:hover { color: var(--ink); }

/* ------------------------------------------------------------------ narrow screens */
/* The rail is a fixed 340px track, so under ~700px the map is a sliver and under
   340px the grid overflows the body entirely. Below the breakpoint, revert to the
   pre-rail arrangement — full-bleed map, panel floating over it — rather than ship a
   map column too small to pan. #tooltip and .map-note stay children of #map and keep
   positioning correctly, since #map is still their containing block either way, and
   the container ResizeObserver re-fits the canvas as the breakpoint is crossed. */
@media (max-width: 700px) {
  body.map-page { display: block; }
  #map { position: absolute; inset: 0; }
  #panel {
    position: absolute; top: 14px; left: 14px; z-index: 10;
    width: 320px; max-width: calc(100% - 28px); max-height: calc(100% - 28px);
    background: color-mix(in srgb, var(--paper) 94%, transparent);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    border: none; border-radius: var(--r-lg); box-shadow: var(--e3);
    padding: 0 16px 14px;
  }
  #panel .brand { border-radius: var(--r-lg) var(--r-lg) 0 0; }
  .login-lockup { width: 280px; margin-bottom: 34px; }
  .login-card h1 { font-size: 25px; }
}

/* ---------------------------------------------------------------- market context

   The corpus is roughly half established, so this section is built to show a gap AS a
   gap. Established facts lead; everything unestablished is collapsed behind one line
   that says how many. A band is never shown without its coverage, and never without the
   "provisional" note when unestablished facts could still move it — a confident-looking
   grade over half a checklist is the exact failure this whole layer exists to avoid.

   The band colors are status, so they are fixed rather than themeable: a tenant must
   not be able to repaint "restrictive" into something that reads favourable. */
.ctx-band { display: flex; align-items: baseline; gap: 8px; margin: 3px 0 7px;
            flex-wrap: wrap; }
.ctx-band b { font-size: 15px; letter-spacing: .01em; }
.ctx-favourable b { color: var(--good); }
.ctx-mixed b      { color: var(--warn); }
.ctx-restrictive b{ color: var(--bad); }
.ctx-unclear b    { color: var(--muted); font-weight: 600; }
.ctx-cov { font-size: 11px; color: var(--muted); }
.ctx-prov { font-size: 11px; color: var(--warn); margin: 0 0 9px;
            border-left: 2px solid var(--warn-line); padding-left: 8px;
            background: var(--warn-soft); padding: 5px 8px;
            border-radius: 0 var(--r-xs) var(--r-xs) 0; }
.ctx-fact { padding: 6px 0; border-top: 1px solid var(--hairline); }
.ctx-fact:first-of-type { border-top: 0; }
.ctx-fact .k { font-size: 12px; color: var(--ink-3); }
.ctx-fact .v { font-weight: 600; }
.ctx-fact .src { font-size: 11px; color: var(--muted); }
/* The quote is the evidence and must stay visible — hiding it behind a click would
   undercut the one claim this section makes. But nine facts x a full statutory sentence
   is a wall in a 340px rail, so clamp to two lines and keep the rest in the tooltip. */
.ctx-fact .q { font-size: 11px; color: var(--muted); font-style: italic;
               margin-top: 3px; display: -webkit-box; -webkit-line-clamp: 2;
               -webkit-box-orient: vertical; overflow: hidden; }
.ctx-lookup { font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
              color: var(--good); font-weight: 600; }
.ctx-gap { font-size: 11px; color: var(--muted); margin-top: 9px; }
.ctx-gap ul { margin: 5px 0 0; padding-left: 16px; }
.ctx-basis { font-size: 11px; color: var(--muted); margin-top: 9px;
             padding-top: 7px; border-top: 1px solid var(--hairline); }

/* ------------------------------------------------------------------ reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important; animation-iteration-count: 1 !important;
    transition-duration: 1ms !important; scroll-behavior: auto !important;
  }
  .login-field { animation: none; opacity: .34; }
}

/* ------------------------------------------------------------------ forced colors */
/* Several fields set outline:none and draw focus as a box-shadow ring. Forced-colors
   mode discards box-shadow and overrides borders, which would leave them with no
   focus indicator at all — put a real outline back for those users. */
@media (forced-colors: active) {
  #addr:focus-visible,
  .login-card input:focus-visible,
  .settings-card input:focus-visible,
  .settings-card select:focus-visible {
    outline: 2px solid CanvasText; outline-offset: 2px;
  }
  .seg button.on { outline: 1px solid CanvasText; }
}

/* Where an unset capability sends you. Sized to sit inside a .cap-row without changing
   its height — this is a rail of one-line rows, not a list of buttons. */
.cap-do { font-size: 11px; letter-spacing: var(--tracked); text-transform: uppercase;
  color: var(--ink-2); background: none; border: 0; border-bottom: 1px solid var(--line);
  padding: 0 0 1px; cursor: pointer; font-family: inherit; text-decoration: none;
  /* nowrap for the same reason .cap-owner has it: .cap-name is the flex:1 that absorbs
     the squeeze, but nothing stops these shrinking once it is at min-content, and
     "OPEN SETTINGS" broken across two lines under a one-line label reads as damage. */
  white-space: nowrap; }
.cap-do:hover { color: var(--ink); border-bottom-color: var(--ink-2); }
/* Deliberately NOT in .cap-do's shape. There is no control anywhere in the app that
   fixes these, so anything reading as a control is a dead end -- and the first cut of
   this was an uppercase bordered tag sitting one row below two uppercase bordered
   links, which is the confusion it exists to remove. Lowercase, italic, no border, no
   underline: a note about who acts, not somewhere to click. */
.cap-owner { font-size: 11.5px; font-style: italic; color: var(--ink-3);
  white-space: nowrap; }
