/* ─────────────────────────────────────────────────────────────────────────────
   Urban Makers — global brand-enforcement layer

   Loaded LAST on every page (after each page's inline <style>), so this is where
   the brand is enforced platform-wide. It carries three things:
     1. the single Google Fonts @import (IBM Plex Sans / Barlow / Source Serif 4 / IBM Plex Mono);
     2. the token :root — the ONE definition of every --um-* / --cat-* / --act
        token (scripts/check-ui.mjs mirrors these values in its hex allowlist);
     3. the cross-page overrides pages rely on to stay consistent — the !important
        density is deliberate, so these rules win over legacy per-page CSS.

   The look: IBM Plex Sans (sentence case) for UI — Barlow only on the wordmark and the page
   title (owner, 2026-09-23: Barlow's i and l read alike at 13 px) — Source Serif 4 for long-form reading,
   IBM Plex Mono for micro-labels; warm paper ground, ink-blue text, warm-grey
   lines, orange (--act) reserved for CTAs and warnings.

   History: this started as the "Phase A override layer" with a planned "Phase B"
   fold-in into the page styles. That framing is obsolete — Phases 3–5 made the
   pages self-consistent, and this file is now the intended brand layer, not a
   temporary shim. Folding any remaining override rule down into a page is
   OPTIONAL future work and MUST be verified with a per-page screenshot diff
   (removing an override is only safe when the page renders identically without it).

   Body font contract:
     default                       → IBM Plex Sans (working tools)
     body.um-serif:not(.um-grove)  → Source Serif 4 (long-form reading, e.g. wiki)
     body.um-grove                 → exempt: the Grove pages (questionnaire, faq)
                                      own their whole canvas (parchment + Jost)
   ───────────────────────────────────────────────────────────────────────── */

