/* MAIA farmer beta — tokens and components, from the design bundle.
   Fonts are local woff2. There is no CDN call and no external request at runtime. */

@font-face { font-family: Spectral; font-style: normal; font-weight: 300;
  font-display: swap; src: url(fonts/spectral-300.woff2) format('woff2'); }
@font-face { font-family: Spectral; font-style: normal; font-weight: 400;
  font-display: swap; src: url(fonts/spectral-400.woff2) format('woff2'); }
@font-face { font-family: Spectral; font-style: italic; font-weight: 400;
  font-display: swap; src: url(fonts/spectral-400-italic.woff2) format('woff2'); }
@font-face { font-family: Spectral; font-style: normal; font-weight: 600;
  font-display: swap; src: url(fonts/spectral-600.woff2) format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400;
  font-display: swap; src: url(fonts/ibm-plex-mono-400.woff2) format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500;
  font-display: swap; src: url(fonts/ibm-plex-mono-500.woff2) format('woff2'); }
/* One variable file covers 400 and 600; shipping it twice would be two identical files. */
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 400 600;
  font-display: swap; src: url(fonts/source-sans-3-variable.woff2) format('woff2'); }

:root {
  --ground:#E2DCCD; --screen:#F6F2E9; --card:#FFFDF8; --inset:#F3EFE5;
  --map:#E7E2D4; --header:#15301E; --strip:#ECE7D9; --strip-line:#DCD5C3;
  --ink:#1C3322; --ink-2:#3A4A3C; --ink-3:#5E6A57; --mute:#8E9384;
  --rule:#E2DCCD; --rule-2:#CFC7B4; --rule-3:#C7C0AE;
  --green:#4E7A52; --field:#CFE0D2; --field-sel:#BCD4C0;
  --waiting:#DCE2D4; --waiting-line:#8E9384;
  --neutral:#DFE3D8; --neutral-line:#B4B7A8;
  --brass:#917A33; --terracotta:#A24F3B; --header-mute:#9FB39F;
  --serif:Spectral,Georgia,serif;
  --mono:'IBM Plex Mono',ui-monospace,monospace;
  --sans:'Source Sans 3',system-ui,sans-serif;
}

* { box-sizing:border-box; }
html,body { margin:0; padding:0; }
body {
  background:var(--screen); color:var(--ink); font-family:var(--sans); font-size:16px;
  -webkit-font-smoothing:antialiased; text-wrap:pretty;
}
a { color:var(--ink); text-decoration:underline; text-underline-offset:3px; }
a:hover { color:var(--green); }
h1,h2,h3 { margin:0; font-family:var(--serif); font-weight:400; letter-spacing:-0.01em; }
button { font-family:var(--mono); cursor:pointer; }

/* --- mono label, the one repeated typographic gesture ------------------- */
.k {
  font-family:var(--mono); font-size:10px; letter-spacing:0.16em;
  text-transform:uppercase; color:var(--mute);
}
.k--ink { color:var(--ink); } .k--brass { color:var(--brass); } .k--dim { color:var(--ink-3); }

/* --- provenance rule: the mark gets lighter and more broken -------------- */
.rule { flex:none; height:0; border-bottom-width:2px; width:34px; }
.rule--measured { border-bottom-style:solid;  border-bottom-color:var(--ink); }
.rule--proxied  { border-bottom-style:dashed; border-bottom-color:var(--ink-3); }
.rule--assumed  { border-bottom-style:dotted; border-bottom-color:var(--mute); }
.rule--absent   { border-bottom-style:none; }
.rule--sm { width:26px; } .rule--lg { width:44px; }

.prov-row { display:flex; align-items:center; gap:12px; }
.prov-row__text { flex:1; font-size:14px; line-height:1.35; color:var(--ink); }
.prov-row__text span { color:var(--mute); }
.prov-row__status {
  font-family:var(--mono); font-size:10px; letter-spacing:0.1em;
  text-transform:uppercase; color:var(--ink-3); white-space:nowrap;
}
/* An absent input has no line to draw, because there is nothing under it. */
.prov-row--absent .prov-row__status { color:var(--brass); }

/* --- shell -------------------------------------------------------------- */
/* --- what this instance is -----------------------------------------------
   A band above the mount, on a copy only. Quiet by default: it is a standing
   fact about the server, not an alarm about the morning, and a red bar over
   every screen for ever is a bar nobody reads by the second day. Brass where
   the age is not known, which is the one register this palette already uses
   for "MAIA cannot stand behind this" (see .looked--off). */
.fact {
  background:var(--strip); border-bottom:1px solid var(--strip-line);
  padding:9px 22px; font-size:13.5px; line-height:1.45; color:var(--ink-2);
}
.fact--unknown { color:var(--brass); }
.fact[hidden] { display:none; }

.app { min-height:100vh; display:flex; flex-direction:column; }
.appbar { display:none; }
.strip { display:none; }

.mhead { padding:22px 22px 0; }
/* Centre, not baseline: a 58px mark on a baseline pushes the date off the
   optical line it used to share with the wordmark. */
.mhead__top { display:flex; justify-content:space-between; align-items:center; }
.mhead__looked { margin-top:10px; }
.mhead__looked .looked {
  font-family:var(--mono); font-size:10px; letter-spacing:0.06em;
}
@media (min-width:760px) { .mhead__looked { display:none; } }
.mhead__brand { display:flex; align-items:center; gap:9px; }
/* Stage 7d: roughly doubled. The header bar grows with it rather than the mark
   crowding the date beside it — see .mhead__top below. */
.mhead__brand img { width:58px; height:58px; display:block; }
.mhead__brand span {
  font-family:var(--serif); font-size:19px; letter-spacing:0.18em; font-weight:600;
}

.main { flex:1; display:flex; flex-direction:column; min-height:0; }
.col { padding:18px 22px 110px; display:flex; flex-direction:column; gap:18px; }

/* --- bottom nav, three slots ------------------------------------------- */
.nav {
  position:fixed; left:0; right:0; bottom:0; height:76px; background:var(--screen);
  border-top:1px solid var(--rule); display:grid; grid-template-columns:repeat(3,1fr);
  z-index:20;
}
.nav a {
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:7px;
  color:var(--mute); text-decoration:none;
}
.nav a i { display:block; width:20px; height:0; border-bottom:2px solid transparent; }
.nav a.is-on { color:var(--ink); }
.nav a.is-on i { border-bottom-color:var(--ink); }
.nav span {
  font-family:var(--mono); font-size:10px; letter-spacing:0.12em; text-transform:uppercase;
}

/* --- alert card --------------------------------------------------------- */
.alert {
  background:var(--card); border:1px solid var(--rule); border-radius:14px;
  padding:20px 18px; display:flex; flex-direction:column; gap:15px;
}
.alert__field {
  font-family:var(--mono); font-size:10px; letter-spacing:0.16em;
  text-transform:uppercase; color:var(--brass);
}
.alert h2 { font-size:26px; line-height:1.2; }
/* The substrate's condition labels are long and technical because they are exact. A long
   one is set smaller rather than truncated: the whole condition is the point. */
.alert h2.is-long { font-size:21px; line-height:1.3; }
.alert__lede { margin:0; font-size:16px; line-height:1.55; color:var(--ink); }
.alert__lede strong { font-weight:600; }
.hr { height:1px; background:var(--rule); }
.alert__why p { margin:0; font-size:15px; line-height:1.55; color:var(--ink-2); }

.rests {
  display:flex; flex-direction:column; gap:10px; background:var(--inset);
  border-radius:10px; padding:14px 14px 15px;
}
/* THE PANEL IS ONE LINE UNTIL SOMEBODY ASKS. The authored summary is the panel; the two
   halves behind the expander are what a sceptic opens. The raw rows are set in the mono face
   so that the difference between a farm-words row and an engine row is visible before it is
   read. */
.rests__line {
  margin:0; font-size:15px; line-height:1.55; color:var(--ink-2);
}
.rests__line--none { color:var(--ink-3); font-style:italic; }
.rests__more {
  display:flex; align-items:center; gap:8px; width:100%; padding:8px 0 0;
  background:transparent; border:0; border-top:1px solid var(--rule);
  font-family:var(--mono); font-size:10px; letter-spacing:0.14em;
  text-transform:uppercase; color:var(--ink-3); cursor:pointer; min-height:34px;
  text-align:left;
}
.rests__more span:first-child { flex:1; }
.rests__body { display:flex; flex-direction:column; gap:14px; padding-top:4px; }
/* AND THE CLOSED STATE HAS TO SAY SO IN THE SAME BREATH. `hidden` is a UA rule of
   `display:none`, and any `display` declaration of ours outranks it, so the line above was
   quietly painting the whole provenance table under a collapsed expander. The week-ahead
   strip never hit this because its body sets padding and not display. Found by the proof
   asking whether the raw rows were on the page rather than whether the flag was set. */
.rests__body[hidden] { display:none; }
.rests__half { display:flex; flex-direction:column; gap:7px; }
.rests__rows { display:flex; flex-direction:column; gap:8px; }
.rests__exact {
  margin:0; font-family:var(--mono); font-size:12px; line-height:1.5; color:var(--ink-3);
  overflow-wrap:anywhere;
}
.rests__note { margin:0; font-size:13px; line-height:1.5; color:var(--brass); }
.prov-row--raw .prov-row__text {
  font-family:var(--mono); font-size:11.5px; line-height:1.45; color:var(--ink-3);
  overflow-wrap:anywhere;
}
.rests__fix { display:flex; gap:8px; align-items:baseline; padding-top:3px; }
.rests__fix div { font-size:13.5px; line-height:1.5; color:var(--ink-3); flex:1; }
.rests__fix a {
  font-family:var(--mono); font-size:11px; letter-spacing:0.08em;
  text-transform:uppercase; white-space:nowrap;
}

/* --- disposition -------------------------------------------------------- */
.disp { display:flex; gap:10px; }
.btn {
  flex:1; min-height:48px; border-radius:9px; background:transparent;
  font-size:12px; letter-spacing:0.12em; text-transform:uppercase;
}
.btn--primary { border:1px solid var(--ink); color:var(--ink); }
.btn--primary:hover { background:var(--field); }
.btn--primary:active { background:var(--field); transform:translateY(1px); }
.btn--quiet { border:1px solid var(--rule-3); color:var(--ink-3); }
.btn--quiet:hover { border-color:var(--mute); color:var(--ink); }
.btn--quiet:active { transform:translateY(1px); }
.btn--solid { border:1px solid var(--ink); background:var(--ink); color:var(--screen); }
.btn--solid:hover { background:var(--header); }
.btn[disabled] { opacity:0.5; cursor:default; }

