/* Grainary — every colour, size, radius and shadow below comes from
 * design/tokens.json. Nothing here is a value chosen at build time. */

/* ---------- Tokens: Daylight (light) is the base ---------- */
/* LAURA-fieldwise-024. Every font size in this file was an absolute px, so a
   farmer who had set his phone's text larger got nothing at all - Laura set the
   root to 32px and measured body height identical to the pixel, not one
   character changed. The audience is a commercial farmer in his fifties reading
   a phone in Highveld sun; he is the most likely person in the country to have
   turned his text up.

   Sizes are rem against this root now. Everything else stays px on purpose -
   padding, radii, borders, the 48px tap floor, the 44px thumbnail - and the
   BREAKPOINTS stay px too: a rem breakpoint moves when the root moves and would
   re-trigger the phone layout on a desktop at large text, which is LAURA-047
   arriving by a second route.

   Line heights became unitless ratios in the same pass, so they scale with the
   size instead of pinning it. */
:root {
  font-size: 100%;

  --surface-100: #f8f4ea;
  --surface-200: #efe7d6;
  --surface-raised: #fffdf7;
  --hairline: #ded2ba;
  --hairline-strong: #8d7d58;
  --ink: #231d14;
  --ink-muted: #5c5240;
  --veld: #2c5c38;
  --on-veld: #fbf8f0;
  --veld-deep: #1d4026;
  --on-veld-deep: #f4f0e4;
  --maize: #8a5d0a;
  /* Nina 056: the colour of a corner handle on the map. Deliberately NOT
     --maize: the rule is that maize means money AS TEXT, and a handle is not
     text. It is brighter than --maize because it has to read against satellite
     imagery, where the darker gold disappears into ploughed ground. */
  --map-handle: #e3b24a;
  --maize-fill: #e3b24a;
  --soil: #9d4a24;
  --sky: #1f6382;
  --frost: #3b4f8a;
  --status-good: var(--veld);
  --status-watch: var(--maize);
  --status-urgent: #ad3418;
  --example-ground: #f0e2c2;

  --shadow-card: 0 1px 2px rgba(35, 29, 20, .07), 0 2px 8px rgba(35, 29, 20, .05);
  --shadow-lifted: 0 2px 4px rgba(35, 29, 20, .08), 0 8px 20px rgba(35, 29, 20, .1);

  --font-display: "Zilla Slab", Georgia, "Times New Roman", serif;
  --font-sans: "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;

  --radius-sm: 8px; --radius-md: 12px; --radius-lg: 18px;
  --radius-xl: 24px; --radius-pill: 999px;

  --tap-min: 48px; --screen-max: 430px;
  --icon-sm: 20px; --icon-md: 24px; --icon-lg: 40px; --focus-ring: 3px;

  color-scheme: light;
}

/* Dusk. Only the tokens that change are redefined — guarded so an explicit
 * light choice still wins under a dark system preference. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --surface-100: #15130f;
    --surface-200: #1e1b15;
    --surface-raised: #262219;
    --hairline: #3a3428;
    --hairline-strong: #6b6350;
    --ink: #f4eee1;
    --ink-muted: #a9a08a;
    --veld: #7fc08f;
    --on-veld: #11271a;
    --veld-deep: #17311f;
    --on-veld-deep: #e4efe6;
    --maize: #e8b74e;
    --maize-fill: #e8b74e;
    --soil: #e08d5f;
    --sky: #78bcdd;
    --frost: #9ab0e8;
    --status-urgent: #f08a68;
    --example-ground: #2e2817;
    --shadow-card: 0 1px 2px rgba(0, 0, 0, .55), 0 2px 10px rgba(0, 0, 0, .4);
    --shadow-lifted: 0 2px 6px rgba(0, 0, 0, .6), 0 10px 24px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
}

/* The toggle wins in both directions. */
:root[data-theme="dark"] {
  --surface-100: #15130f;
  --surface-200: #1e1b15;
  --surface-raised: #262219;
  --hairline: #3a3428;
  --hairline-strong: #6b6350;
  --ink: #f4eee1;
  --ink-muted: #a9a08a;
  --veld: #7fc08f;
  --on-veld: #11271a;
  --veld-deep: #17311f;
  --on-veld-deep: #e4efe6;
  --maize: #e8b74e;
  /* Nina 056: the colour of a corner handle on the map. Deliberately NOT
     --maize: the rule is that maize means money AS TEXT, and a handle is not
     text. It is brighter than --maize because it has to read against satellite
     imagery, where the darker gold disappears into ploughed ground. */
  --map-handle: #e3b24a;
  --maize-fill: #e8b74e;
  --soil: #e08d5f;
  --sky: #78bcdd;
  --frost: #9ab0e8;
  --status-urgent: #f08a68;
  --example-ground: #2e2817;
  --shadow-card: 0 1px 2px rgba(0, 0, 0, .55), 0 2px 10px rgba(0, 0, 0, .4);
  --shadow-lifted: 0 2px 6px rgba(0, 0, 0, .6), 0 10px 24px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

/* ---------- Base ---------- */
* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
  background: var(--surface-100);
  color: var(--ink);
  font: 400 0.9375rem/1.4667 var(--font-sans);
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
}

/* ---------- Layout ----------
 * The design system pinned everything to a 430px phone column, which was right
 * when this was a three-screen demo for one field. It now carries a farm, a
 * shed, a season calendar and a map, so on a laptop it has to use the screen.
 *
 * The approach is a full-bleed shell with a gutter that grows, rather than a
 * fixed centred box: the header spans the window, and the content sits in a
 * readable measure that widens in steps. Text never stretches to 1400px,
 * because a 1400px line is unreadable; the cards use the room instead. */
#app {
  width: 100%;
  margin: 0;
  min-height: 100vh;
  background: var(--surface-100);
  overflow-x: hidden;
}
:root { --shell: 1180px; --gutter: var(--space-4); }

/* Solid ring, offset 2px. Never a glow — a glow is invisible outdoors. */
:focus-visible {
  outline: var(--focus-ring) solid var(--veld);
  outline-offset: 2px;
}

button { font: inherit; color: inherit; cursor: pointer; }

/* Almost no motion. A fill change on tap, a height change on the drawer. */
@media (prefers-reduced-motion: no-preference) {
  .btn, .seg button, .toggle { transition: background-color 120ms ease; }
  .drawer { transition: grid-template-rows 180ms ease; }
}

/* ---------- Type styles, named as tokens.json names them ---------- */
.t-hero         { font: 600 1.75rem/1.1429 var(--font-display); }
.t-screen-title { font: 600 1.375rem/1.2273 var(--font-display); }
.t-section      { font: 600 1.0625rem/1.2941 var(--font-display); }
.t-body         { font: 400 0.9375rem/1.4667 var(--font-sans); }
.t-body-strong  { font: 600 0.9375rem/1.4667 var(--font-sans); }
.t-small        { font: 400 0.8125rem/1.4615 var(--font-sans); }
.t-caption      { font: 500 0.75rem/1.3333 var(--font-sans); }
.t-label        { font: 700 0.6875rem/1.2727 var(--font-sans); letter-spacing: .08em; text-transform: uppercase; }
.t-button       { font: 600 0.9375rem/1.3333 var(--font-sans); }
.t-figure-hero  { font: 600 2rem/1.0625 var(--font-mono); }
.t-figure       { font: 500 1.1875rem/1.2632 var(--font-mono); }
.t-figure-sm    { font: 500 0.875rem/1.2857 var(--font-mono); }
.t-unit         { font: 500 0.6875rem/1.2727 var(--font-mono); letter-spacing: .04em; color: var(--ink-muted); }

.muted { color: var(--ink-muted); }

/* ---------- Header band ---------- */
.hdr {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--veld-deep); color: var(--on-veld-deep);
  padding: var(--space-4);
}
.hdr h1 { font: 600 1.375rem/1.2273 var(--font-display); margin: 0; flex: 1; }
.hdr .mark { display: flex; }
.hdr button {
  min-width: var(--tap-min); min-height: var(--tap-min);
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: 0; color: inherit;
  margin: -12px 0; border-radius: var(--radius-sm);
}
.hdr .fresh { font: 600 0.75rem/1.3333 var(--font-sans); padding: 0 var(--space-2); min-width: auto; }

/* ---------- Phase progress ---------- */
.pp { display: flex; padding: var(--space-4) var(--space-4) var(--space-3); }
.ph {
  flex: 1; min-width: var(--tap-min); min-height: var(--tap-min);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  position: relative; background: transparent; border: 0; padding: 0;
}
.ph .disc {
  width: 40px; height: 40px; border-radius: var(--radius-pill);
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-200); border: 1.5px solid var(--hairline-strong);
  color: var(--ink-muted); z-index: 1;
}
.ph.done .disc, .ph.now .disc { background: var(--veld); border-color: var(--veld); color: var(--on-veld); }
.ph .nm { font: 700 0.6875rem/1.2727 var(--font-sans); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-muted); }
.ph.now .nm { color: var(--ink); }
.ph .rail { position: absolute; top: 20px; left: 50%; width: 100%; height: 2px; background: var(--hairline); z-index: 0; }
.ph .rail.fill { background: var(--veld); }
.ph:last-child .rail { display: none; }

/* The invitation a locked phase gives instead of a refusal. */
.phase-note {
  margin: 0 var(--space-4) var(--space-3);
  background: var(--surface-200); color: var(--ink);
  border-radius: var(--radius-sm); padding: 10px var(--space-3);
  font: 400 0.8125rem/1.4615 var(--font-sans);
}

/* ---------- Layout ---------- */
.pad { padding: 0 var(--space-4) var(--space-6); }

/* ---------- Example ribbon ---------- */
.ribbon {
  background: var(--example-ground); color: var(--ink);
  border-radius: var(--radius-sm); padding: 10px 14px;
  font: 500 0.75rem/1.3333 var(--font-sans);
}
.ribbon b { font-weight: 700; display: block; }
.ribbon span { display: block; color: var(--ink-muted); margin-top: 2px; }

/* ---------- Cards ---------- */
.card {
  background: var(--surface-raised); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card); padding: var(--space-4);
  margin-bottom: 14px;
}
.card.lifted { border-radius: var(--radius-xl); box-shadow: var(--shadow-lifted); }
.card h3 { font: 600 1.0625rem/1.2941 var(--font-display); margin: 0 0 4px; }
.card .sub { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }

h2.hero { font: 600 1.75rem/1.1429 var(--font-display); margin: 22px 0 6px; }
.seasonline { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); margin: 0 0 14px; }

/* ---------- Form controls ---------- */
.field { margin-bottom: 14px; }
.inwrap { position: relative; display: block; }
.field:last-child { margin-bottom: 0; }
label.lab, span.lab {
  font: 700 0.6875rem/1.2727 var(--font-sans); letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-muted); display: block; margin-bottom: 6px;
}
.inp {
  width: 100%; min-height: var(--tap-min); padding: var(--space-3) 14px;
  background: var(--surface-200); border: 1.5px solid var(--hairline-strong);
  border-radius: var(--radius-md); font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink);
}
.inp.num { font: 500 1.1875rem/1.2632 var(--font-mono); padding-right: 46px; }
.suffix {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  font: 500 0.6875rem/1.2727 var(--font-mono); letter-spacing: .04em; color: var(--ink-muted);
  pointer-events: none;
}
.hint { font: 500 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); margin-top: 6px; }
.err { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--status-urgent); margin-top: 6px; }

.seg {
  display: flex; background: var(--surface-200);
  border: 1.5px solid var(--hairline-strong); border-radius: var(--radius-md);
  padding: 3px; gap: 3px;
}
/* The design preview sets these to 42px inside a 48px track, which puts the
 * actual tap target under the 48px floor (screens.md acceptance point 3).
 * The button is what a gloved thumb hits, so the button is 48 and the track
 * grows around it. Ledger STEVE-fieldwise-004. */
.seg button {
  flex: 1; min-height: var(--tap-min); border: 0; background: transparent; border-radius: 9px;
  color: var(--ink-muted); font: 400 0.9375rem/1.4667 var(--font-sans);
  display: flex; align-items: center; justify-content: center; gap: 7px;
}
.seg button[aria-pressed="true"] {
  background: var(--surface-raised); box-shadow: var(--shadow-card);
  color: var(--ink); font-weight: 600;
}
.seg button[aria-pressed="true"].wet { color: var(--sky); }

/* ---------- Crop choice ---------- */
.crop {
  background: var(--surface-raised); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card); padding: var(--space-4); margin-bottom: var(--space-3);
}
.crop.best { border-radius: var(--radius-xl); box-shadow: var(--shadow-lifted); }
.crop.sel { outline: 2.5px solid var(--veld); outline-offset: -1px; }
/* The "BEST ESTIMATE" chip does not shrink — its text is nowrap and its basis
   is auto — so in a narrow card it took 146px of a 248px row and squeezed the
   crop name into 36px. "Sunflower" needed 76px and was cut to about one and a
   half letters. Found by the layout guards at 912px and 1280px, which is the
   same class of fault Allison found on the rand figures: a wider window makes
   the two-column board give each card LESS room, not more.

   The name wins. The chip drops onto its own line when there is not room for
   both, because a badge below a legible crop name is still a badge, and a
   clipped crop name is not a crop name. */
