/*
 * Colour semantics.
 *
 * Brand rule from the scope (D9): green means opportunity, black means actions.
 * Green therefore never appears as ordinary chrome, and stays rare so it keeps
 * meaning something.
 *
 * The important addition is a fourth state. Amber was originally doing three jobs
 * at once: "we could not check this", "this is a real constraint" and "this is
 * deliberately not shown". Those are different, and in a tool whose credibility
 * rests on never looking confident when it isn't, a reader must be able to tell a
 * failed check from a genuine finding at a glance.
 *
 *   green   OPPORTUNITY   improves the case          yield rises under PC120
 *   amber   CONSTRAINT    real, costs money, survivable  overlay applies, transformer loaded
 *   red     BLOCKER       likely stops the project   no wastewater capacity, no stormwater
 *   slate   UNKNOWN       could not be established   check failed, not in dataset, suppressed
 */
:root {
  --ink: #14171a;
  --ink-2: #4a5057;
  --ink-3: #757d85;
  --line: #e2e6ea;
  --bg: #ffffff;
  --panel: #f7f9fa;
  --opportunity: #0f7a52;   /* green: opportunity only */
  --opportunity-bg: #eaf6f1;
  --caution: #8a6100;       /* amber: a real constraint */
  --caution-bg: #fdf6e3;
  --bad: #a3231c;           /* red: a likely blocker */
  --bad-bg: #fdf1f0;
  --unknown: #5b6672;       /* slate: not established, deliberately not alarming */
  --unknown-bg: #eef1f4;
  --action: #14171a;        /* black: actions */
  --radius: 10px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ink: #eef1f3; --ink-2: #b3bcc4; --ink-3: #838d96;
    --line: #2a3138; --bg: #14171a; --panel: #1b2025;
    --opportunity: #3fbc8b; --opportunity-bg: #12281f;
    --caution: #e3b341; --caution-bg: #2a2312;
    --bad: #f08078; --bad-bg: #2c1a18;
    --unknown: #9aa6b2; --unknown-bg: #1f252b;
    --action: #eef1f3;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
}

/* ---------------------------------------------------------------- header */
.top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  padding: 14px 22px; border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: baseline; gap: 10px; }
.mark { font-weight: 700; font-size: 19px; letter-spacing: -0.2px; }
.stage {
  font-size: 12px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: 0.08em;
}
.council { font-size: 13px; color: var(--ink-2); }

main { max-width: 860px; margin: 0 auto; padding: 26px 22px 70px; }

/* ---------------------------------------------------------------- search */
.searchbar { position: relative; }
.searchbar label {
  display: block; font-size: 13px; font-weight: 600; color: var(--ink-2);
  margin-bottom: 6px;
}
#search {
  width: 100%; padding: 12px 14px; font-size: 16px;
  border: 1.5px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
}
#search:focus { outline: none; border-color: var(--action); }
#search:disabled { opacity: 0.5; }

#results {
  list-style: none; margin: 6px 0 0; padding: 4px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); position: absolute; z-index: 20; width: 100%;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.10); max-height: 340px; overflow: auto;
}
#results li { margin: 0; }
.result {
  display: flex; justify-content: space-between; gap: 10px; align-items: center;
  width: 100%; text-align: left; padding: 9px 11px;
  background: none; border: 0; border-radius: 7px; cursor: pointer;
  font: inherit; color: var(--ink);
}
.result:hover, .result:focus { background: var(--panel); outline: none; }
.tag {
  font-size: 11px; color: var(--ink-3); border: 1px solid var(--line);
  border-radius: 999px; padding: 1px 8px; white-space: nowrap;
}
.register { font-size: 12.5px; color: var(--ink-3); margin: 10px 0 0; }
.pending {
  font-size: 12.5px; margin: 8px 0 0; padding: 9px 11px;
  background: var(--caution-bg); color: var(--caution);
  border-radius: 8px; border: 1px solid color-mix(in srgb, var(--caution) 30%, transparent);
}
.placeholder {
  margin: 34px 0 0; padding: 20px; border: 1px dashed var(--line);
  border-radius: var(--radius); color: var(--ink-2); font-size: 14px;
}

/* ---------------------------------------------------------------- report */
.working { color: var(--ink-3); font-size: 14px; }
.prop-head { margin: 30px 0 14px; }
.prop-head h1 { font-size: 23px; margin: 0 0 4px; letter-spacing: -0.3px; }
.back-nav { margin: 0 0 10px; font-size: var(--fs-xs); }
.facts { margin: 0; color: var(--ink-2); font-size: 13.5px; }

/* Slate, not red. A shared title is not a planning problem, it is a case the tool
   cannot assess from here. Prominent so it is not missed, but not alarming. */
.tenure-warn {
  margin: 12px 0 0; padding: 12px 14px; border-radius: 8px;
  background: var(--unknown-bg); color: var(--ink);
  border-left: 4px solid var(--unknown);
}
.tenure-warn p { color: var(--ink-2); }
.tenure-warn strong { display: block; font-size: 14px; margin-bottom: 4px; }
.tenure-warn p { margin: 0; font-size: 12.5px; }

.aerial { margin: 0 0 22px; }
.aerial-wrap { position: relative; border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--line); line-height: 0; }
.aerial img { width: 100%; height: auto; display: block; }
.overlay { position: absolute; inset: 0; width: 100%; height: 100%; }
.overlay .parcel { fill: rgba(0, 200, 255, 0.10); stroke: #00c8ff; stroke-width: 2.5; }
.aerial figcaption { font-size: 11.5px; color: var(--ink-3); margin-top: 6px; }

.card {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; margin: 0 0 16px; background: var(--panel);
}
.card h2 { font-size: 15px; margin: 0 0 3px; letter-spacing: -0.1px; }
.card .sub { font-size: 12.5px; color: var(--ink-3); margin: 0 0 14px; }

.row {
  display: grid; grid-template-columns: 200px 1fr; gap: 6px 14px;
  padding: 8px 0; border-top: 1px solid var(--line); align-items: start;
}
.row:first-of-type { border-top: 0; }
.row .label { font-size: 13px; color: var(--ink-2); }
.row .value { font-size: 14.5px; font-weight: 600; }
.row .meta { grid-column: 2; font-size: 12px; color: var(--ink-3); font-weight: 400; }
.row.highlight .value { color: var(--opportunity); }
.row.warn .value { color: var(--caution); }
.row.bad-row .value { color: var(--bad); }
.row.unknown .value { color: var(--unknown); font-weight: 500; font-style: italic; }

/*
 * A cost line, expandable to correct it. <summary> carries the same label/value
 * rhythm as .row so a closed line reads identically to a plain one; opening it
 * is the only thing that changes.
 */
.cc-line { border-top: 1px solid var(--line); padding: 8px 0; }
.cc-line:first-of-type { border-top: 0; }
.cc-line-summary {
  display: grid; grid-template-columns: 200px 1fr; gap: 6px 14px;
  cursor: pointer; list-style: none; align-items: start;
}
.cc-line-summary::-webkit-details-marker { display: none; }
.cc-line-summary::before { content: "\25B8"; color: var(--ink-3); margin-right: 4px; }
.cc-line[open] .cc-line-summary::before { content: "\25BE"; }
.cc-line-label { font-size: 13px; color: var(--ink-2); }
.cc-line-value { font-size: 14.5px; font-weight: 600; }
.cc-line-meta { margin: 4px 0 0 200px; font-size: 12px; color: var(--ink-3); }
.cc-line-measured .cc-line-value { color: var(--opportunity); }
.cc-line-edit {
  margin: 10px 0 0 200px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.cc-line-edit-label { font-size: 12px; color: var(--ink-2); font-weight: 600; }
.cc-line-edit-hint { font-size: 11.5px; color: var(--ink-3); flex-basis: 100%; }

/* A small square in the label column, so the state is readable without relying on
   colour alone. */
.row.highlight .label::before,
.row.warn .label::before,
.row.bad-row .label::before,
.row.unknown .label::before {
  content: ""; display: inline-block; width: 7px; height: 7px;
  border-radius: 2px; margin-right: 7px; vertical-align: 1px;
}
.row.highlight .label::before { background: var(--opportunity); }
.row.warn .label::before { background: var(--caution); }
.row.bad-row .label::before { background: var(--bad); }
.row.unknown .label::before { background: var(--unknown); }

.legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0 0 18px;
  font-size: 11.5px; color: var(--ink-3); }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

.row ul, .qm, .hits { margin: 0; padding-left: 17px; font-weight: 400; font-size: 14px; }
.row ul li { margin: 1px 0; }

.caution {
  font-size: 12.5px; margin: 12px 0 0; padding: 10px 12px;
  background: var(--caution-bg); color: var(--caution); border-radius: 8px;
}
/* An operator note about how the tool may be used, not a finding about the site.
   Styled distinctly so it is never read as a property problem. */
.licence {
  font-size: 12px; margin: 10px 0 0; padding: 9px 12px;
  background: transparent; color: var(--ink-2); border-radius: 8px;
  border: 1px dashed var(--bad);
}
/* .licence is unused since 6 August 2026: the Watercare internal-only warning was
   removed on instruction. Kept as the pattern for any future licence-gated source. */
.licence::before { content: "Internal only. "; font-weight: 700; color: var(--bad); }
.source { font-size: 11.5px; color: var(--ink-3); margin: 9px 0 0; }
.source a { color: var(--ink-2); }
.bad { color: var(--bad); }
.muted { color: var(--ink-3); padding: 8px 11px; font-size: 13.5px; }
.warn { color: var(--caution); }

table.acc { width: 100%; border-collapse: collapse; font-size: 13.5px; margin-top: 4px; }
table.acc th, table.acc td {
  text-align: left; padding: 7px 9px; border-bottom: 1px solid var(--line);
}
table.acc th { font-size: 12px; color: var(--ink-2); font-weight: 600; }
table.acc tr td:first-child { font-weight: 600; }

/* Five real columns of address/score/area/lots/distance cannot usefully shrink
   below a real minimum, so on a narrow screen it scrolls inside its own box
   rather than pushing the whole page sideways, same as .scr-results below. */
.nbm-relevance-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.nbm-relevance {
  width: 100%; min-width: 480px; border-collapse: collapse; font-size: 13.5px; margin: 4px 0 0;
}
table.nbm-relevance th, table.nbm-relevance td {
  text-align: left; padding: 7px 9px; border-bottom: 1px solid var(--line);
}
table.nbm-relevance th { font-size: 12px; color: var(--ink-2); font-weight: 600; }
table.nbm-relevance tr td:first-child { font-weight: 600; }
.nbm-relevance-score {
  font-variant-numeric: tabular-nums; font-weight: 700; color: var(--opportunity);
}
.nbm-relevance-clickable { cursor: pointer; }
.nbm-relevance-clickable:hover { background: var(--paper-sunk); }

.disclaimer {
  margin-top: 26px; padding-top: 14px; border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--ink-3);
}
.disclaimer p { margin: 0 0 6px; }

@media (max-width: 620px) {
  .row { grid-template-columns: 1fr; gap: 2px; }
  .row .meta { grid-column: 1; }
}

/* A note about something that could not be established. Slate, never amber, so it
   is not mistaken for a finding about the site. */
.unknown-note {
  font-size: 12.5px; margin: 10px 0 0; padding: 10px 12px;
  background: var(--unknown-bg); color: var(--unknown); border-radius: 8px;
}

.actions { display: flex; gap: 10px; margin: 0 0 14px; }
.actions button {
  font: inherit; font-size: 14px; padding: 9px 16px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink);
}
/* Black for actions, per D9. */
.actions button.primary {
  background: var(--action); color: var(--bg); border-color: var(--action); font-weight: 600;
}

/* ---------------------------------------------------------------- register */
#register-btn {
  font: inherit; font-size: 12.5px; padding: 5px 11px; margin-right: 10px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--bg);
  color: var(--ink); cursor: pointer;
}
.changed {
  margin: 0 0 14px; padding: 11px 13px; border-radius: 8px;
  background: var(--caution-bg); color: var(--caution); font-size: 13px;
}
.changed ul { margin: 5px 0 0; padding-left: 17px; }

.reg-backdrop {
  position: fixed; inset: 0; background: rgba(10,14,18,0.55); z-index: 90;
  display: flex; justify-content: center; overflow: auto; padding: 24px 16px 60px;
}
.reg-panel {
  background: var(--bg); border-radius: 12px; padding: 18px 20px;
  width: 760px; max-width: 100%; align-self: flex-start;
}
.reg-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.reg-head h2 { margin: 0; font-size: 17px; }
.reg-head .grow { flex: 1; }
.reg-head button {
  font: inherit; font-size: 13px; padding: 6px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer;
}
.reg-note { font-size: 12px; color: var(--ink-3); margin: 0 0 14px; }
.reg-filterbar { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; }
.reg-filter-label { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.reg-filterbar select { font: inherit; font-size: 12.5px; padding: 4px 8px; border-radius: 7px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink); }
.reg-filter-count { font-size: 12px; color: var(--ink-3); }
.reg-card { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px;
  margin-bottom: 10px; }
