/* Copyright (c) 2025-2026 Tahseen Huda / Ooua */
/*
 * Footing design tokens — the one place these values are defined (Phase 12B).
 * style.css (the app) and web/pages/render.mjs (the public pages) read them as
 * custom properties; src/footing/digest.py parses this file and inlines them into
 * the email. Nothing else may define a size, a space or a colour.
 *
 * Type: 5 sizes, 2 weights, 2 line-heights.  Spacing: 6 steps on a 4 px base.
 * Colour: one neutral ramp, one link, one focus, the permit-type palette (the
 * commonest type is the quietest colour), the dev-application ring, two status
 * colours. Radii ×3, one shadow, one border. Every token has a purpose comment.
 */
:root {
  /* ---- type. rem so a reader's browser font-size preference still applies (1rem = 16px by default) */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --fs-small: .75rem;     /* 12px: tags, notes, captions, axis and legend labels, table heads */
  --fs-body: .875rem;     /* 14px: body text, rows, buttons, inputs */
  --fs-lead: 1rem;        /* 16px: section headings (h2), the lede on pages, KPI numbers in the app */
  --fs-title: 1.25rem;    /* 20px: record and dialog titles, the digest's h1, KPI numbers on pages */
  --fs-display: 1.5rem;   /* 24px: page h1 */
  --fw-normal: 400;       /* running text */
  --fw-bold: 600;         /* headings, values, selected states, buttons */
  --lh-tight: 1.2;        /* headings, KPI numbers, chips and buttons */
  --lh-body: 1.45;        /* everything that wraps */

  /* ---- spacing: 4 px base, six steps */
  --s1: 4px;              /* hairline gaps: dot-to-label, chip padding, inline gaps */
  --s2: 8px;              /* control padding, gaps between chips and rows */
  --s3: 12px;             /* box padding, gutters on phones */
  --s4: 16px;             /* gutters on desktop, section gaps */
  --s5: 24px;             /* between sections, bottom padding */
  --s6: 32px;             /* page-level breathing room (sign-up box, footers) */

  /* ---- radii, shadow, border */
  --r-sm: 4px;            /* tags, small buttons, bars */
  --r-md: 8px;            /* boxes, inputs, popovers, dialogs */
  --r-pill: 999px;        /* chips */
  --shadow: 0 8px 24px rgba(0, 0, 0, .14);   /* anything floating: popovers, sheets, side panels */
  --border: 1px solid var(--line);

  /* ---- neutral ramp (light) */
  --bg: #ffffff;          /* surface: page, cards, inputs, map overlays */
  --bg-soft: #f3f4f6;     /* secondary surface: off chips, KPI cards, code, the sign-up box */
  --line: #e2e5ea;        /* hairlines and borders */
  --fg-muted: #656d7a;    /* secondary text: labels, notes, timestamps, "not disclosed" (≥ 4.5:1 on --bg and --bg-soft) */
  --fg: #16181d;          /* primary text, and the ink: selected tab, primary button, contractor dot, the pin, chart bars */
  --bg-glass: rgba(255, 255, 255, .94);   /* --bg at 94% for boxes that float over the map */

  /* ---- link and focus */
  --link: #1d4ed8;        /* every link, and only links (7.0:1 on --bg) */
  --focus: #0e7490;       /* the focus ring, nothing else (a cyan no data colour uses) */

  /* ---- permit types: ordered quiet → loud by rarity. Renovation is 70% of all permits, so it is the quietest;
     new construction and demolition change a street, so they shout. Same five everywhere: map, chips, rows, charts, pages, email, legend. */
  --type-renovation: #6f7f99;        /* slate, low saturation — the common case */
  --type-other: #a48f6e;             /* khaki — uncommon */
  --type-addition: #e39b1b;          /* amber — uncommon, moderately loud */
  --type-demolition: #8b3dd6;        /* violet — rare, loud */
  --type-new_construction: #d7263d;  /* crimson — rare, loudest */
  --dev: #0f8f5f;                    /* development-application ring and tag */
  --hl-a: #c026d3;                   /* highlight mode, first company: magenta — no data colour uses it */
  --hl-b: #0284c7;                   /* highlight mode, second company: sky blue (renovation is slate, links are indigo) */

  /* ---- status (the sign-up form's result line only) */
  --ok: #0f6e45;  --ok-bg: #e6f6ee;
  --err: #b42318; --err-bg: #fdecea;

  color-scheme: light;
}