/* One tap, then the buttons stand down. The card keeps its height. */
.ack {
  min-height:48px; display:flex; align-items:center; gap:10px;
  animation:maiaAck 420ms ease-out both;
}
.ack__line { font-family:var(--serif); font-style:italic; font-size:16px; }
.ack--useful .ack__line, .ack--useful .rule { color:var(--green); border-bottom-color:var(--green); }
.ack--not_useful .ack__line, .ack--not_useful .rule { color:var(--terracotta); border-bottom-color:var(--terracotta); }
@keyframes maiaAck { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce) { .ack { animation:none; } }

/* --- watched list ------------------------------------------------------- */
.watched { display:flex; flex-direction:column; gap:9px; }
/* AND IT MUST STILL OBEY `hidden`. `expander()` hides its body by setting the `hidden`
   attribute, which works through the UA stylesheet's `[hidden] { display:none }` — and a class
   that sets `display` beats it, because a class selector outranks a UA rule. The older band
   rendered in full behind an expander that said it was collapsed. Caught on the painted page:
   the payload was right, every assertion about it was green, and the screen showed 13 rows
   where the read had banded 8 and 5. */
.watched[hidden] { display:none; }
.watched__row {
  display:flex; justify-content:space-between; gap:14px; align-items:baseline;
  font-size:14.5px; line-height:1.4; color:var(--ink-3);
}
.watched__count { font-family:var(--mono); font-size:11px; color:var(--mute); white-space:nowrap; }
.watched__why { font-size:14px; line-height:1.5; color:var(--ink-3); }
/* The grounds a refusal stands on, one per line. They were one semicolon-joined paragraph
   until 2026-08-26; the substrate holds them as separate rows, so the screen shows them as
   separate lines. */
.watched__grounds { margin:0; padding:0 0 0 16px; list-style:disc; }
.watched__ground { font-size:14px; line-height:1.5; color:var(--ink-3); }
.watched__ground + .watched__ground { margin-top:3px; }
/* A key the declared vocabulary has not caught up with. It says so; nothing is composed. */
.watched__why--none { font-style:italic; color:var(--mute); }

/* --- empty state -------------------------------------------------------- */
.empty { padding-top:40px; display:flex; flex-direction:column; gap:20px; }
.empty h2 { font-weight:300; font-size:34px; line-height:1.15; }
.empty p { margin:0; font-size:16px; line-height:1.65; color:var(--ink-2); }

/* --- map ---------------------------------------------------------------- */
.map { background:var(--map); position:relative; display:flex; flex-direction:column; }
.map svg { display:block; width:100%; height:auto; }
.map__attr {
  font-family:var(--mono); font-size:8.5px; line-height:1.55; letter-spacing:0.04em;
  color:#7C8375; padding:0 14px 10px;
}
/* Strokes are in screen pixels, not map metres. The viewBox is 3 143 units wide and a
   unit is a metre, so a 1.5-unit stroke would render at less than half a pixel and the
   dashes that carry "waiting on you" would vanish entirely. */
.parcel { cursor:pointer; vector-effect:non-scaling-stroke; }
.parcel--running { fill:var(--field); stroke:var(--green); stroke-width:1.5px; }
.parcel--waiting { fill:var(--waiting); stroke:var(--waiting-line); stroke-width:1.5px;
  stroke-dasharray:4 4; }
.parcel--neutral { fill:var(--neutral); stroke:var(--neutral-line); stroke-width:1.5px; }
.parcel--selected { fill:var(--field-sel); stroke:var(--ink); stroke-width:2.5px;
  stroke-dasharray:none; }
/* Label sizes are in map units so they scale with the farm rather than with the browser. */
.parcel__label { font-family:var(--mono); font-size:58px; fill:var(--ink); pointer-events:none; }
.parcel__sub { font-family:var(--mono); font-size:50px; fill:var(--green); pointer-events:none; }

.legend { display:flex; gap:18px; flex-wrap:wrap; }
.legend div { display:flex; align-items:center; gap:8px; }
.legend i { width:14px; height:10px; display:block; }
.legend i.is-running { background:var(--field); border:1.5px solid var(--green); }
.legend i.is-waiting { background:var(--waiting); border:1.5px dashed var(--waiting-line); }
.legend span {
  font-family:var(--mono); font-size:10px; letter-spacing:0.08em;
  text-transform:uppercase; color:var(--ink-3);
}

/* --- panels, lists ------------------------------------------------------ */
.panel {
  background:var(--inset); border-radius:12px; padding:18px 16px;
  display:flex; flex-direction:column; gap:12px;
}
.panel p { margin:0; font-size:16px; line-height:1.5; }
.panel strong { font-weight:600; }
.panel__note { font-size:13.5px; line-height:1.5; color:var(--ink-3); }

.rows { display:flex; flex-direction:column; }
.row {
  display:flex; justify-content:space-between; align-items:baseline; gap:12px;
  padding:14px 0; border-top:1px solid var(--rule); text-decoration:none; color:var(--ink);
}
.rows .row:last-child { border-bottom:1px solid var(--rule); }
.row__name { display:flex; flex-direction:column; gap:3px; }
.row__name b { font-weight:400; font-size:16px; }
.row__name small { font-size:13.5px; color:var(--ink-3); }
.row__chev { font-family:var(--mono); font-size:11px; color:var(--mute); }

.var {
  display:flex; align-items:baseline; gap:12px; padding-bottom:12px;
  border-bottom:1px solid var(--rule);
}
.var__name { flex:1; display:flex; flex-direction:column; gap:3px; }
.var__name b { font-weight:400; font-size:16px; }
.var__name small { font-size:13.5px; color:var(--ink-3); }
.var__mark { display:flex; align-items:center; gap:9px; }
.var__mark span {
  font-family:var(--mono); font-size:10px; letter-spacing:0.08em;
  text-transform:uppercase; color:var(--ink-3);
}

/* --- unlock card: a gap is a sentence with a field under it -------------- */
.unlock {
  background:var(--card); border:1px solid var(--rule); border-radius:12px;
  padding:18px 16px; display:flex; flex-direction:column; gap:14px;
}
.unlock__cond { font-family:var(--serif); font-size:20px; line-height:1.3; }
.unlock__what { font-size:14px; line-height:1.55; color:var(--ink-3); }
.unlock__field { display:flex; flex-direction:column; gap:8px; }
.unlock__field label {
  font-family:var(--mono); font-size:10px; letter-spacing:0.12em;
  text-transform:uppercase; color:var(--ink-3);
}
.unlock__field input {
  min-height:48px; border:1px solid var(--rule-3); border-radius:9px; background:var(--screen);
  padding:0 14px; font-family:var(--sans); font-size:16px; color:var(--ink); width:100%;
}
.unlock__helper { font-size:13px; line-height:1.5; color:var(--mute); }
.unlock__actions { display:flex; gap:10px; }
.unlock__actions .btn--solid { flex:1; }
.unlock__actions .btn--quiet { flex:none; padding:0 16px; }
/* What actually happens on submit — never a promise the engine cannot keep. */
.unlock__truth {
  font-size:13.5px; line-height:1.55; color:var(--ink-3);
  border-left:2px solid var(--rule-2); padding-left:12px;
}
.unlock--done .unlock__truth { color:var(--ink-2); }

/* A statement, not a question. No form, no buttons, and no dashed "waiting" treatment
   either: this is not a field waiting on the farmer, it is a field waiting on a model,
   and the card should not look like something he has failed to answer. */
.unlock--stated { border-style:solid; border-color:var(--rule-2); }

/* --- worth measuring ---------------------------------------------------- */
.wm { display:flex; gap:16px; padding:22px 0; border-top:1px solid var(--rule); }
.wm:last-child { border-bottom:1px solid var(--rule); }
.wm__n { font-family:var(--mono); font-size:12px; color:var(--brass); padding-top:4px;
  width:18px; flex:none; }
.wm__body { display:flex; flex-direction:column; gap:7px; }
.wm__title { font-family:var(--serif); font-size:21px; line-height:1.3; }
.wm__text { font-size:14.5px; line-height:1.6; color:var(--ink-3); }
.wm__line { display:flex; flex-direction:column; gap:4px; }
.wm__title--none { color:var(--ink-3); font-style:italic; }
.wm .rests { margin-top:4px; }
.wm__meta {
  font-family:var(--mono); font-size:10px; letter-spacing:0.1em; text-transform:uppercase;
  color:var(--mute); padding-top:2px;
}
.wm__aside { display:none; }

.footnote { margin:0; font-size:14px; line-height:1.6; color:var(--mute); }

/* ======================================================================== */
/* 760–1180 px — header bar and strip appear; the panel drops beneath the map */
/* ======================================================================== */
@media (min-width:760px) {
  .mhead { display:none; }
  .nav { display:none; }

  .appbar {
    /* Grown from 64px to carry the doubled mark (7d) without cropping it. */
    display:flex; flex:none; height:92px; background:var(--header);
    align-items:center; justify-content:space-between; padding:0 26px;
  }
  .appbar__brand { display:flex; align-items:center; gap:11px; }
  .appbar__brand img { width:66px; height:66px; display:block;
    filter:brightness(0) invert(1); opacity:0.92; }
  .appbar__brand span { font-family:var(--serif); font-size:19px; letter-spacing:0.2em;
    font-weight:600; color:var(--screen); }
  .appbar nav { display:flex; gap:30px; }
  .appbar nav a {
    font-family:var(--mono); font-size:11px; letter-spacing:0.14em; text-transform:uppercase;
    color:var(--header-mute); text-decoration:none; padding-bottom:3px;
    border-bottom:2px solid transparent;
  }
  .appbar nav a.is-on { color:var(--screen); border-bottom-color:var(--green); }
  .appbar__meta { font-family:var(--mono); font-size:11px; letter-spacing:0.1em;
    color:var(--header-mute); }

  .strip {
    display:flex; flex:none; min-height:52px; background:var(--strip);
    border-bottom:1px solid var(--strip-line); align-items:center; gap:34px;
    padding:0 26px; font-family:var(--mono); font-size:11px; letter-spacing:0.08em;
    color:var(--ink-3); flex-wrap:wrap;
  }
  .strip div { display:flex; align-items:center; gap:9px; }
  .strip .rule { width:22px; }
  .strip__end { margin-left:auto; color:var(--mute); }

  /* From here up the shell is the viewport: header and strip are fixed furniture and the
     content scrolls inside. Without this the map column stretches to the length of the
     field list — 4 600 px on this holding — and the farm is centred off the bottom of the
     screen. */
  .app { height:100vh; overflow:hidden; }
  .main { flex:1; min-height:0; overflow:auto; }
  .main.split { overflow:hidden; }

  /* AND THE DOOR SCROLLS TOO. The shell being the viewport means a screen that is not a
     view has nowhere to go: the entry screens mount straight into .app, and a card taller
     than 900 px simply had its foot cut off with no way to reach it. That was invisible
     while the only door was "Which holding?" and nine digits; the consent wording is 2 700
     characters and the building screen is six phases and eleven seasons, and both ran off
     the bottom of a 1440 pt window. Found at Stage 12 by a proof that could not see the
     price on the desktop and could see it on the phone. */
  .entry { min-height:0; overflow-y:auto; }

  .col { padding:26px 26px 40px; max-width:820px; }
  .split { display:flex; flex-direction:column; min-height:0; }
  .split__map { flex:none; height:420px; overflow:hidden; }
  .split__map svg { width:100%; height:100%; }
  .split__side { flex:1; min-height:0; padding:26px 24px 34px;
    display:flex; flex-direction:column; gap:26px; }
  .map__attr { font-size:9.5px; padding:8px 24px 9px; }

  /* 760–1180: the map is capped and the panel sits beneath it, so the pair scrolls
     together inside .main. */
  .split { flex:none; }
}