.crop .top { display: flex; align-items: flex-start; gap: var(--space-3); flex-wrap: wrap; }
.crop .gl { flex: 0 0 var(--icon-lg); color: var(--ink); display: flex; }
.crop .nmb { flex: 1 1 auto; min-width: 9ch; }
.crop .nmb h3 { font: 600 1.0625rem/1.2941 var(--font-display); margin: 0; }
.crop .nmb p { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); margin: 2px 0 0; }
.chip {
  font: 600 0.6875rem/1.2727 var(--font-sans); letter-spacing: .06em; text-transform: uppercase;
  background: var(--veld); color: var(--on-veld);
  border-radius: var(--radius-sm); padding: 5px var(--space-2); white-space: nowrap;
}
.chip.yours { background: var(--surface-200); color: var(--ink); }

/* The money figure. Positive is maize; a loss is ink plus the word. */
/* NINA: "R 709 794 does not read as a number. It reads as the letter R, then
   709, then 794." IBM Plex Mono gives EVERY space one full character advance -
   measured at 32px, a digit is 19.3px and so are U+0020, U+00A0, U+2009 and
   U+202F alike. So a thousands separator is exactly as wide as a digit, and the
   figure falls into three pieces.

   rand() binds the R to the number now (R709 794, the SA convention). This
   narrows what is left: word-spacing acts on the separator and leaves the
   digits untouched, which letter-spacing would not. Measured at -0.4em: the
   gap inside the number drops from 19.2px to 6.4px against a 19.3px digit,
   which is Nina's acceptance point - the gap inside the figure is now visibly
   smaller than the gap between the figure and the words beside it.

   Applied only where a figure stands ALONE. These rules must never reach a
   sentence containing rand(), such as "R 4 800 to R 8 500 /ha", because there
   the spaces between the words are doing real work. */
.fig, .sheet-total, .acidread b, .farmrow .v, .mapha .fig, .sheet-table td.n {
  word-spacing: -0.4em;
}
/* And put it back on the words INSIDE those boxes. word-spacing inherits, and
   the figure carries a small label beside it - "on 204 ha", "/ha", the unit -
   which came out as "o2 0 4a" when the figure's own setting reached it. Caught
   by looking at the render rather than at the rule. */
.fig em, .sheet-total em, .acidread b em, .mapha .fig em,
.fig span, .sheet-total span, .farmrow .v span,
/* And the restacked summary's column label, which is printed by ::before out
   of data-label. "R / ha" was being pulled toward "R/ha" by the cell's own
   setting - the figure beside it needs the narrow gap, the words naming it do
   not. */
.sheet-table td.n::before {
  word-spacing: normal;
}
.fig { font: 600 2rem/1.0625 var(--font-mono); color: var(--maize); margin-top: var(--space-3); }
.fig.loss { color: var(--ink); }
.fig em { font: 500 0.6875rem/1.2727 var(--font-mono); letter-spacing: .04em; color: var(--ink-muted); font-style: normal; margin-left: var(--space-1); }
.fig .lossword { font: 500 0.6875rem/1.2727 var(--font-sans); letter-spacing: .04em; color: var(--ink-muted); font-style: normal; margin-left: var(--space-2); text-transform: none; }
.rng { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); margin-top: 5px; }
.why { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); margin-top: 3px; }
.tot {
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
  margin-top: var(--space-2); padding-top: 10px; border-top: 1px solid var(--hairline);
}
.tot b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); }
.nobudget {
  background: var(--surface-200); border-radius: var(--radius-md);
  padding: var(--space-3) 14px; margin-top: var(--space-3);
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
}

.toggle {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; min-height: var(--tap-min); margin-top: 6px;
  background: transparent; border: 0; border-top: 1px solid var(--hairline);
  color: var(--veld); font: 600 0.9375rem/1.3333 var(--font-sans); padding: 0;
}
.drawer { display: grid; grid-template-rows: 0fr; }
.drawer[data-open="true"] { grid-template-rows: 1fr; }
.drawer > div { overflow: hidden; }
.work {
  background: var(--surface-200); border-radius: var(--radius-md);
  padding: var(--space-3) 14px; margin-top: 10px;
}
.wr {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--hairline); min-height: 34px;
}
.wr:last-of-type { border-bottom: 0; }
.wr > span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.wr b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); white-space: nowrap; }
.wr.sum b { color: var(--maize); font-weight: 600; }
.wr.sum b.loss { color: var(--ink); }
.wr .defn { display: block; font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); margin-top: 2px; }
.wr input {
  width: 108px; text-align: right; min-height: var(--tap-min);
  background: var(--surface-raised); border: 1.5px solid var(--hairline-strong);
  border-radius: var(--radius-md); padding: 6px 10px;
  font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink);
}
.breakeven {
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--hairline);
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
}
.breakeven b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); }
.own {
  display: block; width: 100%; min-height: var(--tap-min); margin-top: 10px;
  background: transparent; border: 1.5px solid var(--hairline-strong);
  border-radius: var(--radius-md); color: var(--ink); font: 600 0.9375rem/1.3333 var(--font-sans);
}

/* No crop makes money. The one state the design did not have. */
.nomoney {
  background: var(--example-ground); color: var(--ink);
  border-radius: var(--radius-md); padding: 14px;
  margin-bottom: 14px;
}
.nomoney b { display: block; font: 600 0.9375rem/1.4667 var(--font-sans); }
.nomoney span { display: block; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); margin-top: 4px; }

/* ---------- Buttons ---------- */
.btn {
  width: 100%; min-height: 52px; border-radius: var(--radius-md);
  border: 1.5px solid transparent; font: 600 0.9375rem/1.3333 var(--font-sans);
}
.btn.primary { background: var(--veld); color: var(--on-veld); }
.btn.primary:active { background: var(--veld-deep); }
.btn.secondary { background: transparent; color: var(--ink); border-color: var(--hairline-strong); }
.btn.secondary:active { background: var(--surface-200); }
.btn:disabled { background: var(--surface-200); color: var(--ink-muted); border-color: transparent; cursor: default; }
.btn-row { display: flex; gap: 10px; margin-top: var(--space-4); }
.btn-row .btn { flex: 1; min-height: var(--tap-min); }
.cta-wrap { margin-top: var(--space-8); }
.disabled-why { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); margin-top: var(--space-2); text-align: center; }
.foot { font: 500 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); text-align: center; margin: 10px 0 var(--space-6); }

/* ---------- Field strip ---------- */
.strip {
  display: flex; align-items: center; gap: var(--space-3);
  min-height: 52px; width: 100%; padding: var(--space-2) 14px;
  background: var(--surface-raised); border: 0;
  border-radius: var(--radius-md); box-shadow: var(--shadow-card);
  margin-bottom: 14px; text-align: left;
}
.strip .t { flex: 1; min-width: 0; }
.strip .t b { font: 600 0.9375rem/1.3333 var(--font-sans); display: block; }
.strip .t span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.strip .chev { color: var(--ink-muted); display: flex; }

/* ---------- Weeks stepper ---------- */
.step { display: flex; align-items: center; gap: 14px; }
.step button {
  width: 52px; height: 52px; border-radius: var(--radius-md);
  border: 1.5px solid var(--hairline-strong); background: var(--surface-200);
  color: var(--ink); font: 500 1.5rem/1 var(--font-mono);
}
.stepval { flex: 1; text-align: center; }
.stepval b { font: 600 2rem/1.0625 var(--font-mono); display: block; }
.stepval b.today { font: 600 1.375rem/1.2273 var(--font-display); }
.stepval span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.link {
  display: inline-block; margin-top: 10px; min-height: var(--tap-min); line-height: var(--tap-min);
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--veld);
  text-decoration: underline; text-underline-offset: 3px;
  background: none; border: 0; padding: 0;
}

/* ---------- Stage timeline ---------- */
.railrow { display: flex; align-items: flex-start; margin: 2px 0 0; }
.st { flex: 1; display: flex; flex-direction: column; align-items: center; position: relative; gap: 7px; }
.st .dot {
  width: 16px; height: 16px; border-radius: var(--radius-pill);
  background: var(--surface-200); border: 1.5px solid var(--hairline-strong);
  display: flex; align-items: center; justify-content: center; z-index: 1; color: var(--on-veld);
}
.st.done .dot { background: var(--veld); border-color: var(--veld); }
.st.now .dot { width: 24px; height: 24px; background: var(--veld); border-color: var(--veld); margin-top: -4px; }
.st .ln { position: absolute; top: 7px; left: 50%; width: 100%; height: 2px; background: var(--hairline); z-index: 0; }
.st.done .ln { background: var(--veld); }
.st:last-child .ln { display: none; }
.st .sn { font: 400 0.75rem/1.25 var(--font-sans); color: var(--ink-muted); text-align: center;
          overflow-wrap: anywhere; hyphens: auto; }
/* The current stage is named once, beneath the rail, at section size. */
.nowstage { font: 600 1.0625rem/1.2941 var(--font-display); color: var(--ink); margin-top: var(--space-3); }
.dayrow { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--hairline); }
.day { font: 500 1.1875rem/1.2632 var(--font-mono); }
.day em { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); font-style: normal; margin-left: 6px; }
.bar { height: 6px; border-radius: var(--radius-pill); background: var(--surface-200); margin-top: 10px; overflow: hidden; }
.bar i { display: block; height: 100%; background: var(--veld); border-radius: var(--radius-pill); }

/* ---------- Alert rows ---------- */
.row {
  display: flex; gap: var(--space-3); padding: 14px 0;
  border-bottom: 1px solid var(--hairline); min-height: var(--tap-min);
}
.row:last-of-type { border-bottom: 0; }
.row .g { flex: 0 0 var(--icon-md); margin-top: 1px; display: flex; }
.row .b { flex: 1; min-width: 0; }
.row .hd { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.row .hd b { font: 600 0.9375rem/1.4667 var(--font-sans); }
.stt { display: inline-flex; align-items: center; gap: 5px; font: 500 0.75rem/1.3333 var(--font-sans); white-space: nowrap; }
.stt i { width: 8px; height: 8px; border-radius: var(--radius-pill); display: inline-block; }
.wy { font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink-muted); margin-top: 2px; display: block; }
.next {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; min-height: var(--tap-min); margin-top: 6px;
  background: transparent; border: 0; border-top: 1px solid var(--hairline);
  color: var(--veld); font: 600 0.9375rem/1.3333 var(--font-sans); padding: 0;
}
.hind {
  background: var(--surface-200); border-radius: var(--radius-md);
  padding: var(--space-3) 14px; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
}
.hind b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); }

/* ---------- Harvest window ---------- */
/* ALLISON-fieldwise-031. Nina's brief promotes the harvest window to the
   headline of this screen, and it was rendering at 19px against a 32px figure
   in the card below it - so the thing called the headline was the smaller of
   the two. It is the largest number on the screen now, which is what "promoted"
   has to mean to a farmer holding the phone. */
.left { font: 500 1.75rem/1.1429 var(--font-mono); margin-top: 10px; }
.card.lifted .left { font-size: 2rem; }
.left em { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); font-style: normal; margin-left: 6px; }
.track { height: 16px; border-radius: var(--radius-pill); background: var(--surface-200); margin: var(--space-3) 0 6px; position: relative; }
.track .win { position: absolute; top: 0; bottom: 0; background: var(--veld); border-radius: var(--radius-pill); }
.track .today { position: absolute; top: -6px; bottom: -6px; width: 3px; background: var(--soil); border-radius: 2px; box-shadow: 0 0 0 2px var(--surface-raised); }
.ends { position: relative; height: 19px; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.ends span { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; }
.tmark { position: relative; height: 16px; font: 400 0.75rem/1.3333 var(--font-sans); color: var(--soil); margin-top: 2px; }
.tmark span { position: absolute; transform: translateX(-50%); white-space: nowrap; }

.verdict { display: flex; align-items: center; gap: var(--space-3); }
.verdict .g { display: flex; }
.verdict b { font: 600 2rem/1.0625 var(--font-mono); letter-spacing: .02em; }
.verdict span { font: 600 0.9375rem/1.3333 var(--font-sans); }
.cap { font: 500 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); margin-top: var(--space-3); }

/* ---------- Price trend ---------- */
.price { font: 500 1.1875rem/1.2632 var(--font-mono); margin-top: 10px; }
.price em { font: 500 0.6875rem/1.2727 var(--font-mono); letter-spacing: .04em; color: var(--ink-muted); font-style: normal; margin-left: 3px; }
.price span { font: 500 0.875rem/1.2857 var(--font-mono); margin-left: 10px; }
.price span.up { color: var(--status-good); }
.price span.down { color: var(--status-urgent); }
svg.chart { display: block; margin-top: var(--space-2); width: 100%; height: auto; }
svg.chart text.ax { font: 400 0.75rem/1 var(--font-sans); fill: var(--ink-muted); }
svg.chart text.val { font: 600 0.8125rem/1 var(--font-mono); fill: var(--maize); }
.src { font: 500 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); margin-top: 10px; }