/* ---- dark: the same tokens, re-valued. System preference unless the reader chose (html[data-theme]). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #111317; --bg-soft: #1b1e24; --line: #2c313a; --fg-muted: #9aa3b0; --fg: #eceef1;
    --bg-glass: rgba(17, 19, 23, .92);
    --shadow: 0 8px 24px rgba(0, 0, 0, .5);
    --link: #8ab4f8; --focus: #22d3ee;
    --type-renovation: #8c9bb3; --type-other: #b39b76; --type-addition: #f2b13a; --type-demolition: #b57cf0; --type-new_construction: #ff5566;
    --dev: #3ccf95; --hl-a: #e879f9; --hl-b: #38bdf8;
    --ok: #6ee7b7; --ok-bg: #0f2a1f; --err: #fca5a5; --err-bg: #3a1513;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #111317; --bg-soft: #1b1e24; --line: #2c313a; --fg-muted: #9aa3b0; --fg: #eceef1;
  --bg-glass: rgba(17, 19, 23, .92);
  --shadow: 0 8px 24px rgba(0, 0, 0, .5);
  --link: #8ab4f8; --focus: #22d3ee;
  --type-renovation: #8c9bb3; --type-other: #b39b76; --type-addition: #f2b13a; --type-demolition: #b57cf0; --type-new_construction: #ff5566;
  --dev: #3ccf95; --hl-a: #e879f9; --hl-b: #38bdf8;
  --ok: #6ee7b7; --ok-bg: #0f2a1f; --err: #fca5a5; --err-bg: #3a1513;
  color-scheme: dark;
}

/* ---- the type colours as classes, so no template needs an inline style */
.t-new_construction { --c: var(--type-new_construction); }
.t-addition { --c: var(--type-addition); }
.t-renovation { --c: var(--type-renovation); }
.t-demolition { --c: var(--type-demolition); }
.t-other { --c: var(--type-other); }
.t-dev { --c: var(--dev); }
.hl-a { --c: var(--hl-a); }
.hl-b { --c: var(--hl-b); }