/* The farm summary belongs over the map on a wide screen and in the column on a narrow
   one. It is rendered in both places and the wrong one is hidden, so neither layout has
   to re-flow a card that was built for the other. */
.map__over { display:none; }
.only-narrow { display:flex; }

/* Alerts carries no map below the wide breakpoint. Screens 01 and 02 of the design are a
   single column — the map is what desktop ADDS to this view, so that selection can bind
   the list to the farm. Below 1180 there is no room for it to answer "where" without
   pushing the alert itself off the screen. */
.split--alerts .split__map { display:none; }

/* ======================================================================== */
/* ≥ 1180 px — as drawn: map and panel side by side                          */
/* ======================================================================== */
@media (min-width:1180px) {
  .split { flex-direction:row; flex:1; min-height:0; }
  .split__map { flex:1; height:auto; min-width:0; min-height:0; position:relative; }
  .split__map svg { width:100%; height:100%; }
  .split__side {
    flex:none; width:420px; min-height:0; overflow:auto;
    background:var(--screen); border-left:1px solid var(--strip-line);
  }
  .map__over { display:flex; }
  .only-narrow { display:none; }
  .split--alerts .split__map { display:block; }
  /* Alerts puts the list first and the map second, per D2. */
  .split--alerts { flex-direction:row-reverse; }
  .split--alerts .split__side {
    width:500px; border-left:none; border-right:1px solid var(--strip-line);
    padding:24px 24px 34px; gap:22px;
  }
  .split--alerts .alert { border-color:var(--rule-2); border-left:3px solid var(--ink); }

  /* DISPLAY IS THE FLAG'S, NOT THE WIDTH'S. This read `display:flex` unconditionally, so
     state.layerSheet was ignored at this width entirely: the panel covered 28% of the map
     and no control, no outside tap and no key could put it away. The sheet button below is
     no longer hidden here either, because a panel that can be closed needs a way back. */
  .map__over { display:none; }
  .map__over.is-open {
    position:absolute; left:24px; top:24px; width:330px; background:var(--screen);
    border:1px solid var(--strip-line); border-radius:12px; padding:20px 18px;
    display:flex; flex-direction:column; gap:13px;
    /* Bounded by the map it sits on, and scrolling inside that bound. The panel is about
       1,060 px tall once the card carries the pass picker, its cap note and the majority
       sentence; the map is 643 px. Without this the last two ran off the bottom of the
       window entirely — present in the DOM, never on screen, and with nothing on the page
       a farmer could scroll to reach them. `scrollIntoView` moved them, which is why the
       first proof of this said "reachable"; no scrollbar existed and no finger could. */
    /* 24 px of top offset, and room at the foot for the attribution strip that sits
       absolutely at the bottom of the map. Without the second term the panel ran underneath
       it: with three layer rows instead of two, the pass picker came to rest behind the
       credits and could only be reached by scrolling the panel — present, reachable, and
       covered, which is its own kind of not-there. */
    max-height:calc(100% - 24px - 56px); overflow-y:auto;
  }
  .map__over > * { flex:none; }
  .map__attr { position:absolute; left:0; right:0; bottom:0; background:var(--map); }

  /* The button that reopens it. It sits where the phone's does; with the panel open it is
     the panel's own heading that names the layer, so the button carries the same name and
     the pair never disagree. */
  .map__sheetbtn { display:inline-flex; }
  /* The compact provenance line is hidden here ONLY while the card is on screen. Closing
     the panel used to be impossible, so "the card is always up at this width" was true and
     this line was pure repetition. Now the panel closes, and without this the greenness
     wash would be on the map with its date, age and coverage nowhere at all. */
  .map.is-panelopen .greenstrip { display:none; }

  .col { max-width:820px; margin:0 auto; padding:64px 40px; }
  .col--wide h2 { font-size:42px; line-height:1.12; }

  .wm { gap:28px; padding:26px 0; }
  .wm__n { font-size:13px; width:26px; padding-top:6px; }
  .wm__body { flex:1; gap:8px; }
  .wm__title { font-size:24px; }
  .wm__text { font-size:15.5px; max-width:54ch; }
  .wm__meta { display:none; }
  .wm__aside {
    display:flex; flex:none; width:210px; flex-direction:column; gap:8px;
    align-items:flex-start; padding-top:8px;
  }
  .wm__aside .k { line-height:1.6; }
}

/* --- utility ------------------------------------------------------------ */
.stack { display:flex; flex-direction:column; gap:14px; }
.stack--sm { gap:8px; }
.stack--lg { gap:26px; }
.muted { color:var(--ink-3); font-size:14px; line-height:1.6; }
.err { padding:26px 22px; font-size:15px; line-height:1.6; color:var(--terracotta); }

/* A record the farmer has already given: shown as held, never as running. */
.unlock__held { display:flex; flex-direction:column; gap:6px; }
.unlock__value { font-family:var(--serif); font-size:20px; line-height:1.3; }

/* --- the front door ----------------------------------------------------- */
.entry {
  flex:1; display:flex; align-items:center; justify-content:center;
  padding:40px 22px 60px; background:var(--screen);
}
.entry__card {
  width:100%; max-width:430px; display:flex; flex-direction:column; gap:16px;
}
.entry__logo { width:124px; height:124px; display:block; margin-bottom:6px; }
.entry__title { font-weight:300; font-size:40px; line-height:1.1; }
.entry__lede { margin:0; font-size:16px; line-height:1.65; color:var(--ink-3); max-width:44ch; }
.entry__field { display:flex; flex-direction:column; gap:8px; margin-top:6px; }
.entry__input {
  min-height:52px; border:1px solid var(--rule-3); border-radius:9px; background:var(--card);
  padding:0 14px; font-family:var(--mono); font-size:20px; letter-spacing:0.14em;
  color:var(--ink); width:100%;
}
.entry__input:focus { outline:2px solid var(--green); outline-offset:1px; }
.entry__helper { font-size:13px; line-height:1.5; color:var(--mute); }
.entry__go { flex:none; min-height:52px; }
/* An anchor styled as a button is not a button until it is told how to fill one. `.btn` sets
   a height and nothing about how its own text sits inside it, which a <button> centres for
   itself and an <a> does not: "Back to the door" sat in the top-left corner of a 52 px box.
   It has read that way on every refusal card since Stage 10 and was seen at Stage 16, in a
   screenshot of the screen this stage put a farmer on. */
a.entry__go { display:flex; align-items:center; justify-content:center; text-decoration:none; }
.entry__answer:empty { display:none; }
/* The refusal is set as prose, not as an error: it is a true statement about the
   substrate, not a mistake the person made. */
.entry__no {
  background:var(--inset); border-radius:12px; padding:18px 16px;
  display:flex; flex-direction:column; gap:9px;
}
.entry__no-line { font-family:var(--serif); font-size:19px; line-height:1.4; color:var(--ink); }
.entry__no-note { font-size:14px; line-height:1.6; color:var(--ink-3); }

/* --- the invitation page: named before anything is spent -----------------
   The holding's own name is the heading AND the wording on the button, because a farmer who
   has been forwarded somebody else's link should be able to see that before he presses
   rather than after. A shade smaller than the door's title: a farm name is longer than
   "Which holding?" and has to sit on two lines at 390 without pushing the button off.

   The notes beneath are the small print, and they are set as prose rather than as a caveat
   strip: what the press costs, how many devices are left, and where a second one comes from.
   The same class as the invitation panel's notes, given the page's own size here, because on
   a card in the middle of a screen 13px is a footnote and this is not one. */
.invite__holding { font-size:34px; }
.invite__enter { margin-top:2px; }
.entry .invite__note,
.entry__second { margin:0; font-size:14px; line-height:1.6; color:var(--ink-3); max-width:46ch; }

/* --- the holding switcher ------------------------------------------------------------------
   The farm's name is already on every screen, so once a browser can hold more than one farm
   the name is also the way to the others and becomes the control. With one holding it stays
   text and nothing here applies: a menu with one item teaches a choice that is not there. */