.reg-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.reg-addr { flex: 1; text-align: left; font: inherit; font-size: 14.5px; font-weight: 600;
  background: none; border: 0; color: var(--ink); cursor: pointer; padding: 0; }
.reg-addr:hover { text-decoration: underline; }
.reg-top select { font: inherit; font-size: 12.5px; padding: 4px 8px; border-radius: 7px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink); max-width: 210px; }
.reg-del { font: inherit; font-size: 12px; padding: 4px 9px; border-radius: 7px;
  border: 1px solid var(--line); background: none; color: var(--ink-3); cursor: pointer; }
/* Site facts as tiles, replacing the old dot-joined .reg-meta text line: one tile
   per fact so a whole register reads at a glance rather than as a run-on sentence. */
.reg-tiles {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 8px 14px; margin: 9px 0 6px;
}
.reg-tile { border-top: 1px solid var(--line); padding-top: 5px; }
.reg-tile-label {
  display: block; font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3); font-weight: 600; margin-bottom: 1px;
}
.reg-tile-value { display: block; font-size: 13.5px; font-weight: 650; color: var(--ink); line-height: 1.2; }
.reg-tile-note { display: block; font-size: 11px; color: var(--ink-3); margin-top: 1px; }
.reg-card textarea { width: 100%; font: inherit; font-size: 13px; padding: 7px 9px;
  border-radius: 7px; border: 1px solid var(--line); background: var(--bg);
  color: var(--ink); resize: vertical; }
.reg-changes { margin-top: 8px; padding: 8px 11px; border-radius: 7px;
  background: var(--caution-bg); color: var(--caution); font-size: 12.5px; }
.reg-changes ul { margin: 4px 0 0; padding-left: 16px; }
.reg-dates { font-size: 11.5px; color: var(--ink-3); margin: 7px 0 0; }

/* ------------------------------------------------------------ batch screen */
#screen-btn {
  font: inherit; font-size: 12.5px; padding: 5px 11px; margin-right: 8px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--bg);
  color: var(--ink); cursor: pointer;
}
.scr-backdrop {
  position: fixed; inset: 0; background: rgba(10,14,18,0.55); z-index: 95;
  display: flex; justify-content: center; overflow: auto; padding: 24px 16px 60px;
}
.scr-panel {
  background: var(--bg); border-radius: 12px; padding: 18px 20px;
  width: 1000px; max-width: 100%; align-self: flex-start;
}
.scr-input {
  width: 100%; font: inherit; font-size: 13px; padding: 9px 11px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink);
  resize: vertical; margin-bottom: 10px;
}
.scr-filterbar { font-size: 12.5px; color: var(--ink-2); margin: 0 0 10px; }
.scr-filterbar label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.scr-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.scr-bar .grow { flex: 1; }
.scr-bar button {
  font: inherit; font-size: 13.5px; padding: 7px 14px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer;
}
.scr-bar button.primary {
  background: var(--action); color: var(--bg); border-color: var(--action); font-weight: 600;
}
.scr-bar button:disabled { opacity: 0.5; cursor: default; }
.scr-prog { font-size: 12.5px; color: var(--ink-3); }
.scr-results { overflow-x: auto; }
.scr-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.scr-table th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-3); padding: 6px 8px; border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--bg);
}
.scr-table td { padding: 6px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
.scr-table td:nth-child(2), .scr-table td:nth-child(4),
.scr-table td:nth-child(5), .scr-table td:nth-child(6) { text-align: right; white-space: nowrap; }
/* Green only where it means opportunity, per D9. */
.scr-win { background: var(--opportunity-bg); }
.scr-win td:first-child { font-weight: 600; }
.scr-up { color: var(--opportunity); font-weight: 700; }
.scr-bad { color: var(--unknown); font-style: italic; }
.scr-flags { color: var(--caution); font-size: 11.5px; }

/* ---------------------------------------------------------------- narrow screens
 * Q19 puts this on a laptop AND a phone, so the phone case is a requirement rather
 * than a nicety. The report already reflows; these are the pieces that did not.
 */
@media (max-width: 620px) {
  .top { padding: 12px 16px; }
  .council { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  #council-switch { max-width: 100%; }
  main { padding: 20px 16px 60px; }
  .actions { flex-wrap: wrap; }
  .actions button { flex: 1 1 auto; }
  .legend { gap: 4px 12px; }

  /* The screened table cannot usefully shrink, so it scrolls inside its own box
     rather than pushing the page sideways. */
  .scr-panel { padding: 14px 14px; }
  .scr-results { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .scr-table { min-width: 620px; }
  .scr-bar { flex-wrap: wrap; }
  .reg-panel { padding: 14px 14px; }
  .reg-top { gap: 8px; }
  .reg-top select { max-width: 100%; flex: 1 1 100%; }
  .reg-addr { flex: 1 1 100%; }
}

/* Whether an overlay covers the whole site or clips part of it. Muted, because it
   qualifies the overlay rather than being a finding of its own. */
.extent { color: var(--ink-3); font-weight: 400; font-size: 12.5px; }

/* A property fronting several roads carries one record per address. Same site. */
.aka { margin: 5px 0 0; font-size: 12.5px; color: var(--ink-3); }

/* Batch screen: picking rows for the register, and the driveway net column. */
.scr-pick { width: 28px; text-align: center; }
.scr-killed { color: #a3261f; font-weight: 600; }
.scr-down { color: #8a5a12; font-weight: 600; }
.scr-actions { display: flex; gap: 8px; align-items: center; margin-top: 12px; }

/* ============================================================================
 * DESIGN PASS, 2 August 2026. Loaded after everything above, so it overrides.
 *
 * The tool was laid out to get data on screen honestly, which was the right first
 * priority and the wrong final one. Three things were wrong and all three are
 * hierarchy rather than decoration:
 *
 *   1. The answer was not the hero. "Yields 2, or 1 once the driveway is in" sat in a
 *      table row at the same weight as "Zone", while the largest text on screen was
 *      the address, which is the least interesting thing on the page.
 *   2. Everything competed. The register status line, the pending changes banner and
 *      the aerial attribution carried the same weight as the assessment.
 *   3. A 1600px window ran one narrow column while the legend floated orphaned
 *      between the buttons and the content.
 *
 * Colour is entirely from theme.css, so another firm's palette is a change there.
 * ========================================================================= */

body { font: var(--fs-base)/var(--lh-base) var(--font); color: var(--ink-800);
  -webkit-font-smoothing: antialiased; }

/* Masthead. The brand signs the page; the stage label is scaffolding and recedes. */
.top { border-bottom: 1px solid var(--line); padding: var(--s-3) var(--s-6); }
.mark { color: var(--brand-700); font-size: var(--fs-md); letter-spacing: -0.3px; }
.stage { color: var(--ink-400); font-size: var(--fs-2xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--tracking-wide); }
.council { font-size: var(--fs-sm); color: var(--ink-500); }

/* Wider, because two cards side by side is the whole reason to have a big screen. */
main { max-width: 1120px; padding: var(--s-8) var(--s-6) var(--s-16); }

.searchbar label { font-size: var(--fs-sm); font-weight: 600; color: var(--ink-700); }
#search {
  font-size: var(--fs-md); padding: var(--s-4);
  border: 1.5px solid var(--line); border-radius: var(--r-md);
  transition: border-color .12s, box-shadow .12s;
}
#search:focus { border-color: var(--brand-500); box-shadow: 0 0 0 3px var(--brand-100); }
#results { border-radius: var(--r-md); box-shadow: var(--shadow-2); border-color: var(--line); }
.result:hover { background: var(--brand-050); }

/* Supporting chrome. Present, checkable, and quiet. */
.register, .placeholder { font-size: var(--fs-xs); color: var(--ink-400); }
.pending {
  font-size: var(--fs-xs); background: var(--warn-050); color: var(--warn-700);
  border: 1px solid transparent; border-left: 3px solid var(--warn-600);
  border-radius: var(--r-sm); padding: var(--s-2) var(--s-3);
}

/* The property, and the answer.
   The address is context and steps back; the assessment steps forward. */
#report > h1, .prop-head h1 { font-size: var(--fs-xl); letter-spacing: var(--tracking-tight); }

.card {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: var(--s-5) var(--s-6); background: var(--paper);
  box-shadow: var(--shadow-1);
}
.card h2 { font-size: var(--fs-md); letter-spacing: -0.2px; color: var(--ink-900); }
.card .sub { font-size: var(--fs-sm); color: var(--ink-400); }

.row { padding: var(--s-3) 0; border-top: 1px solid var(--line-soft); }
.row .label { font-size: var(--fs-sm); color: var(--ink-500); }
.row .value { font-size: var(--fs-md); font-weight: 650; letter-spacing: -0.2px; }
.row .meta { font-size: var(--fs-xs); color: var(--ink-400); }

/*
 * The driveway adjusted yield is the tool's whole point, so it is the one row allowed
 * to shout. Everything else on the card is evidence for it.
 */
.row.hero-row .value { font-size: var(--fs-2xl); line-height: 1.05;
  letter-spacing: var(--tracking-tight); font-weight: 700; }
.row.hero-row { padding: var(--s-4) 0 var(--s-5); }

.row.highlight .value { color: var(--good-700); }
.row.warn .value { color: var(--warn-700); }
.row.bad-row .value { color: var(--bad-600); }
.row.unknown .value { color: var(--unknown-600); }

/* Two up on a wide screen. Cards are self contained, so they tile cleanly. */
@media (min-width: 900px) {
  #report { display: grid; grid-template-columns: 1fr 1fr;
    gap: var(--s-5); align-items: start; }
  /* The address block, the actions and the legend span both columns. */
  #report > :not(.card) { grid-column: 1 / -1; }
  /* Card 1 is the answer and earns the full width. */
  #report .card:first-of-type { grid-column: 1 / -1; }
}

button.primary, .op-bar button.primary {
  background: var(--brand-700); border-color: var(--brand-700);
  color: var(--brand-contrast);
}
button.primary:hover { background: var(--brand-900); }

/* ---------------------------------------------------------------- empty state
 * Opening to a search box and one grey paragraph reads as a broken page: there is no
 * way to know what the tool returns until you already know an address worth typing.
 * This says what comes back, and offers four real properties that demonstrate it,
 * including the two where the driveway changes the answer.
 */
.placeholder {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--paper); padding: var(--s-6); margin-top: var(--s-6);
  color: var(--ink-700);
}
.ph-lead { margin: 0 0 var(--s-6); color: var(--ink-500); font-size: var(--fs-base); }
.ph-heading {
  margin: 0 0 var(--s-3); font-size: var(--fs-2xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--ink-400);
}
.ph-list { margin: 0 0 var(--s-6); padding: 0; list-style: none; }
.ph-list li {
  position: relative; padding-left: var(--s-6); margin-bottom: var(--s-2);
  font-size: var(--fs-sm); color: var(--ink-600); line-height: 1.5;
}
.ph-list li strong { color: var(--ink-900); font-weight: 650; }
.ph-list li::before {
  content: ""; position: absolute; left: 0; top: 7px;
  width: 8px; height: 8px; border-radius: 2px; background: var(--brand-200);
}
.ph-examples { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-3); }
.ph-eg {
  display: flex; flex-direction: column; gap: 2px; text-align: left;
  padding: var(--s-3) var(--s-4); cursor: pointer; font: inherit;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper-sunk);
  transition: border-color .12s, background .12s, transform .08s;
}
.ph-eg:hover { border-color: var(--brand-500); background: var(--brand-050); }
.ph-eg:active { transform: translateY(1px); }
.ph-eg-addr { font-size: var(--fs-sm); font-weight: 650; color: var(--ink-900); }
.ph-eg-why { font-size: var(--fs-xs); color: var(--ink-500); }
@media (max-width: 700px) { .ph-examples { grid-template-columns: 1fr; } }

/* ==========================================================  APP SHELL  =====
 * The register and the change radar were real features hidden behind buttons that
 * opened modals. Nothing on screen suggested the tool did more than look up one
 * address. A rail makes the scope visible, which is most of what was wrong.
 */
.shell { display: grid; grid-template-columns: 244px 1fr; min-height: 100vh; }
.shell[hidden] { display: none; }
.shell-main { min-width: 0; }