/* ---------- Feed switch ---------- */
.feed {
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
  background: var(--surface-200); border: 0; border-radius: var(--radius-md);
  padding: 14px; min-height: 56px; margin-bottom: 14px; text-align: left;
}
.feed .t { flex: 1; min-width: 0; }
.feed .t b { font: 600 0.9375rem/1.3333 var(--font-sans); display: block; }
.feed .t span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.sw { width: 52px; height: 30px; border-radius: var(--radius-pill); background: var(--hairline-strong); position: relative; flex: 0 0 52px; }
.sw i { position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; border-radius: var(--radius-pill); background: var(--surface-raised); transition: left 120ms ease; }
.feed[aria-pressed="true"] .sw { background: var(--veld); }
.feed[aria-pressed="true"] .sw i { left: 25px; }

/* ---------- Loading and error states ---------- */
.skel { background: var(--surface-200); border-radius: var(--radius-sm); display: block; }
.skel.hero { height: 34px; width: 60%; margin-top: var(--space-3); }
.skel.sm { height: 18px; width: 45%; margin-top: 6px; }
.skel.line { height: 19px; width: 80%; margin-top: 6px; }
.errfig { font: 600 1.1875rem/1.2632 var(--font-mono); color: var(--status-urgent); margin-top: var(--space-3); }
.retry {
  min-height: var(--tap-min); margin-top: 10px; padding: 0 var(--space-4);
  background: transparent; border: 1.5px solid var(--hairline-strong);
  border-radius: var(--radius-md); color: var(--ink); font: 600 0.9375rem/1.3333 var(--font-sans);
}



/* ---------- Look-ahead banner (Nina idea 002) ----------
   Deliberately loud, and it always names the real date: the one thing this
   must never do is let a farmer think he is looking at today. */
.lookahead {
  background: var(--soil); color: var(--surface-raised);
  border-radius: var(--radius-md); padding: var(--space-3) 14px;
  margin-bottom: 14px;
}
.lookahead b { display: block; font: 600 0.9375rem/1.4667 var(--font-sans); }
.lookahead span { display: block; font: 400 0.8125rem/1.4615 var(--font-sans); opacity: .92; margin-top: 2px; }
.lookahead button {
  margin-top: 10px; min-height: var(--tap-min); width: 100%;
  background: var(--surface-raised); color: var(--ink);
  border: 0; border-radius: var(--radius-md); font: 600 0.9375rem/1.3333 var(--font-sans);
}

/* ---------- Frost and the calendar, on Plant (Nina idea 005) ---------- */
.frostline {
  display: flex; gap: var(--space-3); align-items: flex-start;
  background: var(--surface-200); border-radius: var(--radius-md);
  padding: var(--space-3) 14px; margin-bottom: 14px;
}
.frostline .g { flex: 0 0 var(--icon-md); display: flex; margin-top: 1px; }
.frostline > span:last-child { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.frostline.late > span:last-child { color: var(--ink); }

/* ---------- Hero swap (Nina idea 008) ----------
   The total row is a control now: tapping it swaps which number is the hero. */
button.tot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  width: 100%; min-height: var(--tap-min); text-align: left;
  background: transparent; border: 0;
  border-top: 1px solid var(--hairline); border-radius: 0;
  margin-top: var(--space-2); padding: 10px 0 0;
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
}
button.tot .totline { flex: 1; min-width: 0; }
button.tot .totline b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); white-space: nowrap; }
button.tot .swap { color: var(--veld); font: 600 0.8125rem/1.4615 var(--font-sans); white-space: nowrap; flex: 0 0 auto; }
@media (max-width: 400px) {
  button.tot { flex-direction: column; align-items: flex-start; gap: var(--space-1); }
}

/* ---------- The season scrubber (Nina, docs/nina-seasonscrub.md) ----------
   The stage rail is the control. A native range input sits transparent over a
   painted track: the browser gives us touch drag, arrow keys for single days
   and a screen-reader label, and we keep the Grainary look. */
.scrubcard .lab { margin-bottom: var(--space-3); }

.scrubwrap { position: relative; height: var(--tap-min); margin: 0 2px; }

.scrubtrack {
  position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%);
  height: 8px; border-radius: var(--radius-pill);
  background: var(--surface-200); overflow: visible;
}
/* The harvest window, so the far end of the rail means something. */
.scrubtrack .harvest {
  position: absolute; top: 0; bottom: 0; right: 0;
  background: var(--hairline); border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}
.scrubtrack .fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: var(--veld); border-radius: var(--radius-pill);
}
.scrubtrack .tick {
  position: absolute; top: -3px; width: 2px; height: 14px;
  background: var(--hairline-strong); border-radius: 1px; transform: translateX(-1px);
}
/* soil is "now" in this design system. It stays put however far you scrub. */
.scrubtrack .now {
  position: absolute; top: -7px; width: 3px; height: 22px;
  background: var(--soil); border-radius: 2px;
  box-shadow: 0 0 0 2px var(--surface-raised); transform: translateX(-1.5px);
}

input.scrub {
  position: absolute; left: 0; top: 0; width: 100%; height: var(--tap-min);
  margin: 0; padding: 0; background: transparent; border: 0;
  -webkit-appearance: none; appearance: none;
}
input.scrub::-webkit-slider-runnable-track { background: transparent; height: var(--tap-min); }
input.scrub::-moz-range-track { background: transparent; height: var(--tap-min); }
input.scrub::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; border-radius: var(--radius-pill);
  background: var(--veld); border: 2.5px solid var(--surface-raised);
  box-shadow: var(--shadow-card); cursor: grab;
}
input.scrub::-moz-range-thumb {
  width: 26px; height: 26px; border-radius: var(--radius-pill);
  background: var(--veld); border: 2.5px solid var(--surface-raised);
  box-shadow: var(--shadow-card); cursor: grab;
}
input.scrub:active::-webkit-slider-thumb { background: var(--veld-deep); cursor: grabbing; }
input.scrub:active::-moz-range-thumb { background: var(--veld-deep); cursor: grabbing; }
input.scrub:focus-visible { outline: var(--focus-ring) solid var(--veld); outline-offset: 2px; border-radius: var(--radius-md); }

.scrubends {
  display: flex; justify-content: space-between; gap: var(--space-2);
  font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); margin-top: 2px;
}

/* The stage names are the jump buttons — the labels that were already there
   gained a job, rather than a second row of chips appearing. */
.stagerow { display: flex; gap: 2px; margin-top: var(--space-3); }
.stagebtn {
  flex: 1; min-width: 0; min-height: var(--tap-min);
  background: transparent; border: 0; border-radius: var(--radius-sm);
  padding: 4px 1px; font: 400 0.6875rem/1.2727 var(--font-sans); color: var(--ink-muted);
  /* Never split a stage name mid-word — "Emergenc/e" reads as a broken app,
     and a farmer reading a wrong-looking stage name stops trusting the rest. */
  overflow-wrap: normal; word-break: keep-all; hyphens: none;
}
.stagebtn.done { color: var(--ink); }
.stagebtn.now { color: var(--ink); font-weight: 600; background: var(--surface-200); }
.stagebtn:active { background: var(--surface-200); }

.readout {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2);
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px solid var(--hairline);
}
.readout b { font: 500 1.1875rem/1.2632 var(--font-mono); }
.readout .d { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.readout .s { font: 600 1.0625rem/1.2941 var(--font-display); color: var(--ink); flex-basis: 100%; }
.scrubcard .hint { margin-top: var(--space-2); }

/* ---------- Multi-field: the switcher and the farm total ---------- */
.fieldbar { padding: 0 var(--space-4) var(--space-3); }
.fieldbar.single { padding-top: 0; }
.fieldbar .chips { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
.fieldbar .chips::-webkit-scrollbar { display: none; }

.fchip {
  display: flex; align-items: center; gap: 6px;
  min-height: var(--tap-min); padding: 6px var(--space-3);
  background: var(--surface-200); border: 1.5px solid transparent;
  border-radius: var(--radius-md); color: var(--ink-muted);
  font: 400 0.8125rem/1.4615 var(--font-sans); white-space: nowrap; flex: 0 0 auto;
}
.fchip.on { background: var(--surface-raised); border-color: var(--veld); color: var(--ink); font-weight: 600; box-shadow: var(--shadow-card); }
.fchip em { font: 500 0.6875rem/1.2727 var(--font-mono); letter-spacing: .04em; color: var(--ink-muted); font-style: normal; }
.fchip.add { min-width: var(--tap-min); justify-content: center; font: 500 1.25rem/1 var(--font-mono); color: var(--veld); }

.addfield {
  min-height: var(--tap-min); width: 100%;
  background: transparent; border: 1.5px dashed var(--hairline-strong);
  border-radius: var(--radius-md); color: var(--veld);
  font: 600 0.9375rem/1.3333 var(--font-sans);
}

.farmtotal .sub { margin-top: 6px; }
.farmrows { margin-top: var(--space-3); border-top: 1px solid var(--hairline); }
.farmrow {
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
  min-height: var(--tap-min); padding: 10px 0; text-align: left;
  background: transparent; border: 0; border-bottom: 1px solid var(--hairline);
}
.farmrow:last-child { border-bottom: 0; }
.farmrow .g { flex: 0 0 20px; display: flex; color: var(--ink); }
.farmrow .t { flex: 1; min-width: 0; }
.farmrow .t b { display: block; font: 600 0.9375rem/1.3333 var(--font-sans); }
.farmrow .t span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.farmrow .v { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--maize); white-space: nowrap; }
.farmrow .v.loss { color: var(--ink); }

/* ---------- The printable summary ---------- */

.overlay { padding: var(--space-4); background: var(--surface-100); min-height: 100vh; }
.overlay-bar { display: flex; gap: 10px; margin-bottom: var(--space-4); }
.overlay-bar .btn { flex: 1; min-height: var(--tap-min); }

.summary-sheet {
  background: var(--surface-raised); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card); padding: var(--space-5);
}
.sheet-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap;
  padding-bottom: var(--space-3); border-bottom: 2px solid var(--ink);
}
.sheet-mark { display: flex; align-items: center; gap: var(--space-2); color: var(--ink); }
.sheet-mark b { font: 600 1.375rem/1.2273 var(--font-display); }
.sheet-when { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }

.sheet-warn {
  background: var(--example-ground); color: var(--ink);
  border-radius: var(--radius-sm); padding: var(--space-3) 14px; margin: var(--space-4) 0;
  font: 500 0.75rem/1.3333 var(--font-sans);
}
.sheet-warn b { display: block; font-weight: 700; }
.sheet-warn span { display: block; color: var(--ink-muted); margin-top: 3px; }

.summary-sheet h2 {
  font: 600 1.0625rem/1.2941 var(--font-display); margin: var(--space-5) 0 var(--space-2);
  padding-bottom: 4px; border-bottom: 1px solid var(--hairline);
}
.sheet-total { font: 600 2rem/1.0625 var(--font-mono); color: var(--maize); }
.sheet-total.loss { color: var(--ink); }
.sheet-total em {
  display: block; font: 400 0.8125rem/1.4615 var(--font-sans);
  color: var(--ink-muted); font-style: normal; margin-top: 4px;
}
.sheet-note { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); margin: var(--space-2) 0 0; }

.sheet-table { width: 100%; border-collapse: collapse; margin-top: var(--space-2); }
.sheet-table th {
  text-align: left; font: 700 0.6875rem/1.2727 var(--font-sans); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-muted);
  padding: 6px 6px 6px 0; border-bottom: 1px solid var(--hairline-strong);
}
.sheet-table td {
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink);
  padding: 7px 6px 7px 0; border-bottom: 1px solid var(--hairline); vertical-align: top;
}
/* LAURA-fieldwise-036, and this one line is the dangerous half of it.
   The base cell is `padding: 7px 6px 7px 0` - already zero on the LEFT - and
   this added `padding-right: 0` on top. So every right-aligned numeric column
   had a zero-pixel gutter on BOTH sides. At desk width the columns are wide
   enough that right-aligned text never reaches the boundary, which is why
   nobody had ever seen it. On a phone the table shrink-wraps to its content,
   every numeric column collapses to exactly its text width, and the numbers
   TOUCH: Laura measured the `68` cell ending at x=239 and the `5.8 t` cell
   beginning at x=239, which a farmer reads as `685.8 tR`.
   It is a padding fault, not a clipping one, and it would still be here if the
   table fitted. */
.sheet-table th.n, .sheet-table td.n {
  text-align: right; padding-left: var(--space-3); padding-right: 0;
  font-family: var(--font-mono);
}
.sheet-table th.n { font-family: var(--font-sans); }
.sheet-table.small td, .sheet-table.small th { font-size: 0.75rem; line-height: 16px; }
.sheet-range td {
  font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted);
  padding-top: 0; border-bottom: 1px solid var(--hairline);
}
.sheet-foot {
  font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted);
  margin-top: var(--space-5); padding-top: var(--space-3); border-top: 1px solid var(--hairline);
}

/* LAURA-fieldwise-036. This used to be `display: block; overflow-x: auto;
   white-space: nowrap` - the table scrolled sideways inside its own box. Laura
   measured what that meant at 320: 370px of table in 256px, with the two rand
   columns 114px off the edge, and no scrollbar drawn on a touch device until a
   drag begins. So the farmer had no way to know the money was there.

   Her ruling, and it is right: this is the page he hands to a bank manager, and
   a column you have to discover by swiping is a column the bank manager will
   not see. It restacks instead - each row becomes its own block, every cell
   carries its column heading from data-label, and the two rand figures come
   last and largest.

   The @media print block below is deliberately untouched: it sets display:table
   and white-space:normal, and at print width the table fits in 860px. */