.switchbtn {
  display:inline-flex; align-items:center; gap:7px; min-height:32px;
  border:1px solid var(--rule-3); border-radius:8px; padding:0 10px;
  background:transparent; color:var(--ink-2); font-size:13px; max-width:34ch;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.switchbtn:hover, .switchbtn:focus-visible { color:var(--ink); border-color:var(--ink); }
/* The mark is drawn rather than typed, so it takes the page's own ink at any weight. */
.switchbtn__mark {
  flex:none; width:0; height:0; border-left:4px solid transparent;
  border-right:4px solid transparent; border-top:5px solid currentColor;
}
.switch {
  position:fixed; z-index:45; left:50%; transform:translateX(-50%);
  top:12px; width:min(460px, calc(100vw - 24px));
  display:flex; flex-direction:column; gap:10px;
  background:var(--card); border:1px solid var(--ink); border-radius:12px;
  padding:16px 18px 18px; box-shadow:0 10px 40px rgba(31,38,28,0.22);
}
.switch__list { display:flex; flex-direction:column; gap:8px; }
/* A row per holding, and the one on screen is marked rather than removed: a list that hid
   where you already are would be a list that changes length as you move about it. */
.switch__one {
  display:flex; flex-direction:column; gap:3px; text-align:left; text-decoration:none;
  border:1px solid var(--rule-2); border-radius:9px; padding:11px 13px;
  background:var(--screen); color:var(--ink); width:100%;
}
.switch__one:hover, .switch__one:focus-visible { border-color:var(--ink); }
.switch__one.is-on { border-color:var(--ink); background:var(--field); }
.switch__name { font-family:var(--serif); font-size:17px; line-height:1.25; }
.switch__sbi { font-family:var(--mono); font-size:11px; color:var(--mute); }
.switch__note { margin:0; font-size:13px; line-height:1.55; color:var(--mute); }

/* The same rows at the front door, where they are the first thing a returning farmer sees. */
.door__held {
  display:flex; flex-direction:column; gap:10px;
  border-bottom:1px solid var(--rule); padding-bottom:18px; margin-bottom:2px;
}

/* --- I have a code -------------------------------------------------------
   Below the SBI and quiet until it is asked for, because most people arriving at
   this door have a holding to look up and not a code in their hand. The two blocks
   are separated and separately headed: a pairing code and an onboarding code are
   different lengths from different alphabets doing different jobs, and one box that
   guessed between them would be a box that is sometimes wrong. */
.entry__havecode { align-self:flex-start; min-height:44px; }
.door__codes {
  display:flex; flex-direction:column; gap:22px;
  border-top:1px solid var(--rule); padding-top:20px; margin-top:4px;
}
.door__block { display:flex; flex-direction:column; gap:10px; }
.door__what { margin:0; font-size:14px; line-height:1.6; color:var(--ink-3); max-width:46ch; }

/* --- the onboarding door -------------------------------------------------
   The third path. Wider than the other two entry screens because the consent
   wording has to be read rather than glanced at, and a 430 px column of it on a
   desktop would be a hundred lines of ribbon. */
.entry--wide { align-items:flex-start; }
.entry__card--wide { max-width:640px; }
.entry__onboard {
  background:var(--inset); border-radius:12px; padding:18px 16px;
  display:flex; flex-direction:column; gap:12px;
}
.entry__onboard .entry__go { margin-top:2px; }
/* A code is read out over the phone and typed in. It is not nine digits, so it does not
   get the SBI field's wide letter spacing. */
.entry__input--code { font-size:16px; letter-spacing:0.04em; }
.entry__select {
  min-height:52px; border:1px solid var(--rule-3); border-radius:9px; background:var(--card);
  padding:0 12px; font-family:var(--sans); font-size:16px; color:var(--ink); width:100%;
}
.entry__select:focus { outline:2px solid var(--green); outline-offset:1px; }

/* THE REGISTERED WORDING, LINE FOR LINE. Every line of the body is its own element
   carrying its own text; nothing is joined, split or retyped. The heading treatment
   is a class chosen by the shape of the line, so a wrong guess changes how a line
   looks and cannot change a byte of what it says. */
.consent__body {
  background:var(--card); border:1px solid var(--rule); border-radius:12px;
  padding:22px 20px; display:flex; flex-direction:column;
  max-height:none;
}
.consent__line {
  font-family:var(--serif); font-size:16px; line-height:1.65; color:var(--ink-2);
  max-width:62ch;
}
/* NO text-transform. Everywhere else in this interface a label like this is set in
   upper case, and here it must not be: the words are the registered consent wording,
   and a farmer who reads "WITHDRAWING" on the screen has been shown something the
   registry does not say, even though not a byte of the text was changed. Weight and
   colour carry the heading instead. */
.consent__head {
  font-family:var(--sans); font-size:14px; font-weight:600; letter-spacing:0.01em;
  color:var(--brass); margin-top:20px;
}
.consent__body > .consent__head:first-child { margin-top:0; }
.consent__gap { height:12px; }
.consent__version { font-size:13px; line-height:1.55; color:var(--mute); max-width:62ch; }
.consent__agree {
  display:flex; gap:12px; align-items:flex-start; padding:16px;
  background:var(--inset); border-radius:12px; cursor:pointer;
  font-size:15.5px; line-height:1.55; color:var(--ink);
}
/* Big enough to hit with a thumb in a yard. */
.consent__tick { width:24px; height:24px; flex:none; margin-top:1px; accent-color:var(--green); }

/* --- the building screen --------------------------------------------------
   Six phases, always, including the ones nothing has happened to yet: a screen
   that draws only what has started cannot show what is still to come. The marks
   are three states and not two, because "already here" and "coming later" are
   both successes and neither is a miss. */
.build__phases { display:flex; flex-direction:column; }
.build__phase {
  display:flex; gap:14px; padding:14px 0; border-top:1px solid var(--rule);
}
.build__phase:last-child { border-bottom:1px solid var(--rule); }
.build__mark { width:10px; height:10px; border-radius:50%; flex:none; margin-top:7px; }
.build__mark--ok    { background:var(--green); }
.build__mark--run   { background:var(--brass); }
.build__mark--later { background:transparent; border:1.5px solid var(--brass); }
.build__mark--todo  { background:transparent; border:1.5px solid var(--rule-3); }
.build__mark--bad   { background:var(--ink-3); }
.build__body { display:flex; flex-direction:column; gap:5px; }
.build__title { font-family:var(--serif); font-size:18px; line-height:1.3; }
.build__state {
  font-family:var(--mono); font-size:11px; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--ink-3);
}
.build__reason { font-size:14px; line-height:1.6; color:var(--ink-3); max-width:58ch; }
.build__seasons { display:flex; flex-wrap:wrap; gap:6px 14px; margin-top:4px; }
.build__season { display:flex; gap:7px; align-items:baseline; }
.build__season-y { font-family:var(--mono); font-size:12px; color:var(--ink-2); }
.build__season-s { font-size:12.5px; color:var(--mute); }
.build__quota {
  background:var(--inset); border-radius:12px; padding:16px;
  display:flex; flex-direction:column; gap:8px;
}
.build__quota p { margin:0; font-size:14.5px; line-height:1.6; color:var(--ink-3); }
.build__leave { font-size:14px; line-height:1.6; color:var(--mute); max-width:58ch; }

@media (min-width:760px) {
  .entry { padding:0 40px; }
  .entry__title { font-size:46px; }
}

/* --- the assistant drawer ------------------------------------------------
   The bundle's rationale for the bottom bar is that the farm has three
   questions and each gets one slot. The assistant is not a fourth question,
   so it is not a fourth slot: it rises over whichever view is open, and the
   view is still there underneath when it closes. */
.ask {
  position:fixed; left:0; right:0; bottom:76px; height:44px; z-index:25;
  background:var(--screen); border-top:1px solid var(--rule);
  display:flex; align-items:center; justify-content:center; gap:10px;
  font-family:var(--mono); font-size:11px; letter-spacing:0.14em;
  text-transform:uppercase; color:var(--ink-3); cursor:pointer; border-bottom:none;
}
.ask:hover { color:var(--ink); }
.ask i { width:20px; height:0; border-bottom:2px dashed var(--ink-3); display:block; }

.drawer {
  position:fixed; inset:0; z-index:30; display:none;
  background:rgba(28,51,34,0.28);
}
.drawer.is-open { display:block; }
.drawer__sheet {
  position:absolute; left:0; right:0; bottom:0; max-height:88vh; min-height:60vh;
  background:var(--screen); border-top:1px solid var(--rule-2);
  border-radius:22px 22px 0 0; display:flex; flex-direction:column;
  animation:maiaRise 260ms cubic-bezier(.22,.61,.36,1) both;
}
@keyframes maiaRise { from { transform:translateY(18px); opacity:0; } to { transform:none; opacity:1; } }
@media (prefers-reduced-motion:reduce) { .drawer__sheet { animation:none; } }

.drawer__head {
  flex:none; display:flex; align-items:baseline; justify-content:space-between;
  padding:18px 22px 12px; border-bottom:1px solid var(--rule);
}
.drawer__title { font-family:var(--serif); font-size:20px; }
.drawer__close { font-family:var(--mono); font-size:14px; color:var(--mute);
  background:none; border:none; cursor:pointer; padding:4px 6px; }
.drawer__log {
  flex:1; overflow:auto; padding:18px 22px 8px;
  display:flex; flex-direction:column; gap:16px;
}
.drawer__foot {
  flex:none; border-top:1px solid var(--rule); padding:14px 22px 18px;
  display:flex; gap:10px; align-items:flex-end; background:var(--screen);
}
.drawer__foot textarea {
  flex:1; min-height:48px; max-height:120px; resize:none;
  border:1px solid var(--rule-3); border-radius:9px; background:var(--card);
  padding:13px 14px; font-family:var(--sans); font-size:16px; color:var(--ink);
  line-height:1.4;
}
.drawer__foot textarea:focus { outline:2px solid var(--green); outline-offset:1px; }
.drawer__send { flex:none; min-height:48px; padding:0 18px; }

/* A farmer's turn and MAIA's turn read differently without a chat bubble in sight. */
.turn { display:flex; flex-direction:column; gap:6px; }
.turn--farmer { align-items:flex-end; }
.turn--farmer .turn__body {
  background:var(--inset); border-radius:12px 12px 4px 12px; padding:11px 14px;
  font-size:15.5px; line-height:1.5; max-width:85%;
}
.turn--maia .turn__body { font-size:15.5px; line-height:1.6; color:var(--ink); }
.turn--maia .turn__body p { margin:0 0 10px; }
.turn--maia .turn__body p:last-child { margin-bottom:0; }
.turn--maia .turn__body strong { font-weight:600; }
/* What it looked at, in the same mono the provenance labels use. */
.turn__tools {
  font-family:var(--mono); font-size:10px; letter-spacing:0.1em; text-transform:uppercase;
  color:var(--mute); display:flex; align-items:center; gap:9px;
}
.turn__tools i { width:20px; height:0; border-bottom:2px solid var(--rule-2); flex:none; }
.turn--thinking .turn__body { color:var(--mute); font-style:italic;
  font-family:var(--serif); }
.turn--error .turn__body { color:var(--terracotta); font-size:14.5px; }

/* The proposal: the field detail's unlock card, pre-filled, inside the drawer. */
.drawer .unlock { margin-top:2px; }
.drawer .unlock__value { font-size:19px; }

@media (min-width:760px) {
  /* No bottom bar up here, so the affordance moves into the dark header. */
  .ask { display:none; }
  .appbar__ask {
    font-family:var(--mono); font-size:11px; letter-spacing:0.14em; text-transform:uppercase;
    color:var(--header-mute); background:none; border:1px solid rgba(159,179,159,0.4);
    border-radius:7px; padding:7px 12px; cursor:pointer; margin-left:22px;
  }
  .appbar__ask:hover { color:var(--screen); border-color:var(--green); }
  .appbar__end { display:flex; align-items:center; }
}
@media (max-width:759px) { .appbar__ask { display:none; } }

@media (min-width:1180px) {
  /* Panel, not sheet: the map and the list stay visible beside it. */
  .drawer { background:none; left:auto; width:420px; }
  .drawer__sheet {
    top:0; bottom:0; right:0; left:0; max-height:none; border-radius:0;
    border-left:1px solid var(--strip-line); border-top:none;
    box-shadow:-18px 0 40px -30px rgba(28,51,34,0.5);
    animation:maiaSlide 260ms cubic-bezier(.22,.61,.36,1) both;
  }
  @keyframes maiaSlide { from { transform:translateX(20px); opacity:0; } to { transform:none; opacity:1; } }
}