.rail {
  display: flex; flex-direction: column; gap: var(--s-6);
  padding: var(--s-5) var(--s-4);
  background: var(--paper-sunk);
  border-right: 1px solid var(--line);
  position: sticky; top: 0; height: 100vh;
}
/*
 * The stacked lockup: icon centred above the wordmark, not side by side. This is
 * the version Trevor approved (21 August 2026) — icon big enough to actually read
 * as the real artwork, wordmark sized to match it rather than a small label next
 * to a small icon. The nav rail is a vertical sidebar with real height to spend,
 * unlike a horizontal top bar, so the stack fits naturally rather than being
 * squeezed into one cramped row.
 */
.rail-mark {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--s-3); text-decoration: none; color: var(--ink-900); padding: var(--s-2);
}
.rail-mark-text { font-size: var(--fs-xl); font-weight: 800; line-height: 1.15; letter-spacing: -0.01em; }
.rail-mark-iq { color: var(--brand-700); }
.rail-mark-text small {
  display: block; font-size: var(--fs-2xs); font-weight: 600; color: var(--brand-700);
  text-transform: uppercase; letter-spacing: var(--tracking-wide); margin-top: var(--s-1);
}
.rail-nav { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.rail-item {
  width: 100%; display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-4); border: 0; border-radius: var(--r-md);
  background: none; font: inherit; font-size: var(--fs-sm); font-weight: 550;
  color: var(--ink-600); cursor: pointer; text-align: left;
  transition: background .12s, color .12s;
}
.rail-item:hover { background: var(--paper); color: var(--ink-900); }
/* Action, not opportunity, so the selected item is black. Green never lands on nav. */
.rail-item.is-on { background: var(--action); color: var(--action-contrast); font-weight: 650; }
.rail-count {
  margin-left: auto; min-width: 20px; padding: 1px 6px; border-radius: 999px;
  background: var(--brand-100); color: var(--brand-900);
  font-size: var(--fs-2xs); font-weight: 700; text-align: center;
}
.rail-item.is-on .rail-count { background: rgba(255,255,255,.18); color: #fff; }
.rail-foot { margin-top: auto; display: grid; gap: var(--s-2); padding: 0 var(--s-2); }
.rail-foot-label {
  font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-wide); color: var(--ink-400);
}
.rail-foot select {
  width: 100%; font: inherit; font-size: var(--fs-xs); padding: var(--s-2);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--paper); color: var(--ink-800);
}

/* Panels rendered as pages rather than floated over one. */
.reg-inline, .rc-inline, .scr-inline { display: block; }
.reg-inline .reg-panel, .rc-inline .rc-panel, .scr-inline .scr-panel {
  max-width: none; margin: 0; box-shadow: none; background: none; padding: 0;
}
.rc-inline .rc-status { color: var(--ink-500); }
.rc-inline .rc-bar { background: var(--line); }
.reg-inline .reg-head, .rc-inline .rc-head, .scr-inline .scr-head {
  position: static; box-shadow: none; padding: 0 0 var(--s-4);
  border-bottom: 1px solid var(--line); margin-bottom: var(--s-5); background: none;
}

@media (max-width: 900px) {
  .shell { grid-template-columns: 1fr; }
  .rail { position: static; height: auto; flex-direction: row; align-items: center;
    flex-wrap: wrap; border-right: 0; border-bottom: 1px solid var(--line); }
  .rail-nav { display: flex; flex-wrap: wrap; flex: 1; }
  .rail-foot { margin: 0; }
  /*
   * The stacked lockup (icon above wordmark, ~180px tall) is a desktop-sidebar
   * treatment: the rail has a full column of height to spend there. Collapsed
   * into a horizontal top bar it has none, so the mark goes back to compact and
   * horizontal here rather than dragging the whole bar down to accommodate it.
   * Found live at 375px: without this the mark's height alone pushed the rail to
   * 468px tall before a single nav item or a search box appeared on screen.
   */
  .rail-mark { flex-direction: row; text-align: left; gap: var(--s-2); padding: 0; }
  .rail-logo { width: 36px; height: 36px; }
  .rail-mark-text { font-size: var(--fs-md); }
}

/* The rail carries the brand and the council selector, so the header is reduced to a
   breadcrumb. Two of each on one screen was the cost of bolting a shell onto a page
   that used to be the whole app. */
.top { padding: var(--s-5) var(--s-7) 0; border: 0; background: none; }
.crumb {
  margin: 0; font-size: var(--fs-2xs); font-weight: 700; color: var(--ink-400);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
}
.view { padding: 0 var(--s-7) var(--s-8); }

/* Coverage strip. Sits above the cards: reading the answer first and "we could not
   reach three sources" last is the wrong order. */
.cover {
  display: grid; gap: var(--s-2); margin: var(--s-5) 0 var(--s-6);
  padding: var(--s-4) var(--s-5); border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--paper-sunk);
}
.cover-bar { height: 5px; border-radius: 3px; background: var(--line); overflow: hidden; }
.cover-fill { height: 100%; background: var(--brand-700); transition: width .3s; }
.cover-text { font-size: var(--fs-sm); color: var(--ink-600); line-height: 1.5; }
.cover-text strong { color: var(--ink-900); }
/* Thin coverage is a real caution, so it is amber. It is never green: coverage is not
   opportunity, and a fully checked property with no potential must not look positive. */
.cover-moderate { border-color: var(--warn-200); background: var(--warn-050); }
.cover-moderate .cover-fill { background: var(--warn-600); }
.cover-thin { border-color: var(--bad-200); background: var(--bad-050); }
.cover-thin .cover-fill { background: var(--bad-600); }

/* ------------------------------------------------------------ opportunity map
 * SVG, no library, no tiles. A choropleth needs shapes and a fill, not photography,
 * and a basemap would buy the tool its first runtime dependency and its first licence
 * exposure to show roads nobody is reading.
 */
.view-title { font-size: var(--fs-2xl); margin: var(--s-1) 0 var(--s-5); }
.map-wrap { max-width: 720px; }

/* Which question the map is answering.
 *
 * A real fieldset with radios rather than two styled buttons, because the choice is
 * between two different measures over two different populations and it has to read as
 * a choice: both labels visible at once, arrow keys working, and the group announced
 * as "Showing". The native input is kept and drawn rather than replaced, so checked
 * and focus state come from the browser instead of from CSS that can fall out of step
 * with it. */
.map-views {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
  border: 0; padding: 0; margin: 0 0 var(--s-4);
}
.map-views legend {
  float: left; margin: 0 var(--s-3) 0 0; padding: 0;
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-400);
}
.map-view {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); cursor: pointer;
  font-size: var(--fs-xs); color: var(--ink-700); line-height: 1;
}
.map-view:hover { border-color: var(--ink-400); }
.map-view:has(input:checked) {
  border-color: var(--brand-900); background: var(--map-1); color: var(--ink-900);
  font-weight: 650;
}
.map-view:has(input:focus-visible) {
  outline: 2px solid var(--brand-900); outline-offset: 2px;
}
.map-view input { margin: 0; accent-color: var(--brand-900); }

.map-svg {
  width: 100%; height: auto; display: block;
  background: var(--paper-sunk); border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.map-board {
  stroke: var(--paper); stroke-width: 1; cursor: pointer;
  transition: fill .12s, stroke .12s;
}
.map-board:hover, .map-board:focus {
  stroke: var(--ink-900); stroke-width: 2; outline: none;
}
/* A sequential green ramp: this is opportunity, which is the one thing green means. */
:root {
  --map-none: #e9ecef; --map-0: #f3f6f1;
  --map-1: #ddf5cd; --map-2: #b4e79a; --map-3: #6ccb35; --map-4: #2a8500;
}
/* The dark ramp. `:root:not([data-theme="light"])` on its own matches when NO theme
   attribute is set, which is the default, so the dark colours were applying in light
   mode and painting an unshaded board solid black. It has to be inside the media
   query, with the explicit attribute as the separate override. */
:root[data-theme="dark"] {
  --map-none: #232323; --map-0: #1c231a;
  --map-1: #24401a; --map-2: #2f5c1a; --map-3: #4e9c22; --map-4: #7fd94a;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --map-none: #232323; --map-0: #1c231a;
    --map-1: #24401a; --map-2: #2f5c1a; --map-3: #4e9c22; --map-4: #7fd94a;
  }
}

/*
 * Per-zone ramps for the zone-type filter. Green is reserved for the whole-board
 * figure, so a zoomed-in single zone gets its own hue: the point of colouring it
 * differently is telling a reader at a glance "you are no longer looking at every
 * zone" without them having to read the picker first.
 */
:root {
  --map-mhu-1: #cfe4fb; --map-mhu-2: #93c3f3; --map-mhu-3: #4f97e6; --map-mhu-4: #14559c;
  --map-mhs-1: #cdf3ee; --map-mhs-2: #8ee0d5; --map-mhs-3: #35b8a6; --map-mhs-4: #0d7568;
  --map-shz-1: #e6d9fb; --map-shz-2: #c6a4f2; --map-shz-3: #9757e0; --map-shz-4: #5c1f9c;
  --map-llz-1: #faeac9; --map-llz-2: #f0c876; --map-llz-3: #dd9a25; --map-llz-4: #966300;
  --map-rcs-1: #ecdfd0; --map-rcs-2: #d3b48c; --map-rcs-3: #a97d47; --map-rcs-4: #6b4a1f;
  --map-thab-1: #fbd9e8; --map-thab-2: #f2a0c4; --map-thab-3: #d94e8f; --map-thab-4: #941a5c;
}
:root[data-theme="dark"] {
  --map-mhu-1: #16283f; --map-mhu-2: #1b3a5c; --map-mhu-3: #2e6098; --map-mhu-4: #5fa3e8;
  --map-mhs-1: #123531; --map-mhs-2: #154f48; --map-mhs-3: #1f8578; --map-mhs-4: #4fd9c4;
  --map-shz-1: #2a1a40; --map-shz-2: #3d215c; --map-shz-3: #6a37a0; --map-shz-4: #b671f0;
  --map-llz-1: #3a2c12; --map-llz-2: #5c4416; --map-llz-3: #996f1e; --map-llz-4: #e8ae3f;
  --map-rcs-1: #2b2016; --map-rcs-2: #40301f; --map-rcs-3: #6b4e2c; --map-rcs-4: #c19a5e;
  --map-thab-1: #3a1226; --map-thab-2: #57173a; --map-thab-3: #93275f; --map-thab-4: #f062a8;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --map-mhu-1: #16283f; --map-mhu-2: #1b3a5c; --map-mhu-3: #2e6098; --map-mhu-4: #5fa3e8;
    --map-mhs-1: #123531; --map-mhs-2: #154f48; --map-mhs-3: #1f8578; --map-mhs-4: #4fd9c4;
    --map-shz-1: #2a1a40; --map-shz-2: #3d215c; --map-shz-3: #6a37a0; --map-shz-4: #b671f0;
    --map-llz-1: #3a2c12; --map-llz-2: #5c4416; --map-llz-3: #996f1e; --map-llz-4: #e8ae3f;
    --map-rcs-1: #2b2016; --map-rcs-2: #40301f; --map-rcs-3: #6b4e2c; --map-rcs-4: #c19a5e;
    --map-thab-1: #3a1226; --map-thab-2: #57173a; --map-thab-3: #93275f; --map-thab-4: #f062a8;
  }
}
.map-zone-picker { display: flex; align-items: center; gap: var(--s-3); margin: 0 0 var(--s-4); }
.map-zone-label {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-xs); color: var(--ink-700); font-weight: 600;
}
.map-zone-select {
  font: inherit; font-size: var(--fs-sm); padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); color: var(--ink-900); max-width: 320px;
}
.map-zone-active-note {
  margin: 0 0 var(--s-3); font-size: var(--fs-xs); font-weight: 600; color: var(--ink-700);
}
.map-legend {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  margin-top: var(--s-4); font-size: var(--fs-xs); color: var(--ink-500);
}
.map-key { display: inline-flex; align-items: center; gap: var(--s-2); }
.map-key i {
  width: 14px; height: 14px; border-radius: 3px;
  border: 1px solid rgba(0,0,0,.12); display: inline-block;
}
.map-note {
  margin-top: var(--s-4); font-size: var(--fs-xs); color: var(--ink-500);
  line-height: 1.55; max-width: 62ch;
}

/* Map labels. Paint-order stroke gives each glyph a halo so names stay readable over
   both the palest and the darkest fills without a panel behind them. */
.map-label { pointer-events: none; }
.map-label-name, .map-label-pct {
  paint-order: stroke; stroke: var(--paper); stroke-width: 3px; stroke-linejoin: round;
}
.map-label-name { font-size: 9px; font-weight: 650; fill: var(--ink-900); }
.map-label-pct { font-size: 9.5px; font-weight: 700; fill: var(--brand-900); }

/* Attribution is a CC BY 4.0 condition, so it is not optional styling. */
.map-attr { margin-top: var(--s-2); font-size: var(--fs-2xs); color: var(--ink-400); }
.map-tiles image { image-rendering: auto; }