@import url('https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&family=IBM+Plex+Sans:wght@400;500;600&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root{
  --um-font:'IBM Plex Sans',system-ui,sans-serif;        /* text, fields, controls, pills, tables */
  --um-font-display:'Barlow',sans-serif;                 /* the wordmark and the page title only */
  --um-mono:'IBM Plex Mono',ui-monospace,monospace;      /* quote numbers, IDs, money in columns — never buttons, pills or sentences */
  --um-serif:'Source Serif 4',Georgia,serif;              /* long reading, and the printed Discovery cards' titles and questions — never a control */
  --um-ink:#1E2A32;
  --ink-soft:#4A5A64;
  --um-paper:#FBFAF7;
  --um-paper-mid:#F3F1EA;
  --um-paper-dark:#D8D5CC;
  --um-sage:#3E7C4F;
  --um-sage-dark:#1E2A32;
  --um-beige:#E8C9A0;
  --um-beige-light:#FFF4E5;
  --um-beige-mid:#EFE3D0;
  --um-success:#3E7C4F;
  --um-warn:#E8630A;
  --um-danger:#B3432B;
  --um-link:#2C5F8A;        /* the one hyperlink blue (matches --cat-cli) */
  --um-line:#D8D5CC;        /* border/rule alias of --um-paper-dark */
  --um-danger-tint:#FBEEEA; /* danger/destructive tint */
  /* Small-text ink variants (WCAG AA): the pure CTA orange and gold sit at
     ~3.1:1 on their tints — fine for fills/borders/large text, too low for
     ≤13px text. Use these for small orange/gold TEXT; fills keep the originals. */
  --um-warn-ink:#BC5008;
  --cat-fin-ink:#8D6A1B;

  /* hub categories: gold / green / blue / plum / slate (orange reserved) */
  --cat-fin:#B08422;  --cat-fin-tint:#FAF3E0;  --cat-fin-line:#ECDCAE;
  --cat-exe:#3E7C4F;  --cat-exe-tint:#EDF5EF;  --cat-exe-line:#C5DCCB;
  --cat-cli:#2C5F8A;  --cat-cli-tint:#EDF3F8;  --cat-cli-line:#C2D6E6;
  --cat-rep:#7A5C7E;  --cat-rep-tint:#F3EDF4;  --cat-rep-line:#DCCBDE;
  --cat-tool:#4A5A64; --cat-tool-tint:#EEF0F1; --cat-tool-line:#CBD2D6;
  --act:#E8630A;      --act-tint:#FFF4E5;

  /* person identity: a fixed colour per human, so the same face reads as the
     same colour on every surface. Solved rather than eyeballed — a
     farthest-point search over muted, light HSL (S ≤ 36%, L 76–86%) under two
     hard constraints:
       · ≥7.2:1 against --um-ink, because these are FILLS behind black
         initials at 11.5px, so AAA or it isn't a label;
       · ≥1.12:1 against white, so a chip still reads as coloured in a row.
     Result: minimum pairwise ΔE 14.4 across all ten. That floor is the point.
     Slots are handed out by hash, so ANY subset can end up in use — "the ten
     look fine in order" would be no guarantee at all. Ten covers a studio
     roster; an eleventh person shares a colour, which degrades to "two people
     look alike", never to "unreadable". Borders come from --um-line so the set
     reads as one family rather than ten loud badges. */
  --who-1:#D4B3AF;  --who-2:#ACD8AC;  --who-3:#D8ACCF;  --who-4:#D8CFAC;  --who-5:#B3B3DB;
  --who-6:#B6CEBA;  --who-7:#E6D1E6;  --who-8:#CEE8E8;  --who-9:#ACC6D8;  --who-10:#E6D9D1;

  /* Photo markup inks (photo-markup.js, 2026-09-29): what a designer draws on a site photo. Saturated on
     purpose, they sit on a photograph and never on the page; white and black are the neutrals. */
  --mk-red:#E53935;  --mk-yellow:#FDD835;  --mk-green:#43A047;  --mk-blue:#1E88E5;  --mk-white:#FFFFFF;  --mk-black:#1E2A32;

  /* The Discovery cards' print (cards.html, 2026-10-01, the "Journal" look the owner picked): a paper ground, a warm
     ink, an olive for the small caps, a hairline and the tone a card shows before it has a picture. On the printed
     card only, never on the app's own surfaces (check-ui allows these values here and nowhere else). */
  --deck-paper:#F3EDE4;  --deck-ink:#1C1916;  --deck-olive:#6A6950;  --deck-line:#CFC5B6;  --deck-tone:#DDD2C1;

  /* group/table headers: paper-mid fill so they read as structure, not CTA
     (the CTA/warning tint stays --act-tint only) — UI-CONSISTENCY-PLAN §3.3 */
  --um-head-bg:var(--um-paper-mid);
  --um-head-border:var(--um-paper-dark);

  /* ── The scale (design round two, 2026-09-09 — UI-CONSISTENCY-PLAN §9.2) ──
     Seven type sizes, three radii, two shadows, three control heights. Every
     new rule reads these; check-ui's type-scale / radius-scale rules hold the
     line. Cards and dialogs take the 8px radius, controls 4px, and the 999px
     pill is for STATUS only. */
  --um-fs-title:26px; --um-fs-h2:20px; --um-fs-lead:16px; --um-fs-body:14px;
  --um-fs-ui:13px;    --um-fs-meta:12px; --um-fs-label:11px;
  --um-radius:8px;    --um-radius-sm:4px; --um-radius-card:8px; --um-radius-pill:999px;
  --um-shadow-menu:0 4px 12px -4px rgba(30,42,50,.18);
  --um-shadow-dialog:0 16px 40px -16px rgba(30,42,50,.35);
  --um-h-sm:28px;     --um-h:32px;        --um-h-lg:36px;
}

/* ── type ──
   Grove pages (questionnaire, faq) carry `um-grove` on <body> and own their whole
   canvas (parchment + Jost, via their own :root). Exempt them from the global body
   font/background/colour repaint AND from the um-serif override — their bodies also
   carry `um-serif`, so without the second :not() they'd still be forced to Source
   Serif 4. With both scoped, their parchment renders seamlessly (no floating-strip
   seam) instead of paper + serif. Every other page (incl. serif wiki) is unaffected. */
body:not(.um-grove){
  font-family:var(--um-font) !important;
  font-size:var(--um-fs-body) !important;   /* 14 on every page — library/vendors/boards came down from 16 */
  background:var(--um-paper) !important;
  color:var(--um-ink) !important;
}
body.um-serif:not(.um-grove){font-family:'Source Serif 4',Georgia,serif !important}
/* The Grove pages keep their parchment palette through the tokens (2026-09-24): the master's controls read
   the tokens, so on a Grove page they draw in Grove. The values are each Grove page's own :root (--sans,
   --fg, --fg2, --border, --accent, --card, --bg-alt), so no Grove colour is written here. */