@media (max-width: 460px) {
  .summary-sheet { padding: var(--space-4); }
  .sheet-table, .sheet-table tbody, .sheet-table tr, .sheet-table td { display: block; }
  .sheet-table thead { position: absolute; width: 1px; height: 1px;
                       overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .sheet-table tr {
    padding: var(--space-3) 0; border-bottom: 1px solid var(--hairline);
  }
  .sheet-table td { border: 0; padding: 2px 0; text-align: left; }
  /* The field name, or the figure's name, is the heading of its own block. */
  .sheet-table td.rowhead {
    font: 600 0.9375rem/1.3333 var(--font-sans); padding-bottom: var(--space-2);
  }
  /* Label on the left, value on the right, one pair per line. */
  .sheet-table td[data-label] {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: var(--space-3);
  }
  .sheet-table td[data-label]::before {
    content: attr(data-label);
    font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
    flex: 0 0 auto;
  }
  .sheet-table td.money { font-size: 0.9375rem; font-weight: 500; }
  .sheet-range td { white-space: normal; padding-top: var(--space-2);
                    font-style: normal; }
  .sheet-table tr.sheet-range { border-bottom: 0; padding-top: 0; }
}

/* ---------- Print: only the sheet, on paper ---------- */
@media print {
  .overlay-bar { display: none !important; }
  .hdr, .pp, .fieldbar, .phase-note { display: none !important; }
  body, #app, .overlay { background: #fff !important; }
  #app { max-width: none; box-shadow: none; }
  .overlay { padding: 0; min-height: 0; }
  .summary-sheet {
    box-shadow: none; border-radius: 0; padding: 0;
    background: #fff; color: #000;
  }
  .summary-sheet .sheet-total { color: #000; }
  .sheet-warn { background: #f2f2f2; border: 1px solid #000; }
  .sheet-table { display: table; white-space: normal; }
  .sheet-table td, .sheet-table th { border-color: #999; }
  tr, .sheet-table { page-break-inside: avoid; }
  @page { margin: 16mm; }
}

/* ---------- Rotation: the irrigated double crop ---------- */
.rotation .sub { margin-bottom: var(--space-3); }
.rotrow {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0; border-bottom: 1px solid var(--hairline);
}
.rotrow:last-of-type { border-bottom: 0; }
.rotrow .g { flex: 0 0 var(--icon-md); display: flex; color: var(--ink); }
.rotrow .t { flex: 1; min-width: 0; }
.rotrow .t b { display: block; font: 600 0.9375rem/1.3333 var(--font-sans); }
.rotrow .t span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.rotrow .v { font: 500 1.1875rem/1.2632 var(--font-mono); color: var(--maize); white-space: nowrap; text-align: right; }
.rotrow .v.loss { color: var(--ink); }
.rotrow .v.muted { color: var(--ink); }
.rotrow .v em { display: block; font: 500 0.6875rem/1.2727 var(--font-mono); letter-spacing: .04em; color: var(--ink-muted); font-style: normal; }
.rotgap {
  background: var(--example-ground); color: var(--ink);
  border-radius: var(--radius-sm); padding: var(--space-3) 14px; margin-top: var(--space-3);
  font: 500 0.75rem/1.3333 var(--font-sans);
}
.rotgap b { display: block; font-weight: 700; }
.rotgap span { display: block; color: var(--ink-muted); margin-top: 3px; }

/* ---------- Soil: acid saturation, not a pH rainbow ---------- */
.soilprofile { margin-top: var(--space-3); border-top: 1px solid var(--hairline); }
.sp { display: flex; justify-content: space-between; gap: var(--space-3); padding: 8px 0; border-bottom: 1px solid var(--hairline); }
.sp:last-child { border-bottom: 0; }
.sp span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.sp b { font: 500 0.8125rem/1.4615 var(--font-sans); color: var(--ink); text-align: right; }

.soilwhy, .soilgap {
  background: var(--surface-200); color: var(--ink);
  border-radius: var(--radius-md); padding: var(--space-3) 14px; margin-top: var(--space-3);
  font: 500 0.75rem/1.3333 var(--font-sans);
}
.soilgap { background: var(--example-ground); }
.soilwhy b, .soilgap b { display: block; font-weight: 700; }
.soilwhy span, .soilgap span { display: block; color: var(--ink-muted); margin-top: 3px; }

.acidwrap { margin-top: var(--space-2); }
.acidtrack {
  position: relative; height: 14px; border-radius: var(--radius-pill);
  background: var(--surface-200);
}
.acidtrack .target {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: var(--veld); border-radius: var(--radius-pill);
}
.acidtrack .tick { position: absolute; top: -4px; width: 2px; height: 22px; border-radius: 1px; transform: translateX(-1px); }
.acidtrack .tick.crit { background: var(--maize); }
.acidtrack .tick.imp { background: var(--status-urgent); }
.acidtrack .here {
  position: absolute; top: -7px; width: 3px; height: 28px;
  background: var(--soil); border-radius: 2px;
  box-shadow: 0 0 0 2px var(--surface-raised); transform: translateX(-1.5px);
}
.acidends { display: flex; justify-content: space-between; font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); margin-top: var(--space-2); }
.acidread { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-3); }
.acidread b { font: 600 2rem/1.0625 var(--font-mono); }
.acidread b em { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink-muted); font-style: normal; margin-left: 2px; }
.acidread .stt { display: inline-flex; align-items: center; gap: 6px; font: 600 0.8125rem/1.4615 var(--font-sans); }

.soilrow { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--hairline); }
.soilrow span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.soilrow b { font: 500 1.1875rem/1.2632 var(--font-mono); }
.soilrow em { font: 400 0.75rem/1.3333 var(--font-sans); font-style: normal; color: var(--ink-muted); }
.soilrow em.warn { color: var(--status-urgent); flex: 1 0 100%; }

/* The small block that answers the Satellite chip. Sits directly under it, at
   the same inset, so it reads as that control's answer and not as a page-level
   message. Capped in width so it never runs the width of a laptop screen. */
.satsoon {
  position: absolute; top: calc(var(--space-4) + 58px);
  inset-inline-end: var(--space-4); z-index: 3;
  max-width: min(280px, calc(100vw - var(--space-4) * 2));
  display: flex; flex-direction: column; gap: 4px;
  padding: var(--space-3) 14px; border-radius: 14px;
  background: var(--surface-200); border: 1.5px solid var(--hairline-strong);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.32);
}
.satsoon b { font: 600 0.875rem/1.3571 var(--font-sans); }
.satsoon span { font: 400 0.8125rem/1.3846 var(--font-sans); color: var(--ink-muted); }

/* The chip itself, while the feature is announced rather than available. */
.mapchip.layers.soon { opacity: 1; }
.mapchip.layers.soon em {
  font: 600 0.625rem/1.3 var(--font-sans); font-style: normal;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-muted); margin-inline-start: 4px;
}

/* ---------- Location row and rainfall ---------- */
.placebtn {
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
  min-height: var(--tap-min); padding: var(--space-2) 14px; text-align: left;
  background: var(--surface-200); border: 1.5px solid var(--hairline-strong);
  border-radius: var(--radius-md); color: var(--ink);
}
.placebtn .g { flex: 0 0 20px; display: flex; }
.placebtn .t { flex: 1; min-width: 0; }
.placebtn .t b { display: block; font: 600 0.9375rem/1.3333 var(--font-sans); }
.placebtn .t span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.placebtn .chev { color: var(--ink-muted); display: flex; }

.rainwrap { margin-top: var(--space-3); }
.raintrack { position: relative; height: 14px; border-radius: var(--radius-pill); background: var(--surface-200); }
.raintrack .band { position: absolute; top: 0; bottom: 0; background: var(--sky); border-radius: var(--radius-pill); }
.raintrack .med { position: absolute; top: -5px; width: 3px; height: 24px; background: var(--ink); border-radius: 2px; box-shadow: 0 0 0 2px var(--surface-raised); transform: translateX(-1.5px); }
.rainends { display: flex; justify-content: space-between; gap: var(--space-2); font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); margin-top: var(--space-2); }
.rainends .mid { color: var(--ink); font-weight: 600; }
.rainshift {
  background: var(--surface-200); border-radius: var(--radius-md);
  padding: var(--space-3) 14px; margin-top: var(--space-3);
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
}
.rainshift b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); }

.seasonrow { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); padding: var(--space-3) 0; border-bottom: 1px solid var(--hairline); }
.seasonrow span { font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink-muted); }
.seasonrow b { font: 500 1.1875rem/1.2632 var(--font-mono); color: var(--ink); }

.fcrows { margin-top: var(--space-2); }
.fcrow { display: flex; align-items: center; gap: var(--space-3); padding: 9px 0; border-bottom: 1px solid var(--hairline); min-height: 40px; }
.fcrow:last-child { border-bottom: 0; }
.fcrow .d { flex: 1; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink); }
.fcrow .mm { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink-muted); min-width: 64px; text-align: right; }
.fcrow .mm.wet { color: var(--sky); font-weight: 600; }
.fcrow .tp { font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); min-width: 74px; text-align: right; }

/* ---------- Equipment: capacity against the window ---------- */
.capline { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); padding: 9px 0; border-bottom: 1px solid var(--hairline); }
.capline span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.capline b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); text-align: right; }
.capwet {
  background: var(--surface-200); border-radius: var(--radius-md);
  padding: var(--space-3) 14px; margin-top: var(--space-3);
  font: 500 0.75rem/1.3333 var(--font-sans);
}
.capwet b { display: block; font-weight: 700; }
.capwet span { display: block; color: var(--ink-muted); margin-top: 3px; }

/* ---------- Two levels: the way up, and the season calendar ---------- */
/* The one back control in the app. It carries a word as well as an arrow,
   because an arrow on its own says only that something is behind you. The word
   is dropped below 360px, where the field name needs the room more. */
.hdr .up { min-width: var(--tap-min); min-height: var(--tap-min); margin-left: -8px;
           gap: 2px; padding-inline-end: var(--space-2); }
.hdr .up .upsay { font: 500 0.875rem/1.2857 var(--font-sans); white-space: nowrap; }
@media (max-width: 359px) { .hdr .up .upsay { display: none; } }

.cal { margin-top: var(--space-3); }
.calmonths { display: flex; padding-left: 64px; padding-right: 26px; }
.calmonths span { flex: 1; font: 400 0.625rem/1.4 var(--font-sans); color: var(--ink-muted); text-align: left; }
.calbody { position: relative; }
.caltoday {
  position: absolute; top: 0; bottom: 0; width: 2px; background: var(--soil);
  z-index: 2; margin-left: 64px; transform: translateX(-1px);
  /* the track sits between the 64px label and the 26px glyph */
  left: 0;
}
.calrow { display: flex; align-items: center; gap: var(--space-2); padding: 5px 0; }
/* LAURA-fieldwise-024, found while converting to rem. This column was pinned
   at 56px with nowrap, so the moment text scaled the field NAMES stopped
   fitting - overflow began at 113 per cent, before a farmer had turned his text
   up very far at all, and the name of his own field was the first thing cut.

   Sized in ch now, which is a unit of the font it holds, so the column grows
   with the words in it. 7ch is 56px at the 12px this label renders at. The
   ellipsis stays for a genuinely long name at any size. */
.calrow .n { flex: 0 0 7.5ch; font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calrow .track { flex: 1; position: relative; height: 16px; background: var(--surface-200); border-radius: var(--radius-sm); }
.calrow .track i { position: absolute; top: 0; bottom: 0; border-radius: 3px; }
.calrow .track .win { background: var(--hairline); }
.calrow .track .grow { background: var(--veld); }
.calrow .track .grow.proj { background: repeating-linear-gradient(90deg, var(--veld) 0 6px, transparent 6px 9px); }
.calrow .track .harv { background: repeating-linear-gradient(45deg, var(--maize) 0 3px, transparent 3px 6px); }
.calrow .c { flex: 0 0 18px; display: flex; color: var(--ink-muted); }

/* ---------- Cross-device ----------
   Phone: one column at 430px, as the design requires.
   Tablet and desktop: the FIELD screens stay 430 (they are a phone form
   factor and widening them would stretch a form across a desk). The FARM
   screen is a planning surface, so it uses the room it is given. */


@media (min-width: 720px) {
  .calrow .n { flex: 0 0 11.5ch; font-size: 0.8125rem; }
  .calmonths { padding-left: 98px; }
  .caltoday { margin-left: 98px; }
}

/* An on-screen keyboard eats the bottom of the viewport. Give every input
   room to be scrolled clear of it, and leave space under the last card. */
input, .inp, .scrub { scroll-margin-block: 96px; }
.pad { padding-bottom: var(--space-10); }



/* Coarse pointers (touch) get a little more room between tappable rows. */
@media (pointer: coarse) {
  .farmrow, .fcrow, .row { padding-top: 12px; padding-bottom: 12px; }
}

/* ---------- Keyboard room ----------
   While the on-screen keyboard is open, the app grows by the keyboard's own
   height so a field at the very bottom of the page has somewhere to scroll
   to. Without this, the last input on a screen can never be lifted clear. */
:root { --kb: 0px; }
#app { padding-bottom: var(--kb); transition: padding-bottom 120ms ease; }
html.kb-open { scroll-padding-bottom: var(--kb); }


/* ---------- The three-point range ----------
   Each end is named, so a negative in the "bad year" column reads as a bad
   year rather than as the expectation. The likely figure is the only one at
   hero size, which is what the eye should land on. */
.range3 {
  display: flex; gap: 2px; margin-top: var(--space-3);
  background: var(--hairline); border-radius: var(--radius-md); overflow: hidden;
}
.r3 {
  flex: 1; min-width: 0; padding: 10px var(--space-2);
  background: var(--surface-raised); text-align: center;
  /* Each cell is its own container, so the figure inside it can be sized from
     the width it actually got rather than from the window. The window was
     never the problem: at 1366px the board goes two-up, so a WIDER screen
     makes this cell NARROWER while the type gets bigger. */
  container-type: inline-size;
}
.r3 .l {
  display: block; font: 700 0.625rem/1.4 var(--font-sans);
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-muted);
}
/* ALLISON-grainary: the headline rand figure was being cut off and the part
   left behind read as a plausible number. R 225 666 showed as "R 225". Nine of
   nine figures clipped at laptop width, with no ellipsis and nothing else to
   say anything had been removed — and the small print under the same card gave
   the correct per-hectare figure, so the card contradicted itself.

   A farmer decides off this number. Three things now stand between it and that
   failure, in order:
     1. the size is derived from the cell's own width (100cqi), not the window;
     2. fitFigures() in app.js measures the rendered box after every paint and
        steps the size down until it genuinely fits, whatever the string;
     3. if something still does not fit, it ELLIPSES. A visibly truncated
        number is a farmer asking a question. A silently truncated one is a
        farmer making a decision on it.
   The divisor is the monospace advance width: IBM Plex Mono is 0.6em per
   character, so about 9 characters of "R 225 666" need 5.4em. */