/* Copyright (c) 2025-2026 Tahseen Huda / Ooua */
/*
 * Components shared by the app (style.css loads after this) and the public pages
 * (prerender.mjs writes tokens.css + this + the page rules as /p.css). One definition
 * of the base text styles, focus ring, buttons, inputs, tags, KPIs, notes, charts and
 * the sign-up box. Every value is a token from tokens.css.
 */
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; background: var(--bg); }
body { font: var(--fw-normal) var(--fs-body) / var(--lh-body) var(--font); color: var(--fg); }
h1, h2, h3 { line-height: var(--lh-tight); font-weight: var(--fw-bold); }
b, strong, th { font-weight: var(--fw-bold); }
p { margin: var(--s2) 0; }
input { margin: 0; }
a { color: var(--link); }
a:hover { text-decoration-thickness: 2px; }
button, select, input { font: inherit; color: inherit; }
button { cursor: pointer; line-height: var(--lh-tight); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* one visible focus ring everywhere: the focus colour with a surface-coloured halo so it reads on ink, paper and the map */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; box-shadow: 0 0 0 4px var(--bg); }
.muted { color: var(--fg-muted); font-size: var(--fs-small); }
.note { font-size: var(--fs-small); color: var(--fg-muted); margin: var(--s1) 0 var(--s2); }
.nd { color: var(--fg-muted); font-style: italic; }
/* secondary buttons and button-shaped links */
.btn { border: var(--border); background: var(--bg); color: var(--fg); border-radius: var(--r-md); padding: 0 var(--s3); min-height: 36px; display: inline-flex; align-items: center; justify-content: center; gap: var(--s1); text-decoration: none; }
.btn:hover { background: var(--bg-soft); }
.btn.primary { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.actions { display: flex; gap: var(--s2); margin: var(--s2) 0; flex-wrap: wrap; }
.actions button, .actions a { border: var(--border); background: var(--bg); color: var(--fg); border-radius: var(--r-md); padding: 0 var(--s3); min-height: 36px; display: inline-flex; align-items: center; gap: var(--s1); text-decoration: none; font-size: var(--fs-small); }
.actions button:hover, .actions a:hover { background: var(--bg-soft); }
.actions .primary { background: var(--fg); color: var(--bg); border-color: var(--fg); }
input[type=search], input[type=email], input[type=text], .signup select { font-size: var(--fs-lead); /* 16px: iOS does not zoom the page on focus */
  color: var(--fg); background: var(--bg); border: var(--border); border-radius: var(--r-md); padding: var(--s2) var(--s3); min-height: 40px; width: 100%; }
/* tags: static labels (type, city, scale, dev application); the dot takes its colour from the .t-* class (tokens.css) */
.tag { display: inline-flex; align-items: center; gap: var(--s1); font-size: var(--fs-small); line-height: var(--lh-tight); padding: 0 var(--s2); min-height: 20px; border-radius: var(--r-sm); background: var(--bg-soft); color: var(--fg); max-width: 100%; white-space: nowrap; }
.tag i, i.t { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: 0 0 auto; background: var(--c); }
i.t { margin-right: var(--s1); vertical-align: 0; }
.tag.dev { border: 1px solid var(--dev); background: var(--bg); }
.tag.scale { background: var(--bg); border: 1px dashed var(--line); }
.tag.build { background: var(--fg); color: var(--bg); }
.tag.withheld { background: var(--bg); border: var(--border); color: var(--fg-muted); font-style: italic; }
.tag.city { background: var(--bg); border: var(--border); color: var(--fg-muted); font-weight: var(--fw-bold); letter-spacing: .02em; }
/* KPI cards */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: var(--s2); margin: var(--s3) 0; }
.kpis div { background: var(--bg-soft); border-radius: var(--r-md); padding: var(--s2) var(--s3); min-width: 0; }
.kpis b { display: block; font-size: var(--fs-title); line-height: var(--lh-tight); overflow-wrap: anywhere; }
.kpis span { font-size: var(--fs-small); color: var(--fg-muted); }
/* stacked year bars (.t-* segments, or an ink segment) + axis */
.bars { display: flex; align-items: stretch; gap: var(--s1); height: 90px; margin: var(--s5) 0 var(--s1); border-bottom: var(--border); }
.bars div { flex: 1; height: 100%; display: flex; flex-direction: column-reverse; justify-content: flex-start; min-width: 4px; max-width: 48px; position: relative; }
.bars span { display: block; flex: none; background: var(--c, var(--fg)); }
.bars small { position: absolute; top: -16px; left: 0; right: 0; text-align: center; font-size: var(--fs-small); line-height: var(--lh-tight); color: var(--fg-muted); white-space: nowrap; }
.axis { display: flex; gap: var(--s1); font-size: var(--fs-small); color: var(--fg-muted); }
.axis span { flex: 1; text-align: center; min-width: 4px; max-width: 48px; }
/* the weekly digest sign-up box */
.signup { margin: var(--s6) 0 var(--s2); padding: var(--s3); border: var(--border); border-radius: var(--r-md); background: var(--bg-soft); }
.signup h2, .signup h3 { border: 0; margin: 0 0 var(--s1); padding: 0; }
.signup label, .signup .field { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-small); color: var(--fg-muted); margin: var(--s2) 0; }
.signup .two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s2); }
.signup .two label { min-width: 0; }
.signup select { width: 100%; min-width: 0; }
.signup .where { font-size: var(--fs-body); color: var(--fg); }
.signup .linkish { align-self: flex-start; border: 0; background: none; color: var(--link); padding: var(--s1) 0; min-height: 24px; font-size: var(--fs-small); text-decoration: underline; }
.signup .hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.signup button[type=submit] { width: 100%; padding: var(--s3); border: 0; border-radius: var(--r-md); background: var(--fg); color: var(--bg); font-weight: var(--fw-bold); margin-top: var(--s1); }
.signup button[disabled] { opacity: .6; }
.signup .msg { min-height: 24px; margin: var(--s2) 0 0; font-size: var(--fs-small); color: var(--fg-muted); }
.signup .msg:not(:empty) { color: var(--fg); padding: var(--s2) var(--s3); border-radius: var(--r-md); border: var(--border); background: var(--bg); }
.signup .msg.ok { border-color: var(--ok); background: var(--ok-bg); color: var(--ok); }
.signup .msg.ok::before { content: "✓ "; font-weight: var(--fw-bold); }
.signup .msg.err { border-color: var(--err); background: var(--err-bg); color: var(--err); }
.signup .msg.err::before { content: "! "; font-weight: var(--fw-bold); }
/* theme toggle: moon = switch to dark, sun = switch to light (aria-pressed = dark is on) */
.theme-btn { width: 36px; padding: 0; font-size: var(--fs-lead); }
.theme-btn::before { content: "☾"; }
.theme-btn[aria-pressed="true"]::before { content: "☀"; }
/* a failed fetch is said, not shown as "nothing here" (Phase 14): the same red as a failed sign-up */
.note.err { border: var(--border); border-color: var(--err); background: var(--err-bg); color: var(--err); padding: var(--s2) var(--s3); border-radius: var(--r-sm); }
.note.err::before { content: "! "; font-weight: var(--fw-bold); }
.note.err .linkish { color: inherit; text-decoration: underline; }
header{display:flex;align-items:center;gap:var(--s3);padding:var(--s2) var(--s4);border-bottom:var(--border)}
header .brand{font-weight:var(--fw-bold);font-size:var(--fs-lead);color:var(--fg);text-decoration:none}header nav{margin-left:auto;display:flex;align-items:center;gap:var(--s1);font-size:var(--fs-small);flex-wrap:wrap}header nav a{display:inline-flex;align-items:center;min-height:32px;padding:0 var(--s2);border-radius:var(--r-md)}
header .theme-btn{border:var(--border);background:var(--bg);color:var(--fg);border-radius:var(--r-md);min-height:32px;width:32px;display:inline-flex;align-items:center;justify-content:center}
header nav .short{display:none}@media(max-width:479px){header{padding:var(--s2) var(--s3)}header nav{flex-wrap:nowrap}header nav .long{display:none}header nav .short{display:inline}}
main{max-width:860px;margin:0 auto;padding:var(--s3) var(--s4) var(--s6)}footer{max-width:860px;margin:0 auto;padding:var(--s4);border-top:var(--border);font-size:var(--fs-small);color:var(--fg-muted)}
.crumbs{font-size:var(--fs-small);color:var(--fg-muted);margin:var(--s1) 0 var(--s3)}.crumbs a{color:var(--fg-muted)}.crumbs span{margin:0 var(--s1)}
h1{font-size:var(--fs-display);margin:var(--s1) 0 var(--s2);overflow-wrap:anywhere}h2{font-size:var(--fs-lead);margin:var(--s5) 0 var(--s2);padding-top:var(--s2);border-top:var(--border)}h3{font-size:var(--fs-body);margin:var(--s4) 0 var(--s2)}
.lede{margin:0 0 var(--s3)}
.wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}table{border-collapse:collapse;width:100%}th{text-align:left;font-weight:var(--fw-bold);color:var(--fg-muted);font-size:var(--fs-small);padding:var(--s2) var(--s2) var(--s2) 0;border-bottom:var(--border);white-space:nowrap}td{padding:var(--s2) var(--s2) var(--s2) 0;border-bottom:var(--border);vertical-align:top}td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}td.d{white-space:nowrap;color:var(--fg-muted)}td .desc{display:block;color:var(--fg-muted);font-size:var(--fs-small);max-width:52ch}
ul.list{list-style:none;margin:0;padding:0;columns:2;column-gap:var(--s5)}ul.list li{padding:var(--s1) 0;border-bottom:var(--border);break-inside:avoid;display:flex;justify-content:space-between;gap:var(--s2)}ul.list .muted{white-space:nowrap}
@media(max-width:600px){ul.list{columns:1}table.cards,table.cards tbody,table.cards tr,table.cards td{display:block}table.cards thead{display:none}table.cards tr{padding:var(--s2) 0;border-bottom:var(--border)}table.cards td{padding:0;border:0}table.cards td.num{text-align:left}table.cards td[data-l]::before{content:attr(data-l) ": ";color:var(--fg-muted);font-size:var(--fs-small)}table.cards td .desc{max-width:none}table.cards td.d{display:inline-block;margin-right:var(--s3)}}
.letters{display:flex;flex-wrap:wrap;gap:var(--s2);margin:var(--s2) 0 var(--s4);font-size:var(--fs-small)}.letters a{display:inline-flex;align-items:center;justify-content:center;min-width:32px;min-height:32px;padding:0 var(--s2);border:var(--border);border-radius:var(--r-md);text-decoration:none}