body.um-grove{--um-font:var(--sans);--um-ink:var(--fg);--ink-soft:var(--fg2);--um-line:var(--border);--act:var(--accent);
  --um-paper:var(--card,var(--bg));--um-paper-mid:var(--bg-alt,var(--bg))}
input,button,select,textarea{font-family:var(--um-font)}

/* headings: semibold, sentence case, no compression (the text face; the page title alone is Barlow) */
.page-title,.phead-name,.hub-band h2,.lane-head h2,
.section-title,.psec-head h2,.hub-welcome h1,.hero h1,.rgrp-title,
.md-body h1,.md-body h2,.md-body h3,.wiki-title,.acc-head .section-title,
.pc-name,.modal-box h2,.modal-box h3{
  font-family:var(--um-font) !important;
  text-transform:none !important;
  letter-spacing:0 !important;
  font-weight:600;
}
.page-title{font-size:var(--um-fs-title);font-weight:600}
.page-title,.phead-name,.hub-welcome h1,.hero h1,.wiki-title{font-family:var(--um-font-display) !important}   /* the page title is the one heading in the display face */
.phead-name,.hub-welcome h1,.hero h1{font-weight:600}
/* section labels: 11/600 caps, letterspaced — the one place capitals live */
.hub-band h2,.lane-head h2,.um-label{font-size:var(--um-fs-label) !important;font-weight:600;letter-spacing:.06em !important;text-transform:uppercase !important;color:var(--ink-soft)}
.md-body h2{border-bottom:1.5px solid var(--um-paper-dark) !important;padding-bottom:6px}

/* Labels and status tags: the text face, not mono. Round two retired monospace as a
   "voice" — it read as a terminal on pills and sentences. Mono is for codes
   and money only (.plrow .seq, quote numbers, tabular amounts). */
.tleg,.contract-mini .lbl,.dg-item label,
.plrow .desc small,.hub-crumb{
  font-family:var(--um-font) !important;
  font-size:var(--um-fs-label);font-weight:600;letter-spacing:.05em;text-transform:uppercase;
}
.pc-type,.pc-noq,.won-toggle{
  font-family:var(--um-font) !important;
  font-size:var(--um-fs-label);font-weight:600;letter-spacing:.04em;
}
.plrow .seq{font-family:var(--um-mono) !important;font-variant-numeric:tabular-nums}
/* the icon set (db.js umIcon / chrome.js): sits on the text baseline, never shrinks */
.um-ic{display:inline-block;vertical-align:-.19em;flex:none}
/* status pill spec — the ONLY pill shape; sentence case, four semantic pairs */
.um-pill{display:inline-flex;align-items:center;gap:6px;height:22px;padding:0 9px;font-family:var(--um-font);text-transform:none;letter-spacing:normal;border-radius:var(--um-radius-pill);font-size:var(--um-fs-meta);font-weight:500;line-height:1;white-space:nowrap;background:var(--cat-tool-tint);color:var(--ink-soft)}
.um-pill::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.um-pill.ok{background:var(--cat-exe-tint);color:var(--um-success)}
.um-pill.act{background:var(--act-tint);color:var(--um-warn-ink)}
.um-pill.bad{background:var(--um-danger-tint);color:var(--um-danger)}
.um-pill.dim{background:var(--cat-tool-tint);color:var(--ink-soft)}   /* the neutral pair, named, so markup can say what it means (batch 2, 2026-09-22) */

/* ── surfaces: 6px cards, warm-grey lines ── */
.phead,.hub-welcome,.hero,.acc-section,.psec,.pcard,
.rgrp,.modal-box,.card,.wiki-card,.project-card{
  border-radius:var(--um-radius-card) !important;
  border-color:var(--um-paper-dark) !important;
}
.plrow{border-radius:var(--um-radius-sm) !important}
.phead,.hub-welcome,.hero,.pcard{border:1px solid var(--um-paper-dark) !important}
/* Direction B (owner pick, 2026-09-09): a card carries no coloured edge. The
   stage colour lives on the lane header's dot; the card is a hairline box. */