.r3 b {
  display: block; font: 500 0.9375rem/1.3333 var(--font-mono); color: var(--ink);
  margin-top: 3px; white-space: nowrap;
  font-size: min(15px, calc((100cqi - 16px) / 5.4));
  overflow: hidden; text-overflow: ellipsis;
}
.r3 .u { display: block; font: 500 0.625rem/1.3 var(--font-mono); letter-spacing: .03em; color: var(--ink-muted); }

/* The expected case is the one that matters, so it is the only one at size
   and the only one in the money colour. */
.r3.likely { background: var(--surface-200); }
.r3.likely .l { color: var(--ink); }
.r3.likely b {
  font: 600 1.5rem/1.1667 var(--font-mono); color: var(--maize); margin-top: 2px;
  font-size: min(24px, calc((100cqi - 16px) / 5.4));
}
/* A loss is ink, never the money colour, and never status-urgent — the column
   label is already doing the work of saying what it is. */
.r3.neg b { color: var(--ink); }

@media (max-width: 380px) {
  .r3 b { font-size: 0.8125rem; }
  .r3.likely b { font-size: 1.25rem; line-height: 24px; }
  .r3 .l { font-size: 0.5625rem; letter-spacing: .05em; }
}

/* ---------- The app icon, top right ---------- */
/* LAURA-fieldwise-042, and it is a bug rather than a trade-off: app.js's own
   comment beside this markup says "Below 1024 the farmer is a disc in the
   header, taking the decorative app icon's slot. At 1024 and up the rail
   carries him instead and the icon returns - CSS decides which."

   CSS decided nothing. There was no rule hiding it, so the icon rendered on
   every phone alongside the farmer disc, and the header carried FIVE children
   on one row. Laura measured it at 360: the four non-title items take 223px
   plus gaps, leaving the h1 - the name of the field the whole screen is about -
   just 79px, 22 per cent of the header. All three demo field names wrap to two
   lines; an Afrikaans farm name takes three.

   The rule the comment describes, written down. */
.hdr .appicon { display: none; }
@media (min-width: 1024px) {
  .hdr .appicon {
    width: 30px; height: 30px; border-radius: 7px; flex: 0 0 30px;
    margin-left: 2px; display: block;
  }
}
@media (max-width: 360px) { .hdr .fresh { padding: 0 4px; font-size: 0.6875rem; } }

/* ---------- The shed, pairings and the sequence ---------- */
.kitrow { display: flex; align-items: center; gap: var(--space-3); padding: 10px 0; border-bottom: 1px solid var(--hairline); }
.kitrow:last-child { border-bottom: 0; }
.kitrow .t { flex: 1; min-width: 0; }
.kitrow .t b { display: block; font: 600 0.9375rem/1.3333 var(--font-sans); }
.kitrow .t span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.kitrow .rm {
  min-width: var(--tap-min); min-height: var(--tap-min);
  background: transparent; border: 0; color: var(--ink-muted); font: 400 1.375rem/1 var(--font-sans);
  border-radius: var(--radius-sm);
}
.addrow { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.addrow .inp { flex: 1 1 86px; min-width: 0; }
.addrow select.inp { flex: 2 1 150px; }
.addrow .own { flex: 1 1 100%; margin-top: 0; }

.pair { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--hairline); }
.pairhead { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.pairhead b { font: 600 0.9375rem/1.3333 var(--font-sans); }
.pairhead span { font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); }
.pairrow {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: 9px var(--space-2); margin-top: 6px;
  background: var(--surface-200); border-radius: var(--radius-sm);
  font: 400 0.8125rem/1.4615 var(--font-sans);
}
.pairrow.best { background: var(--surface-raised); box-shadow: inset 0 0 0 1.5px var(--veld); }
.pairrow.no { opacity: .72; }
.pairrow .n { flex: 1 1 86px; font-weight: 600; }
.pairrow .l, .pairrow .f, .pairrow .c { font: 500 0.8125rem/1.3846 var(--font-mono); color: var(--ink-muted); }
.pairrow .c { color: var(--ink); }
.pairrow.best .l, .pairrow.best .f, .pairrow.best .c { color: var(--ink); }
.pairrow .x { flex: 1 1 auto; color: var(--ink-muted); font-size: 0.75rem; }
.pairrow .tick { display: flex; }

.seqrow { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; padding: 10px 0; border-bottom: 1px solid var(--hairline); }
.seqrow:last-of-type { border-bottom: 0; }
.seqrow .n { flex: 1 1 110px; font: 400 0.875rem/1.3571 var(--font-sans); }
.seqrow .r, .seqrow .h, .seqrow .d { font: 500 0.8125rem/1.3846 var(--font-mono); color: var(--ink-muted); }
.seqrow .d { color: var(--ink); }
.seqrow .pre { flex: 1 1 100%; font: 400 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); }

/* ---------- The map ---------- */
.mapbox {
  height: 320px; margin-top: var(--space-3);
  border-radius: var(--radius-md); overflow: hidden;
  background: var(--surface-200); border: 1.5px solid var(--hairline-strong);
}
.mapha {
  margin-top: var(--space-3); font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink-muted);
  min-height: 24px;
}
.mapha b { font: 600 1.5rem/1.1667 var(--font-mono); color: var(--ink); }
.leaflet-container { font: inherit; }

/* ================================================================== *
 * RESPONSIVE SHELL — phone, tablet, laptop
 *
 * One rule does the centring: a horizontal pad that is the normal gutter on a
 * phone and grows to absorb the surplus on a wide screen. The header stays
 * full-bleed and its contents line up with the body.
 * ================================================================== */
.hdr, .pp, .fieldbar, .pad, .phase-note, .overlay {
  padding-inline: max(var(--gutter), calc((100% - var(--shell)) / 2));
}
.phase-note { margin-inline: 0; }
.fieldbar { padding-bottom: var(--space-3); }

/* ---------- Tablet ---------- */
@media (min-width: 680px) {
  :root { --gutter: var(--space-6); }
  h2.hero { font-size: 2rem; line-height: 38px; }
  .mapbox { height: 420px; }
}

/* ---------- Laptop and up ----------
 * The cards become a two-column board. Anything that reads as a heading, a
 * banner or a full-width action spans both, so the page still has a spine. */
@media (min-width: 1000px) {
  .hdr h1 { font-size: 1.625rem; line-height: 32px; }
  .pp { padding-top: var(--space-5); }
  .mapbox { height: 480px; }
  /* These two used to be flat 17px and 28px. On the two-column board that is
     the moment the cell is at its narrowest, so the type grew exactly where
     there was least room for it. */
  .range3 .r3 b { font-size: min(17px, calc((100cqi - 16px) / 5.4)); }
  .range3 .r3.likely b {
    font-size: min(28px, calc((100cqi - 16px) / 5.4)); line-height: 32px;
  }
  .pad { padding-bottom: var(--space-10); }
}

/* ---------- The two columns ----------
 * A form column and a results column, as explicit wrappers. Placing cards into
 * grid columns as siblings does not work: auto-placement advances a row for
 * each item, so the form column ends up with one card and a long empty gap
 * beside the results. Two containers, each stacking its own children. */
.cols { display: block; }
@media (min-width: 1000px) {
  .cols {
    display: grid;
    grid-template-columns: minmax(300px, 360px) minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
  }
  /* The setup column follows you down a long results column. */
  .side { position: sticky; top: var(--space-4); }
  .side > .card:last-child { margin-bottom: 0; }
  .main > :last-child { margin-bottom: 0; }
}
@media (min-width: 1400px) {
  .cols { grid-template-columns: minmax(320px, 380px) minmax(0, 1fr); }
}