/* Board drill-through. Everything here is already loaded with the map, so the click
   costs no requests. */
.board-detail:empty { display: none; }
.board-detail {
  margin-top: var(--s-6); padding: var(--s-5) var(--s-6);
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--paper-sunk);
  max-width: 720px;
}
.bd-title { margin: 0 0 var(--s-2); font-size: var(--fs-lg); }
.bd-lead { margin: 0 0 var(--s-5); color: var(--ink-600); font-size: var(--fs-sm); line-height: 1.55; }
.bd-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.bd-table th {
  text-align: left; font-size: var(--fs-2xs); text-transform: uppercase;
  letter-spacing: var(--tracking-wide); color: var(--ink-400);
  padding: 0 var(--s-3) var(--s-2) 0; border-bottom: 1px solid var(--line); font-weight: 700;
}
.bd-table td { padding: var(--s-3) var(--s-3) var(--s-3) 0; border-bottom: 1px solid var(--line-soft); }
.bd-table td:nth-child(n+2) { text-align: right; font-variant-numeric: tabular-nums; }
.bd-pct { font-weight: 700; color: var(--brand-900); }
.bd-note { margin: var(--s-4) 0 0; font-size: var(--fs-xs); color: var(--ink-500); line-height: 1.55; }

/* Every view says what it is for. A page whose purpose has to be explained in
   conversation is a page that has failed. */
.view-lead {
  margin: 0 0 var(--s-6); max-width: 64ch;
  font-size: var(--fs-base); color: var(--ink-600); line-height: 1.6;
}

/* The options ladder. A near miss is the most actionable thing on the page, so it is
   given its own line rather than left as one row in a table of failures. */
.ladder { margin: var(--s-4) 0 var(--s-3); }
.ladder-head {
  margin: 0 0 var(--s-2); font-size: var(--fs-2xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--ink-400);
}
.ladder-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.ladder-table th {
  text-align: left; font-size: var(--fs-2xs); font-weight: 600; color: var(--ink-400);
  padding: 0 var(--s-3) var(--s-1) 0; border-bottom: 1px solid var(--line);
}
.ladder-table td { padding: var(--s-2) var(--s-3) var(--s-2) 0; border-bottom: 1px solid var(--line-soft); }
.ladder-table td:first-child { font-weight: 700; width: 3em; }
.ladder-verdict { text-align: right; font-variant-numeric: tabular-nums; }
.ladder-yes .ladder-verdict { color: var(--good-600); font-weight: 650; }
.ladder-no .ladder-verdict { color: var(--ink-400); }
.ladder-near {
  margin: var(--s-3) 0 0; padding: var(--s-3) var(--s-4);
  border-left: 3px solid var(--warn-600); background: var(--warn-050);
  border-radius: var(--r-sm); font-size: var(--fs-sm); line-height: 1.55; color: var(--ink-700);
}
/* A secondary, lower-confidence aside under ladder-near: a professional's own
   reading rather than a cited rule, so it is visually quieter than the finding
   above it, not a second version of the same claim. */
.ladder-note {
  margin: var(--s-2) 0 0; padding: var(--s-2) var(--s-4);
  font-size: var(--fs-xs); line-height: 1.5; color: var(--ink-500); font-style: italic;
}

/* The tool's own mark, in the nav rail — SubdivideIQ, not the client brand shown
   on client-facing output (see app/clientBrand.js for that split). Sized to match
   the approved stacked lockup (icon above wordmark), not squeezed beside the text
   at icon-font size: the real artwork is detail-heavy and needs real pixels to
   read as the actual logo rather than a generic small icon. */
.rail-logo { display: block; width: 88px; height: 88px; }

/* ---------------------------------------------------------------- map filters */
/*
 * Two groups side by side, wrapping to one column when there is no room. Deliberately
 * quieter than the view radios above them: the view switch changes WHICH QUESTION the
 * map answers, the filters narrow the answer, and the visual weight should say so.
 */
.map-filters {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  align-items: flex-start;
  margin: 0 0 var(--s-4); padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper-50, var(--paper));
}
.map-filter-group { border: 0; padding: 0; margin: 0; min-width: 15rem; flex: 1 1 18rem; }
.map-filter-group legend {
  padding: 0; margin: 0 0 var(--s-1);
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-400);
}
.map-filter-hint {
  margin: 0 0 var(--s-2); font-size: var(--fs-2xs); color: var(--ink-400);
}
.map-filter {
  display: flex; align-items: center; gap: var(--s-2);
  padding: 2px 0; cursor: pointer;
  font-size: var(--fs-xs); color: var(--ink-700); line-height: 1.5;
}
.map-filter:hover { color: var(--ink-900); }
.map-filter input { margin: 0; accent-color: var(--brand-900); }
.map-filter:has(input:checked) { color: var(--ink-900); font-weight: 650; }
.map-filter:has(input:focus-visible) {
  outline: 2px solid var(--brand-900); outline-offset: 2px; border-radius: var(--r-sm);
}
/* The regional size of each filter, pushed right so the column reads as a list. */
.map-filter-n {
  margin-left: auto; font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xs); color: var(--ink-400);
}
.map-filter-clear {
  flex: 0 0 auto; align-self: flex-end;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); cursor: pointer;
  font-size: var(--fs-2xs); color: var(--ink-700);
}
.map-filter-clear:hover { border-color: var(--ink-400); color: var(--ink-900); }

/* What the filters did. Never let a filtered map look unfiltered. */
.map-applied {
  margin: var(--s-3) 0 0; font-size: var(--fs-xs);
  font-weight: 650; color: var(--ink-900);
}
.map-caution {
  margin: var(--s-1) 0 0; font-size: var(--fs-2xs);
  color: var(--ink-700);
  border-left: 3px solid var(--warn, #b8860b); padding-left: var(--s-3);
}

/* The collapsed shell around the filters. Native <details>. */
.map-filter-shell {
  margin: 0 0 var(--s-4);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper);
}
.map-filter-summary {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2);
  padding: var(--s-3) var(--s-4); cursor: pointer;
  font-size: var(--fs-xs); color: var(--ink-700); list-style: none;
}
.map-filter-summary::-webkit-details-marker { display: none; }
/* Own disclosure triangle, so it reads the same in every browser. */
.map-filter-summary::before {
  content: "\25B8"; display: inline-block; margin-right: var(--s-1);
  color: var(--ink-400); transition: transform .12s ease;
}
.map-filter-shell[open] .map-filter-summary::before { transform: rotate(90deg); }
.map-filter-summary:focus-visible {
  outline: 2px solid var(--brand-900); outline-offset: -2px; border-radius: var(--r-md);
}
.map-filter-summary-label { font-weight: 700; color: var(--ink-900); }
.map-filter-summary-off { color: var(--ink-400); }
/* When a filter IS on, the collapsed summary has to say so loudly. */
.map-filter-summary-on { color: var(--brand-900); font-weight: 650; }
/* Inside the shell the panel loses its own frame; the shell provides it. */
.map-filter-shell .map-filters {
  margin: 0; border: 0; border-top: 1px solid var(--line);
  border-radius: 0 0 var(--r-md) var(--r-md);
}

/* A boot failure must not look like a slow load. See the inline script in index.html. */
.register-error {
  color: var(--ink-900);
  background: #fdf3e3;
  border-left: 3px solid var(--warn, #b8860b);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
}
.register-error kbd {
  font: inherit; font-weight: 700;
  background: rgba(0,0,0,.06); border-radius: 3px; padding: 0 3px;
}

/* Where the picker shows the council's own name for a suburb it corrected. Quiet:
   it is provenance, not a warning. */
.tag-quiet {
  background: transparent; border: 1px solid var(--line);
  color: var(--ink-400); font-weight: 400;
}

/* ============================================================== THE STATE CHIP
 *
 * One component, five modifiers, replacing the colour-on-text state styling that
 * had accumulated in six near-copies across this file, the map legend, the batch
 * table and both documents.
 *
 * WHY A CHIP RATHER THAN COLOURED TEXT. Colour alone carries a state only for
 * people who can see it, on a screen, in the mode it was designed for. A chip
 * carries GLYPH + WORD + COLOUR, so the state survives greyscale, print, dark mode
 * and colour blindness with any one of the three removed.
 *
 * THE UNRESOLVED STATE IS THE POINT OF THE WHOLE SYSTEM. "Could not establish" is
 * the most dangerous answer this tool gives, because silence reads as a pass and a
 * landowner told "no constraints" on a constrained site is the worst thing that can
 * leave the building. So it does not get a calm grey badge like the others: it gets
 * a hatched fill, a dashed border and a question mark. It should look unfinished,
 * because it is.
 *
 * Geometry is from the design handoff, which specified it against the real tokens.
 */
.chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px 4px 7px;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  white-space: nowrap;
  vertical-align: middle;
}
.chip-glyph {
  width: 15px; height: 15px; border-radius: 3px;
  display: grid; place-items: center;
  font-size: 10px; font-weight: 800; line-height: 1;
  /*
   * THE GLYPH INK HAS TO FLIP WITH THE THEME.
   *
   * This was hardcoded white, which is right in light mode where the -600 tokens are
   * deep (#2a8500) and wrong in dark, where the whole state ramp inverts and -600
   * becomes a LIGHT colour (#6ccb35). Measured in dark before the fix: 2.05:1 for
   * opportunity, 2.26 constraint, 2.98 blocker, 3.78 reference, against a 4.5 floor.
   * The words were always fine, which is how it survived a visual check: the fault
   * was only in the small glyph, and only in one theme.
   */
  color: var(--chip-ink, #fff);
  flex: 0 0 auto;
}
.chip-word {
  font-size: 11px; font-weight: 750;
  text-transform: uppercase; letter-spacing: .04em; line-height: 1.2;
}

.chip-good { background: var(--good-050); border-color: var(--good-200); }
.chip-good .chip-glyph { background: var(--good-600); }
.chip-good .chip-word  { color: var(--good-700); }

.chip-warn { background: var(--warn-050); border-color: var(--warn-200); }
.chip-warn .chip-glyph { background: var(--warn-600); }
.chip-warn .chip-word  { color: var(--warn-700); }

.chip-bad { background: var(--bad-050); border-color: var(--bad-200); }
.chip-bad .chip-glyph { background: var(--bad-600); }
.chip-bad .chip-word  { color: var(--bad-700); }

/*
 * Unresolved. Hatched, dashed, and the glyph is a ring rather than a square so it
 * does not read as a fourth member of the settled family.
 */
.chip-unknown {
  border-style: dashed; border-color: var(--unknown-200);
  background-color: var(--unknown-050);
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0 5px,
    color-mix(in srgb, var(--unknown-600) 14%, transparent) 5px 7px);
}
.chip-unknown .chip-glyph { background: var(--unknown-600); border-radius: 50%; }
.chip-unknown .chip-word  { color: var(--unknown-700); }

/*
 * Reference: established, correct, and not part of the decision. A fifth state, and
 * it earns its place. Without it the givens (zone, minimum site area) would have to
 * borrow "not established", which would claim the tool failed to find something it
 * found perfectly well. Deliberately the quietest thing on the page.
 */
.chip-ref { background: var(--paper-sunk); border-color: var(--line); }
/* --ink-500, not --ink-400: white on 400 measures 3.61:1, under the 4.5 floor. The
   reference chip is the quietest one and still has to be readable. */
.chip-ref .chip-glyph { background: var(--ink-500); }
.chip-ref .chip-word  { color: var(--ink-500); }

/* Dense rows: the change radar and the batch table. */
.chip-sm { gap: 6px; padding: 3px 9px 3px 6px; }
.chip-sm .chip-glyph { width: 14px; height: 14px; font-size: 9px; }
.chip-sm .chip-word { font-size: 10.5px; letter-spacing: .03em; }

@media print {
  /* Tinted fills must survive, or every chip prints as an identical grey outline
     and the state system stops working on the output that matters most. */
  .chip { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* ------------------------------------------------------ cards carrying a state */
/*
 * The left edge is the state at a glance from the far side of a desk; the chip in
 * the header is the state named. Both, because the edge is fast and the word is
 * unambiguous, and neither alone does both jobs.
 */
.card-head {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--s-3); margin-bottom: var(--s-3);
}
.card-head h2 { margin: 0; }
.card-bad     { border-left: 4px solid var(--bad-600); }
.card-unknown { border-left: 4px solid var(--unknown-600); }
.card-warn    { border-left: 4px solid var(--warn-600); }
.card-good    { border-left: 4px solid var(--good-600); }
/* Reference cards get a hairline, not a bar: they are the givens, not a finding. */
.card-ref     { border-left: 4px solid var(--line); }

/* The state key, built from real chips rather than colour swatches. */
.legend { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); align-items: center; }
.legend-item {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-xs); color: var(--ink-400);
}