.pcard{border-left:1px solid var(--um-paper-dark) !important}
/* (.hub-pip left these two lists on 2026-09-11: a hub-row pip is a bare mono
   count — shared-ui.css owns it. Batch 2, 2026-09-22: every status badge, count
   and stage pill left them too — they are .um-pill / .um-count / .um-tag now.) */
.tleg{border-radius:var(--um-radius-sm) !important}
.details-grid{border-top:1.5px solid var(--um-paper-dark) !important}
.hub-band .hub-rule,.lane-head .lane-rule{background:var(--um-paper-dark) !important;height:1.5px !important}
.hub-legdot,.lane-head .lane-dot{border-radius:50% !important}   /* a category is a dot (§3.4) */

/* ── buttons: the canon lives in shared-ui.css (DESIGN-GUIDE.md §3.1, batch 1 2026-09-22);
   the theme no longer force-paints .btn-*. The segment, the toggle chip and the count-tab
   joined them there on 2026-09-24 (one definition in the master, not a face here over six
   pages' own sizes). ── */

/* ── number fields: no steppers, anywhere ─────────────────────────────────────
   Nobody nudges a price a cent at a time with a 4px arrow — they type it. The
   stepper ate a third of every narrow field, hijacked the scroll wheel over the
   quote cards and the catalogue rows, and put a click target next to the one
   people actually want. This is deliberately global (this file loads LAST on
   every page): quantities, prices, discounts, add-on rates, variant deltas,
   commission percentages, floorplan dimensions — one rule, every page, so a new
   number field can never arrive with arrows again.
   They stay type=number: the numeric keypad on a phone and ↑/↓ from the
   keyboard both still work, which is how anyone who wants to step actually
   steps. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none;margin:0}
input[type="number"]{-moz-appearance:textfield;appearance:textfield}
/* Date and time fields draw the SAME stepper — the Xero bill period, the pay-run
   month, the timeline item's start and end times. Only the spin button goes:
   `appearance:textfield` here would take the calendar and clock pickers with it,
   and those are the controls people actually use. */
input[type="date"]::-webkit-inner-spin-button,
input[type="time"]::-webkit-inner-spin-button,
input[type="month"]::-webkit-inner-spin-button,
input[type="week"]::-webkit-inner-spin-button,
input[type="datetime-local"]::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none;display:none;margin:0}

/* ── app bar: paper-light with ink rule (article-header style) ──
   The Phase-2 interim bar repaints are now fully retired: quote-view's dark
   top bar migrated to chrome.js (Phase 3c) and catalogue's .cat-pending-head
   slide-in header was tokenised to the same paper+ink chrome treatment in its
   own page CSS (Phase 5b), so no page-local bar rides this repaint anymore. */
.app-bar,.appbar{
  background:var(--um-paper) !important;
  color:var(--um-ink) !important;
  border-bottom:2px solid var(--um-ink);
  box-shadow:none !important;
}
.app-bar *,.appbar *{color:var(--um-ink) !important}
.app-bar-logo{font-family:var(--um-font-display) !important;font-weight:700;letter-spacing:.12em}
.app-bar-btn{font-family:var(--um-font) !important;background:transparent !important;border:none !important;text-decoration:none !important;font-weight:500;color:var(--ink-soft) !important}
.app-bar-btn:hover{color:var(--um-ink) !important}
/* the builder Save stays the bar's dark primary */
.app-bar .save-btn{background:var(--um-ink) !important;color:var(--um-paper) !important;border-radius:var(--um-radius-sm) !important;font-family:var(--um-font) !important;font-weight:500}

