/* AMII shared design system — single source of truth for tokens + base components.
   Extracted from the hand-rolled per-template styles (capture.html was the fullest set).
   Page-specific CSS still lives in each template's {% block styles %}; put anything
   reused by 2+ templates HERE so it never drifts again. */

:root {
  /* ══ WORLD 2 — WARM PROPERTY (founder choice, 31 Jul 2026) ══════════════════════════════
     Cream grounds, one confident clay accent, a deep plum band for chrome (masthead, hero,
     login art). "A property is not a form." Token NAMES are unchanged so every template keeps
     resolving — only the VALUES move. Light is the standard view; dark stays OPT-IN below.
     Contrast proof (all body pairings ≥4.5:1) is printed in docs/executor_d1_report.md. */
  color-scheme: light;
  --bg: #fbf6f1; --surface: #ffffff; --surface-2: #f4ebe4;   /* cream, not white — a room, not a spreadsheet */
  --border: #ece0d7; --border-strong: #d3c0b4;
  --text: #241a18; --text-muted: #6b5a52;   /* 15.8:1 and 6.1:1 on cream */
  /* No third text tier ships. --text-faint COLLAPSES to --text-muted: it was documented at
     2.48:1 (decorative only), and World 2 retires the tier rather than carry the hazard. Where a
     template leaned on faintness for hierarchy, that hierarchy now comes from size/weight/position
     (every site is listed in the D-1 report). */
  --text-faint: var(--text-muted);
  --accent: #c2402a; --accent-hover: #9d3121; --accent-bg: #fbe8e2;   /* clay — 4.8:1 on cream */
  /* White on the clay fill is 5.17:1 (AA). Dark mode flips this to dark ink (its accent is lighter). */
  --accent-contrast-text: #fff;
  /* The deep plum band — CHROME ONLY (masthead, property hero, login art), never a body-text
     surface. --deep-text 13.3:1 and --deep-muted 7.6:1 on it. */
  --deep: #3a1e22; --deep-2: #4a282c; --deep-text: #f7efe9; --deep-muted: #c9b3ab;
  --deep-rule: rgba(247,239,233,0.18);
  /* Status pills (badges, tags). Each still carries its WORD (platform rule 3). */
  --amber-bg: #f7edd8; --amber-text: #7a5714;
  --green-bg: #e4efe3; --green-text: #2f5d33;
  --red-bg: #fbe8e2; --red-text: #9d3121;
  /* --purple has no World 2 hue; DERIVED into the plum family — warm tint + deep-2 ink (10.2:1). */
  --purple-bg: #ece3e4; --purple-text: #4a282c;
  --gray-bg: #efe7e1; --gray-text: #6b5a52;   /* 5.36:1 on gray-bg */
  --radius-sm: 10px; --radius: 16px; --radius-lg: 22px; --radius-pill: 999px;   /* generous, plus a pill */
  /* DOCUMENT/RECORD spine, DERIVED into World 2's deep family (the prompt's instruction): --slate
     is the section rule/spine, --band-bg / --zebra tint the ledger. --mono is the evidence voice
     (hashes, refs, timestamps, filenames). None carry meaning without a word beside them. */
  --slate: #3a1e22; --band-bg: #f4ebe4; --zebra: #f9f1ea;
  --sans: "Avenir Next", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --text-xs: 12px; --text-sm: 14px; --text-base: 16px; --text-lg: 20px; --text-xl: 26px; --text-2xl: 34px;
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 72px;
  --measure: 1120px; --card-gap: var(--space-5);   /* World 2 additions (new names) */
  --shadow-sm: 0 1px 2px rgba(58,30,34,0.05);
  --shadow: 0 6px 20px rgba(58,30,34,0.07);
  --shadow-lg: 0 16px 40px rgba(58,30,34,0.10);
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1); --dur: 180ms;
  /* MOTION VOICE (shared with templates/landing.html so the app and the public page move the same
     way). Emil: the built-in CSS easings are too weak — an entrance uses a STRONG ease-out, never
     ease-in. --ease-out is the entrance/press curve; --ease-io is for something moving on screen.
     Durations live at the call site because they express distance, not house style. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-io: cubic-bezier(0.77, 0, 0.175, 1);
}
/* DARK MODE — OPT-IN, not automatic. Applies ONLY when something sets
   <html data-theme="dark">; it no longer fires from @media (prefers-color-scheme: dark),
   so an OS in dark mode still opens this app in LIGHT. The dark tokens (and the
   --accent-contrast-text dark-ink fix) are preserved here for whenever a dark toggle is
   wanted — nothing is thrown away, it is just no longer the default. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #1a1714; --surface: #221e1a; --surface-2: #2a2521; --border: #332d27;
  --border-strong: #443c34; --text: #f0ebe3; --text-muted: #b3a89b; --text-faint: #7d746a;
  --accent: #f97316; --accent-hover: #fb8b3c; --accent-bg: #3a2416;
  /* DARK INK on the dark accent: var(--bg) #1a1714 on #f97316 = 6.37:1 (AA/AAA),
     versus white's failing 2.80:1. WCAG AA for normal text is 4.5:1. */
  --accent-contrast-text: var(--bg);
  /* Dark structural accent + ledger tints: a light slate reads on the dark ground; the band and
     zebra sit a step off --surface so the ruled document rhythm survives the dark theme. */
  --slate: #9aa7b4; --band-bg: #26303a; --zebra: #201c19;
  --amber-bg: #38290f; --amber-text: #edbd6a;
  /* Paired -bg/-text so every badge stays legible on dark, not dark-on-dark
     (values from index.html's considered dark treatment). */
  --green-bg: #16311f; --green-text: #6fdc9b; --red-bg: #3a1d18; --red-text: #f0a595;
  --purple-bg: #241a3a; --purple-text: #bfa8f2; --gray-bg: #2b2621; --gray-text: #b3a89b;
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
  --shadow: 0 4px 16px rgba(0,0,0,0.36); --shadow-lg: 0 12px 32px rgba(0,0,0,0.5);
}

* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  /* One humanist sans throughout (World 2), the same face the report is set in, so the dashboard
     and the document it produces read as one system; graceful system fallback where it is absent.
     Mono is reserved for the evidence voice. */
  font-family: var(--sans);
  font-size: var(--text-base); line-height: 1.55; -webkit-font-smoothing: antialiased;
}

/* Layout */
.wrap { max-width: 640px; margin: 0 auto; padding: var(--space-4); }
.center { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: var(--space-4); }

/* Typography */
h1 { font-size: var(--text-lg); font-weight: 650; letter-spacing: -0.02em; margin: var(--space-2) 0 var(--space-1); }
.sub { color: var(--text-muted); font-size: var(--text-sm); margin: 0 0 var(--space-4); }
a.back { color: var(--accent); text-decoration: none; font-size: var(--text-sm); }
a.back:hover { text-decoration: underline; }

/* Surfaces. World 2: cards sit softly on the cream — a low shadow, generous radius. They do
   NOT hover (elevation is a resting state, not motion). Plum never fills a content card
   (founder ruling 31 Jul 17:33: plum frames the work; the work is on cream and white). */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-5); box-shadow: var(--shadow-sm);
}

/* NOTE: no shared `.btn` or bare input/select rule on purpose. `.btn` already exists in
   capture.html / review_cards.html meaning a bordered auto-width control; a shared
   `width:100%` accent `.btn` would collide with it, and a global input rule bled
   `width:100%` into every control in the larger templates. Style buttons/inputs
   per-template; the shared layer is tokens + layout + .card + callouts. */

/* ── PLATFORM RULES (learned the hard way; keep them so they outlive us) ───────────────
   1. TEXT ON THE ACCENT FILL uses var(--accent-contrast-text), never a hardcoded #fff.
      White passes on the LIGHT accent (5.18:1) but FAILS on the lighter dark accent
      (#f97316 = 2.80:1). The token flips to dark ink in dark mode; a raw #fff reintroduces
      the AA failure. --text-faint is DECORATIVE ONLY (2.48:1 on the light bg): anything a
      user must READ — timestamps, hashes, tokens, filenames — uses --text-muted.
   2. GRADIENTS are CHROME ONLY — headers, empty states, the login card. NEVER on an
      evidence surface (photo cards, /report, /compare, anything printable). A gradient's
      contrast varies per pixel, so automated checkers miss the failing point, and a
      gradient on an evidence record reads as "spin". Text over a gradient needs a solid or
      semi-opaque scrim behind it.
   3. RED vs GREEN must ALWAYS carry a second channel — a word, a count, an icon. ~8% of
      men read nothing from the red/green hue pair alone. The status badges already show
      counts; this rule exists so nobody "simplifies" that away. */