/* ---------- Large laptop and desktop ---------- */
@media (min-width: 1400px) {
  :root { --shell: 1320px; }
  .cropgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* A mouse gets a hover state; a thumb does not need one. */
@media (hover: hover) and (pointer: fine) {
  .farmrow:hover, .fchip:hover, .stagebtn:hover,
  .strip:hover, .feed:hover, .placebtn:hover { background: var(--surface-200); }
  .crop[role="button"]:hover { box-shadow: var(--shadow-lifted); }
  .btn.primary:hover { background: var(--veld-deep); }
  .own:hover, .retry:hover { background: var(--surface-200); }
}

/* The printable summary is a document, so it keeps a reading measure of its
   own rather than spreading across a 1320px board. */
#app.wide .overlay { padding-inline: max(var(--gutter), calc((100% - 900px) / 2)); }


/* ---------- Crop cards as a board ----------
   One per row on a phone, two on a tablet, three on a laptop. They are peers
   being compared, so they should be read side by side where there is room. */
.cropgrid { display: grid; gap: var(--space-3); grid-template-columns: 1fr; align-items: start; }
.cropgrid .crop { margin-bottom: 0; }
@media (min-width: 680px) {
  .cropgrid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
}

/* ---------- Desktop polish ----------
   A button stretched across 1320px reads as a mistake. Actions keep a
   sensible measure and sit where the eye expects them. */
@media (min-width: 1000px) {
  .card > .own, .cta-wrap .btn, .card > .addfield, .card > .retry { max-width: 420px; }
  .cta-wrap { display: flex; justify-content: flex-start; }
  .cta-wrap .btn { flex: 0 1 420px; }
  .disabled-why { text-align: left; }
  .foot { text-align: left; max-width: 70ch; }
  /* Long prose keeps a readable line length even in a wide card. */
  .card > .sub, .card > .why, .card > .cap, .card > .hint { max-width: 78ch; }
  .sheet-warn span, .soilwhy span, .capwet span, .rotgap span { max-width: 90ch; }
}

/* ------------------------------------------------------------------ *
 * What leaves this phone — the privacy disclosure (CARL-fieldwise-009).
 * A <details> so it sits on every screen the app opens on without pushing
 * the farmer's own numbers below the fold.
 * ------------------------------------------------------------------ */
.privacy {
  margin: var(--space-5) 0 0;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}
.privacy > summary {
  cursor: pointer;
  padding: var(--space-3) var(--space-4);
  font: 600 0.875rem/1.4286 var(--font-sans);
  color: var(--ink);
  min-height: var(--tap-min);
  display: flex;
  align-items: center;
}
.privacy[open] > summary { border-bottom: 1px solid var(--hairline); }
.privacy p,
.privacy ul {
  margin: var(--space-3) var(--space-4);
  font: 400 0.8125rem/1.4615 var(--font-sans);
  color: var(--ink-muted);
}
.privacy ul { padding-inline-start: var(--space-4); }
.privacy li { margin-bottom: var(--space-2); }
.privacy b { color: var(--ink); font-weight: 600; }

/* ------------------------------------------------------------------ *
 * Phase 2 — the sign-in gate.
 * One card, centred, full height. It is the first thing a farmer sees,
 * so it carries the mark and nothing else competes with it.
 * ------------------------------------------------------------------ */
.gate {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  /* --kb, not --kbd. The app's keyboard machinery sets --kb; the typo meant the
     gate got no clearance at all and the Sign in button sat under the keyboard. */
  padding: var(--space-5) var(--space-4) calc(var(--space-5) + var(--kb, 0px));
  background: var(--surface-100);
}
.gatecard {
  width: 100%;
  max-width: 420px;
  margin-top: 6vh;
  padding: var(--space-5);
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.gatecard .mark { display: block; margin-bottom: var(--space-3); }
.gatecard .gatebrand .mark { margin-bottom: 0; }
.gatecard h1 {
  font: 600 1.625rem/1.2308 var(--font-display);
  color: var(--ink);
  margin: 0 0 var(--space-2);
}
.gatecard .lede {
  font: 400 0.9375rem/1.4667 var(--font-sans);
  color: var(--ink-muted);
  margin: 0 0 var(--space-4);
}
.gatecard .demotag {
  font: 600 0.75rem/1.4167 var(--font-sans);
  color: var(--ink);
  background: var(--example-ground);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-4);
}
.gatecard .field { margin-bottom: var(--space-4); }
.gatecard .lab {
  display: block;
  font: 600 0.8125rem/1.3846 var(--font-sans);
  color: var(--ink);
  margin-bottom: var(--space-1);
}
.gatecard .lab .opt { font-weight: 400; color: var(--ink-muted); }
.gatecard .inp {
  width: 100%;
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  font: 400 1.0625rem/1.4118 var(--font-sans);   /* 17px: iOS zooms the page below 16 */
  color: var(--ink);
  background: var(--surface-100);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-md);
  box-sizing: border-box;
}
.gatecard .primary {
  width: 100%;
  min-height: var(--tap-min);
  font: 600 1rem/1.375 var(--font-sans);
  color: var(--on-veld);
  background: var(--veld);
  border: 0;
  border-radius: var(--radius-md);
  cursor: pointer;
}
.gatecard .primary[disabled] { opacity: .6; cursor: default; }
.gatelinks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: space-between;
  margin-top: var(--space-4);
}
.linkish {
  min-height: var(--tap-min);
  padding: 0 var(--space-1);
  font: 500 0.875rem/1.4286 var(--font-sans);
  /* --veld, not --veld-deep. --veld-deep is dark in BOTH themes, so on the dark
   * card it measured 1.13:1 against the background — the forgot-password link
   * was in the DOM, focusable, and invisible. --veld flips with the theme. */
  color: var(--veld);
  background: none;
  border: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.gatecard .err {
  font: 500 0.875rem/1.4286 var(--font-sans);
  color: var(--status-urgent);
  margin: 0 0 var(--space-3);
}
.gatecard .notice {
  font: 500 0.875rem/1.4286 var(--font-sans);
  color: var(--ink);
  background: var(--surface-200);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin: 0 0 var(--space-4);
}
.gatecard .foot { text-align: left; margin: var(--space-4) 0 0; }
.gatecard .privacy { margin-top: var(--space-4); }

/* The save warning sits under the header, where "Start fresh" lives. */
.savewarn {
  font: 500 0.8125rem/1.4615 var(--font-sans);
  color: var(--ink);
  background: var(--status-watch);
  padding: var(--space-3) var(--space-4);
}

@media (min-width: 1000px) {
  .gatecard { margin-top: 10vh; }
}

/* NINA-idea-022. The farmer's own figure sits above the published one in the
   sources table, so the page cannot quote a yield it did not use. */
.sheet tr.ownrow td { background: var(--example-ground); font-weight: 600; }

/* NINA-idea-024 — "what would change my mind". One line under the range that
   turns an argument about the answer into a question about the farmer's own
   yields, which is the entire point of the demo. */
.changemind {
  margin: var(--space-3) 0 0;
  padding: var(--space-2) var(--space-3);
  font: 400 0.8125rem/1.4615 var(--font-sans);
  color: var(--ink);
  background: var(--surface-200);
  border-left: 3px solid var(--veld);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.changemind b { font-family: var(--font-mono); font-weight: 600; }

/* NINA-idea-025 — a field that is finished for the season. */
.donerow { margin-bottom: var(--space-4); }
.donebtn {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; min-height: var(--tap-min);
  padding: 0 var(--space-3);
  font: 500 0.875rem/1.4286 var(--font-sans);
  color: var(--ink-muted);
  background: var(--surface-100);
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--radius-md);
  cursor: pointer; text-align: left;
}
.donebtn.on {
  color: var(--ink);
  background: var(--surface-200);
  border-style: solid;
  border-color: var(--veld);
}
.chip.done { background: var(--surface-200); color: var(--ink-muted); }

/* NINA-idea-023 — one sentence, the way an agronomist talks. */
.plainline {
  margin: var(--space-2) 0 var(--space-4);
  font: 400 0.9375rem/1.4667 var(--font-sans);
  color: var(--ink);
  max-width: 54ch;
}

/* ------------------------------------------------------------------ *
 * The yard — the side rail.  Nina: grainary-records/docs/nina-side-rail.md
 *
 * Present at 1024 and up, absent below. The breakpoint is inclusive: AT 1024
 * the rail is there, so a test at 1024 has a defined expected result.
 *
 * Below 1024 it is display:none AND inert, because a keyboard Tab sweep must
 * never land inside something nobody can see.
 * ------------------------------------------------------------------ */
:root { --yard: 208px; }

.yard { display: none; }
.withyard { display: contents; }

@media (min-width: 1024px) {
  .yard {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--yard);
    padding: 0 var(--space-2) var(--space-3);
    background: var(--surface-200);
    border-inline-end: 1px solid var(--hairline);
    overflow: hidden;
    z-index: 5;
  }
  .ytop { flex: 1 1 auto; overflow-y: auto; padding-top: 68px; }

  /* The content column starts after the rail. The header band is chrome, not
     content, so it still spans the WHOLE window and the rail sits under its
     first rail-width — but its text begins at the content edge, so the title
     and the first card below it line up. Nina's acceptance points 17 and 17a,
     and the pair most likely to be got wrong.

     The header is pulled back out of the content column by exactly the rail's
     width, then padded by the rail plus the same inset the body gets. The
     inset is recomputed against the WINDOW minus the rail, because inside the
     header 100% is now the window again. */
  .withyard { display: block; margin-inline-start: var(--yard); }
  .pp, .fieldbar, .pad, .phase-note, .overlay {
    padding-inline: max(var(--gutter), calc((100% - var(--shell)) / 2));
  }
  .hdr {
    position: relative;
    margin-inline-start: calc(-1 * var(--yard));
    padding-inline-start: calc(var(--yard) +
      max(var(--gutter), (100% - var(--yard) - var(--shell)) / 2));
    padding-inline-end:
      max(var(--gutter), calc((100% - var(--yard) - var(--shell)) / 2));
    z-index: 6;
  }
  /* The mark sits in the rail's slot, above the rail, out of the flow — so the
     first in-flow header item starts at the content edge and aligns with the
     body. */
  .hdr .mark {
    position: absolute;
    inset-inline-start: var(--space-4);
    top: 50%;
    transform: translateY(-50%);
  }

  /* One field switcher on screen, never two: the rail has absorbed the chips. */
  .fieldbar { display: none; }

}

@media (min-width: 1400px) { :root { --yard: 240px; } }

.yrow, .yadd, .yfarmer {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; min-height: var(--tap-min);
  padding: 0 var(--space-3);
  font: 500 0.9375rem/1.3333 var(--font-sans);
  color: var(--ink);
  background: none; border: 0;
  border-radius: var(--radius-md);
  text-align: left; cursor: pointer;
}
/* Three signals on the active row, only one of them colour. */
.yrow.on, .yfarmer.on {
  background: var(--surface-raised);
  font-weight: 700;
  box-shadow: inset 3px 0 0 var(--veld);
}
.yl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.yg { display: inline-flex; width: 20px; flex: 0 0 20px; color: var(--ink-muted); }
.yhead {
  display: block;
  padding: var(--space-3) var(--space-3) var(--space-1);
  font: 600 0.6875rem/1.2727 var(--font-sans);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-muted);
}
.ygroup { margin: var(--space-2) 0; }
.yempty { margin: 0 var(--space-3); font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.yadd { color: var(--veld-deep); font-weight: 600; }
.yfarmer {
  flex: 0 0 auto;
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--hairline);
  border-radius: 0;
}

/* The farmer disc. veld-deep is the chrome token — not maize, which means
   money here, and not veld, which means growth and go. */
.ydisc {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: 0 0 32px;
  border-radius: 50%;
  background: var(--veld-deep); color: var(--on-veld-deep);
  font: 700 0.875rem/1 var(--font-sans);
}
.ydisc.big { width: 44px; height: 44px; flex-basis: 44px; font-size: 1.125rem; }
.hfarmer {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap-min); min-height: var(--tap-min);
  background: none; border: 0; cursor: pointer;
}
.hfarmer .ydisc { background: var(--on-veld-deep); color: var(--veld-deep); }

/* ---------- The profile, and the two destructive controls ---------- */
.profilecard .prow { display: flex; align-items: center; gap: var(--space-3); }
.pwho { display: flex; flex-direction: column; min-width: 0; }
.pwho b { font: 600 1rem/1.375 var(--font-sans); color: var(--ink); }
.pmail { font: 400 0.8125rem/1.3846 var(--font-sans); color: var(--ink-muted); overflow-wrap: anywhere; }

.confirm { display: flex; flex-direction: column; gap: var(--space-2); }
.cwhat { margin: 0 0 var(--space-1); font: 400 0.875rem/1.4286 var(--font-sans); color: var(--ink); }
.btn.danger {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: var(--tap-min);
  background: var(--status-urgent); color: #fff;
  border: 0; border-radius: var(--radius-md);
  font: 600 0.9375rem/1.3333 var(--font-sans); cursor: pointer;
}

/* The farmer is in the rail at 1024 and up, so the header disc goes and the app
   icon returns. Exactly one farmer on screen at any width (Nina's acceptance
   point 14). This sits at the END of the file on purpose: an identical rule
   inside the earlier media block was overridden by the .hfarmer base rule
   below it — equal specificity, later wins. */
@media (min-width: 1024px) {
  /* `.hdr .hfarmer`, not `.hfarmer`: the existing `.hdr button { display:flex }`
     is one class plus one type, which outranks a single class — and a media
     query adds no specificity of its own. Two classes beats it. */
  .hdr .hfarmer { display: none; }
}

/* ---------- Cloudflare Turnstile ----------
   The widget is a fixed-size iframe Cloudflare renders itself. It is given its
   own row with the same rhythm as a field so it does not crowd the button, and
   it is allowed to shrink on a narrow phone rather than forcing the card wide. */
.cfts { margin: 0 0 var(--space-3); min-height: 0; }
.cfts:empty { display: none; }
.cfts iframe { max-width: 100%; }
/* LAURA-fieldwise-046 / NINA 1.3. The `:empty` guard above does not fire,
   because Turnstile leaves a child wrapper in the box - so when the widget
   cannot connect, Cloudflare's OWN error card is drawn there: a red stop glyph,
   "Unable to connect to website", and a Troubleshoot link, sitting directly
   above the Sign in button on the first screen a farmer ever sees.

   Nina logged it as an empty hole; her screenshot showed it is worse than that.

   The app already fails open and retries on its own, so the farmer loses
   nothing by not seeing Cloudflare's diagnostics - he is not the person who can
   act on them. The box collapses while it is in that state and comes back the
   moment a retry mounts a working widget. */
.cfts.cfts-broken { display: none; }

/* The one roadmap line, on the profile. Deliberately quiet: it is an aside to a
   farmer looking at his own account, not a banner under every figure. */
.whatsnext {
  margin: var(--space-4) var(--space-4) 0; color: var(--ink-soft);
  font: 400 0.8125rem/1.4615 var(--font-sans);
}

/* The six-digit code from the sign-up email. Monospace and widely tracked so a
   farmer can check it digit by digit against the one on screen in his mail app,
   which is the actual task. */
.inp.code {
  font: 600 1.5rem/1.3333 var(--font-mono); letter-spacing: .28em;
  text-align: center; padding-inline: var(--space-2);
}

/* ---------- The shed's capacity card ----------
   The one question the page exists to answer, so it is the only lifted card on
   it. The days figure is --ink, never --maize: maize is money and nothing else,
   and a count of days is not money. Rands inside the drawer are maize. */
.card.cap.lifted { border-radius: var(--radius-xl); box-shadow: var(--shadow-lifted); }
.caphero { display: flex; align-items: baseline; gap: var(--space-2); margin-top: var(--space-2); }
.caphero b { font: 600 2rem/1.0625 var(--font-mono); color: var(--ink); }
.caphero .u { font: 500 0.6875rem/1.2727 var(--font-mono); letter-spacing: .04em; color: var(--ink-muted); }
.capwhy { margin: var(--space-2) 0 0; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.caprule { height: 1px; background: var(--hairline); margin: var(--space-4) 0; }
.capstat { display: flex; align-items: center; gap: var(--space-2); }
.capstat b { font: 600 0.9375rem/1.4667 var(--font-sans); color: var(--ink); }
.capreason { margin: var(--space-1) 0 0; font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink-muted); }
.capmore {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  min-height: 48px; padding: 0 var(--space-3); background: var(--surface-200);
  border: 1px solid var(--hairline-strong); border-radius: var(--radius-sm);
  font: 600 0.9375rem/1.3333 var(--font-sans); color: var(--ink); cursor: pointer;
}
.capdrawer {
  margin-top: var(--space-3); padding: var(--space-3);
  background: var(--surface-200); border-radius: var(--radius-md);
}
.capdrawer .dr { display: flex; justify-content: space-between; gap: var(--space-3); padding: 6px 0; }
.capdrawer .dr span { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.capdrawer .dr b { font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); white-space: nowrap; }
.capdrawer .dr b.money { color: var(--maize); }
.cap-note { margin: var(--space-3) 0 0; font: 500 0.75rem/1.3333 var(--font-sans); color: var(--ink-muted); }
.ticks { margin: var(--space-3) 0 0; padding-left: var(--space-4); }
.ticks li { font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink); margin-bottom: 4px; }