/* ------------------------------------------------------------ map: mode switch */
/*
 * Segmented control, Map on the left and default. Two buttons rather than radios
 * because this changes HOW the same data is drawn, not which data is shown; the
 * view radios above it already carry the "two different questions" job and using
 * the same control for both would flatten a real distinction.
 */
.map-modes {
  display: inline-flex; gap: 0; margin: 0 0 var(--s-4);
  border: 1px solid var(--line); border-radius: var(--r-md);
  overflow: hidden; background: var(--paper);
}
.map-mode {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-xs); color: var(--ink-700); line-height: 1;
}
.map-mode + .map-mode { border-left: 1px solid var(--line); }
.map-mode:hover { background: var(--paper-sunk); color: var(--ink-900); }
.map-mode.is-on {
  background: var(--brand-700); color: var(--brand-contrast, #fff); font-weight: 650;
}
.map-mode:focus-visible { outline: 2px solid var(--brand-900); outline-offset: -2px; }

/* Tiles have no basemap behind them, so they carry their own separation. */
.map-svg-tiles { background: transparent; }
.map-svg-tiles .map-board { stroke: var(--paper); stroke-width: 2; }

/* ================================================================ SUBURB PICKER
 * A dropdown and a highlight, not a second choropleth: the board colouring under
 * it never has to be torn down and rebuilt to bring it back.
 */
.map-suburb-picker {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  margin: 0 0 var(--s-4);
}
.map-suburb-label {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-xs); color: var(--ink-700); font-weight: 600;
}
.map-suburb-select {
  font: inherit; font-size: var(--fs-sm); padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); color: var(--ink-900); max-width: 260px;
}
.map-suburb-clear {
  appearance: none; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); color: var(--ink-700); cursor: pointer;
  font-size: var(--fs-xs); padding: var(--s-2) var(--s-3);
}
.map-suburb-clear:hover { background: var(--paper-sunk); color: var(--ink-900); }

/* The outline itself, drawn on top of everything else in the SVG. */
.map-suburb-highlight {
  fill: color-mix(in srgb, var(--brand-700) 14%, transparent);
  stroke: var(--brand-900); stroke-width: 2.5; pointer-events: none;
}

.map-suburb-panel {
  margin: 0 0 var(--s-4); padding: var(--s-4);
  border: 1px solid var(--line); border-left: 3px solid var(--brand-700);
  border-radius: var(--r-md); background: var(--paper-sunk);
}
.map-suburb-h { margin: 0 0 var(--s-2); font-size: var(--fs-sm); }
.map-suburb-note { margin: var(--s-2) 0 0; font-size: var(--fs-xs); color: var(--ink-500); }
.map-suburb-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.map-suburb-table th {
  text-align: right; font-size: var(--fs-2xs); color: var(--ink-500); font-weight: 600;
  padding: var(--s-1) var(--s-2);
}
.map-suburb-table th:first-child { text-align: left; }
.map-suburb-table td {
  text-align: right; padding: var(--s-1) var(--s-2);
  border-top: 1px solid var(--line-soft); font-variant-numeric: tabular-nums;
}
.map-suburb-td-label { text-align: left; color: var(--ink-700); font-variant-numeric: normal; }
.map-suburb-delta { margin: var(--s-3) 0 0; font-size: var(--fs-sm); font-weight: 650; color: var(--brand-700); }
.map-suburb-export { margin: var(--s-4) 0 0; display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.map-suburb-export button {
  font: inherit; font-size: var(--fs-xs); padding: 6px 12px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-800); cursor: pointer;
}
.map-suburb-export button:hover { background: var(--paper-sunk); }
.map-suburb-export-status { font-size: var(--fs-xs); color: var(--ink-500); }

/* The suburb lead-finder: same button styling as export, its own results list. */
.map-leadfinder { margin: var(--s-3) 0 0; }
.map-leadfinder > button {
  font: inherit; font-size: var(--fs-xs); padding: 6px 12px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-800); cursor: pointer;
}
.map-leadfinder > button:hover { background: var(--paper-sunk); }
.map-leadfinder > button:disabled { opacity: 0.5; cursor: default; }
.map-leadfinder-list { margin: var(--s-3) 0 0; padding: 0 0 0 1.4em; }
.map-leadfinder-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3);
  padding: 5px 0; border-bottom: 1px solid var(--line-soft);
}
.map-leadfinder-addr {
  font: inherit; font-size: var(--fs-sm); font-weight: 600; text-align: left; padding: 0;
  border: 0; background: none; color: var(--ink-900); cursor: pointer;
}
.map-leadfinder-addr:hover { text-decoration: underline; }
.map-leadfinder-meta { font-size: var(--fs-2xs); color: var(--ink-400); white-space: nowrap; }
.map-lf-high .map-leadfinder-meta { color: var(--good-700); font-weight: 650; }
.map-lf-moderate .map-leadfinder-meta { color: var(--warn-700); }

/* ============================================================ DELTA TABLE
 * The biggest movers under Plan Change 120, at the foot of the page.
 */
.map-delta-table { margin: var(--s-8) 0 0; }
.map-delta-h { margin: 0 0 var(--s-1); font-size: var(--fs-md); }
.map-delta-note { margin: 0 0 var(--s-4); font-size: var(--fs-sm); color: var(--ink-500); max-width: 60ch; }
.map-delta-t { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.map-delta-t th {
  text-align: right; font-size: var(--fs-2xs); color: var(--ink-500); font-weight: 600;
  padding: var(--s-2); border-bottom: 1px solid var(--line);
}
.map-delta-t th:first-child { text-align: left; }
.map-delta-t td {
  text-align: right; padding: var(--s-2); border-top: 1px solid var(--line-soft);
  font-variant-numeric: tabular-nums;
}
.map-delta-suburb { text-align: left; font-weight: 600; color: var(--ink-900); font-variant-numeric: normal; }
.map-delta-change { color: var(--good-700); font-weight: 650; }
.map-delta-row:hover { background: var(--paper-sunk); }
.map-delta-row:focus-visible { outline: 2px solid var(--brand-900); outline-offset: -2px; }

/* ================================================================ VERDICT BAND
 *
 * The answer, as the largest thing on the page. Tinted in its own state so the
 * reader knows the shape of the answer before reading a word of it.
 */
.verdict {
  border: 1px solid var(--line); border-left: 4px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-5) var(--s-6);
  margin: 0 0 var(--s-4);
}
.verdict-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); margin-bottom: var(--s-2);
}
.verdict-eyebrow {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-500);
}
.verdict-figure {
  display: flex; align-items: baseline; gap: var(--s-2);
  margin: 0 0 var(--s-3);
}
/* Tabular figures so the number does not shuffle when it changes. */
.verdict-n {
  font-size: var(--fs-3xl, 40px); font-weight: 750; line-height: 1;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.verdict-unit { font-size: var(--fs-lg, 19px); font-weight: 650; }
.verdict-sep { color: var(--ink-400); }
.verdict-alt { font-size: var(--fs-lg, 19px); font-weight: 650; color: var(--ink-500); }
.verdict-line { margin: 0 0 var(--s-2); font-size: var(--fs-md); font-weight: 650; }
.verdict-sub { margin: 0; font-size: var(--fs-sm); line-height: 1.55; color: var(--ink-700); }

.verdict-good { background: var(--good-050); border-left-color: var(--good-600); }
.verdict-good .verdict-n, .verdict-good .verdict-unit { color: var(--good-700); }
.verdict-warn { background: var(--warn-050); border-left-color: var(--warn-600); }
.verdict-warn .verdict-n, .verdict-warn .verdict-unit { color: var(--warn-700); }
.verdict-bad { background: var(--bad-050); border-left-color: var(--bad-600); }
.verdict-bad .verdict-n, .verdict-bad .verdict-unit { color: var(--bad-700); }
/*
 * Unresolved: hatched, matching the chip. A verdict the tool could not reach must
 * not sit in a calm neutral panel that reads like a considered answer.
 */
.verdict-unknown {
  border-left-color: var(--unknown-600); border-style: solid dashed dashed dashed;
  background-color: var(--unknown-050);
  background-image: repeating-linear-gradient(
    -45deg, transparent 0 7px,
    color-mix(in srgb, var(--unknown-600) 9%, transparent) 7px 10px);
}
.verdict-unknown .verdict-n, .verdict-unknown .verdict-unit { color: var(--unknown-700); }

/* The coverage strip, hatched when anything is unresolved, for the same reason. */
.cover-partial, .cover-thin {
  background-image: repeating-linear-gradient(
    -45deg, transparent 0 7px,
    color-mix(in srgb, var(--unknown-600) 9%, transparent) 7px 10px);
  border: 1px dashed var(--unknown-200);
}

/* ============================================================= CHANGE RADAR
 * Two sections with different sources answering different questions, so the break
 * between them is a hard rule rather than a heading change.
 */
.rc-card-head { display: flex; align-items: baseline; gap: var(--s-3); }
.rc-changes { list-style: none; margin: var(--s-2) 0 0; padding: 0; }
.rc-changes li {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-2) 0; border-top: 1px solid var(--line-soft);
}
.rc-changes li:first-child { border-top: 0; }
.rc-change-text { font-size: var(--fs-sm); line-height: 1.5; }

/* How much a change matters, kept visually separate from the good/warn/bad chip
   beside it (that answers "is this good news"; this answers "does it matter") -
   weight and shade vary, hue does not, so the two badges never compete for the
   same colour vocabulary. */
.rc-impact {
  flex: 0 0 auto; font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; padding: 2px 7px; border-radius: var(--r-pill, 999px);
  align-self: center; white-space: nowrap;
}
.rc-impact-high { background: var(--ink-800); color: var(--paper); }
.rc-impact-moderate { background: var(--paper-sunk); color: var(--ink-700); border: 1px solid var(--line); }
.rc-impact-low { background: none; color: var(--ink-400); border: 1px solid var(--line-soft); }

.rc-horizon { margin-top: var(--s-8); border-top: 2px solid var(--line); padding-top: var(--s-5); }
.rc-horizon-h {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-400); margin: 0 0 var(--s-3);
}
/* Sunk, because this is background rather than today's work. */
.rc-horizon-panel {
  background: var(--paper-sunk); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s-3) var(--s-4);
}
.rc-horizon-summary {
  cursor: pointer; display: flex; align-items: baseline; gap: var(--s-3);
  list-style: none;
}
.rc-horizon-summary::-webkit-details-marker { display: none; }
.rc-horizon-summary::before {
  content: "\25B8"; color: var(--ink-400); transition: transform .12s ease;
}
.rc-horizon-panel[open] .rc-horizon-summary::before { transform: rotate(90deg); }
.rc-horizon-title { font-size: var(--fs-sm); font-weight: 650; color: var(--ink-900); }
.rc-horizon-count { font-size: var(--fs-xs); color: var(--ink-400); }
.rc-horizon-list { list-style: none; margin: var(--s-4) 0 0; padding: 0; }
.rc-horizon-list li {
  padding: var(--s-3) 0; border-top: 1px solid var(--line);
}
.rc-horizon-top { display: flex; align-items: baseline; gap: var(--s-3); margin-bottom: 2px; }
.rc-horizon-tag {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-500);
  background: var(--paper); border: 1px solid var(--line);
  border-radius: 999px; padding: 2px 8px;
}
.rc-horizon-date { font-size: var(--fs-2xs); color: var(--ink-400); font-variant-numeric: tabular-nums; }
.rc-horizon-event { margin: 0; font-size: var(--fs-sm); font-weight: 600; color: var(--ink-900); }
.rc-horizon-means { margin: 2px 0 0; font-size: var(--fs-xs); color: var(--ink-700); line-height: 1.5; }
.rc-horizon-src { font-size: var(--fs-2xs); }
.rc-horizon-empty { margin: var(--s-3) 0 0; font-size: var(--fs-xs); color: var(--ink-400); }

@media (prefers-reduced-motion: reduce) {
  .rc-horizon-summary::before { transition: none; }
}

/* ------------------------------------------------- map: filter chips + rail */
.map-chipbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
  margin: 0 0 var(--s-3);
}
.map-chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 4px 8px 4px 10px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px;
  font-size: var(--fs-2xs); line-height: 1;
}
.map-chip-exclude { background: var(--warn-050); border-color: var(--warn-200); color: var(--warn-700); }
.map-chip-require { background: var(--good-050); border-color: var(--good-200); color: var(--good-700); }
.map-chip-x { font-size: 13px; opacity: .65; }
.map-chip:hover .map-chip-x { opacity: 1; }
.map-chip:focus-visible { outline: 2px solid var(--brand-900); outline-offset: 1px; }
.map-chip-note { font-size: var(--fs-2xs); color: var(--warn-700); font-weight: 650; }