/* Callouts */
.note {
  background: var(--amber-bg); color: var(--amber-text); border: 1px solid currentColor;
  border-radius: var(--radius-sm); padding: 10px var(--space-3);
  font-size: var(--text-xs); line-height: 1.45;
}
.err { color: var(--accent); font-size: var(--text-sm); }

/* Callout — ONE component for every page-level inline banner/notice (legal holds,
   unclassifiable files, dispute-window notices, files outside the tenancy model). A tinted
   surface + a 1px hairline FULL border in the tone — NEVER a thick colored side border, which
   is the AI-UI "side-tab" tell. Severity is carried by the tone AND the banner's own words;
   colour is never the only signal (platform rule 3). Positioning/animation stay OUT of here so
   the fixed toast can borrow the tone without inheriting a layout. Secondary text is tinted
   from the tone, never gray, and never dimmed with opacity (the Unit-1 amber-on-amber lesson). */
.callout {
  --callout-bg: var(--surface-2); --callout-fg: var(--text); --callout-bd: var(--border-strong);
  background: var(--callout-bg); color: var(--callout-fg);
  border: 1px solid var(--callout-bd); border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4); font-size: var(--text-sm); line-height: 1.5;
  margin: 0 0 var(--space-4);
}
.callout--info    { --callout-bg: var(--surface-2); --callout-fg: var(--text);       --callout-bd: var(--border-strong); }
.callout--warn    { --callout-bg: var(--amber-bg);  --callout-fg: var(--amber-text); --callout-bd: var(--amber-text); }
.callout--error   { --callout-bg: var(--red-bg);    --callout-fg: var(--red-text);   --callout-bd: var(--red-text); }
.callout--success { --callout-bg: var(--green-bg);  --callout-fg: var(--green-text); --callout-bd: var(--green-text); }
.callout a { color: inherit; font-weight: 650; }
.callout strong { font-weight: 700; }
.callout .callout-sub { display: block; margin-top: var(--space-1); font-size: var(--text-xs); }
.callout .callout-sub.mono { font-family: ui-monospace, monospace; word-break: break-all; }

/* Move-out proximity flag — a warn-tone callout with its own name because it appears on BOTH
   the property header and every portfolio card. Defined ONCE here (was duplicated verbatim in
   index.html and portfolio.html). A fact about timing surfaced early, never a verdict. */
.moveout-flag {
  background: var(--amber-bg); color: var(--amber-text);
  border: 1px solid currentColor; border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3); font-size: var(--text-sm);
  line-height: 1.45; margin: var(--space-3) 0 var(--space-4);
}
.moveout-flag a { color: inherit; font-weight: 650; text-decoration: underline; }
/* Sub-note under the flag. NO opacity (amber-on-amber at .85 fell below AA — Unit 1). */
.moveout-flag .moveout-sub { display: block; margin-top: var(--space-1); font-size: 11px; }

/* ── Landlord navigation (LANDLORD pages only; tenant pages never include _nav.html).
   Shared chrome, so it lives here rather than drifting across five templates. It is
   layout + tokens only - no bare button/input rule, no .btn. The active state is a quiet
   accent underline, never a filled pill: this is a wayfinder, not a call to action. */
/* World 2's DEEP PLUM masthead framing the cream work surface. The founder liked the "colour
   separation between header and portfolio" — it is structural (deep chrome over a light record),
   not a palette. Plum is CHROME ONLY (founder, 31 Jul 17:33): the masthead and the login ground,
   never a content card. Pill nav; the avatar inverts to the clay accent, one warm mark in the
   chrome. deep-muted 7.6:1 / deep-text 13.3:1 on plum. */