/* ---------- The shed: groups, rows, empty boxes and the sheet ----------
   One card per group, rows separated by a hairline, the add row last. A row is
   a real <button> at 60px, because this gets used with a work glove on. */
.shedgroup {
  display: flex; align-items: center; gap: var(--space-2);
  margin: var(--space-6) 0 var(--space-2);
}
.shedgroup .count {
  font: 500 0.75rem/1.3333 var(--font-mono); color: var(--ink-muted);
  background: var(--surface-200); border-radius: var(--radius-pill); padding: 2px 8px;
}
.card.rows { padding: 0; overflow: hidden; }
.shedrow {
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
  min-height: 60px; padding: var(--space-3) var(--space-4);
  background: none; border: 0; border-bottom: 1px solid var(--hairline);
  text-align: left; cursor: pointer; color: var(--ink);
}
.shedrow:last-child { border-bottom: 0; }
.shedrow .g { display: flex; color: var(--ink-muted); flex: none; }
.shedrow .t { flex: 1; min-width: 0; }
.shedrow .t b { display: block; font: 600 0.9375rem/1.4667 var(--font-sans); }
.shedrow .t .s { display: block; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.shedrow .v {
  font: 500 1.1875rem/1.2632 var(--font-mono); color: var(--ink-muted); flex: none;
  white-space: nowrap;
}
/* Never blank, never a dash: a blank reads as "nothing here" and a dash reads
   as zero. It says what is missing, in the colour that means missing. */
.shedrow .v.missing { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--soil); }
.shedrow.link { border: 1px solid var(--hairline-strong); border-radius: var(--radius-md);
                margin-top: var(--space-4); }
.shedadd {
  display: block; width: 100%; min-height: 52px; padding: var(--space-3) var(--space-4);
  background: none; border: 0; border-top: 1px solid var(--hairline);
  font: 600 0.9375rem/1.3333 var(--font-sans); color: var(--veld); text-align: left; cursor: pointer;
}
.shedempty {
  display: block; width: 100%; min-height: 84px; padding: var(--space-4);
  background: none; border: 1.5px dashed var(--veld); border-radius: var(--radius-lg);
  text-align: left; cursor: pointer;
}
.shedempty b { display: block; font: 600 0.9375rem/1.4667 var(--font-sans); color: var(--veld); }
.shedempty span { display: block; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }

/* The add / edit sheet. */
.sheetwrap {
  position: fixed; inset: 0; background: rgba(29, 64, 38, .55);
  display: flex; align-items: flex-end; justify-content: center; z-index: 40;
}
.sheet {
  width: 100%; max-width: var(--shell); max-height: 92vh; overflow-y: auto;
  background: var(--surface-raised); border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--space-3) var(--space-4) var(--space-6);
}
.sheet .grab {
  display: block; width: 44px; height: 4px; border-radius: 2px;
  background: var(--hairline); margin: 0 auto var(--space-4);
}
.sheet h3 { margin: 0 0 var(--space-2); }
.sheetlede { margin: 0 0 var(--space-4); font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink-muted); }
.unitrow { display: flex; gap: var(--space-2); align-items: stretch; }
.unitrow .inp { flex: 1; min-width: 0; }
.unitlab { display: flex; align-items: center; font: 500 0.8125rem/1.3846 var(--font-mono); color: var(--ink-muted); }
.linkish.tall { min-height: 48px; }
.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip {
  min-height: 48px; padding: 0 var(--space-3); border-radius: var(--radius-pill);
  border: 1px solid var(--hairline-strong); background: none;
  font: 500 0.9375rem/1.3333 var(--font-sans); color: var(--ink); cursor: pointer;
}
.chip.on { background: var(--veld); border-color: var(--veld); color: var(--on-veld); }
.bandcard {
  display: block; position: relative; margin-bottom: var(--space-3);
  padding: var(--space-4); border-radius: var(--radius-md);
  background: var(--surface-200); cursor: pointer;
}
.bandcard.on { outline: 2.5px solid var(--veld); outline-offset: -1px; }
.bandcard input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.bandcard b { display: block; font: 600 0.9375rem/1.4667 var(--font-sans); }
.bandcard .bd { display: block; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.bandcard .br { display: block; margin-top: 4px; font: 500 0.875rem/1.2857 var(--font-mono); color: var(--ink); }
.whatchanges {
  margin-top: var(--space-4); padding: var(--space-3);
  background: var(--surface-200); border-radius: var(--radius-md);
}
.whatchanges b { font: 600 0.9375rem/1.4667 var(--font-sans); }
.whatchanges p { margin: 4px 0 0; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.danger {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  width: 100%; min-height: 48px; background: none;
  border: 1px solid var(--status-urgent); border-radius: var(--radius-sm);
  color: var(--status-urgent); font: 600 0.9375rem/1.3333 var(--font-sans); cursor: pointer;
}

/* The Plant screen's link into the shed carries a summary like "2 tractors,
   3 implements". Left to itself that string is nowrap and sits in a column that
   sizes to its content, so it widened the left column and squeezed the crop
   cards until their names were clipped — caught by the layout guards, which is
   what they are for. It shrinks and ellipses instead. */
/* RICHARD'S PHONE, 8 Oct. This row — built the same morning — broke on the
   device it is for: "The shed" split across two lines while "1 tractor, 2
   implements, 1 other" ran off the right edge in monospace and was clipped.
   My earlier ellipsis patch only hid the overflow; it did not stop the label
   breaking, and an ellipsis in the middle of a farmer's own inventory is not
   an answer either.

   So the summary goes BELOW the label on a phone instead of beside it, in the
   body face rather than mono — it is a sentence about his machines, not a
   figure to be read off in a column. */
.shedrow.link { align-items: flex-start; }
.shedrow.link .t { flex: 1 1 auto; min-width: 0; }
.shedrow.link .t b { white-space: nowrap; }
.shedrow.link .v {
  display: block; min-width: 0; margin-top: 2px;
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
  white-space: normal; overflow: visible; text-align: left;
}

/* ================================================================== *
   SCREENS B, C, D — the drawing surface.
   Nina's map redesign, 7 October. It was a 320px letterbox inside a page that
   scrolls, so a one-finger pan sometimes moved the page instead of the map and
   a 42 ha field was being drawn through a slot. Full-bleed now.
   Rule from §6.2: nothing floating over the map is translucent or blurred. A
   glass control over satellite imagery in Highveld sun cannot be read.
 * ================================================================== */
.drawscreen { position: fixed; inset: 0; z-index: 50; background: var(--surface-200); }
/* z-index: 0 is load-bearing, not decoration.
   Leaflet gives its own panes z-indexes from 200 to 700 and its controls 1000.
   Without a stacking context of its own, the map's internals sit in the SAME
   context as the app's chips — so every control was painted over and the whole
   drawing surface came up as a bare map with nothing on it. It looked fine in
   the DOM and measured correctly; it was only visible by looking at a real
   browser. */
.drawmap { position: absolute; inset: 0; z-index: 0; }

/* Every floating control: solid ground, real border, real shadow, 48px. */
.mapchip {
  position: absolute; min-width: 48px; min-height: 48px;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 0 var(--space-2);
  background: var(--surface-raised); color: var(--ink);
  border: 1.5px solid var(--hairline-strong); border-radius: var(--radius-md);
  box-shadow: var(--shadow-lifted);
  font: 600 0.9375rem/1.3333 var(--font-sans); cursor: pointer; z-index: 2;
}
.mapchip:disabled { color: var(--ink-muted); cursor: default; }
.mapchip .t { font: 600 0.8125rem/1.2308 var(--font-sans); white-space: nowrap; }
.mapchip.done { top: var(--space-3); left: var(--space-3); }
.mapchip.layers { top: var(--space-3); right: var(--space-3); flex-direction: column; gap: 0; }
.mapchip.layers .t { font-size: 0.625rem; line-height: 12px; }

/* Bottom-right column, where a thumb already is. */
.mapctl {
  position: absolute; right: var(--space-3); bottom: 148px;
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px; z-index: 2;
}
/* The zoom pair stays square; only the locate control grows into a labelled
   pill, and only until the first corner exists. */
.mapctl .mapchip { position: static; width: 48px; }
.mapctl .mapchip.wide { width: auto; }
/* Before a single corner exists, the locate control is a labelled pill: that is
   the first thing he needs and a bare glyph will not say it (Nina, Screen C). */
.mapctl .mapchip.wide { padding: 0 var(--space-3); }

.mapbar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
  background: var(--surface-raised); border-top: 1px solid var(--hairline);
}
.mapha { flex: 1; min-width: 0; }
.mapha .fig { display: block; font: 600 1.5rem/1.1667 var(--font-mono); color: var(--ink); }
.mapha .sub { display: block; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.mapha .hint { font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.mapacts { display: flex; gap: var(--space-2); flex: none; }
.mapacts .btn { min-height: 48px; display: inline-flex; align-items: center; gap: 6px; }

/* LAURA-fieldwise-037. `bottom: 92px` was a GUESS at the bar's height, and the
   bar is not 92px: it holds a 28px figure, a 19px sub-line, its own padding and
   env(safe-area-inset-bottom), so it grows with the content and the handset. At
   390px it ran to 115px and swallowed 23px of each 48px control - nearly half a
   tap target, behind an opaque surface at a higher z-index.

   So the bar is measured after every paint and its real height published as
   --mapbar-h. The 92px stays only as the fallback for the frame before the
   first measurement. */
.mapmodes {
  position: absolute; left: var(--space-3); z-index: 2;
  bottom: calc(var(--mapbar-h, 92px) + var(--space-2));
  display: flex; align-items: center; gap: var(--space-3);
}
/* 48 is the floor, not a target (Nina §6.2 rule 1, AP-7). The first version of
   this strip was 40 and failed its own acceptance point — measured, not eyed. */
.seg.small button { min-width: 48px; min-height: 48px; font-size: 0.8125rem;
                    padding: 0 var(--space-3); }
.mapattr {
  position: absolute; left: var(--space-4); bottom: 76px; margin: 0; z-index: 2;
  font: 500 0.6875rem/1.2727 var(--font-sans); color: var(--ink-muted);
  background: var(--surface-raised); padding: 2px 6px; border-radius: var(--radius-sm);
}

/* A corner the farmer can move: 28px of visible disc inside a 48px hit area,
   which is also the app's tap-target floor. The last one placed carries a
   second ring, so he can see which one Undo will take. */
.cornerwrap { display: flex; align-items: center; justify-content: center; }
.cornerwrap .corner {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--map-handle); border: 2px solid #fff; box-sizing: border-box;
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
}
.cornerwrap.last .corner { box-shadow: 0 0 0 3px var(--veld), 0 1px 3px rgba(0,0,0,.4); }

/* SCREEN D — no tiles. A flat hatched ground, not grey nothing. */
.drawscreen.nosignal { display: flex; align-items: center; justify-content: center; }
.nosignal .hatch {
  position: absolute; inset: 0; background: var(--surface-200);
  background-image: repeating-linear-gradient(45deg,
    var(--hairline) 0 1px, transparent 1px 14px);
}
.nspanel {
  position: relative; z-index: 2; max-width: 360px; margin: var(--space-4);
  padding: var(--space-5); background: var(--surface-raised);
  border: 1px solid var(--hairline-strong); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lifted);
}
.nspanel b { display: block; font: 600 1.0625rem/1.2941 var(--font-display); margin-bottom: var(--space-2); }
.nspanel span { display: block; font: 400 0.9375rem/1.4667 var(--font-sans); color: var(--ink-muted);
                margin-bottom: var(--space-4); }
.nspanel .btn { width: 100%; min-height: 48px; }
.nspanel .soon {
  margin: var(--space-4) 0 0; padding-top: var(--space-3);
  border-top: 1px solid var(--hairline);
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
}

/* The field's shape without a map. Drawn from the saved corners, so it works
   with no tiles and no network — a farmer who has drawn a field should be able
   to see it whatever the signal is doing. */
.shapesvg path { fill: var(--veld); fill-opacity: .18; stroke: var(--veld); stroke-width: 2;
                 stroke-linejoin: round; }
.shapetile { display: flex; align-items: center; gap: var(--space-3); }
.shapetile .shapesvg { flex: none; background: var(--surface-200);
                       border-radius: var(--radius-sm); }
.shapetile .t b { display: block; font: 500 0.9375rem/1.3333 var(--font-mono); color: var(--ink); }
.shapetile .t span { display: block; font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted); }
.shapenone {
  flex: none; width: 56px; height: 56px; border: 1.5px dashed var(--hairline-strong);
  border-radius: var(--radius-sm);
}
.drawit { width: 100%; min-height: 48px; display: inline-flex; align-items: center;
          justify-content: center; gap: var(--space-2); margin-top: var(--space-2); }

/* A field row carries its own outline, so he recognises his lands by shape. */
.farmrow .g .shapesvg { background: var(--surface-200); border-radius: var(--radius-sm); }
.farmrow .g.sm { display: flex; align-items: center; color: var(--ink-muted); }