.map-layout { display: flex; gap: var(--s-5); align-items: flex-start; }
.map-col { flex: 1 1 auto; min-width: 0; position: relative; }
.map-svg { cursor: grab; touch-action: none; }
.map-svg-dragging { cursor: grabbing; }
.map-zoom-reset {
  position: absolute; top: var(--s-3); right: 40px;
  appearance: none; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--paper); color: var(--ink-700); cursor: pointer;
  font-size: var(--fs-2xs); padding: var(--s-1) var(--s-2); white-space: nowrap;
}
.map-zoom-reset:hover { background: var(--paper-sunk); color: var(--ink-900); }
.map-rail {
  flex: 0 0 15rem; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); padding: var(--s-3) var(--s-4);
}
.map-rail-h {
  margin: 0 0 var(--s-2); font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-400);
}
.map-rail-list { list-style: none; margin: 0; padding: 0; }
.map-rail-row {
  display: flex; align-items: center; gap: var(--s-2); width: 100%;
  padding: var(--s-2) 0; border: 0; border-top: 1px solid var(--line-soft);
  background: transparent; cursor: pointer; text-align: left;
  font-size: var(--fs-xs); color: var(--ink-700);
}
.map-rail-list li:first-child .map-rail-row { border-top: 0; }
.map-rail-row:hover { color: var(--ink-900); }
.map-rail-sw { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto; }
.map-rail-name { flex: 1 1 auto; }
.map-rail-pct { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--brand-900); }

/* The rail below the map once there is no room beside it. */
@media (max-width: 900px) {
  .map-layout { flex-direction: column; }
  .map-rail { flex: 1 1 auto; width: 100%; }
}

/* ================================================================= TWO LENSES */
.lens-bar { display: flex; gap: var(--s-3); margin: 0 0 var(--s-5); flex-wrap: wrap; }
.lens {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); cursor: pointer; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); min-width: 15rem;
}
.lens:hover { border-color: var(--ink-400); }
.lens-n {
  width: 22px; height: 22px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--paper-sunk); color: var(--ink-500);
  font-size: var(--fs-2xs); font-weight: 800;
}
.lens-tx { display: flex; flex-direction: column; gap: 1px; }
.lens-t { font-size: var(--fs-sm); font-weight: 650; color: var(--ink-900); }
.lens-sub { font-size: var(--fs-2xs); color: var(--ink-400); }
/* Lens 1 is green (opportunity), lens 2 amber (constraints): the same state
   vocabulary the rest of the tool uses, so the colour is not a second language. */
.lens[data-lens="potential"].is-on { border-color: var(--good-600); background: var(--good-050); }
.lens[data-lens="potential"].is-on .lens-n { background: var(--good-600); color: #fff; }
.lens[data-lens="data"].is-on { border-color: var(--warn-600); background: var(--warn-050); }
.lens[data-lens="data"].is-on .lens-n { background: var(--warn-600); color: #fff; }
.lens:focus-visible { outline: 2px solid var(--brand-900); outline-offset: 2px; }

/* ------------------------------------------------------------ constraint map */
.dm { display: flex; gap: var(--s-5); align-items: flex-start; flex-wrap: wrap; }
.dm-panel {
  flex: 0 0 15rem; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); padding: var(--s-3) var(--s-4);
}
.dm-panel-h {
  margin: 0 0 var(--s-2); font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-400);
}
.dm-panel-h-2 { margin-top: var(--s-4); padding-top: var(--s-3); border-top: 1px solid var(--line); }
.dm-layer {
  display: flex; align-items: center; gap: var(--s-2);
  padding: 3px 0; font-size: var(--fs-xs); color: var(--ink-700); cursor: pointer;
}
.dm-layer input { accent-color: var(--brand-900); margin: 0; }
/* The layers that cannot be drawn, stated rather than omitted. */
.dm-gap {
  margin-top: var(--s-4); padding-top: var(--s-3);
  border-top: 1px dashed var(--unknown-200);
}
.dm-gap-h {
  margin: 0 0 var(--s-1); font-size: var(--fs-2xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: var(--unknown-700);
}
.dm-gap-item { margin: 0; font-size: var(--fs-xs); color: var(--ink-500); }
.dm-gap-note { margin: var(--s-2) 0 0; font-size: var(--fs-2xs); color: var(--ink-400); line-height: 1.45; }

/*
 * flex-basis 0, not auto. With "auto" the browser sizes this item's hypothetical width
 * from its CONTENT before deciding whether the row fits: .dm-stack is inline-block and
 * sizes to the map's fixed pixel width, so that content, not the actual available
 * space, was what decided whether .dm wrapped. The panel plus that content routinely
 * exceeded the container even when the container had room for both once .dm-right was
 * allowed to shrink, so the map fell to its own line below the panel with the gap
 * between them reading as a layout bug rather than as the (correct) point at which it
 * had run out of room. Basis 0 makes the wrap decision about the container's actual
 * width, not the map's preferred one, and min-width 0 lets it shrink past its content's
 * min-content size instead of forcing a wrap to avoid doing so.
 */
.dm-right { flex: 1 1 0%; min-width: 0; }
/* Jump-to-address, above the map. Same visual language as #search/#results (the
   property lookup box) at a scale that fits beside a map rather than leading a
   whole page. */
.dm-search { position: relative; margin-bottom: var(--s-3); max-width: 26rem; }
.dm-search-input {
  width: 100%; padding: var(--s-2) var(--s-3); font-size: var(--fs-sm);
  border: 1.5px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); color: var(--ink-900);
}
.dm-search-input:focus {
  outline: none; border-color: var(--brand-500); box-shadow: 0 0 0 3px var(--brand-100);
}
.dm-search-results {
  list-style: none; margin: var(--s-1) 0 0; padding: 4px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--paper);
  position: absolute; z-index: 20; width: 100%; box-shadow: var(--shadow-2);
  max-height: 18rem; overflow: auto;
}
.dm-search-results li { margin: 0; }
.dm-search-result {
  display: block; width: 100%; text-align: left; padding: var(--s-2) var(--s-3);
  background: none; border: 0; border-radius: var(--r-sm); cursor: pointer;
  font: inherit; font-size: var(--fs-sm); color: var(--ink-900);
}
.dm-search-result:hover, .dm-search-result:focus-visible { background: var(--brand-050); outline: none; }
.dm-search-result:disabled { opacity: .5; cursor: default; }
.dm-search-empty { padding: var(--s-2) var(--s-3); font-size: var(--fs-xs); color: var(--ink-400); }
.dm-img { position: absolute; inset: 0; width: 100%; height: 100%; }
.dm-overlay { pointer-events: none; }
.dm-parcel-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.dm-parcel { fill: none; stroke: var(--brand-500); stroke-width: 2.5; }
.dm-note { margin: var(--s-2) 0 0; font-size: var(--fs-2xs); color: var(--ink-400); line-height: 1.5; }

/*
 * Real geometry (pipes, points, control boundaries), drawn as SVG rather than an
 * image, from app/datamap.js's FEATURE_LAYERS. Two hues, not one per layer: the
 * meaning here is "what kind of thing is this", not the opportunity/constraint/
 * blocker/unknown state vocabulary the rest of the tool uses, so it deliberately
 * does not borrow those colours. Blue reads as infrastructure everywhere from a
 * council GIS viewer to a road sign; purple keeps the remaining Unitary Plan
 * controls visually apart from both the infrastructure and the brand-green
 * parcel outline they all sit on top of.
 */
.dm-feature-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.dm-feat-infrastructure { stroke: #1d6fc4; fill: #1d6fc4; }
.dm-feat-overlay { stroke: #7c4dbd; fill: #7c4dbd; }
.dm-feat-polyline { fill: none; stroke-width: 2.5; }
.dm-feat-polygon { fill-opacity: .18; stroke-width: 2; }
.dm-feat-point { stroke: var(--paper); stroke-width: 1.5; }

/*
 * -------------------------------------------------- shared tile map engine
 * app/tilemap.js: the LINZ tile view, drag to pan, zoom, and basemap toggle shared
 * by the constraint map (Lens 2) and the nearby-subdivisions map. What each of them
 * draws ON TOP of the tiles (overlays and a parcel outline here, marker dots on the
 * nearby map) stays in its own class namespace beside this section.
 */
/*
 * display: block, not inline-block. inline-block sizes to its CONTENT, which is
 * .tm-view at its fixed pixel width, so .tm-view's own `max-width: 100%` had
 * nothing meaningful to be 100% of and the map could overflow the page on a
 * narrower window than the one it was measured against at build time. block lets
 * this stretch to fill its container's actual flex-allocated space, which is what
 * `max-width: 100%` needs to mean something.
 */
.tm-stack { position: relative; display: block; }
.tm-view {
  position: relative; overflow: hidden; max-width: 100%;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--paper-sunk);
  /* touch-action none: a finger drag pans the map, not the page underneath it. */
  cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none;
}
.tm-view.is-dragging { cursor: grabbing; }
/* The corner drag handle every browser already draws for a resizable box. Min so a
   drag can never leave the map too small to read; max so it can never be dragged
   into something absurd on a large monitor. */
.tm-view.tm-resizable {
  resize: both; min-width: 320px; min-height: 240px; max-height: 900px;
}
/*
 * Everything a render draws lives in here, one level below .tm-view, so a drag can
 * translate this single element for instant visual feedback without touching
 * .tm-view's own box (the fixed, clipped window). position: absolute + inset: 0
 * both fills .tm-view and gives this element's own absolutely positioned children
 * (.tm-tiles, markers, overlays) the containing block their inset: 0 needs.
 */
.tm-content { position: absolute; inset: 0; }
.tm-tiles { position: absolute; inset: 0; }
/* -webkit-user-drag belt-and-braces alongside tilemap.js's img.draggable = false:
   an <img> is a native drag source by default, and a real mouse drag starting on
   a tile (most of the map's surface) triggered that instead of the pan handlers. */
.tm-tile { position: absolute; display: block; -webkit-user-drag: none; user-drag: none; }
.tm-zoom {
  position: absolute; top: var(--s-3); right: var(--s-3);
  display: flex; flex-direction: column; border-radius: var(--r-sm); overflow: hidden;
  border: 1px solid var(--line); background: var(--paper);
}
.tm-zoom-b {
  border: 0; background: transparent; cursor: pointer;
  width: 28px; height: 28px; font-size: 15px; line-height: 1; color: var(--ink-900);
}
.tm-zoom-b + .tm-zoom-b { border-top: 1px solid var(--line); }
.tm-zoom-b:hover { background: var(--paper-sunk); }

/* Aerial or topographic. Top left, where a slippy map's own basemap switcher usually
   sits, so it reads as a familiar control rather than a WeSubdivide invention. */
.tm-basemap {
  position: absolute; top: var(--s-3); left: var(--s-3);
  display: flex; border-radius: var(--r-sm); overflow: hidden;
  border: 1px solid var(--line); background: var(--paper);
}
.tm-basemap-b {
  border: 0; background: transparent; cursor: pointer;
  padding: 5px var(--s-3); font-size: var(--fs-2xs); font-weight: 600;
  color: var(--ink-500);
}
.tm-basemap-b + .tm-basemap-b { border-left: 1px solid var(--line); }
.tm-basemap-b:hover { background: var(--paper-sunk); }
.tm-basemap-b[aria-pressed="true"] { background: var(--brand-900); color: #fff; }

/* The explicit resize control: normal document flow directly under the map, not
   another small icon competing with the corner controls. Plain text labels
   ("− Smaller" / "Larger +") on purpose — a size icon alone is exactly the kind
   of subtle affordance this replaces. */
.tm-size { display: flex; gap: var(--s-2); margin-top: var(--s-2); }
.tm-size-b {
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--paper);
  cursor: pointer; padding: 5px var(--s-3); font-size: var(--fs-xs); font-weight: 600;
  color: var(--ink-700);
}
.tm-size-b:hover { background: var(--paper-sunk); border-color: var(--ink-400); }

/* The key for whichever active overlays use more than one shade. A layer with a
   single colour (an ordinary yes/no overlay) needs no key and gets none: see
   fetchLegend in datamap.js. */
.dm-legend { margin-top: var(--s-3); }
.dm-legend-group { margin-bottom: var(--s-3); }
.dm-legend-group:last-child { margin-bottom: 0; }
.dm-legend-h {
  margin: 0 0 var(--s-1); font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: .03em; color: var(--ink-500);
}
.dm-legend-row {
  display: flex; align-items: center; gap: var(--s-2);
  padding: 2px 0; font-size: var(--fs-xs); color: var(--ink-700);
}
.dm-legend-sw { flex: none; border: 1px solid var(--line); border-radius: 2px; }
.dm-legend-label { line-height: 1.3; }


/*
 * Dark mode: the state ramp inverts, so a -600 fill is light and the glyph on it must
 * be dark. Set once here rather than per state, because every -600 in dark is light.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --chip-ink: #10140d; }
}
:root[data-theme="dark"] { --chip-ink: #10140d; }

/* The proposed group in the data-layer panel, held apart from the operative ones. */
.dm-proposed-h {
  margin: var(--s-3) 0 var(--s-1); padding-top: var(--s-2);
  border-top: 1px dashed var(--warn-200);
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--warn-700);
}
.dm-layer-proposed { color: var(--warn-700); }