/* --- the week ahead ------------------------------------------------------
   Under the conditions strip, same mono register, dashed throughout. A model's
   forecast on a ~10 km grid is no firmer than the modelled archive above it. */
.fcast { display:none; }

@media (min-width:760px) {
  .fcast {
    display:flex; flex:none; align-items:center; gap:22px; flex-wrap:wrap;
    background:var(--strip); border-bottom:1px solid var(--strip-line);
    padding:9px 26px 10px;
  }
  .fcast__lead {
    display:flex; align-items:center; gap:9px;
    font-family:var(--mono); font-size:11px; letter-spacing:0.08em; color:var(--ink-3);
  }
  .fcast__lead .rule { width:22px; }
  .fcast__where { color:var(--mute); letter-spacing:0.04em; }
  .fcast__days { display:flex; gap:20px; flex-wrap:wrap; }
  .fcast__day {
    display:flex; align-items:baseline; gap:7px;
    font-family:var(--mono); font-size:11px; letter-spacing:0.04em; color:var(--ink-3);
  }
  .fcast__day.is-today .fcast__when { color:var(--ink); }
  .fcast__when { min-width:30px; color:var(--mute); text-transform:uppercase;
    letter-spacing:0.1em; font-size:10px; }
  .fcast__temp b { font-weight:500; color:var(--ink); }
  .fcast__temp span { color:var(--mute); margin-left:3px; }
  .fcast__rain { color:var(--mute); }
  /* Rain is the thing a farmer scans for; it earns the ink, nothing else changes. */
  .fcast__rain.is-wet { color:var(--ink); }
  .fcast__note {
    font-family:var(--mono); font-size:9.5px; letter-spacing:0.04em; color:var(--mute);
    flex-basis:100%; line-height:1.5;
  }
}

/* --- parcels over the OS base -------------------------------------------
   Three treatments, so the choice is made by looking rather than arguing.
   All three keep the design's vocabulary: solid green for running, dashed
   grey for waiting on you, heavy ink for selected. What changes is how the
   line survives being drawn over printed cartography. */
.map svg.has-base .parcel__casing {
  fill:none; stroke:#FFFDF8; stroke-width:4px; stroke-linejoin:round;
  vector-effect:non-scaling-stroke; opacity:0.9;
}
/* A — outline only: the base shows through, the casing carries legibility. */
.map svg.treat-a .parcel { fill:none; }
.map svg.treat-a .parcel--selected { fill:var(--field-sel); fill-opacity:0.45; }

/* B — casing plus a wash: state colour still reads as an area, not just a line. */
.map svg.treat-b .parcel { fill-opacity:0.34; }
.map svg.treat-b .parcel--selected { fill-opacity:0.6; }