/* ── stage lanes: quote=blue, close=orange, start=yellow, ongoing=green, handover=slate ── */
.lane-to_quote{--st:#2C5F8A;--st-tint:#EDF3F8}
.lane-to_close{--st:#E8630A;--st-tint:#FFF4E5}
.lane-to_start{--st:#876D1A;--st-tint:#FAF3E0}  /* deep gold — the old #C9A227 was 2.2:1 on the tint */
.lane-ongoing{--st:#3E7C4F;--st-tint:#EDF5EF}
.lane-handing_over{--st:#4A5A64;--st-tint:#EEF0F1}
.pc-amt{color:#3E7C4F !important}
.pc-pl{color:var(--um-warn-ink) !important;background:#FFF4E5 !important}
.pc-won{color:#3E7C4F !important;background:#EDF5EF !important}

/* ── punchlist types (semantic; orange only on "incomplete" = warning-like) ── */
.t-inc{--tc:#E8630A;--tint:#FFF4E5}
.t-fun{--tc:#B3432B;--tint:#FBEEEA}
.t-wrk{--tc:#3E7C4F;--tint:#EDF5EF}
.t-cos{--tc:#2C5F8A;--tint:#EDF3F8}

/* ── needs-you banner: the CTA-orange moment. Border + body text derive from the
   reserved --act/--um-warn orange (a low-alpha rgba line; the body text is the
   orange ink-darkened for readable contrast on the tint) so the warmth comes from
   one sanctioned source, not one-off warm hexes. ── */
/* (the hub needs-you banner is .hub-need in shared-ui.css since round two — one orange dot and a sentence, not a boxed tint) */

/* ── budget pills ── */
.approved-budget-box{background:#EDF5EF !important;border:1px solid #C5DCCB !important;border-radius:var(--um-radius-card) !important}
.approved-budget-label{color:#3E7C4F !important}
.approved-budget-amount{color:#1E2A32 !important}

/* ── wiki / reading bodies ── */
.md-body{font-family:'Source Serif 4',Georgia,serif !important;font-size:16px;line-height:1.65}
.md-body code{border-radius:4px !important;font-family:var(--um-mono) !important}
.md-body table th{background:var(--um-paper-mid) !important;color:var(--um-ink) !important;
  font-family:var(--um-font) !important;font-weight:600}

:focus-visible{outline:2px solid var(--um-ink) !important;outline-offset:2px}

/* ── print safety: quotes / VOs / payout slips print on white ── */
@media print{
  /* This MUST out-specify `body:not(.um-grove){background:var(--um-paper)!important}`
     above. Both are !important, so specificity decides — a plain `body` selector
     loses, and the parchment then paints every part of a sheet the document
     doesn't reach (the blank off-white block under a short last page). */
  body,body:not(.um-grove),body.um-grove{background:#fff !important}
  .app-bar,.appbar{border-bottom:none}
}

/* ── WhatsApp-linked phone numbers (waHtml in db.js) ─────────────────────────
   Every mobile in the app is a chat link; an unparseable number renders as
   .wa-plain so it reads as inert rather than looking broken. */
.wa-link{color:var(--cat-cli);text-decoration:none;border-bottom:1px dotted currentColor}
.wa-link::after{content:" ";font-size:.9em;opacity:.85;border:0}
.wa-link:hover{color:var(--um-ink)}
.wa-plain{color:inherit}

/* ── Sticky dialog actions ───────────────────────────────────────────────────
   Save / Cancel stay on screen instead of hiding at the bottom of a long form.
   Loaded last, so this reaches every page's own modal markup.

   Two container shapes exist. The `.modal` family (app.css) is a flex column
   whose LIST scrolls, so its footer never left the viewport — nothing to do.
   The `.modal-box` family scrolls the whole box, so its footer has to pin
   itself. The negative margins pull it out to the box's edges and swallow the
   box's own bottom padding, so nothing scrolls into a gap beneath it; each page
   declares its own padding as --mbox-padx/--mbox-pady on .modal-box. */
.modal-box{scroll-padding-bottom:76px}
.modal-box>.modal-footer{
  position:sticky;bottom:calc(-1 * var(--mbox-pady,24px));z-index:4;
  margin:16px calc(-1 * var(--mbox-padx,24px)) calc(-1 * var(--mbox-pady,24px));
  padding:13px var(--mbox-padx,24px) var(--mbox-pady,24px);
  background:inherit;                       /* .modal-box paints itself white */
  box-shadow:0 -6px 14px -12px rgba(30,28,20,.5);
}
@media(max-width:640px){
  /* Phone modals run to the bottom edge — keep the bar clear of the home bar. */
  .modal-box>.modal-footer{padding-bottom:calc(var(--mbox-pady,24px) + env(safe-area-inset-bottom))}
}