/* --------------------------------------------------- overlay drill-down */
.ov-detail > summary, .ov-clear > summary { cursor: pointer; }
.ov-detail > summary::marker, .ov-clear > summary::marker { color: var(--ink-400); }
.ov-what {
  margin: var(--s-2) 0 0; font-size: var(--fs-xs); line-height: 1.55;
  color: var(--ink-700);
}
.ov-effect {
  margin: var(--s-2) 0 0; font-size: var(--fs-xs); line-height: 1.55;
  color: var(--ink-900); font-weight: 600;
}
/* An unwritten effect is stated as unwritten, in the unresolved styling, rather than
   left as an absence a reader would take for "nothing to say". */
.ov-gap {
  margin: var(--s-2) 0 0; padding: var(--s-2) var(--s-3);
  font-size: var(--fs-2xs); line-height: 1.5; color: var(--unknown-700);
  background: var(--unknown-050); border: 1px dashed var(--unknown-200);
  border-radius: var(--r-sm);
}
.ov-src { margin: var(--s-1) 0 0; font-size: var(--fs-2xs); color: var(--ink-400); }
.ov-clear-list { margin: var(--s-2) 0 0; padding-left: var(--s-5); }
.ov-clear-list li { font-size: var(--fs-2xs); color: var(--ink-500); }

/* ------------------------------------------------------ nearby subdivisions */
.nearby-list li { display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap; }
.nearby-d {
  flex: 0 0 3.6rem; text-align: right; font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xs); color: var(--ink-400);
}
.nearby-link {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--brand-900); font-weight: 600;
  text-decoration: underline; text-underline-offset: 2px;
}
.nearby-link:hover { color: var(--brand-700); }
.nearby-link:focus-visible { outline: 2px solid var(--brand-900); outline-offset: 2px; }
.nearby-meta { font-size: var(--fs-2xs); color: var(--ink-400); }

/*
 * Map on top, full width, detail card below: NOT side by side. Splitting the row
 * with the detail card capped the map at roughly half the card's width regardless
 * of how big the tile view was allowed to grow, which is exactly what read as "still
 * very small" once it had real imagery in it. Stacked, the map gets the whole card
 * to grow into, and the corner resize handle (see .tm-resizable) has real room to
 * make a difference.
 */
.nbm-label {
  margin: var(--s-3) 0 var(--s-1); font-size: var(--fs-xs); font-weight: 700; color: var(--ink-500);
}
.nbm-wrap { display: flex; flex-direction: column; gap: var(--s-3); margin: var(--s-2) 0; }
/* The tile map itself is sized by tilemap.js against .nbm-maphost's own width, the
   same fill-the-panel behaviour as the constraint map. overflow:hidden is a
   backstop, not the fix: a map widget should never bleed past its own box
   regardless of what tilemap.js computed, on any screen size. */
.nbm-maphost { width: 100%; overflow: hidden; }
/* Markers are plain absolutely positioned divs inside .tm-content, placed in
   screen pixels computed from mpp (see the module comment in nearbymap.js), not
   SVG: there is no shared coordinate space to draw SVG against once the map can be
   panned and zoomed, so each render recomputes and re-places every marker. */
.nbm-subject {
  position: absolute; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  background: var(--ink-900); border: 2px solid var(--paper); border-radius: 2px;
  pointer-events: none;
}
.nbm-dot {
  position: absolute; width: 14px; height: 14px; margin: -7px 0 0 -7px;
  background: var(--brand-700); border: 2px solid var(--paper); border-radius: 50%;
  cursor: pointer;
}
.nbm-dot:hover, .nbm-dot:focus-visible { background: var(--brand-900); }
.nbm-dot.is-selected { background: var(--warn-700); }
.nbm-dot:focus-visible { outline: 2px solid var(--brand-900); outline-offset: 1px; }
.nbm-detail { flex: 1 1 12rem; min-width: 10rem; padding-top: var(--s-1); }
.nbm-hint { margin: 0; font-size: var(--fs-2xs); color: var(--ink-400); }
.nbm-meta { margin: var(--s-1) 0 0; font-size: var(--fs-2xs); color: var(--ink-400); }

/* -------------------------------------------------------------- report builder */
.rb-backdrop {
  position: fixed; inset: 0; background: rgba(10, 14, 18, .55); z-index: 90;
  display: flex; justify-content: center; overflow: auto; padding: var(--s-6) var(--s-4) 60px;
}
.rb-panel {
  background: var(--paper); border-radius: var(--r-md); padding: var(--s-5) var(--s-6);
  width: 820px; max-width: 100%; align-self: flex-start;
}
.rb-head-bar { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-2); }
.rb-head-bar h2 { margin: 0; font-size: var(--fs-lg); }
.rb-head-bar .grow { flex: 1; }
.rb-head-bar button {
  font: inherit; font-size: var(--fs-xs); padding: 6px 12px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-900); cursor: pointer;
}

.rb-summary {
  margin: 0 0 var(--s-5); padding: var(--s-3) var(--s-4); border-radius: var(--r-sm);
  background: var(--paper-sunk); font-size: var(--fs-sm); font-weight: 600;
}

.rb-group { margin-bottom: var(--s-6); }
.rb-group-h {
  margin: 0 0 var(--s-2); padding-bottom: var(--s-1); border-bottom: 1px solid var(--line);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-500);
}

/*
 * One field, any state. Grid rather than flex so the label column stays a fixed
 * width across a whole group and rows read as a table without being one, which
 * matters once values start wrapping onto a second line.
 */
.rb-field {
  display: grid; grid-template-columns: 13rem 1fr auto; align-items: start;
  gap: var(--s-3); padding: 6px 8px; border-radius: var(--r-sm); border-left: 3px solid transparent;
}
.rb-field + .rb-field { margin-top: 2px; }
.rb-label { font-size: var(--fs-xs); color: var(--ink-500); padding-top: 2px; }
.rb-value { font-size: var(--fs-sm); color: var(--ink-900); }
.rb-value.rb-empty { color: var(--ink-400); font-style: italic; }
.rb-meta { grid-column: 2 / 3; font-size: var(--fs-2xs); color: var(--ink-400); }
.rb-feeds {
  grid-row: 1; grid-column: 3; font-size: var(--fs-2xs); color: var(--ink-400);
  white-space: nowrap; text-align: right;
}

.rb-s-auto { border-left-color: var(--line); background: var(--paper-sunk); }
.rb-s-filled { border-left-color: var(--good-600); background: var(--good-050); }
.rb-s-needed { border-left-color: var(--warn-600); background: var(--warn-050); }
.rb-s-draft { border-left-color: var(--warn-600); }
.rb-draft-tag { color: var(--warn-700); font-style: italic; }

.rb-input {
  grid-column: 2 / 3; width: 100%; font: inherit; font-size: var(--fs-sm);
  padding: 5px 8px; border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--paper); color: var(--ink-900); box-sizing: border-box;
}
textarea.rb-input { min-height: 3.4em; resize: vertical; }
.rb-input-narrow { width: 6.5rem; display: inline-block; }
.rb-suffix { font-size: var(--fs-xs); color: var(--ink-400); align-self: center; }

.rb-comps { margin-top: var(--s-2); overflow-x: auto; }
.rb-comps-table { width: 100%; border-collapse: collapse; font-size: var(--fs-xs); }
.rb-comps-table th {
  text-align: left; padding: 4px 6px; font-size: var(--fs-2xs); color: var(--ink-500);
  border-bottom: 1px solid var(--line);
}
.rb-comps-table td { padding: 3px 6px; }
.rb-comps-input, .rb-comps-table select {
  width: 100%; font: inherit; font-size: var(--fs-xs); padding: 4px 6px;
  border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--paper);
  color: var(--ink-900); box-sizing: border-box;
}
.rb-comps-remove {
  font: inherit; font-size: var(--fs-2xs); padding: 3px 8px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-500); cursor: pointer;
}
.rb-comps-add {
  margin-top: var(--s-2); font: inherit; font-size: var(--fs-xs); padding: 6px 12px;
  border-radius: var(--r-sm); border: 1px dashed var(--line); background: var(--paper);
  color: var(--brand-900); cursor: pointer;
}

.rb-options { display: flex; flex-direction: column; gap: var(--s-4); margin-top: var(--s-2); }
.rb-option { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-3) var(--s-4); }
.rb-option-h { margin: 0 0 var(--s-1); font-size: var(--fs-sm); }
.rb-option-cost { margin: 0 0 var(--s-3); font-size: var(--fs-xs); color: var(--ink-500); }

/*
 * The schematic subdivision diagram. Abstract bar comparison, deliberately not a
 * site plan: see app/gapDiagram.js.
 */
.gap-d-figure-wrap { margin: var(--s-4) 0 0; }
.gap-d-svg { width: 100%; max-width: 440px; height: auto; display: block; }
.gap-d-label { font-size: var(--fs-2xs); fill: var(--ink-500); }
.gap-d-figure { font-size: var(--fs-2xs); font-weight: 650; fill: var(--ink-800); }
.gap-d-needed { fill: var(--paper-sunk); stroke: var(--line); stroke-width: 1; }
.gap-d-available { fill: var(--good-600); }
.gap-d-gap {
  fill: var(--warn-050); stroke: var(--warn-600); stroke-width: 1; stroke-dasharray: 3 2;
}
.gap-d-bracket { fill: none; stroke: var(--warn-600); stroke-width: 1.5; }
.gap-d-shortfall { font-size: var(--fs-2xs); font-weight: 650; fill: var(--warn-700); }
.gap-d-caption { margin-top: var(--s-1); font-size: var(--fs-2xs); color: var(--ink-400); }

/*
 * Small-screen fixes, part 2. Placed at the end of the file deliberately: several
 * of the rules being overridden here (.rb-field, .card-head) are defined much
 * later in the cascade than the main narrow-screens block above, so a fix placed
 * up there loses on source order even while its media query correctly matches.
 */
@media (max-width: 620px) {
  /* The measured-accessway fields (report builder and the live assessment card):
     a fixed 13rem label column left barely 50-60px for the input on a phone. One
     column, label above value, same order the grid already puts them in. */
  .rb-field { grid-template-columns: 1fr; gap: 4px; }
  .rb-meta { grid-column: 1; }
  .rb-input-narrow { width: 100%; max-width: 10rem; }

  /* Fixed-pixel map canvases (tilemap.js) are capped to their panel's real width
     at build time; the corner drag handle can still be dragged past it, so this
     keeps that case from re-opening the same page-width overflow. */
  .tm-view, .nbm-maphost { max-width: 100%; }
}

/* The opportunity score's part-by-part breakdown: one line per component, in the
   same order the formula string lists them, so a reader can check the arithmetic
   against the total straight above it. */
.opp-parts { margin: 0; padding: 0 0 0 1.1em; font-size: var(--fs-sm); color: var(--ink-600); }
.opp-parts li { margin: 2px 0; }

/* Split into what adds to the score and what counts against it — two columns,
   not one mixed list, so the two families of component read apart at a glance. */
.opp-parts-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); margin: var(--s-2) 0 0; }
.opp-parts-col { border-left: 3px solid; padding-left: var(--s-3); }
.opp-parts-col-good { border-color: var(--good-200); }
.opp-parts-col-bad { border-color: var(--bad-200); }
.opp-parts-col-h {
  margin: 0 0 var(--s-1); font-size: var(--fs-2xs); font-weight: 650; text-transform: uppercase;
  letter-spacing: 0.04em;
}
.opp-parts-col-good .opp-parts-col-h { color: var(--good-700); }
.opp-parts-col-bad .opp-parts-col-h { color: var(--bad-700); }

@media (max-width: 620px) {
  .opp-parts-cols { grid-template-columns: 1fr; gap: var(--s-4); }
}

/* ------------------------------------------------------------------ pipeline */
/*
 * Two swimlanes, both horizontally scrollable independently: Active (the 11
 * lifecycle stages) on top, On Hold / Cold / Lost underneath. A card drags
 * sideways between stage columns or down into the parked lane; both are the
 * same drop handler, see app/pipeline.js.
 */