/* C — no casing, heavier ink. The control: is a casing actually needed? */
.map svg.treat-c .parcel__casing { display:none; }
.map svg.treat-c .parcel { fill:none; stroke-width:2.5px; }
.map svg.treat-c .parcel--waiting { stroke:#2C3B2E; }
.map svg.treat-c .parcel--running { stroke:#285030; }

/* Labels sit on printed detail, so they carry their own ground. */
.parcel__label.has-halo, .parcel__sub.has-halo {
  paint-order:stroke fill; stroke:#FFFDF8; stroke-width:6px; stroke-linejoin:round;
}

/* --- authored language ---------------------------------------------------
   The rule author's sentences, rendered as written. The qualifier is where a
   rule says what its own agreement is worth, so it is never clamped or hidden
   behind a "more" link — it gets its own label and its full length. */
.alert__sowhat { margin:0; font-size:15px; line-height:1.6; color:var(--ink-2); }

/* The trial sentence. It says what the two arms ARE, and it sits above the finding
   rather than beside it, because a reader who does not know there is a trial on this
   field cannot make sense of a sentence about how far apart two plans came out. Only
   an enrolled holding is ever sent one. */
.alert__trial {
  margin:0; font-size:15px; line-height:1.6; color:var(--ink-2);
  border-left:2px solid var(--rule); padding-left:13px;
}
.alert__qualifier {
  display:flex; flex-direction:column; gap:6px;
  border-left:2px solid var(--brass); padding-left:13px;
}
.alert__qualifier p { margin:0; font-size:14.5px; line-height:1.6; color:var(--ink-3); }

/* --- the week ahead, on a phone (Stage 5e) --------------------------------
   One line until it is asked for. A phone opens on alerts, and a seven-day strip
   unrolled at the top of the screen would be a weather feed with a farm attached.
   Bundle tokens throughout; the dashed rule is the same proxied mark every other
   modelled reading carries, and the honesty line is never behind the disclosure. */
.wk { display:block; padding:14px 22px 0; }

.wk__head {
  display:flex; align-items:center; gap:10px; width:100%;
  padding:0 0 11px; background:none; border:0; text-align:left;
  border-bottom:1px dashed var(--rule-2);
  font-family:var(--mono); font-size:11px; line-height:1.4; letter-spacing:0.08em;
  color:var(--ink-3); cursor:pointer;
}
.wk__head .rule { width:22px; }
.wk__label { text-transform:uppercase; letter-spacing:0.12em; font-size:10px; color:var(--ink); }
.wk__glance {
  flex:1; color:var(--mute); letter-spacing:0.04em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.wk__chev { color:var(--mute); font-size:11px; }

.wk__body { padding:13px 0 2px; }
.wk__days { display:flex; flex-direction:column; }
/* Rows, not columns: seven columns at 390 pt can only be had by dropping the type
   below the floor the rest of the screen holds. */
.wk__day {
  display:grid; grid-template-columns:1fr auto auto auto auto; gap:14px; align-items:baseline;
  padding:7px 0; border-bottom:1px solid var(--rule);
  font-family:var(--mono); font-size:12px; letter-spacing:0.03em; color:var(--ink-3);
}
.wk__day:last-child { border-bottom:0; }
.wk__when {
  text-transform:uppercase; letter-spacing:0.1em; font-size:10px; color:var(--mute);
}
.wk__day:first-child .wk__when { color:var(--ink); }
.wk__temp b { font-weight:500; color:var(--ink); }
.wk__temp span { color:var(--mute); margin-left:4px; }
.wk__rain { color:var(--mute); min-width:46px; text-align:right; }
/* Rain is the thing a farmer scans for; it earns the ink, nothing else changes. */
.wk__rain.is-wet { color:var(--ink); }
.wk__wind { color:var(--mute); min-width:52px; text-align:right; }

.wk__note {
  margin-top:11px; font-family:var(--serif); font-size:13px; line-height:1.45;
  color:var(--ink-3);
}
.wk__attr {
  margin-top:6px; font-family:var(--mono); font-size:10px; letter-spacing:0.04em;
  color:var(--mute);
}

/* The desktop strip already carries the week, so the disclosure is a phone element. */
@media (min-width:760px) { .wk { display:none; } }

/* --- when MAIA last looked (Stage 6c) ------------------------------------
   The verdict is the substrate's. Current reads in the measured register because it is a
   fact about a run that happened; anything else takes the assumed dotted rule and the
   brass of a thing that wants attention, because a screen computed from stale state is
   still showing numbers and the farmer cannot tell by looking. */
.looked { display:flex; align-items:center; gap:9px; }
.looked span { color:var(--ink-3); }
.looked .rule { width:22px; }
.looked--off span { color:var(--brass); }

/* --- awaiting a clean rebuild (Stage 6c) --------------------------------- */
.rebuild {
  background:var(--card); border:1px solid var(--rule-2); border-left:3px solid var(--brass);
  padding:16px 18px;
}
.rebuild__reason {
  font-family:var(--serif); font-size:15px; line-height:1.5; color:var(--ink);
}
.rebuild__what {
  margin-top:9px; font-family:var(--serif); font-size:14px; line-height:1.5;
  color:var(--ink-3);
}
.rebuild__rows { margin-top:14px; border-top:1px dotted var(--rule-2); padding-top:11px; }
.rebuild__row {
  display:flex; gap:14px; justify-content:space-between; align-items:baseline;
  padding:4px 0; font-family:var(--mono); font-size:11px; letter-spacing:0.03em;
  color:var(--ink-3);
}
.rebuild__k {
  color:var(--mute); text-transform:uppercase; letter-spacing:0.1em; font-size:10px;
  flex:none; white-space:nowrap;
}
.rebuild__row span:last-child { text-align:right; }

/* --- failure notices (Stage 7) -------------------------------------------
   Three failures, three treatments. A refusal is the substrate speaking and reads in the
   measured register; unreachable and render faults are this side of the boundary and take
   the terracotta the design reserves for something wrong. */
.err { margin:22px; padding:16px 18px; border:1px solid var(--rule-2); background:var(--card); }
.err__what { font-family:var(--serif); font-size:17px; line-height:1.4; color:var(--ink); }
.err__why {
  margin-top:8px; font-family:var(--serif); font-size:14px; line-height:1.5; color:var(--ink-3);
}
.err__detail {
  margin-top:11px; padding-top:9px; border-top:1px dotted var(--rule-2);
  font-family:var(--mono); font-size:11px; letter-spacing:0.03em; color:var(--mute);
  word-break:break-word;
}
.err--refused     { border-left:3px solid var(--ink); }
.err--unreachable { border-left:3px solid var(--terracotta); }
.err--render      { border-left:3px solid var(--terracotta); }

/* A rejected date. Terracotta, because it is the farmer being told his input was not
   taken — not a gap in the substrate, which is a different thing and reads differently. */
.unlock__bad:empty { display:none; }
.unlock__bad {
  margin-top:10px; font-family:var(--serif); font-size:14px; line-height:1.45;
  color:var(--terracotta);
}

/* --- selection over the base (Stage 7c) ----------------------------------
   The alerts map's job is to answer "which field is this alert about" in a glance. Over
   flat tone a fill difference did that on its own; over hedgerows, contours and field
   names it does not, so the binding is carried by weight and by suppressing the rest.
   Unselected parcels drop to a hairline and lose their casing, which stops 45 white
   outlines competing with the one that matters. */
.map svg.has-base .parcel--neutral {
  fill:none; stroke:var(--neutral-line); stroke-width:1px; opacity:0.55;
  vector-effect:non-scaling-stroke;
}
.map svg.has-base .parcel--selected {
  fill:var(--field-sel); fill-opacity:0.5;
  stroke:var(--ink); stroke-width:3.5px; vector-effect:non-scaling-stroke;
}
/* The casing is what lifts the selected boundary off a busy base, so it is kept for the
   selected parcel and dropped for the others. */
.map svg.has-base .parcel--selected + .parcel__casing,
.map svg.has-base.is-selecting .parcel__casing { opacity:0.9; }

/* Why a map has no base. Stated where the base would have been, in the same register as
   every other absence on these screens: the gap named, not left to look like a blank. */
.map__note {
  position:absolute; left:0; right:0; bottom:34px; padding:0 22px;
  font-family:var(--mono); font-size:10px; line-height:1.5; letter-spacing:0.03em;
  color:var(--mute);
}

/* --- looking back (Stage 7b) ---------------------------------------------
   The date in the header is the control. It reads as the fact it already was until you
   look for it, and takes the brass of something the farmer has changed when it is not
   today — the same brass every other "attend to this" carries. */
.asof__tap {
  background:none; border:0; padding:0; cursor:pointer; display:inline-flex;
  align-items:center; gap:6px; font:inherit; color:inherit; letter-spacing:inherit;
  border-bottom:1px dotted currentColor;
}
.asof__tap.is-past { color:var(--brass); border-bottom-style:solid; }
.asof__chev { font-size:0.85em; opacity:0.7; }

.asof {
  background:var(--card); border-bottom:1px solid var(--rule-2);
  padding:16px 22px 18px; display:flex; flex-direction:column; gap:10px;
}
.asof__row { display:flex; gap:10px; flex-wrap:wrap; align-items:center; }
.asof__input {
  min-height:44px; min-width:9.5em; border:1px solid var(--rule-3); border-radius:8px;
  background:var(--screen); padding:0 12px;
  font-family:var(--mono); font-size:15px; letter-spacing:0.08em; color:var(--ink);
}
.asof__bad:empty { display:none; }
.asof__bad { font-family:var(--serif); font-size:14px; color:var(--terracotta); }
.asof__note {
  font-family:var(--serif); font-size:13px; line-height:1.5; color:var(--ink-3); max-width:56ch;
}

/* The band. It cannot be missed, and it names both days. */
.back {
  background:var(--card); border-left:3px solid var(--brass);
  border-bottom:1px solid var(--rule-2);
  padding:15px 22px 17px; display:flex; flex-direction:column; gap:7px;
}
.back__what {
  margin:0; font-family:var(--serif); font-size:18px; line-height:1.35; color:var(--ink);
}
.back__why {
  margin:0; font-family:var(--serif); font-size:14px; line-height:1.5; color:var(--ink-3);
  max-width:64ch;
}
.back__today { align-self:flex-start; margin-top:4px; }

/* A verdict that cannot be given here, and the reason in its place. */
.disp__off {
  font-family:var(--serif); font-size:14px; line-height:1.5; color:var(--mute);
  border-top:1px dotted var(--rule-2); padding-top:12px;
}

/* --- field names (Stage 8f) ----------------------------------------------
   The farmer's name in Spectral, the parcel reference in mono beneath it, always both.
   No provenance rule: a name is the farmer's own fact and needs no vouching. */
.fname { display:flex; flex-direction:column; gap:2px; }
.fname__name {
  font-family:var(--serif); font-size:19px; line-height:1.2; color:var(--ink);
}
.fname__ref {
  font-family:var(--mono); font-size:11px; letter-spacing:0.08em; color:var(--mute);
}
/* Unnamed: the reference alone, exactly as the app read before names existed. */
.fname__ref--alone { font-size:12px; color:var(--ink-3); }
.fname--compact .fname__name { font-size:15px; }
.fname--compact .fname__ref { font-size:10px; }

/* Rename is a word, not an icon. */
.rename {
  background:none; border:0; padding:0; cursor:pointer;
  font-family:var(--mono); font-size:10px; letter-spacing:0.12em; text-transform:uppercase;
  color:var(--mute); border-bottom:1px dotted currentColor;
}
.rename:hover { color:var(--ink); }
.fname__row { display:flex; align-items:baseline; gap:12px; flex-wrap:wrap; }
.fname__edit { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-top:4px; }
.fname__input {
  min-height:40px; min-width:12em; border:1px solid var(--rule-3); border-radius:8px;
  background:var(--card); padding:0 12px;
  font-family:var(--serif); font-size:18px; color:var(--ink);
}

/* --- weather marks (Stage 8d) --------------------------------------------
   Line art only: the same 1.4 stroke as the provenance rules, no fills, no colour, so the
   week-ahead row reads as one family with the marks that grade every other value. */
.wmark { display:block; flex:none; overflow:visible; }
.wmark path, .wmark circle {
  fill:none; stroke:var(--ink-3); stroke-width:1.4; stroke-linecap:round;
  stroke-linejoin:round; vector-effect:non-scaling-stroke;
}
.fcast__day .wmark, .wk__day .wmark { stroke:var(--ink-3); }
/* A day with no mark keeps the column's width, so the row does not jump. */
.wmark--none { width:18px; height:18px; flex:none; }

/* --- map layers (Stage 8b) -----------------------------------------------
   Two controls, one question each: what colours the ground, and do I want to see where the
   alerts are. No layer-stack metaphor, no GIS vocabulary. */
.layers {
  background:var(--card); border:1px solid var(--rule-2); padding:14px 16px 12px;
  display:flex; flex-direction:column; gap:8px; max-width:380px;
}
.layers__head { display:flex; align-items:center; justify-content:space-between; gap:10px; }
.layers__close {
  background:none; border:none; cursor:pointer; padding:4px 6px; margin:-4px -6px -4px 0;
  font-family:var(--mono); font-size:13px; line-height:1; color:var(--mute);
  /* 44 px of target on a phone, without 44 px of ink. */
  min-width:32px; min-height:32px;
}
.layers__close:hover, .layers__close:focus-visible { color:var(--ink); }
.lyr__list { display:flex; flex-direction:column; gap:2px; }
.lyr {
  display:flex; align-items:center; gap:10px; width:100%; text-align:left;
  background:none; border:0; padding:6px 0; cursor:pointer;
  font-family:var(--serif); font-size:15px; color:var(--ink-3);
}
.lyr.is-on { color:var(--ink); }
.lyr__dot {
  flex:none; width:13px; height:13px; border-radius:50%;
  border:1.5px solid var(--rule-3); background:var(--screen);
}
.lyr.is-on .lyr__dot { border-color:var(--ink); box-shadow:inset 0 0 0 3px var(--ink); }
.lyr__dot--box { border-radius:3px; }
.lyr__label { flex:1; }
.lyr__currency {
  font-family:var(--mono); font-size:10px; letter-spacing:0.04em; color:var(--mute);
}
.lyr__note {
  font-family:var(--serif); font-size:12px; line-height:1.45; color:var(--mute);
}

/* The pinned provenance card: undismissable while the layer is on, because the figures on
   it are what make the colours readable honestly. */
.prov-card {
  background:var(--card); border:1px solid var(--rule-2); border-left:3px solid var(--brass);
  padding:14px 16px; display:flex; flex-direction:column; gap:9px; max-width:380px;
}
.prov-card__what {
  margin:0; font-family:var(--serif); font-size:13px; line-height:1.5; color:var(--ink-3);
}
.prov-card__attr {
  font-family:var(--mono); font-size:9px; letter-spacing:0.04em; color:var(--mute);
}
.ramp { display:flex; gap:0; align-items:flex-end; }
.ramp__bin { display:flex; flex-direction:column; gap:3px; align-items:flex-start; flex:1; }
.ramp__swatch { display:block; width:100%; height:13px; background:var(--map); }
.ramp__swatch[data-bin="0"] { background:#E8E2CE; }
.ramp__swatch[data-bin="1"] { background:#D3DCC0; }
.ramp__swatch[data-bin="2"] { background:#B2C9A4; }
.ramp__swatch[data-bin="3"] { background:#8AAF83; }
.ramp__swatch[data-bin="4"] { background:#4E7A52; }
.ramp__swatch--end { width:0; }
.ramp__edge {
  font-family:var(--mono); font-size:9px; letter-spacing:0.02em; color:var(--mute);
}
.ramp__legend {
  display:flex; align-items:center; gap:8px;
  font-family:var(--mono); font-size:10px; letter-spacing:0.04em; color:var(--mute);
}
.ramp__hatch {
  width:22px; height:12px; flex:none; border:1px solid var(--rule-2);
  background:repeating-linear-gradient(45deg, var(--map) 0 3px, var(--mute) 3px 4px);
}
.passpick__row { display:flex; align-items:center; gap:9px; }
.passpick {
  /* min-width:0 so the select may shrink inside a flex row: without it the control keeps
     its intrinsic width, which is the width of its longest option, and the scrollbar the
     bounded panel now carries clipped its chevron off the right-hand edge. */
  flex:1; min-width:0; max-width:100%;
  min-height:34px; border:1px solid var(--rule-3); border-radius:7px;
  background:var(--screen); font-family:var(--mono); font-size:11px; color:var(--ink);
  padding:0 8px;
}
/* Keep the scrollbar's width reserved so the panel's contents do not shift by 15 px the
   moment the card appears and makes it scrollable. */
.map__over { scrollbar-gutter:stable; }

/* The wash itself: a flat fill per parcel, from its own bin. Never interpolated.
   Scoped to svg.has-wash so it out-specifies treatment A's `fill:none`, which is right for
   an outline-only map and wrong for a map whose whole job is the fill. */
.map svg.has-wash .parcel--wash { stroke:var(--ink-3); stroke-width:1px; fill-opacity:0.9; }
.map svg.has-wash .parcel--wash[data-bin="0"] { fill:#E8E2CE; }
.map svg.has-wash .parcel--wash[data-bin="1"] { fill:#D3DCC0; }
.map svg.has-wash .parcel--wash[data-bin="2"] { fill:#B2C9A4; }
.map svg.has-wash .parcel--wash[data-bin="3"] { fill:#8AAF83; }
.map svg.has-wash .parcel--wash[data-bin="4"] { fill:#4E7A52; }
/* No value on this pass: hatched, never interpolated from a neighbour. The hatch is the
   point — a farmer must be able to see at a glance how much of his farm MAIA cannot speak
   for on this pass. */
.map svg.has-wash .parcel--nowash {
  fill:url(#hatchNone); stroke:var(--mute); stroke-width:1px; fill-opacity:0.75;
}
/* Alerts hatch the parcel and composite over any wash. */
.map svg .parcel--alerted { stroke:var(--terracotta); stroke-width:2.5px; }
.map svg.has-wash .parcel--alerted { stroke-width:3px; }

/* Alerts hatch over whatever the ground is coloured by. Never feathered at the edge. */
.map svg .parcel__alerthatch {
  fill:url(#hatchAlert); fill-opacity:0.34; pointer-events:none;
  stroke:var(--terracotta); stroke-width:2px; vector-effect:non-scaling-stroke;
}

/* --- the layer sheet, below 1180 (Stage 8b) ------------------------------
   The overlay panel is a desktop affordance. On a phone the same two controls arrive as a
   sheet over the map, opened by a button that says which layer is on — one component with
   a change of prominence, not a second implementation. */
.map__sheetbtn {
  position:absolute; left:14px; top:14px; z-index:6;
  display:inline-flex; align-items:center; gap:8px;
  background:var(--card); border:1px solid var(--rule-2); border-radius:8px;
  padding:9px 13px; cursor:pointer;
  font-family:var(--mono); font-size:11px; letter-spacing:0.08em; color:var(--ink);
}

/* The compact provenance line. Below 1180 the full card is inside the sheet and the sheet
   covers the map, so this is what stays on screen once it closes: the date, the age and the
   coverage, which are the three figures without which the wash is just colour. At ≥1180 the
   card itself is pinned and uncovered, so this would only repeat it. */
.greenstrip {
  position:absolute; left:14px; top:56px; z-index:6; max-width:calc(100% - 28px);
  display:inline-flex; align-items:baseline; gap:9px; text-align:left;
  background:var(--card); border:1px solid var(--rule-2); border-radius:8px;
  padding:7px 12px; cursor:pointer;
}
.greenstrip__k {
  font-family:var(--mono); font-size:10px; letter-spacing:0.1em; color:var(--brass);
}
.greenstrip__v { font-family:var(--mono); font-size:11px; color:var(--ink); }

.passpick__wrap { display:flex; flex-direction:column; gap:7px; }
.passpick__note { font-size:13px; line-height:1.45; color:var(--mute); margin:0; }
.passpick__note--maj { color:var(--ink); }
.linkish {
  background:none; border:none; padding:0; cursor:pointer;
  font:inherit; color:var(--ink); text-decoration:underline; text-underline-offset:2px;
}

@media (max-width:1179px) {
  .split__map { position:relative; }
  /* A sheet anchored to the viewport, not to the map. It was absolute inside .split__map,
     which is about 440 px tall on a phone: the layer list and the card together are taller
     than that, so the foot of the card — the pass picker, which is the entire fallback for
     a stale anchor — was cut off below the map's edge. Bound it by the viewport instead and
     let it scroll, and the control is reachable at 390 pt. */
  .map__over {
    display:none; position:fixed; left:0; right:0; bottom:0; top:auto; z-index:40;
    max-height:82vh; overflow-y:auto; -webkit-overflow-scrolling:touch;
    flex-direction:column; gap:10px;
    padding:16px 14px calc(18px + env(safe-area-inset-bottom));
    background:var(--screen); border-top:1px solid var(--strip-line);
    border-radius:14px 14px 0 0; box-shadow:0 -10px 34px rgba(31,38,28,0.16);
  }
  .map__over.is-open { display:flex; }
  .map__over > * { flex:none; }
  .layers, .prov-card { max-width:none; }
}

/* --- time: the sparkline and the season strip (Stage 8c) ------------------
   The coverage ribbon uses the same hatch vocabulary as the map's "no value on this pass",
   because it is the same fact in a different projection: MAIA has nothing here, and the
   reason is configuration rather than weather. */
.spark { width:100%; height:auto; display:block; }
.spark__line { fill:none; stroke:var(--green); stroke-width:1.6; stroke-linejoin:round; }
.spark__dot { fill:var(--green); stroke:none; }
/* Hollow, not absent: a partly clouded reading is still a reading, and saying which is the
   only genuine weather fact the series carries. */
.spark__dot--partial { fill:var(--card); stroke:var(--green); stroke-width:1.2; }
.spark__cov--on  { fill:var(--field); }
.spark__cov--off { fill:url(#covOff); opacity:0.55; }
.spark__year { font-family:var(--mono); font-size:9px; fill:var(--mute); }
.spark__guide { stroke:var(--rule-2); stroke-width:0.6; stroke-dasharray:2 3; }
.spark__gy { font-family:var(--mono); font-size:8px; fill:var(--mute); }
.spark__stopline { stroke:var(--terracotta); stroke-width:1; stroke-dasharray:3 3; }
.spark__stopdot { fill:var(--terracotta); }
.spark__stopnote {
  margin:0; font-size:13.5px; line-height:1.5; color:var(--ink);
  border-left:3px solid var(--terracotta); padding-left:11px;
}
.spark__note { margin:0; font-size:12.5px; line-height:1.5; color:var(--mute); }
.spark__legend { display:flex; flex-wrap:wrap; gap:14px; }
.spark__key {
  display:inline-flex; align-items:center; gap:6px;
  font-family:var(--mono); font-size:10px; letter-spacing:0.06em; color:var(--ink-3);
}
.spark__swatch { width:16px; height:9px; display:inline-block; border-radius:2px; }
.spark__swatch--on { background:var(--field); }
.spark__swatch--off {
  background:repeating-linear-gradient(45deg,var(--mute) 0 2px,transparent 2px 5px);
}
.spark__swatch--partial {
  width:9px; border-radius:50%; background:var(--card);
  border:1.4px solid var(--green);
}

.seasons { display:flex; flex-direction:column; gap:7px; }
.season {
  display:grid; grid-template-columns:58px 1fr; gap:4px 10px; align-items:center;
}
.season__yr { font-family:var(--mono); font-size:11px; color:var(--ink-3); }
.season__bar {
  position:relative; min-height:26px; display:flex; align-items:center;
  background:var(--inset); border-radius:5px; padding:0 9px; overflow:hidden;
}
.season__fill {
  position:absolute; left:0; top:0; bottom:0; background:var(--field); border-radius:5px 0 0 5px;
}
/* One bar, one meaning. The fill was also carrying peak NDVI as a colour, which put dark
   ink on a dark green ground and made the figures beside it unreadable — and asked one bar
   to say two things at once. Length is how many readings; the peak is printed in words. */
/* A year nobody looked at is a hatched band across the WHOLE row, never a short bar. A bar
   whose length reads as "how much there was" must not be handed to a year that had no
   looking in it: the farmer would price an unpulled season and a poor one identically. */
.season__bar--off {
  background:repeating-linear-gradient(45deg,var(--rule-2) 0 3px,var(--inset) 3px 7px);
}
.season__bar--none { background:transparent; border:1px dashed var(--rule-2); }
.season__figs {
  position:relative; font-family:var(--mono); font-size:10.5px; color:var(--ink-2);
}
.season__crop { grid-column:2; font-size:12.5px; color:var(--ink-3); }
.season__crop--none { color:var(--mute); font-style:italic; }
.season.is-off .season__yr { color:var(--mute); }
.spark__reconcile {
  margin:0; font-size:13px; line-height:1.5; color:var(--ink-2);
  background:var(--inset); border-radius:7px; padding:10px 12px;
}

.compare { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.compare__pane {
  background:var(--card); border:1px solid var(--rule); border-radius:9px;
  padding:10px 11px 12px; display:flex; flex-direction:column; gap:6px;
}
.compare__map { width:100%; height:auto; aspect-ratio:1/1; display:block; }
/* The same five bins as the map's ramp. Written again rather than widening the map's own
   selectors: those are scoped to `.map svg.has-wash` on purpose, to out-specify treatment
   A's `fill:none`, and a pane has no treatment A to fight. Sharing the selector would drag
   that fight somewhere it does not belong. */
.compare__map .parcel { stroke:var(--ink-3); stroke-width:1px; fill-opacity:0.92; }
.compare__map .parcel--wash[data-bin="0"] { fill:#E8E2CE; }
.compare__map .parcel--wash[data-bin="1"] { fill:#D3DCC0; }
.compare__map .parcel--wash[data-bin="2"] { fill:#B2C9A4; }
.compare__map .parcel--wash[data-bin="3"] { fill:#8AAF83; }
.compare__map .parcel--wash[data-bin="4"] { fill:#4E7A52; }
.compare__map .parcel--nowash { stroke:var(--mute); fill-opacity:0.75; }
.compare__says { margin:0; font-family:var(--mono); font-size:11px; color:var(--ink); }
/* An empty pane is not greyed out and is not blank: it carries the reason at the same
   weight the other pane carries its number. */
.compare__says--none { font-family:var(--sans); font-size:12.5px; line-height:1.45; color:var(--ink-2); }
.compare__cov { margin:0; font-size:11.5px; line-height:1.4; color:var(--mute); }

/* --- kept conversations and citation cards (Stage 8e) --------------------- */
.drawer__kept {
  background:none; border:1px solid var(--rule-2); border-radius:7px; padding:6px 10px;
  font-size:10px; letter-spacing:0.1em; text-transform:uppercase; color:var(--ink-3);
}
.drawer__kept:hover { border-color:var(--mute); color:var(--ink); }
.convos { display:flex; flex-direction:column; gap:6px; padding:0 4px 10px; }
.convo { display:flex; align-items:stretch; gap:8px; }
.convo__open {
  flex:1; min-width:0; text-align:left; background:var(--inset); border:none;
  border-radius:8px; padding:9px 11px; display:flex; flex-direction:column; gap:3px;
}
.convo__open:hover { background:var(--field); }
/* The title is the farmer's own sentence, stored whole. It is clipped to one line here and
   carried in full in the title attribute — shortened on screen, never shortened in the
   store, and never rewritten by anything. */
.convo__title {
  font-family:var(--sans); font-size:13.5px; color:var(--ink); text-transform:none;
  letter-spacing:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.convo__when {
  font-family:var(--mono); font-size:10px; color:var(--mute); letter-spacing:0.06em;
}
.convo__del {
  background:none; border:1px solid var(--rule-2); border-radius:8px; padding:0 10px;
  font-size:10px; letter-spacing:0.08em; text-transform:uppercase; color:var(--ink-3);
}
.convo__del:hover { border-color:var(--terracotta); color:var(--terracotta); }
.convos__none, .convos__where {
  margin:0; font-size:12.5px; line-height:1.5; color:var(--mute);
}

.cites { display:flex; flex-direction:column; gap:9px; margin-top:11px; }
.cite { display:block; text-decoration:none; }
.cite--field {
  display:flex; align-items:center; gap:11px; background:var(--inset);
  border-radius:9px; padding:9px 11px;
}
.cite__map { width:78px; flex:none; }
.cite__map .map, .cite__map svg { width:78px; height:78px; display:block; }
.cite__name { display:flex; flex-direction:column; gap:2px; min-width:0; }
.cite__name small { color:var(--ink-3); font-size:12px; }
/* The alert card renders at its own full size; the drawer does not shrink or restyle it,
   because a smaller alert is a different alert. */
.cite--alert .alert { border-color:var(--rule-2); }

/* --- true colour (Stage 8b2) ---------------------------------------------
   Over a photograph the boundary needs its casing to stay legible, and the parcel fill has
   to get out of the way entirely: the point of this layer is the ground, not the polygon. */
.map svg.has-photo .parcel { fill:none; stroke:#FFFDF8; stroke-width:2.4px; }
.map svg.has-photo .parcel__casing { stroke:rgba(28,51,34,0.55); stroke-width:4.5px; }
.map svg.has-photo .parcel--selected { stroke:#F6F2E9; stroke-width:3.4px; }
.map svg.has-photo .parcel__label, .map svg.has-photo .parcel__sub { fill:#FFFDF8; }
.map svg.has-photo .parcel__label.has-halo, .map svg.has-photo .parcel__sub.has-halo {
  paint-order:stroke; stroke:rgba(21,48,30,0.75); stroke-width:3px;
}
.map svg.has-photo .parcel__alerthatch { stroke:#F0C9BC; }
.prov-card__budget { margin:0; font-size:12px; line-height:1.5; color:var(--mute); }

/* --- the door, under auth (Stage 10) -------------------------------------- */
.entry__ended {
  background:var(--inset); border-left:3px solid var(--brass); border-radius:0 8px 8px 0;
  padding:12px 14px; display:flex; flex-direction:column; gap:5px; text-align:left;
}
.entry__ended-line { font-size:15px; color:var(--ink); }
.entry__ended-note { font-size:13.5px; line-height:1.5; color:var(--ink-3); }

/* --- issuing an invitation from inside the app --------------------------------------------
   The control is small and quiet: an operator needs it a few times a year, and it sits beside
   the holding's name rather than competing with anything a farmer reads every morning. */
.invitebtn {
  flex:none; background:none; border:1px solid var(--rule-2); border-radius:8px;
  padding:7px 12px; cursor:pointer; min-height:32px;
  font-family:var(--mono); font-size:11px; letter-spacing:0.08em; color:var(--mute);
}
.invitebtn:hover, .invitebtn:focus-visible { color:var(--ink); border-color:var(--ink); }

/* The panel carrying the one copy of the code. Fixed rather than in the flow, because it is
   the only thing on screen that matters while it is up, and it must not push the map about. */
.invite {
  position:fixed; z-index:45; left:50%; transform:translateX(-50%);
  top:12px; width:min(560px, calc(100vw - 24px));
  display:flex; flex-direction:column; gap:10px;
  background:var(--card); border:1px solid var(--ink); border-radius:12px;
  padding:16px 18px 18px; box-shadow:0 10px 40px rgba(31,38,28,0.22);
}
.invite__row { display:flex; align-items:center; gap:10px; }
.invite__val {
  flex:1; min-width:0; overflow-x:auto; white-space:nowrap;
  font-family:var(--mono); font-size:12px; color:var(--ink);
  background:var(--screen); border:1px solid var(--rule-2); border-radius:6px; padding:8px 10px;
}
.invite__note { font-size:13px; color:var(--mute); margin:0; }
/* The one field an in-app invitation now asks for. Monospaced input is wrong here: this is a
   person's name, not a code, and the door's code boxes are the ones that letter-space. */
.invite__who { font-family:var(--sans); font-size:16px; letter-spacing:normal; min-height:46px; }
.invite__issue { min-height:46px; flex:none; }
.invite__note--warn { color:var(--ink); }
/* The first line of each panel says which of the two it is. Set as prose rather than as a
   label, because the distinction is a sentence and not a tag. */
.invite__what, .pair__what {
  margin:0; font-size:14px; line-height:1.55; color:var(--ink-2);
}

/* --- signing in on your other device -------------------------------------------------------
   NEXT TO THE INVITATION AND DELIBERATELY NOT LIKE IT. An invitation brings in a person and
   is an operator's to give; this brings in a screen and belongs to whoever is signed in. They
   sit side by side in the header, so the difference has to be legible at a glance and not
   only on reading: this one is the one with the square in it. */
.pairbtn {
  flex:none; background:none; border:1px solid var(--rule-2); border-radius:8px;
  padding:7px 12px; cursor:pointer; min-height:32px;
  font-family:var(--mono); font-size:11px; letter-spacing:0.08em; color:var(--mute);
}
.pairbtn:hover, .pairbtn:focus-visible { color:var(--ink); border-color:var(--ink); }

.pair {
  position:fixed; z-index:45; left:50%; transform:translateX(-50%);
  top:12px; width:min(420px, calc(100vw - 24px));
  display:flex; flex-direction:column; gap:10px; align-items:stretch;
  background:var(--card); border:1px solid var(--ink); border-radius:12px;
  padding:16px 18px 18px; box-shadow:0 10px 40px rgba(31,38,28,0.22);
  max-height:calc(100vh - 24px); overflow-y:auto;
}
/* Big enough to be found by a camera held at arm's length across a desk, and capped so the
   panel still fits a 390 pt screen with its words. */
.pair__qr {
  width:min(232px, 62vw); height:auto; align-self:center;
  border:1px solid var(--rule); border-radius:8px;
}
/* Read aloud across a room, so it is set at the size a person reads aloud from. */
.pair__code {
  align-self:center; font-family:var(--mono); font-size:26px; letter-spacing:0.18em;
  color:var(--ink); background:var(--screen);
  border:1px solid var(--rule-2); border-radius:8px; padding:10px 16px;
}
.pair__note { font-size:13px; color:var(--mute); margin:0; text-align:center; }
.pair__note--warn { color:var(--ink); text-align:left; }

/* --- the operator hub ----------------------------------------------------------------------
   DELIBERATELY NOT THE FARMER'S CHROME. The farmer's views are a column on a phone with a map
   in them and a nav bar at the foot; the hub is a list of facts read on a laptop by somebody
   deciding whether to end somebody's access. Sharing the app bar would have made the two look
   like the same screen with different contents, and the one place that must never be confusing
   is the one where revoking happens. It reuses the type scale, the rules and the tokens, so it
   is the same design and not a second one. */
.hub__bar {
  display:flex; align-items:baseline; gap:18px; flex-wrap:wrap;
  padding:18px 22px 12px; border-bottom:1px solid var(--rule);
}
.hub__title {
  font-family:var(--serif); font-size:19px; letter-spacing:0.01em; color:var(--ink);
}
.hub__tabs { display:flex; gap:16px; }
.hub__tab {
  font-size:14px; text-decoration:none; color:var(--ink-3);
  border-bottom:1px solid transparent; padding-bottom:2px;
}
.hub__tab--on { color:var(--ink); border-bottom-color:var(--ink); }
.hub__trail {
  margin-left:auto; display:flex; align-items:baseline; gap:16px;
  font-size:14px; color:var(--ink-3);
}
.hub__out { flex:none; min-height:0; font-size:13px; padding:4px 10px; }

.hub__body { padding:16px 22px 48px; max-width:920px; }
.hub__lede { margin:0 0 16px; font-size:15px; line-height:1.55; color:var(--ink-2); }
.hub__note { margin:2px 0 12px; font-size:13.5px; line-height:1.5; color:var(--ink-3); }
.hub__unknown { color:var(--brass); }

.hub__rows { display:flex; flex-direction:column; gap:0; }
.hub__row {
  display:block; text-decoration:none; color:var(--ink);
  padding:14px 0; border-top:1px solid var(--rule);
}
.hub__row:last-child { border-bottom:1px solid var(--rule); }
.hub__row--flat { cursor:default; }
.hub__rowhead { display:flex; align-items:baseline; gap:12px; }
.hub__name { font-family:var(--serif); font-size:17px; color:var(--ink); }
.hub__sbi { font-size:13px; color:var(--ink-3); }
.hub__facts { display:flex; gap:26px; flex-wrap:wrap; margin-top:8px; }
.hub__fact { font-size:14px; line-height:1.5; color:var(--ink-2); min-width:150px; }
.hub__fact--wide { flex:1; min-width:240px; }

.hub__listing { margin:26px 0 0; }
.hub__lines { display:flex; flex-direction:column; }
.hub__line {
  display:flex; gap:14px; padding:10px 0; border-top:1px solid var(--rule);
}
.hub__linehead {
  flex:0 0 92px; font-size:11.5px; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--ink-3); padding-top:3px;
}
.hub__linebody { flex:1; font-size:14.5px; line-height:1.5; color:var(--ink); }
.hub__quiet { font-size:13px; line-height:1.5; color:var(--ink-3); }

@media (max-width:560px) {
  .hub__facts { gap:14px; }
  .hub__line { flex-direction:column; gap:2px; }
  .hub__linehead { flex:none; }
}

/* The hub's issuance panel and its grouped listings. */
.hub__acts { display:flex; gap:10px; margin:0 0 6px; }
.hub__act { flex:none; min-height:0; font-size:13px; padding:6px 14px; }
.hub__panel:empty { display:none; }
.hub__issue, .hub__issued {
  margin:10px 0 18px; padding:14px 16px;
  border:1px solid var(--rule-2); border-radius:9px; background:var(--inset);
}
.hub__issue .entry__field { margin:8px 0 0; }
.hub__issue .btn { flex:none; min-height:0; padding:8px 16px; }
.hub__code {
  margin:10px 0 6px; padding:10px 12px; border:1px solid var(--rule-3); border-radius:7px;
  background:var(--card); font-family:var(--mono);
  font-size:13.5px; word-break:break-all; color:var(--ink);
}
/* WHAT IS OVER, SET BACK FROM WHAT IS OPEN. The grouped half is a fact about the past and
   must not read at the same weight as a session somebody could be using this minute. */
.hub__over { margin-top:10px; padding-left:14px; border-left:2px solid var(--rule); }
.hub__over .hub__linehead { color:var(--brass); }
.hub__line--group .hub__linehead { font-size:15px; letter-spacing:0; text-transform:none; }

/* THE ONE GROUP THAT OPENS. `<details>` rather than a click handler, so the open state is the
   browser's, keyboard and screen-reader behaviour come for nothing, and a screen that fails to
   run its JavaScript still shows the names rather than an inert row. The marker is moved to
   the right-hand end because the count is the thing that has to line up down the left with
   every other group's. */
.hub__fold > summary { cursor:pointer; list-style:none; }
.hub__fold > summary::-webkit-details-marker { display:none; }
.hub__fold > summary::after {
  content:'+'; margin-left:auto; padding-left:12px; align-self:center;
  font-family:var(--mono); font-size:15px; color:var(--ink-3);
}
.hub__fold[open] > summary::after { content:'–'; }
.hub__fold > summary:hover .hub__linebody { color:var(--ink); }
.hub__foldrows { margin:0 0 10px 92px; padding-left:14px; border-left:2px solid var(--rule); }
.hub__foldrows .hub__linehead { flex:0 0 96px; font-size:12.5px; text-transform:none;
  letter-spacing:0; }
@media (max-width:560px) {
  .hub__foldrows { margin-left:0; }
}

/* The marker's own words, as written. Monospace because it is a file's contents and not prose
   this interface composed, and pre-wrapped because the unit that wrote it chose the lines. */
.hub__marker {
  margin:6px 0 0; padding:10px 12px; border-left:2px solid var(--rule-3);
  background:var(--card); font-family:var(--mono); font-size:12.5px; line-height:1.55;
  color:var(--ink-2); white-space:pre-wrap; overflow-x:auto;
}