/* SCREEN F — what the drawing did to the hectares, and the way back.
   Hectares drive the money on three other screens, so the number changing is
   something the farmer is told about, not something he discovers. */
.hanotice {
  display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-3);
  padding: var(--space-3); background: var(--surface-200);
  border-radius: var(--radius-md); font: 400 0.8125rem/1.4615 var(--font-sans);
}
.hanotice.ask { display: block; border: 1.5px solid var(--hairline-strong); }
.hanotice b { font-weight: 600; }
.hanotice span { flex: 1; }
.hanotice .btn-row { margin-top: var(--space-3); display: flex; gap: var(--space-2); }
.hanotice .btn { flex: 1; min-height: 48px; }

/* The farm's other fields while he draws — faint, named, and never clickable,
   so they cannot swallow a tap meant for a corner. */
/* LAURA-fieldwise-038, Tier 1. These sat on --ink with a --surface-100 halo,
   and BOTH flip with the theme while the map underneath does not: the OSM
   raster is the same pale paper whatever the farmer's phone is set to. In dark
   mode the label came out #f4eee1 on a #f2efe9 tile - about 1.0:1, which is
   invisible, measured.

   Anything drawn ON a third-party raster has to be fixed ink, not a theme
   token. These are the colours the tiles were designed against, so they hold in
   both themes and the halo keeps them legible over a dark field or a road. */
.othername {
  font: 600 0.6875rem/1.6364 var(--font-sans); color: #231d14;
  text-align: center;
  text-shadow: 0 0 3px #f7f4ee, 0 0 3px #f7f4ee, 0 0 3px #f7f4ee, 0 1px 2px #f7f4ee;
  pointer-events: none;
}

/* A control that is announced rather than half-built. It must not look live. */
.linkish.soon { color: var(--ink-muted); }
.linkish.soon em {
  font-style: normal; font: 700 0.625rem/1.4 var(--font-sans); letter-spacing: .06em;
  text-transform: uppercase; background: var(--surface-200); color: var(--ink-muted);
  border-radius: var(--radius-pill); padding: 1px 6px; margin-left: 4px;
}

/* The one address in the app a farmer is asked to write to. */
.whatsnext a { color: var(--veld); text-decoration: underline; }

/* The sheet's own actions run the full width of it. */
.sheet .btn.wide { width: 100%; min-height: 48px; margin-top: var(--space-4); }
/* NINA-fieldwise-065: .seg had no width floor, so the kW/hp segments measured
   29-34px against the app's 48px rule. */
.seg button { min-width: 48px; }

/* One dated settlement, and a loud word when it has gone cold. */
.pricecard .price { font: 600 2rem/1.0625 var(--font-mono); color: var(--maize); }
.pricecard .price em { font: 500 0.8125rem/1.3846 var(--font-mono); font-style: normal;
                       color: var(--ink-muted); margin-left: 4px; }
.stalewarn {
  margin: var(--space-3) 0 0; padding: var(--space-3);
  background: var(--surface-200); border-left: 3px solid var(--status-urgent);
  border-radius: var(--radius-sm); font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink);
}

/* His buyer's offer is the number that decides a sale. The exchange figure is
   a reference he judges it against — so the invitation to replace it is said
   out loud rather than hidden behind an edit mode. */
.ownprompt {
  margin: var(--space-3) 0 0; padding: var(--space-3);
  background: var(--surface-200); border-radius: var(--radius-sm);
  font: 400 0.8125rem/1.4615 var(--font-sans); color: var(--ink-muted);
}
.ownprompt .linkish { font-size: 0.8125rem; }

/* The mark was briefly made a button - "go home" - and Richard's verdict on it
   was that it was worthless, which measurement agreed with: on the farm screen
   it redrew the screen he was already on. It is a mark again, and the header's
   left slot carries a real back control instead (see .hdr .up above). This rule
   stays as a tombstone: if a button.mark ever reappears in the header, it is
   this mistake coming back. */


/* RICHARD'S PHONE, 8 Oct — three readability faults he circled.

   FORECAST ROWS. "0 mm" and "12-26 degC" were crammed against the right edge
   with nothing lining up between rows, so a farmer scanning for the wet day
   had to read every line. The two figures get fixed columns now, so the
   millimetres sit under the millimetres. (degC became a real degree sign in
   app.js - "degC" is how a program writes it, not how a person reads it.) */
/* The columns were already aligned and already right-justified at line 882 —
   a second .fcrow block here would have been two sources of truth for one
   layout, which is the fault I wrote a guard against in the data this morning.
   So this adds only what was actually wrong: the temperature column was too
   narrow for "12-26 degC" and the pair sat hard against the right edge. With a
   real degree sign the string is shorter, and a little more room stops the
   figures touching the card edge. */
.fcrow .tp { min-width: 80px; white-space: nowrap; }
.fcrow .mm { white-space: nowrap; }

/* FIELD ROWS. "planted 7 Oct" wrapped with "Oct" alone on the next line and
   the rand figure was squeezed between the text and the chevron. The figure
   gets its own column and never shrinks; the description wraps under itself
   instead of fighting it. */
.farmrow { align-items: flex-start; }
.farmrow .t { flex: 1 1 auto; min-width: 0; }
.farmrow .t span { white-space: normal; }
.farmrow .v { flex: 0 0 auto; white-space: nowrap; padding-left: var(--space-2); }

/* LAURA-fieldwise-034, Tier 1 — and it is the FIRST screen after the demo
   button, on the width most South African farmers actually hold.

   At 360px the row ran out of room before the field name got any. Measured:
   shape thumbnail 56 + crop glyph 20 + money 84 + chevron 20 + four 12px gaps
   = 228px consumed, leaving the text column 68px and the row 153px tall. "Vlei
   kamp" broke in half; the description stacked four lines. At 320 the rows ran
   241px.

   It is an arithmetic problem, so the fix is arithmetic. Below 420px:

     - the crop glyph goes. It is pure duplication — the line underneath already
       says "Maize" in words, and words beat a 20px pictogram in the sun.
     - the shape thumbnail comes down to 44px. It is a recognition cue at a
       glance, not a map, and it is still the largest thing in the row.
     - the rand figure moves UNDER the description instead of fighting it for
       the same line. That is how a phone list row is built everywhere else,
       and it is the single change that gives the name its width back.

   That returns the text column from 68px to roughly 212px. */
@media (max-width: 419px) {
  /* A grid, not a flex row: the shape and the chevron each span both lines, and
     the name and the figure stack between them. Flex-wrap was tried first and
     put the chevron on a line of its own, which looked broken. */
  .farmrow {
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr) 20px;
    column-gap: var(--space-2); row-gap: 2px;
    align-items: start;
  }
  .farmrow .g.sm { display: none; }
  .farmrow .g { grid-column: 1; grid-row: 1 / span 2; flex: none; align-self: center; }
  .farmrow .g .shapesvg { width: 44px; height: 44px; }
  .farmrow .t { grid-column: 2; grid-row: 1; }
  .farmrow .v {
    grid-column: 2; grid-row: 2; padding-left: 0;
    font: 500 0.9375rem/1.3333 var(--font-mono);
  }
  .farmrow .chev { grid-column: 3; grid-row: 1 / span 2; align-self: center; }
}

/* THE BEST ESTIMATE CHIP. A pill outline the size of a button, sitting beside
   the crop name and crowding the GOOD YEAR column. It is a label, not a
   control, so it stops pretending to be one: no outline, smaller, and it sits
   under the crop name where it cannot squeeze a figure. */
.crop .chip {
  border: 0; background: none; padding: 0; margin-top: 2px;
  font: 700 0.625rem/1.4 var(--font-sans); letter-spacing: .08em;
  text-transform: uppercase; color: var(--veld); white-space: nowrap;
}

/* The legal pages, and the one control that is a right rather than a feature. */
.legal .legalcard h3 { margin: 0 0 var(--space-2); }
.legal .legalcard p { margin: 0 0 var(--space-3); font: 400 0.9375rem/1.5333 var(--font-sans); }
.legal .legalcard p:last-child { margin-bottom: 0; }
.legal .legalcard a { color: var(--veld); text-decoration: underline; }
.legalbar { margin-bottom: var(--space-4); }
.legalbar .btn { min-height: 48px; }
.legallinks { display: flex; flex-direction: column; gap: var(--space-2); align-items: flex-start; }
.gatelegal { display: flex; gap: var(--space-4); justify-content: center; margin-top: var(--space-3); }
.danger-card { border: 1px solid var(--status-urgent); }
.btn.danger {
  background: none; border: 1.5px solid var(--status-urgent); color: var(--status-urgent);
  font: 600 0.9375rem/1.3333 var(--font-sans); min-height: 48px;
}
.btn.danger.wide { width: 100%; }
.danger-card .btn-row { display: flex; gap: var(--space-2); }
.danger-card .btn-row .btn { flex: 1; }

/* ALLISON-fieldwise-005 — removing a field. It sits at the foot of the field
   editor, away from everything a farmer taps often, and it is a link until he
   asks for it: a full-width red button on a screen he visits to change his
   hectares would be a trap, not a control. Laura's LAURA-040 point applies here
   too, so the two choices do NOT look alike — one is a bordered red button, the
   other a plain link. */
.dangerrow { margin-top: var(--space-5); padding-top: var(--space-4);
             border-top: 1px solid var(--hairline); }
.dangerrow .linkish.danger { color: var(--status-urgent); }
.dangerrow .btn.danger { width: 100%; }
.dangerrow .linkish { display: block; margin: var(--space-2) auto 0; }
.dangerrow .hint { margin-top: var(--space-2); }

/* The soil card's texture chips carry their clay band, so a farmer holding a
   lab report can match his own number to a word rather than guessing. */
.soilcard .chips { gap: var(--space-2); }
.soilcard .chip { flex-direction: column; align-items: flex-start; min-height: 56px;
                  padding: 8px var(--space-3); border-radius: var(--radius-md); }
.soilcard .chip .chipsub { display: block; font: 400 0.6875rem/1.2727 var(--font-sans);
                           color: var(--ink-muted); }
.soilcard .chip.on .chipsub { color: var(--on-veld); opacity: .85; }
.soilmore { margin-top: var(--space-3); }
.soilmore summary { min-height: 48px; display: flex; align-items: center;
                    font: 600 0.8125rem/1.4615 var(--font-sans); color: var(--veld);
                    cursor: pointer; }

/* DEMO MODE. It must never stop looking like a demonstration: somebody will
   screenshot it and show a neighbour, so the bar is on every screen and not
   just the first one. */
.demobar {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  background: var(--example-ground); color: var(--ink);
  font: 400 0.8125rem/1.4615 var(--font-sans);
}
.demobar span { flex: 1; min-width: 180px; }
.demobar .linkish { flex: none; min-height: 44px; font-weight: 600; }
/* NINA 1.2. This was a 48px bordered pill directly beneath another 48px
   bordered pill of identical weight, colour and border - the upper one a
   privacy disclosure, the lower one the only thing most farmers will ever see.
   Nothing told a stranger which was the way in.

   It is the one filled control on the screen after Sign in, and it carries the
   three demo fields' own outlines so he can see what he is about to be shown.
   Taller than 48 because the shapes need the room, and that is the point: it
   must not be mistakable for the disclosure beneath it. */
.demobtn {
  width: 100%; min-height: 72px; margin-top: var(--space-2);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--space-3);
  background: var(--surface-200); border: 1.5px solid var(--veld);
  border-radius: var(--radius-md); color: var(--ink);
  font: 600 0.9375rem/1.3333 var(--font-sans); cursor: pointer;
}
.demobtn .demoshapes { display: flex; align-items: center; gap: var(--space-3); }
.demobtn .demoshapes svg { display: block; }
/* The outlines are a cue, not a map - they must not shout louder than the
   words under them. */
.demobtn .demoshapes svg * { opacity: 0.85; }
.demobtn .demoword { display: block; }

/* The caption moved ABOVE the button. Below it, it explained a control the
   farmer had already pressed. */
.demohint {
  margin: var(--space-4) 0 0; text-align: center;
  font: 400 0.75rem/1.4167 var(--font-sans); color: var(--ink-muted);
}

/* NINA 1.1. The app named itself on every screen except the one where a
   stranger arrives. */
.gatebrand {
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-2); margin-bottom: var(--space-3);
}
.gatebrand .mark { display: block; margin: 0; }
.gatename { font: 600 1.375rem/1.2273 var(--font-display); color: var(--ink); }

/* ROGER-fieldwise-035. The summary sheet's Read column now marks a figure that
 * has passed its own freshness window. It is a document going to a bank, so
 * this is a quiet factual mark, not an alarm - the sentence in the Source
 * column carries the detail. */
.sheet-table .stalemark {
  display: inline-block;
  margin-left: var(--space-1);
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  /* The app's own "watch" colour, which already means exactly this: not an
     error, something to keep an eye on. Invented --warn-* tokens with
     fallbacks were replaced with the real ones so this follows the palette
     into dark mode instead of staying beige in it. */
  background: var(--example-ground);
  color: var(--status-watch);
  font: 600 0.625rem/1.4 var(--font-sans);
  letter-spacing: .04em;
  text-transform: uppercase;
  vertical-align: 1px;
}