.pl-head { margin: 0 0 var(--s-5); }
.pl-head h2 { margin: 0 0 var(--s-2); }
.pl-sub { margin: 0; max-width: 68ch; color: var(--ink-500); font-size: var(--fs-sm); line-height: 1.5; }

.pl-board { display: flex; flex-direction: column; gap: var(--s-6); }
.pl-lane { border-radius: var(--r-lg); padding: var(--s-4); }
.pl-lane-active { background: var(--paper-sunk); }
.pl-lane-parked { background: var(--paper-sunk); opacity: 0.92; }
.pl-lane-label {
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-500); margin: 0 0 var(--s-3);
}
.pl-lane-track { display: flex; gap: var(--s-3); overflow-x: auto; padding-bottom: var(--s-2); }

.pl-col {
  flex: 0 0 240px; background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r-md); display: flex; flex-direction: column; max-height: 70vh;
}
.pl-lane-parked .pl-col { flex-basis: 220px; }
.pl-col-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-2);
  padding: var(--s-3) var(--s-3) var(--s-2); border-bottom: 1px solid var(--line);
}
.pl-col-title { font-size: var(--fs-xs); font-weight: 650; color: var(--ink-800); }
.pl-col-count {
  font-size: var(--fs-2xs); font-weight: 700; color: var(--ink-400);
  background: var(--paper-sunk); border-radius: var(--r-pill); padding: 1px 7px;
}
.pl-col-body {
  flex: 1; overflow-y: auto; padding: var(--s-2); display: flex; flex-direction: column;
  gap: var(--s-2); min-height: 60px; transition: background-color .12s;
}
.pl-col-body.is-dragover { background: var(--good-050); }

.pl-card {
  background: var(--paper); border: 1px solid var(--line); border-left: 3px solid var(--line);
  border-radius: var(--r-sm); padding: var(--s-2) var(--s-3); cursor: grab;
  display: flex; flex-direction: column; gap: 4px; box-shadow: var(--shadow-1);
}
.pl-card.is-dragging { opacity: 0.4; }
.pl-card.pl-temp-hot { border-left-color: var(--bad-600); }
.pl-card.pl-temp-warm { border-left-color: var(--warn-600); }
.pl-card.pl-temp-cold { border-left-color: var(--unknown-600); }
.pl-card-addr {
  font: inherit; font-size: var(--fs-xs); font-weight: 650; text-align: left; padding: 0;
  border: 0; background: none; color: var(--ink-900); cursor: pointer; line-height: 1.3;
}
.pl-card-addr:hover { text-decoration: underline; }
.pl-card-meta { font-size: var(--fs-2xs); color: var(--ink-400); }
.pl-card-score {
  align-self: flex-start; font-size: var(--fs-2xs); font-weight: 700; padding: 1px 6px;
  border-radius: var(--r-pill);
}
.pl-score-high { background: var(--good-050); color: var(--good-700); }
.pl-score-moderate, .pl-score-low { background: var(--warn-050); color: var(--warn-700); }

.pl-temp-row { display: flex; gap: 3px; margin-top: 2px; }
.pl-temp-b {
  flex: 1; font: inherit; font-size: 10px; font-weight: 650; padding: 2px 0;
  border-radius: 5px; border: 1px solid var(--line); background: var(--paper-sunk);
  color: var(--ink-400); cursor: pointer;
}
.pl-temp-b-hot.is-on { background: var(--bad-600); border-color: var(--bad-600); color: var(--paper); }
.pl-temp-b-warm.is-on { background: var(--warn-600); border-color: var(--warn-600); color: var(--paper); }
.pl-temp-b-cold.is-on { background: var(--unknown-600); border-color: var(--unknown-600); color: var(--paper); }

@media (max-width: 620px) {
  .pl-col { flex-basis: 200px; }
  .pl-lane { padding: var(--s-3); }
}

/* Action tracker: sits under the pipeline board, same mount. */
.at-wrap { margin-top: var(--s-7); padding-top: var(--s-6); border-top: 1px solid var(--line); }
.at-h { margin: 0 0 var(--s-1); }
.at-sub { margin: 0 0 var(--s-3); color: var(--ink-500); font-size: var(--fs-sm); }
.at-banner {
  margin: 0 0 var(--s-4); padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: var(--warn-050); color: var(--warn-700); font-size: var(--fs-xs); max-width: 68ch;
}
.at-form {
  display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center;
  margin: 0 0 var(--s-5); padding: var(--s-4); border-radius: var(--r-lg); background: var(--paper-sunk);
}
.at-input { flex: 1 1 260px; font: inherit; font-size: var(--fs-sm); padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-sm); }
.at-select, .at-date {
  font: inherit; font-size: var(--fs-sm); padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--paper);
}
.at-urgent-label { display: flex; align-items: center; gap: 5px; font-size: var(--fs-sm); color: var(--ink-600); }
.at-add-btn {
  font: inherit; font-size: var(--fs-sm); font-weight: 650; padding: var(--s-2) var(--s-4);
  border: 0; border-radius: var(--r-sm); background: var(--action); color: var(--paper); cursor: pointer;
}
.at-groups { display: flex; flex-direction: column; gap: var(--s-5); }
.at-empty { color: var(--ink-400); font-size: var(--fs-sm); }
.at-group-label {
  margin: 0 0 var(--s-2); font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--ink-400);
}
.at-group-overdue .at-group-label { color: var(--bad-700); }
.at-group-this-week .at-group-label { color: var(--warn-700); }
.at-list { display: flex; flex-direction: column; gap: var(--s-2); }
.at-item {
  display: flex; align-items: flex-start; gap: var(--s-3); padding: var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--paper);
}
.at-group-overdue .at-item { border-left: 3px solid var(--bad-600); }
.at-group-this-week .at-item { border-left: 3px solid var(--warn-600); }
.at-item-done { opacity: 0.6; }
.at-item-done .at-title { text-decoration: line-through; }
.at-check { margin-top: 3px; }
.at-item-body { flex: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.at-title { font-size: var(--fs-sm); font-weight: 600; color: var(--ink-900); }
.at-item-meta { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }
.at-lead-btn {
  font: inherit; font-size: var(--fs-2xs); padding: 2px 0; border: 0; background: none;
  color: var(--brand-700); text-decoration: underline; cursor: pointer;
}
.at-due { font-size: var(--fs-2xs); color: var(--ink-400); }
.at-due-overdue { color: var(--bad-700); font-weight: 650; }
.at-due-this-week { color: var(--warn-700); font-weight: 650; }
.at-badge-urgent {
  font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 1px 6px; border-radius: var(--r-sm); background: var(--bad-050); color: var(--bad-700);
}
.at-del {
  font: inherit; font-size: var(--fs-2xs); padding: var(--s-1) var(--s-2); border: 0; background: none;
  color: var(--ink-400); cursor: pointer;
}
.at-del:hover { color: var(--bad-700); }
.at-done-toggle {
  margin-top: var(--s-4); font: inherit; font-size: var(--fs-xs); padding: 0; border: 0;
  background: none; color: var(--ink-500); text-decoration: underline; cursor: pointer;
}
.at-done-list { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-3); }

@media (max-width: 620px) {
  .at-form { flex-direction: column; align-items: stretch; }
  .at-input, .at-select, .at-date, .at-add-btn { flex: 1 1 auto; }
}

/* The opportunity score's circular progress ring. Track + arc, arc clockwise
   from the top, number centred inside — replaces the plain "38 / 100" figure. */
.opp-ring-wrap { position: relative; width: 120px; height: 120px; margin: var(--s-2) 0; }
.opp-ring-svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.opp-ring-track { fill: none; stroke: var(--line); }
.opp-ring-arc { fill: none; stroke-linecap: round; transition: stroke-dashoffset .4s ease; }
.opp-ring-good { stroke: var(--good-600); }
.opp-ring-warn { stroke: var(--warn-600); }
.opp-ring-bad { stroke: var(--bad-600); }
.opp-ring-num {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 30px; font-weight: 750; letter-spacing: -0.02em; color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------- SCORE MODEL SETTINGS
 * The opportunity score's editable weights. Six groups mirroring the same
 * breakdown the assessment card already shows, each with a toggled help box
 * and a row of number inputs.
 */
.sm-head { max-width: 72ch; margin: 0 0 var(--s-5); }
.sm-head h2 { margin: 0 0 var(--s-2); }
.sm-sub { margin: 0; color: var(--ink-500); font-size: var(--fs-sm); line-height: 1.55; }
.sm-status { margin: 0 0 var(--s-2); font-size: var(--fs-xs); color: var(--ink-400); }
.sm-range {
  margin: 0 0 var(--s-6); font-size: var(--fs-sm); font-weight: 650; color: var(--ink-800);
  font-variant-numeric: tabular-nums;
}
.sm-groups { display: flex; flex-direction: column; gap: var(--s-5); margin: 0 0 var(--s-6); }
.sm-group { padding: var(--s-4); border-radius: var(--r-lg); background: var(--paper-sunk); }
.sm-group-head { display: flex; align-items: center; gap: var(--s-2); }
.sm-group-head h3 { margin: 0; font-size: var(--fs-md); }
.sm-help-btn {
  width: 20px; height: 20px; padding: 0; border-radius: 50%; border: 1px solid var(--ink-400);
  background: none; color: var(--ink-500); font: inherit; font-size: var(--fs-2xs); font-weight: 700;
  line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.sm-help-btn:hover { border-color: var(--ink-800); color: var(--ink-800); }
.sm-help-box {
  margin: var(--s-2) 0 0; padding: var(--s-3); border-radius: var(--r-sm); background: var(--paper);
  border: 1px solid var(--line); font-size: var(--fs-sm); color: var(--ink-600); line-height: 1.5;
  max-width: 68ch;
}
.sm-fields { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-3); }
.sm-field {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  font-size: var(--fs-sm); color: var(--ink-700);
}
.sm-field-label { flex: 1; }
.sm-field-input-wrap { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.sm-field-input {
  width: 72px; font: inherit; font-size: var(--fs-sm); padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--paper);
  text-align: right; font-variant-numeric: tabular-nums;
}
.sm-field-suffix { font-size: var(--fs-xs); color: var(--ink-400); }
.sm-actions { display: flex; gap: var(--s-3); }
.sm-save-btn {
  font: inherit; font-size: var(--fs-sm); font-weight: 650; padding: var(--s-2) var(--s-5);
  border: 0; border-radius: var(--r-sm); background: var(--action); color: var(--paper); cursor: pointer;
}
.sm-save-btn:disabled { opacity: 0.6; cursor: default; }
.sm-reset-btn {
  font: inherit; font-size: var(--fs-sm); padding: var(--s-2) var(--s-4); border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--paper); color: var(--ink-700); cursor: pointer;
}
.sm-reset-btn:hover { background: var(--paper-sunk); }

@media (max-width: 620px) {
  .sm-field { flex-wrap: wrap; }
  .sm-actions { flex-direction: column; align-items: stretch; }
}

/* ------------------------------------------------------------- auth gate */

.auth-gate {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  background: var(--paper-sunk); padding: var(--s-6);
}
.auth-gate[hidden] { display: none; }
.auth-gate-panel {
  width: 100%; max-width: 360px; display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: var(--s-2);
}
.auth-gate-logo { width: 64px; height: 64px; margin-bottom: var(--s-2); }
.auth-gate-panel h1 { font-size: var(--fs-2xl); font-weight: 800; letter-spacing: -0.01em; margin: 0; }
.auth-gate-panel h1 .rail-mark-iq { color: var(--brand-700); }
.auth-gate-panel > p { color: var(--ink-500); margin: 0 0 var(--s-4); }
#auth-gate-mount { width: 100%; }
#auth-gate-denied {
  width: 100%; padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: var(--bad-050); color: var(--bad-700); border: 1px solid var(--bad-200);
  font-size: var(--fs-sm); text-align: left; margin: 0 0 var(--s-4);
}
#auth-gate-denied[hidden] { display: none; }

.account-menu {
  margin-top: auto; display: flex; flex-direction: column; gap: var(--s-1);
  padding: var(--s-2); border-top: 1px solid var(--line);
}
.account-email {
  font-size: var(--fs-2xs); color: var(--ink-400); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.account-signout {
  font: inherit; font-size: var(--fs-xs); text-align: left; padding: 0; border: 0;
  background: none; color: var(--ink-600); cursor: pointer; width: max-content;
}
.account-signout:hover { color: var(--ink-900); text-decoration: underline; }

.sync-warning {
  position: fixed; left: 50%; bottom: var(--s-4); transform: translateX(-50%);
  z-index: 999; max-width: 440px; padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md); background: var(--bad-050); color: var(--bad-700);
  border: 1px solid var(--bad-200); font-size: var(--fs-sm); text-align: center;
  box-shadow: 0 4px 16px rgba(0,0,0,.12);
}