.appnav {
  position: sticky; top: 0; z-index: 40;
  background: var(--deep); color: var(--deep-text);
}
.appnav-in {
  /* ONE measure, read from the token, so the masthead's content edge sits on the same column as
     the record beneath it. index.html's .measure and portfolio's .wrap read the SAME token — three
     hardcoded copies of a number were how they drifted before. */
  max-width: var(--measure); margin: 0 auto; padding: 0 var(--space-4);
  display: flex; align-items: center; gap: var(--space-4); min-height: 60px; flex-wrap: wrap;
}
.appnav .brand {
  font-weight: 700; letter-spacing: -0.03em; font-size: var(--text-lg);
  color: var(--deep-text); text-decoration: none; flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 9px;
}
.appnav .brand:hover { color: var(--deep-text); }
.appnav .brand .dot { width: 11px; height: 11px; border-radius: 999px; background: var(--accent); flex: none; }
.appnav nav { display: flex; gap: var(--space-1); align-items: center; overflow-x: auto; }
.appnav nav a {
  color: var(--deep-muted); text-decoration: none; font-size: var(--text-sm);
  font-weight: 600; padding: 8px var(--space-3); white-space: nowrap;
  border-radius: var(--radius-pill);
}
.appnav nav a:hover { color: var(--deep-text); background: var(--deep-2); }
.appnav nav a.active { color: var(--deep-text); background: rgba(247,239,233,0.14); }
.appnav .ctx { margin-left: auto; display: flex; align-items: center; gap: var(--space-3);
  flex: 0 0 auto; }
.appnav .ctx .prop { color: var(--deep-muted); font-size: var(--text-sm); }
.appnav .ctx form { margin: 0; }
.appnav .ctx .signout,
.appnav .ctx .ctx-link {
  background: none; border: 0; color: var(--deep-muted); cursor: pointer;
  font-size: var(--text-sm); text-decoration: none; padding: 6px var(--space-3);
  border-radius: var(--radius-pill); font-family: inherit;
}
.appnav .ctx .ctx-link:hover,
.appnav .ctx .signout:hover { color: var(--deep-text); background: var(--deep-2); }
.appnav .ctx .ctx-link[aria-current] { color: var(--deep-text); background: rgba(247,239,233,0.14); }
/* The avatar inverts to the clay accent — one warm mark in the plum chrome. */
.appnav .ctx .avatar { width: 34px; height: 34px; border-radius: 999px; background: var(--accent);
  color: var(--accent-contrast-text); display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--text-sm); font-weight: 700; flex: none; }
@media (max-width: 560px) {
  .appnav .ctx .prop { display: none; }   /* the property name is repeated in the page head */
}

/* The report's SPINE as a document device: a slate double rule (a 2px slate line, then a 1px
   line 2px below it). Shared by the property page and the portfolio so both read as the working
   face of the evidence document. Never a side tab — it is a full-width horizontal rule. */
.spine { border: 0; border-top: 2px solid var(--slate); margin: 0; }
.spine + .spine { border-top-width: 1px; margin-top: 2px; }

/* Consistent page header: an h1 and one muted subtitle, same place and scale on every
   page. Half the pages had this and half did not. */
.page-head { margin: 0 0 var(--space-5); }
.page-head h1 { font-size: var(--text-xl); font-weight: 650; letter-spacing: -0.02em; margin: 0; }
.page-head .sub { display: block; color: var(--text-muted); font-size: var(--text-sm); margin-top: var(--space-1); }

/* Designed empty state: one sentence, one optional action, no icon. Every list/grid that
   can be empty uses this instead of a bare "nothing here". */
.empty-state {
  border: 1px dashed var(--border-strong); border-radius: var(--radius);
  padding: var(--space-6) var(--space-4); text-align: center; color: var(--text-muted);
  font-size: var(--text-sm); line-height: 1.5;
}
.empty-state strong { display: block; color: var(--text); font-size: var(--text-base);
  font-weight: 600; margin-bottom: var(--space-1); }
.empty-state a { color: var(--accent); }

/* Honour a reduced-motion preference across EVERY page that loads app.css (was defined only
   in index.html, so portfolio and the tenant pages animated regardless). The old rule here
   nulled a backdrop-filter the .appnav no longer sets — a no-op; this is the real one. */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
