/* ==========================================================================
   KraalBook — the shared stylesheet for kraalbook.co.za

   This file is THE design system. Before 2026-08-12 every page carried its own
   copy of a :root block and there was no type scale — DESIGN.md listed both as
   its known weaknesses. This replaces them. DESIGN.md documents what is here;
   if the two disagree, fix the disagreement rather than picking one silently.

   Adopted from the redesign set of 2026-08-12. The brand green is unchanged
   (#1F5C2E); the paper background, the slab headings and the ear-tag yellow
   accent are new, replacing white / Plus Jakarta Sans+Inter / orange #E07B1A.

   Contrast: every colour pair used for text in here was computed, not
   eyeballed. The figures are in DESIGN.md. Do not introduce a new pair
   without checking it.
   ========================================================================== */

:root {
  /* --- Colour roles ----------------------------------------------------- */
  --veld:        #1F5C2E;  /* brand primary — unchanged from the old system */
  --veld-deep:   #133B1D;  /* headings, ink bands, document chrome */
  --veld-dark:   #184A25;  /* primary hover */
  --veld-press:  #0F3018;  /* primary pressed */
  --veld-wash:   #E8EFE9;  /* tint fill — .band--tint */
  --paper:       #FBF9F4;  /* page background — stock-register paper */
  --paper-2:     #FFFFFF;  /* raised surface: cards, documents */
  --rule:        #DED8C9;  /* ledger rule — DECORATIVE dividers only */
  --rule-strong: #C4BBA6;  /* heavier divider — decorative, exempt from 3:1 */
  --border-ui:   #8A8371;  /* ANY interactive boundary: inputs, buttons,
                              clickable cards. 3.59:1 on paper, so it meets
                              WCAG 1.4.11. NEVER use --rule-strong on a
                              control — it fails. */
  --ink:         #1C1F1A;  /* body text — 15.8:1 on paper */
  --ink-soft:    #5C6156;  /* secondary text — 6.1:1 on paper, 5.4:1 on wash */
  --tag:         #E8B32B;  /* ear-tag yellow — the accent */
  --tag-ink:     #3A2B05;  /* the only text colour permitted on --tag */
  --soil:        #8A4B2A;  /* overdue, errors, destructive */
  --soil-wash:   #F6E9E1;

  /* On --veld-deep backgrounds. Body copy there is --on-ink; the muted pair
     is --on-ink-soft at 6.0:1. Never put --ink-soft on a dark band. */
  --on-ink:      #E9EFE9;
  --on-ink-soft: #9DBBA4;
  --on-ink-rule: #2C5637;

  /* --- Type scale ------------------------------------------------------- */
  /* Pick from this ladder and nothing in between. */
  --t-hero:  clamp(34px, 6.2vw, 60px);
  --t-h2:    clamp(26px, 3.6vw, 38px);
  --t-h3:    20px;
  --t-body:  17px;
  --t-small: 15px;
  --t-micro: 13px;

  --f-head: "Zilla Slab", Georgia, "Times New Roman", serif;
  --f-body: "Source Sans 3", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --f-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;

  /* --- Layout ----------------------------------------------------------- */
  --shell: 1200px;
  --shell-prose: 800px;      /* articles, guide, legal */
  --gut: 20px;
  --radius: 6px;
  --tap: 48px;               /* touch minimum — the audience is phone-first */

  /* --header-h is the header's INNER height (what .header-in sets). The sticky
     header also paints a 1px bottom border, so what actually covers the page is
     --header-total. Anchor offsets must use the TOTAL — using the inner height
     hides a hairline of content under the border, and worse, it invites the
     "just add 16px" fudge that this file used to carry. */
  --header-h: 68px;
  --header-border: 1px;
  --header-total: calc(var(--header-h) + var(--header-border));

  /* The gap a reader should SEE between the sticky header and the first line of
     a section they jumped to. This is the number to tune if the top of a page
     feels tight or cavernous — not the padding, and not the scroll offset. */
  --anchor-gap: 28px;
}

@media (min-width: 768px) { :root { --gut: 32px; } }

/* ---------------------------------------------------------------- reset -- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--t-body)/1.6 var(--f-body);
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }

/* [hidden] must win. Any author rule that sets `display` on an element — and
   this sheet sets it on .btn, .tiers, .actions and more — silently defeats the
   attribute, because an author declaration beats the UA stylesheet. That is not
   theoretical: /pricing hides its trial CTA for a country we are not open in
   (Mauritius, Seychelles, Comoros) with `hidden`, and .btn's display:inline-flex
   was keeping it on screen. Found during the port on 2026-08-12; the bug
   predated it. Also protects the install tab panels. */
[hidden] { display: none !important; }

h1, h2, h3, h4 {
  font-family: var(--f-head);
  color: var(--veld-deep);
  margin: 0;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

p { margin: 0; }
a { color: var(--veld); }
ul, ol { margin: 0; padding: 0; }

/* One focus ring, everywhere, always visible. */
:focus-visible {
  outline: 3px solid var(--veld);
  outline-offset: 2px;
  border-radius: 3px;
}
.band--ink :focus-visible,
.site-footer :focus-visible { outline-color: var(--tag); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.shell {
  width: 100%; max-width: var(--shell);
  margin-inline: auto; padding-inline: var(--gut);
}
.shell--prose { max-width: var(--shell-prose); }

/* ------------------------------------------------------------ skip link -- */
.skip {
  position: fixed; top: 12px; left: 12px; z-index: 9999;
  transform: translateY(-200%);
  background: var(--veld); color: #fff; font-weight: 600;
  padding: 14px 18px; border-radius: var(--radius); text-decoration: none;
}
.skip:focus-visible { transform: translateY(0); }

/* --------------------------------------------------------------- header -- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.header-in { min-height: var(--header-h); display: flex; align-items: center; gap: 16px; }

/* Header labels never wrap. A two-word label like "User guide" was breaking onto a second line
   inside its own 44px box, making that one item 48px tall and the whole bar look broken — which
   reads as a layout fault rather than a tight fit. Two-word labels are the norm in Afrikaans and
   isiZulu ("Ngena ngemvume"), so this is not an English-only nicety. If a label genuinely cannot
   fit, the answer is a wider breakpoint, not a wrapped word. */
header .nav a, .header-in .btn { white-space: nowrap; }

.brand {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--f-head); font-weight: 700; font-size: 21px;
  color: var(--veld-deep); text-decoration: none; margin-right: auto;
}
.brand img { width: 32px; height: 32px; flex: none; }

/* Below 480px the header needs 433px of a 375px screen. The wordmark is 98px of that and
   the cheapest thing to drop: the mark alone still identifies the site, and unlike moving
   the language switch into the drawer it hides no CONTROL — a farmer who cannot read
   English must still be able to SEE EN/AF/ZU without opening a menu.
   sr-only rather than display:none is load-bearing: the logo carries alt="", so hiding the
   text outright would leave the home link with no accessible name at all. */
@media (max-width: 479px) {
  .brand-text {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .header-in { gap: 8px; }
  /* The primary CTA used to fit here only because its label WRAPPED to two lines, which looked
     broken. Now that header labels never wrap it takes its true single-line width (131px), so it
     has to earn the space back with tighter padding instead. The language buttons keep their
     44px minimum — that is the touch-target floor (brand.md §3), not a spacing choice. */
  .header-in .btn { padding-inline: 12px; }
}

.nav { display: none; }
/* The full desktop header - nav links + separator, no burger - only appears once there is room
   for it. At 1024px it needed 970px of 969 and wrapped "Get the app" onto two lines, once a
   third button joined the row. 1200 leaves headroom for isiZulu, whose button labels
   ("Ngena ngemvume", "Cela ukuboniswa") run far longer than the English this was eyeballed
   against. The BUTTONS stay from 1024 - they fit there beside the burger. Only the links wait. */
@media (min-width: 1200px) {
  .nav { display: flex; align-items: center; gap: 4px; }
  .nav a {
    display: inline-flex; align-items: center; min-height: 44px;
    padding: 0 12px; border-radius: var(--radius);
    color: var(--ink); text-decoration: none;
    font-size: var(--t-small); font-weight: 600;
  }
  .nav a:hover { background: var(--veld-wash); color: var(--veld-deep); }
  .nav a[aria-current="page"] {
    color: var(--veld); box-shadow: inset 0 -2px 0 var(--veld); border-radius: 0;
  }
}

.header-sep { display: none; }
@media (min-width: 1200px) {
  .header-sep {
    display: block; width: 1px; align-self: stretch;
    background: var(--rule); margin: 14px 12px;
  }
}

/* The language switch is BUTTONS, not links. The site translates in place off
   the saved kb-lang key — there are no /af or /zu routes and adding them would
   be a different architecture. A redesign that ships <a href="/af"> ships three
   dead links on every page. */
.lang { display: flex; gap: 2px; }
.lang button {
  display: grid; place-items: center; min-width: 44px; min-height: 44px;
  border: 0; background: none; border-radius: var(--radius); cursor: pointer;
  font: 600 var(--t-micro)/1 var(--f-mono);
  color: var(--ink-soft); letter-spacing: .04em;
}
.lang button:hover { background: var(--veld-wash); }
.lang button[aria-pressed="true"] { background: var(--veld-wash); color: var(--veld-deep); }

/* .hdr-cta marks a header button that belongs to desktop only. The extra .header-in is
   LOAD-BEARING: .btn sets display:inline-flex further down this file at equal specificity,
   so a bare .hdr-cta{display:none} loses on source order and the button renders at EVERY
   width. That is what put Sign in beside the burger on a phone and pushed the header to
   547px against a 375px screen. Do not simplify this selector. */
.header-in .hdr-cta { display: none; }
@media (min-width: 1024px) { .header-in .hdr-cta { display: inline-flex; } }

.burger {
  display: inline-grid; place-items: center;
  width: var(--tap); height: var(--tap);
  background: none; border: 1px solid var(--border-ui);
  border-radius: var(--radius); cursor: pointer;
}
.burger span {
  display: block; width: 20px; height: 2px; background: var(--ink);
  box-shadow: 0 -6px 0 var(--ink), 0 6px 0 var(--ink);
}
@media (min-width: 1200px) { .burger { display: none; } }

.drawer {
  display: none;
  border-top: 1px solid var(--rule);
  background: var(--paper);
  padding: 8px var(--gut) 20px;
}
.drawer[data-open="true"] { display: block; }
@media (min-width: 1024px) { .drawer { display: none !important; } }
.drawer a {
  display: flex; align-items: center; min-height: var(--tap);
  padding: 0 8px; text-decoration: none; color: var(--ink); font-weight: 600;
  border-bottom: 1px solid var(--rule);
}
.drawer a:last-child { border-bottom: 0; }

/* ----------------------------------------------------------------- band -- */
/* --band-pad drives the padding AND the anchor offset below. Set it, never
   `padding-block` directly, or the two go back to disagreeing. */
.band { --band-pad: 64px; padding-block: var(--band-pad); border-top: 1px solid var(--rule); }
.band:first-of-type { border-top: 0; }
.band--tint { background: var(--veld-wash); }
.band--ink { background: var(--veld-deep); color: var(--on-ink); border-top: 0; }
.band--ink h2, .band--ink h3 { color: #fff; }
.band--flush { --band-pad: 48px; }
@media (min-width: 768px) { .band { --band-pad: 88px; } }

/* The eyebrow is a form label, not decoration: it names the section the way a
   field is named on a stock register. */
.eyebrow {
  font: 600 var(--t-micro)/1 var(--f-mono);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft);
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 18px;
}
.eyebrow::after { content: ""; flex: 1; height: 1px; background: var(--rule); }
.band--ink .eyebrow { color: var(--on-ink-soft); }
.band--ink .eyebrow::after { background: var(--on-ink-rule); }

/* 28px, matching --anchor-gap, and both are "one block ends, another begins".
   It was 40px, which put the largest hole in the section BELOW the intro — so
   the space under the heading beat the space above the whole section, and the
   eye read the gap instead of the words. The intra-head steps are 18 and 14, so
   the rhythm now descends 28 / 18 / 14 and closes at 28. */
.band-head { max-width: 62ch; margin-bottom: 28px; }
.band-head h2 { font-size: var(--t-h2); }
.band-head p { margin-top: 14px; color: var(--ink-soft); font-size: var(--t-body); }
.band--ink .band-head p { color: #B9CDBE; }

/* -------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: var(--tap); padding: 0 22px;
  border-radius: var(--radius); border: 1.5px solid transparent;
  font: 600 var(--t-body)/1 var(--f-body);
  text-decoration: none; cursor: pointer; text-align: center;
}
.btn--primary { background: var(--veld); color: #fff; }
.btn--primary:hover { background: var(--veld-dark); }
.btn--primary:active { background: var(--veld-press); }
.btn--secondary { background: transparent; color: var(--veld); border-color: var(--veld); }
.btn--secondary:hover { background: var(--veld-wash); }
.btn--onink { background: var(--tag); color: var(--tag-ink); }
.btn--onink:hover { background: #F0C246; }
.btn--block { width: 100%; }
.btn[disabled], .btn[aria-disabled="true"] {
  background: #EDEAE1; color: #666B60; border-color: transparent; cursor: not-allowed;
}

.cta-row { display: flex; flex-direction: column; gap: 12px; }
@media (min-width: 480px) { .cta-row { flex-direction: row; flex-wrap: wrap; } }

/* --------------------------------------------- ear-tag chip (signature) -- */
.tag {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 36px; padding: 0 14px 0 11px;
  background: var(--tag); color: var(--tag-ink);
  border-radius: 4px 16px 16px 4px;      /* the shape of a management tag */
  font: 600 var(--t-micro)/1 var(--f-mono);
  letter-spacing: .02em; text-decoration: none;
}
.tag::before {
  content: ""; width: 7px; height: 7px; flex: none;
  border-radius: 50%; background: rgba(0,0,0,.32);   /* the punch hole */
}
.tag--quiet { background: var(--paper-2); color: var(--veld-deep); box-shadow: inset 0 0 0 1px var(--border-ui); }
.tag--quiet:hover { background: var(--veld-wash); box-shadow: inset 0 0 0 1px var(--veld); }
.tag--quiet::before { background: var(--tag); }
.tag--alert { background: var(--soil-wash); color: var(--soil); box-shadow: inset 0 0 0 1px #E0C4B4; }
.tag--alert::before { background: var(--soil); }

.tag-row { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; }

/* ----------------------------------------------------------------- hero -- */
.hero { padding-block: 44px 56px; }
@media (min-width: 900px) { .hero { padding-block: 72px 80px; } }
.hero-grid { display: grid; gap: 44px; align-items: start; }
@media (min-width: 900px) { .hero-grid { grid-template-columns: 1.05fr .95fr; gap: 56px; } }

.hero h1 { font-size: var(--t-hero); max-width: 15ch; font-weight: 700; }
.hero h1 em { font-style: normal; color: var(--veld); }
.hero-sub { margin-top: 20px; font-size: clamp(17px, 2vw, 19px); color: var(--ink-soft); max-width: 54ch; }
.hero .cta-row { margin-top: 28px; }
.hero-note { margin-top: 16px; font-size: var(--t-small); color: var(--ink-soft); }
.hero .tag-row { margin-top: 32px; }

/* ---------------------------------------- document card (the signature) -- */
/* Shows the artifact the product actually produces, in HTML. This is why the
   site carries no hero photograph: the old hero-bg.jpg alone was 203 KB, more
   than this entire site. The audience is often on prepaid data. */
.doc {
  background: var(--paper-2);
  border: 1px solid var(--rule-strong);
  border-radius: 4px;
  box-shadow: 0 1px 0 var(--rule), 0 12px 28px -18px rgba(19,59,29,.4);
  overflow: hidden;
}
.doc-head {
  background: var(--veld-deep); color: #fff;
  padding: 14px 18px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  font: 600 var(--t-micro)/1.3 var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase;
}
.doc-head span:last-child { color: var(--tag); }
.doc-id { padding: 18px; border-bottom: 1px solid var(--rule); }
.doc-id .num {
  font: 600 clamp(22px, 4vw, 28px)/1 var(--f-mono);
  color: var(--veld-deep); letter-spacing: .01em;
}
.doc-id .meta { margin-top: 8px; font-size: var(--t-small); color: var(--ink-soft); }

.doc-fields { display: grid; grid-template-columns: 1fr 1fr; border-bottom: 1px solid var(--rule); }
.doc-fields > div { padding: 12px 18px; }
.doc-fields > div + div { border-left: 1px solid var(--rule); }
.doc-fields .k {
  font: 600 11px/1 var(--f-mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-soft);
}
.doc-fields .v { margin-top: 6px; font-weight: 600; color: var(--veld-deep); }
.doc-fields .v--ok { color: var(--veld); }
.doc-fields .v--bad { color: var(--soil); }

.doc-rows { list-style: none; }
.doc-rows li {
  display: grid; grid-template-columns: 88px 1fr;
  gap: 12px; align-items: baseline;
  padding: 11px 18px;
  border-bottom: 1px solid var(--rule);
  font-size: var(--t-small);
}
.doc-rows time {
  font: 500 var(--t-micro)/1.4 var(--f-mono);
  color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.doc-rows b { color: var(--veld-deep); font-weight: 600; }
.doc-rows b.bad { color: var(--soil); }
.doc-rows small { display: block; color: var(--ink-soft); }
.doc-foot {
  padding: 16px 18px; background: var(--veld-wash);
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  font-size: var(--t-small); font-weight: 600; color: var(--veld-deep);
}

/* ----------------------------------------------------------------- grid -- */
.grid { display: grid; gap: 20px; }
.grid--2, .grid--3 { grid-template-columns: 1fr; }
@media (min-width: 620px) { .grid--2, .grid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .grid--3 { grid-template-columns: repeat(3, 1fr); } }

.card {
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 24px;
}
.card h3 { font-size: var(--t-h3); margin-bottom: 8px; }
.card p { color: var(--ink-soft); font-size: var(--t-small); }
.card-num {
  font: 600 var(--t-micro)/1 var(--f-mono);
  color: var(--veld); letter-spacing: .1em; display: block; margin-bottom: 12px;
}
/* A card you can click is a control, so it takes --border-ui, not --rule. */
a.card { text-decoration: none; display: block; border-color: var(--border-ui); }
a.card:hover { border-color: var(--veld); background: var(--paper); }
a.card h3 { color: var(--veld); }

/* --------------------------------------------------------- before/after -- */
.compare { display: grid; gap: 20px; }
@media (min-width: 860px) { .compare { grid-template-columns: 1fr 1fr; } }
.panel { border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden; background: var(--paper-2); }
.panel-head {
  padding: 14px 20px; font: 600 var(--t-micro)/1 var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase;
}
.panel--old .panel-head { background: var(--soil-wash); color: var(--soil); }
.panel--new .panel-head { background: var(--veld-deep); color: var(--tag); }
.panel ul { list-style: none; }
.panel li {
  padding: 14px 20px 14px 46px; border-top: 1px solid var(--rule);
  position: relative; font-size: var(--t-small);
}
/* Marks are decorative — the panel heading already says which list is which,
   so a screen reader is not told "times" fifteen times. */
.panel li::before {
  position: absolute; left: 20px; top: 14px;
  font: 600 15px/1.6 var(--f-mono);
}
.panel--old li::before { content: "\00D7"; color: var(--soil); }
.panel--new li::before { content: "\2713"; color: var(--veld); }

/* ----------------------------------------------------------------- tabs -- */
.tablist { display: flex; gap: 8px; border-bottom: 1px solid var(--rule); margin-bottom: 28px; }
.tablist button {
  min-height: var(--tap); padding: 0 18px;
  background: none; border: 0; border-bottom: 3px solid transparent;
  font: 600 var(--t-body)/1 var(--f-body); color: var(--ink-soft);
  cursor: pointer;
}
.tablist button[aria-selected="true"] { color: var(--veld-deep); border-bottom-color: var(--veld); }
.tablist button:hover { color: var(--veld-deep); }

.install-grid { display: grid; gap: 32px; }
@media (min-width: 860px) { .install-grid { grid-template-columns: 320px 1fr; gap: 48px; } }

/* The official store badges are images and must never be recreated in CSS —
   see DESIGN.md "Badge assets". .store-btn is only for the fallback state
   (no listing / invite-only), which must not look like an official badge.

   The two badges need OPPOSITE backings and neither choice is ours to make:
   Apple supplies a white-bodied badge (measured average luma 201), which
   disappears on this site's paper and tint; Google supplies a black one
   (luma 42), which disappears on anything dark. Since they live in mutually
   exclusive tab panels, the visitor never sees the two backings together. */
.store-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 76px; padding: 14px 18px;
  border-radius: var(--radius); text-decoration: none;
}
.store-badge img { height: 48px; width: auto; }
.store-badge--ondark { background: var(--veld-deep); }
.store-badge--ondark:hover { background: #0E2C15; }
.store-badge--onlight { padding-inline: 0; }
.store-btn {
  display: flex; align-items: center; gap: 12px;
  min-height: 60px; padding: 0 20px; width: 100%;
  background: var(--veld-deep); color: #fff; border: 0; border-radius: var(--radius);
  text-decoration: none; text-align: left;
}
.store-btn:hover { background: #0E2C15; }
.store-btn small {
  display: block; font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: #A9C4AF; font-family: var(--f-mono);
}
.store-btn strong { font-size: 18px; font-weight: 600; }

.steps-num { list-style: none; counter-reset: s; }
.steps-num li {
  counter-increment: s; position: relative;
  padding: 0 0 20px 44px; font-size: var(--t-body);
}
.steps-num li::before {
  content: counter(s, decimal-leading-zero);
  position: absolute; left: 0; top: 2px;
  font: 600 var(--t-micro)/1.6 var(--f-mono);
  color: var(--veld); background: var(--veld-wash);
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
}

/* -------------------------------------------------------------- reports -- */
.rep-group + .rep-group { margin-top: 36px; }
.rep-group h3 {
  font-size: var(--t-h3); padding-bottom: 10px; margin-bottom: 0;
  border-bottom: 2px solid var(--rule-strong);
}
.rep-list { list-style: none; }
.rep-list li {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 4px; border-bottom: 1px solid var(--rule);
  font-size: var(--t-small);
}
@media (min-width: 700px) { .rep-list { columns: 2; column-gap: 40px; } .rep-list li { break-inside: avoid; } }

.badge {
  font: 600 11px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--veld-deep); background: #D3E4D8;
  padding: 5px 9px; border-radius: 999px; flex: none;
}
/* Kept for the two statuses the i18n dictionaries already carry. All sixteen
   reports are Live today; these exist so a future one can ship honestly. */
.badge--progress { background: #F3E3BF; color: #4A3708; }
.badge--soon { background: #E6E2D6; color: #4A4738; }

/* ------------------------------------------------------------------ faq -- */
.faq { max-width: 78ch; border-top: 1px solid var(--rule); }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  min-height: var(--tap); padding: 14px 4px; cursor: pointer;
  font-family: var(--f-head); font-weight: 600; font-size: 18px; color: var(--veld-deep);
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font: 400 26px/1 var(--f-mono); color: var(--veld); flex: none; }
.faq details[open] summary::after { content: "\2013"; }
.faq .answer { padding: 0 4px 20px; color: var(--ink-soft); max-width: 68ch; }
.faq .answer p + p, .faq .answer ol, .faq .answer ul { margin-top: 14px; }

/* ---------------------------------------------------------------- prose -- */
.prose { max-width: 70ch; }
.prose h2 { font-size: clamp(22px, 3vw, 28px); margin-top: 44px; }
.prose h3 { font-size: var(--t-h3); margin-top: 28px; }
.prose p, .prose li { margin-top: 14px; color: var(--ink); }
.prose ul { margin-top: 14px; padding-left: 22px; list-style: disc; }
.prose ol { margin-top: 14px; padding-left: 22px; }
.prose li { padding-left: 4px; }
.prose .lede { font-size: 19px; color: var(--ink-soft); }
.prose a { color: var(--veld); }

/* Callouts. .tip is guidance; .warn is "you can lose data" — used for the
   don't-uninstall-with-records-pending warning, which is the single most
   expensive mistake a farmer can make in this app. */
.tip, .warn {
  margin-top: 20px; padding: 16px 18px; border-radius: var(--radius);
  font-size: var(--t-small);
}
.tip { background: var(--veld-wash); border-left: 4px solid var(--veld); }
.warn { background: var(--soil-wash); border-left: 4px solid var(--soil); }
.tip > :first-child, .warn > :first-child { margin-top: 0; }

/* ------------------------------------------------------- sticky contents -- */
/* guide.html and office-guide.html are long documents with a table of
   contents that must stay reachable. Below the breakpoint it collapses to a
   normal block at the top of the page rather than a sticky rail. */
.doc-layout { display: grid; gap: 32px; }
@media (min-width: 1000px) { .doc-layout { grid-template-columns: 260px 1fr; gap: 48px; align-items: start; } }
.toc { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--paper-2); padding: 18px; }
@media (min-width: 1000px) { .toc { position: sticky; top: calc(var(--header-h) + 20px); max-height: calc(100vh - var(--header-h) - 40px); overflow-y: auto; } }
.toc h2 { font: 600 var(--t-micro)/1 var(--f-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 12px; }
.toc ul { list-style: none; }
.toc a {
  display: flex; align-items: center; min-height: 40px;
  color: var(--ink); text-decoration: none; font-size: var(--t-small);
  border-bottom: 1px solid var(--rule);
}
.toc li:last-child a { border-bottom: 0; }
.toc a:hover, .toc a[aria-current="true"] { color: var(--veld); font-weight: 600; }

/* ---------------------------------------------------------------- table -- */
.table-wrap { overflow-x: auto; margin-top: 20px; }
table { border-collapse: collapse; width: 100%; font-size: var(--t-small); }
th, td { text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--rule); }
thead th {
  font: 600 var(--t-micro)/1 var(--f-mono); letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-soft);
  border-bottom: 2px solid var(--rule-strong);
}

/* -------------------------------------------------------------- gallery -- */
.gallery { display: flex; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 12px; }
.gallery figure { flex: 0 0 78%; max-width: 300px; scroll-snap-align: start; margin: 0; }
@media (min-width: 700px) { .gallery figure { flex: 0 0 250px; } }
.gallery img { aspect-ratio: 9/19; object-fit: cover; border: 1px solid var(--rule-strong); border-radius: 14px; background: var(--paper-2); }
.gallery figcaption { margin-top: 10px; font-size: var(--t-small); color: var(--ink-soft); }
.gallery-ctrl { display: flex; gap: 8px; margin-top: 8px; }
.gallery-ctrl button {
  width: 44px; height: 44px; border: 1px solid var(--border-ui); background: var(--paper-2);
  border-radius: var(--radius); cursor: pointer; font-size: 18px; color: var(--veld-deep);
}
.gallery-ctrl button:hover { background: var(--veld-wash); }

/* ---------------------------------------------------------------- trust -- */
.trust { display: grid; gap: 1px; background: var(--on-ink-rule); border: 1px solid var(--on-ink-rule); border-radius: var(--radius); overflow: hidden; }
@media (min-width: 620px) { .trust { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .trust { grid-template-columns: repeat(4, 1fr); } }
.trust > div { background: var(--veld-deep); padding: 22px; }
.trust h3 { font-size: 17px; margin-bottom: 6px; }
.trust p { font-size: var(--t-small); color: #B9CDBE; }
/* On light bands the same component inverts. */
.band:not(.band--ink) .trust { background: var(--rule); border-color: var(--rule); }
.band:not(.band--ink) .trust > div { background: var(--paper-2); }
.band:not(.band--ink) .trust p { color: var(--ink-soft); }

/* ---------------------------------------------------------------- forms -- */
.field { margin-bottom: 22px; max-width: 44ch; }
.field label { display: block; font-weight: 600; margin-bottom: 6px; color: var(--veld-deep); }
.field .hint { display: block; font-size: var(--t-small); color: var(--ink-soft); margin-bottom: 8px; }
.field input, .field textarea, .field select {
  width: 100%; min-height: var(--tap); padding: 12px 14px;
  border: 1.5px solid var(--border-ui); border-radius: var(--radius);
  background: var(--paper-2); font: 400 var(--t-body)/1.4 var(--f-body); color: var(--ink);
}
.field textarea { min-height: 140px; resize: vertical; }
.field input:focus-visible, .field textarea:focus-visible, .field select:focus-visible { border-color: var(--veld); }
.field .err { display: none; margin-top: 8px; font-size: var(--t-small); color: var(--soil); font-weight: 600; }
.field[data-invalid="true"] input, .field[data-invalid="true"] textarea { border-color: var(--soil); }
.field[data-invalid="true"] .err { display: block; }
/* Honeypot: off-screen, never display:none — some bots skip hidden fields. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --------------------------------------------------------------- footer -- */
.site-footer { background: var(--veld-deep); color: #C3D5C8; padding-block: 56px 40px; }
.foot-grid { display: grid; gap: 32px; }
@media (min-width: 720px) { .foot-grid { grid-template-columns: repeat(3, 1fr); } }
.foot-grid h3 {
  color: var(--on-ink-soft); font-family: var(--f-mono); font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase; margin-bottom: 10px; font-weight: 600;
}
.foot-grid ul { list-style: none; }
.foot-grid a {
  display: flex; align-items: center; min-height: 40px; color: #E3ECE4;
  text-decoration: none; font-size: var(--t-small);
}
.foot-grid a:hover { color: var(--tag); text-decoration: underline; }
.foot-badge { margin-top: 12px; }
.foot-badge img { height: 22px; width: auto; }

/* Facebook + WhatsApp, injected beside the LinkedIn badge by app.js.
   currentColor on the glyphs is what lets ONE rule serve both footers: the
   landing page's is dark green, every other page's is light. A hard-coded
   colour would be invisible on one of the two.
   Sized to the LinkedIn badge's 22px so the three marks share a baseline. */
/* The contact page's visible WhatsApp route, under the help-assistant tip. */
.wa-line { margin: 10px 0 0; color: var(--ink-soft); font-size: var(--t-small); }
.wa-line a { font-weight: 600; font-variant-numeric: tabular-nums; }

.social-row {
  display: inline-flex; align-items: center; gap: 10px;
  margin-left: 10px; vertical-align: -5px;
}
.social-link {
  display: inline-flex; align-items: center; justify-content: center;
  /* 44px is the tap target; the glyph stays 22px inside it. Padding rather than
     width so the row does not jump when it lands next to the badge. */
  min-width: 32px; min-height: 32px;
  color: inherit; opacity: .85; border-radius: var(--radius);
  transition: opacity .15s ease;
}
.social-link:hover, .social-link:focus-visible { opacity: 1; }
@media (max-width: 600px) {
  /* Thumb-sized on a phone, where the footer is the one place a farmer taps
     through to WhatsApp.
     `.social-row .social-link` and not `.social-link`: the landing page's footer
     sets `.foot-grid a { min-height: 40px }`, which is (0,1,1) and outranks a
     lone class — so the plain selector silently lost the height while keeping
     the width, and the icons came out 44x40. --tap is the site's own stated
     minimum and the reason for it is in DESIGN.md: a phone, outdoors, gloves. */
  .social-row .social-link { min-width: var(--tap); min-height: var(--tap); }
  /* On a phone the row wraps below the LinkedIn badge instead of sitting beside
     it, so the 10px it needs as a neighbour becomes a stray indent. Worse, the
     tap box centres a 22px glyph inside 48px, so the MARK starts 13px in even at
     margin 0 — the icons looked shunted right of the badge above them. Pull back
     by exactly that padding so the glyph's left edge lines up with the badge's. */
  .social-row { margin-left: calc((var(--tap) - 22px) / -2); }
}

/* The disclaimer and the gov.za link are a Google Play condition, not styling.
   Never remove either in a cleanup — see CLAUDE.md "Hard rules". */
.legal {
  margin-top: 44px; padding-top: 24px; border-top: 1px solid var(--on-ink-rule);
  font-size: var(--t-micro); line-height: 1.7; color: #A9C0AE;
}
.legal a { color: var(--tag); }
.legal p + p { margin-top: 10px; }

/* ---------------------------------------------------------------- misc -- */
.toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  background: var(--veld-deep); color: #fff; padding: 14px 20px;
  border-radius: var(--radius); font-weight: 600; z-index: 500;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- Anchor targets: clear the sticky header, and DON'T PAY TWICE ---------
   THE BUG THIS FIXES, because it will come back the moment someone "tidies"
   one of these two numbers in isolation:

   The rule below used to be `calc(var(--header-h) + 16px)` for every [id] on
   the site. That silently assumes an element's top edge is where its text
   starts. For a `.band` it is not — 88px of padding comes first. So a jump to
   #install put the SECTION top 84px down, then the section's own padding put
   the heading a further 88px down, and the reader met 103px of empty green
   before the words "Step one".

   Both numbers were individually defensible, set by different reasoning, and
   nothing connected them — which is exactly why nudging either one never fixed
   it. Drop the padding and the section collides with the one above it; drop the
   scroll offset and the header covers the heading.

   The fix is to subtract the padding the element ALREADY provides, so what gets
   positioned is the CONTENT, not the box. Both sides now derive from
   --band-pad, so changing the padding moves the anchor offset with it.

   max(0px, …) is not defensiveness about the maths — it is the case where a
   band's padding already exceeds header + gap. Clamping there lets the tinted
   background slide up under the sticky header, which is what you want anyway. */
[id] { scroll-margin-top: calc(var(--header-total) + var(--anchor-gap)); }
.band[id] {
  scroll-margin-top: max(0px, calc(var(--header-total) + var(--anchor-gap) - var(--band-pad)));
}

@media print {
  .site-header, .drawer, .gallery-ctrl, .skip, #kb-chat-bubble { display: none !important; }
  body { background: #fff; }
  .band { padding-block: 24px; }
}

/* ==========================================================================
   LEGACY PAGE CLASSES

   The content pages predate this stylesheet — each carried its own inline
   <style> with its own :root. Rather than rewrite fifteen pages of working
   markup (the help assistant, the Turnstile contact form, the import
   walkthrough and two long trilingual guides all live in there), their SHARED
   class vocabulary is defined once, here, against the new tokens.

   This is a migration layer, not a second design system. It should SHRINK:
   when a page is genuinely rebuilt on the components above, delete its classes
   from here. Do not add new names to this section — new work uses the
   components above.
   ========================================================================== */

/* --- page containers ---------------------------------------------------- */
.wrap { width: 100%; max-width: var(--shell-prose); margin-inline: auto; padding: 40px var(--gut) 72px; }
main > article, body > article { width: 100%; max-width: var(--shell-prose); margin-inline: auto; padding: 40px var(--gut) 72px; }

/* guide.html + office-guide.html use .shell as their contents+body grid.
   body class="doc-page" keeps that meaning without breaking .shell elsewhere. */
/* `>` IS LOAD-BEARING — do not relax it back to a descendant selector.
   `.shell` is the site's generic width container, and nav.js puts one INSIDE
   the sticky header (`.shell.header-in`). As a descendant selector this rule
   also caught that one, turning the header into a two-column grid that stacked
   its seven items: a 345px sticky header on /docs and /office-guide, eating 43%
   of an 800px screen on every scroll position. The page content shell is a
   direct child of <body>, the header's is not, so the child combinator selects
   exactly the one this layout is for. */
body.doc-page > .shell { max-width: 1080px; display: grid; grid-template-columns: 1fr; gap: 32px; padding-block: 40px 72px; }
@media (min-width: 1000px) {
  body.doc-page > .shell { grid-template-columns: 260px 1fr; gap: 48px; align-items: start; }
}

/* --- page headings ------------------------------------------------------ */
h1.title, .title { font-size: var(--t-h2); margin: 0; }
.lead, .intro { color: var(--ink-soft); font-size: 19px; max-width: 62ch; margin: 14px 0 28px; }
.updated, .byline { color: var(--ink-soft); font-size: var(--t-small); margin: 8px 0 20px; }
.crumb {
  font: 600 var(--t-micro)/1 var(--f-mono); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-soft); margin: 0 0 14px;
}
.crumb a { color: var(--ink-soft); }

/* Long-form bodies. The old sheets put an orange bar under every h2; the bar
   is now an ear-tag yellow rule, in the ledger vernacular. */
.content h2, article h2 { font-size: clamp(22px, 3vw, 28px); margin: 44px 0 10px; }
.content h2::after, article h2::after {
  content: ""; display: block; width: 48px; height: 2px;
  background: var(--tag); border-radius: 2px; margin-top: 12px;
}
.content h3, article h3 { font-size: var(--t-h3); margin: 28px 0 6px; }
.content p, .content li, article p, article li { font-size: var(--t-body); margin-top: 12px; }
.content ul, .content ol, article ul, article ol { padding-left: 22px; margin-top: 12px; }
.content { min-width: 0; }

code {
  background: var(--veld-wash); padding: 2px 6px; border-radius: 4px;
  font: 500 .9em var(--f-mono);
}
pre.example, pre {
  background: var(--paper-2); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 14px; overflow-x: auto; font: 500 var(--t-micro)/1.7 var(--f-mono); margin: 12px 0;
}

/* --- callouts ----------------------------------------------------------- */
/* .tip and .warn are defined above and already match. .note is the third. */
.callout { border-radius: var(--radius); padding: 16px 18px; margin: 20px 0; font-size: var(--t-small); }
.box { background: var(--veld-wash); border-left: 4px solid var(--veld); border-radius: var(--radius); padding: 16px 18px; margin: 20px 0; font-size: var(--t-small); }
.box strong { color: var(--veld-deep); }
.note, .draft, .coming-soon {
  background: #FBF2DF; border-left: 4px solid var(--tag);
  border-radius: var(--radius); padding: 16px 18px; margin: 20px 0;
  font-size: var(--t-small); color: #4A3708;
}
.note a, .draft a, .coming-soon a { color: #6B4E0A; }
.callout-title { margin: 0 0 6px; font-family: var(--f-head); font-weight: 600; font-size: var(--t-h3); }
.callout__badge {
  display: inline-block; background: var(--tag); color: var(--tag-ink);
  font: 600 11px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase;
  padding: 5px 11px; border-radius: 999px; margin-bottom: 8px;
}
.callout p:last-child, .callout ul:last-child, .box p:last-child, .note p:last-child { margin-bottom: 0; }

/* --- small inline pieces ------------------------------------------------ */
.stepnum {
  display: inline-block; background: var(--veld); color: #fff;
  font: 600 var(--t-micro)/1 var(--f-mono); border-radius: 999px;
  padding: 5px 11px; margin-right: 6px; vertical-align: middle;
}
.req { color: var(--soil); font-weight: 600; }
.hint { color: var(--ink-soft); font-size: var(--t-small); }
.tablewrap { overflow-x: auto; margin: 20px 0; }
.tablewrap table { min-width: 560px; }
th { background: var(--veld-wash); }

/* A bare .btn (no modifier) meant "primary" on the old pages. :where() keeps
   specificity at zero so .btn--secondary still wins. */
.btn:where(:not([class*="btn--"])) { background: var(--veld); color: #fff; }
.btn:where(:not([class*="btn--"])):hover { background: var(--veld-dark); }

/* --- resources hub ------------------------------------------------------ */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; }
a.rcard {
  display: block; text-decoration: none; color: var(--ink);
  background: var(--paper-2); border: 1px solid var(--border-ui);
  border-radius: var(--radius); padding: 24px;
}
a.rcard:hover { border-color: var(--veld); background: var(--paper); }
a.rcard h2 { font-size: var(--t-h3); margin: 8px 0 6px; }
a.rcard h2::after { display: none; }         /* no rule inside a card */
a.rcard p { color: var(--ink-soft); font-size: var(--t-small); margin: 0; }
/* .tag is the ear-tag chip everywhere else; inside a resource card the old
   markup used it as a plain label. Scoped override, not a redefinition. */
a.rcard .tag {
  display: block; min-height: 0; padding: 0; background: none; color: var(--veld);
  border-radius: 0; font: 600 var(--t-micro)/1 var(--f-mono);
  letter-spacing: .1em; text-transform: uppercase;
}
a.rcard .tag::before { display: none; }

/* --- contents rail (guide pages) ---------------------------------------- */
.toc-title { font: 600 var(--t-micro)/1 var(--f-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); margin: 0 0 12px; }
.help-cta { margin-top: 40px; background: var(--veld-wash); border-radius: var(--radius); padding: 22px; }
.help-cta a.btn { margin-top: 10px; }

/* --- import page -------------------------------------------------------- */
.dl { background: var(--veld-wash); border: 1px solid var(--rule); border-radius: var(--radius); padding: 28px 22px; text-align: center; margin: 0 0 20px; }
.dl .btn { font-size: 18px; padding: 0 32px; min-height: 60px; }
.dl .hint { margin-top: 14px; }
.shot { border: 1px solid var(--rule-strong); border-radius: var(--radius); overflow: hidden; margin: 16px 0; }
.cta { background: var(--veld-wash); border-radius: var(--radius); padding: 22px; margin: 28px 0; }

/* --- accordions (import + articles) ------------------------------------- */
details.faq {
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 0 16px; margin: 12px 0; background: var(--paper-2);
}
details.faq summary {
  cursor: pointer; font-family: var(--f-head); font-weight: 600;
  padding: 14px 28px 14px 0; list-style: none; position: relative;
  min-height: var(--tap); display: flex; align-items: center;
}
details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::after { content: "+"; position: absolute; right: 2px; font: 400 24px/1 var(--f-mono); color: var(--veld); }
details.faq[open] summary::after { content: "\2013"; }
details.faq .ans { padding: 0 0 16px; color: var(--ink-soft); }

/* --- legal pages -------------------------------------------------------- */
.eml { font-family: var(--f-mono); }

/* --- page-level footer (the simple centred one, not .site-footer) ------- */
footer:not(.site-footer) {
  border-top: 1px solid var(--rule); margin-top: 44px; padding: 24px var(--gut);
  text-align: center; color: var(--ink-soft); font-size: var(--t-small);
}
footer:not(.site-footer) a { color: var(--veld); text-decoration: none; margin: 0 8px; }
footer:not(.site-footer) a:hover { text-decoration: underline; }

/* --- 404 ---------------------------------------------------------------- */
img.logo { width: 84px; height: 84px; margin: 0 auto 20px; }
.code { font: 700 clamp(48px, 10vw, 76px)/1 var(--f-mono); color: var(--veld); margin: 0; }
.actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
.actions a { text-decoration: none; font-weight: 600; min-height: var(--tap); display: inline-flex; align-items: center; padding: 0 22px; border-radius: var(--radius); }
.actions .primary { background: var(--veld); color: #fff; }
.actions .primary:hover { background: var(--veld-dark); }
.actions .ghost { background: var(--paper-2); color: var(--veld); border: 1.5px solid var(--veld); }
.actions .ghost:hover { background: var(--veld-wash); }
.links { margin-top: 24px; font-size: var(--t-small); }
.links a { color: var(--veld); text-decoration: none; margin: 0 8px; }

/* --- /help : the assistant --------------------------------------------- */
header.bar { background: var(--veld-deep); color: #fff; padding: 20px var(--gut) 22px; }
header.bar .row { max-width: 720px; margin-inline: auto; display: flex; align-items: center; gap: 14px; }
header.bar img { width: 46px; height: 46px; }
header.bar h1 { font-size: var(--t-h3); margin: 0; color: #fff; }
header.bar p { margin: 2px 0 0; font-size: var(--t-small); color: var(--on-ink-soft); }
header.bar a.home { margin-left: 10px; color: var(--on-ink); text-decoration: none; font-size: var(--t-small); font-weight: 600; white-space: nowrap; }
header.bar .lang-switch { margin-left: auto; display: flex; gap: 2px; }
header.bar .lang-switch button {
  border: 0; background: transparent; color: var(--on-ink);
  font: 600 var(--t-micro)/1 var(--f-mono); min-width: 44px; min-height: 44px;
  border-radius: var(--radius); cursor: pointer;
}
header.bar .lang-switch button[aria-pressed="true"] { background: var(--tag); color: var(--tag-ink); }

.chat {
  background: var(--paper-2); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 16px; display: flex; flex-direction: column; gap: 12px; min-height: 320px;
}
.msgs { display: flex; flex-direction: column; gap: 12px; }
.msg { display: flex; gap: 10px; align-items: flex-start; max-width: 100%; }
.msg .av { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font-size: 15px; }
.msg.bot .av { background: var(--veld-wash); }
.msg.you { flex-direction: row-reverse; }
.msg.you .av { background: var(--tag); color: var(--tag-ink); }
.bubble { padding: 10px 14px; border-radius: 12px; font-size: var(--t-small); white-space: pre-wrap; overflow-wrap: anywhere; }
.msg.bot .bubble { background: var(--veld-wash); border-top-left-radius: 3px; }
.msg.you .bubble { background: var(--veld); color: #fff; border-top-right-radius: 3px; }
.typing .bubble { color: var(--ink-soft); font-style: italic; }
.fallback { border: 1px dashed var(--tag); background: #FBF2DF; border-radius: var(--radius); padding: 12px 14px; margin-left: 40px; color: #4A3708; }
.demo-badge { display: none; margin: 0 auto 14px; max-width: 720px; background: #FBF2DF; border: 1px solid #EBD9AC; color: #4A3708; font-size: var(--t-small); padding: 10px 14px; border-radius: var(--radius); text-align: center; }
.demo-badge.show { display: block; }

/* --- /contact : the form ------------------------------------------------ */
/* This markup predates .field — it is bare label + input inside #contactForm.
   Scoped to the form so it cannot leak into anything else. */
#contactForm { max-width: 44rem; }
#contactForm label { display: block; font-weight: 600; margin: 20px 0 6px; color: var(--veld-deep); }
#contactForm input, #contactForm select, #contactForm textarea {
  width: 100%; min-height: var(--tap); padding: 12px 14px;
  border: 1.5px solid var(--border-ui); border-radius: var(--radius);
  background: var(--paper-2); font: 400 var(--t-body)/1.4 var(--f-body); color: var(--ink);
}
#contactForm textarea { min-height: 150px; resize: vertical; }
#contactForm input:focus-visible, #contactForm select:focus-visible, #contactForm textarea:focus-visible { border-color: var(--veld); }
.opt { color: var(--ink-soft); font-weight: 400; font-size: var(--t-small); }
#tsBox { margin: 20px 0 0; min-height: 66px; }

button.send {
  margin-top: 20px; min-height: var(--tap); padding: 0 28px;
  background: var(--veld); color: #fff; border: 0; border-radius: var(--radius);
  font: 600 var(--t-body)/1 var(--f-body); cursor: pointer;
}
button.send:hover { background: var(--veld-dark); }
button.send[disabled] { opacity: .6; cursor: wait; }

/* ⚠️ These two are EMPTY until the form is submitted and are revealed by
   inline style from the page's own script. Without display:none they render
   as two blank coloured boxes on first paint. The component .panel above (the
   homepage's before/after column) does not set display, so this must. */
.panel.ok, .panel.err {
  display: none; border-radius: var(--radius); padding: 16px 18px;
  margin: 20px 0; font-size: var(--t-body); overflow: visible;
}
.panel.ok { background: var(--veld-wash); border: 1px solid var(--rule); border-left: 4px solid var(--veld); color: var(--veld-deep); }
.panel.err { background: var(--soil-wash); border: 1px solid #E0C4B4; border-left: 4px solid var(--soil); color: var(--soil); }

/* ==========================================================================
   /pricing

   Ported from the page's own inline sheet on 2026-08-12, the last page to move.
   Everything is scoped under .pricing-page on purpose: this page reuses .box,
   .band, .note, .wrap and .btn with DIFFERENT meanings from the components
   above (its .box is a white bordered card, not a green tip; its .note is a
   bordered cell, not an amber callout; its .wrap is 1040px, not prose width).
   Scoping was the cheap way to keep the page's layout reasoning intact without
   renaming classes its own script reassigns at runtime.

   ⚠️ Class names here are load-bearing. renderTiers() rewrites `.notice` to
   "notice notice-info"/"notice notice-wait" and `.tier-price` to
   "tier-price"/"tier-price pending". Do not rename them.

   Spacing is the page's original 4pt grid, written out as literals rather than
   re-declaring a --s1..--s8 scale on the page.
   ========================================================================== */

.pricing-page .wrap { width: 100%; max-width: 1040px; margin-inline: auto; padding-inline: var(--gut); }

/* The two sections above the plan cards give back their generous block padding.
   .wrap ships `padding: 40px var(--gut) 72px`, and the pricing override only
   replaced the INLINE padding — so 72px of section bottom met 40px of the next
   section's top and put 112px of empty page between the country picker and the
   prices. Everything below the cards keeps the site's normal rhythm. */
.pricing-page section[aria-labelledby="country-h"] { padding-block: 0 20px; }
.pricing-page #tiers { padding-block: 0 72px; }

/* THE SAME BUG, ONE ELEMENT HIGHER — missed when the two sections above were
   fixed. `.head` sets its own block padding (24/16, and 16/12 on a phone), then
   nests a `.wrap`, which ships `padding: 40px var(--gut) 72px`. So the page top
   paid 40 + 16 = 56px before the accent rule, and the intro was followed by
   72 + 12 = 84px of nothing before the country picker — on a phone, the largest
   thing on the first screen was the hole under the paragraph.

   `.wrap` is a generic WIDTH container that also carries page-level BLOCK
   padding, which is only ever right when it is the outermost element. Nest it
   and that padding lands mid-page. Same shape as the `.shell` bug in the doc
   layout; see DESIGN.md. */
.pricing-page .head .wrap { padding-block: 0; }
.pricing-page h1 { font-size: var(--t-h2); }
.pricing-page h2 { font-size: 24px; }
.pricing-page h3 { font-size: 19px; }

/* The accent bar under a section heading. Ear-tag yellow now, not orange. */
.pricing-page .rule { width: 44px; height: 3px; background: var(--tag); border-radius: 999px; margin-bottom: 16px; }

/* Everything above the plan cards is on a budget: on a 1280x800 laptop the three
   cards must be visible without scrolling, and previously the heading, a 19px
   lead, the country bar AND the free-documents notice all sat above them. The
   notice has moved below the cards; the rest is tightened here. The lead stays —
   it is the page's positioning line — but at --t-body rather than 19px. */
/* 16px is the PHONE value and is repeated in the max-600 block below, where the
   reasoning for it lives. Desktop gets 40px, which is what every other page's
   top gap measures (contact's breadcrumb sits 40px under the header) — this page
   used to reach ~56px only by accident, because the nested `.wrap` added 40 on
   top of this 16. With that removed, 16px alone left the page looking cropped
   against the header on a wide screen. */
.pricing-page .head { padding: 16px 0 12px; }
@media (min-width: 601px) { .pricing-page .head { padding: 40px 0 16px; } }
.pricing-page .head .rule { margin-bottom: 10px; }
.pricing-page .head p { margin-top: 8px; color: var(--ink-soft); font-size: var(--t-body); max-width: 72ch; }

/* --- country bar --------------------------------------------------------
   This is the control that changes every price and limit on the page, and it
   used to read as a caption with a dropdown after it. Now it is the loudest
   thing between the heading and the cards: tinted ground, its own label above
   the control rather than inline, and a select sized like a primary field. */
.pricing-page .country {
  border: 1px solid var(--rule); border-radius: var(--radius); padding: 16px 20px;
  background: var(--veld-wash);
  display: flex; flex-wrap: wrap; gap: 16px 24px; align-items: center;
}
.pricing-page .country-pick { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.pricing-page .country-pick label {
  font: 600 var(--t-micro)/1.2 var(--f-mono);
  letter-spacing: 0.04em; text-transform: none;
  color: var(--veld-deep);
}
.pricing-page .country-select {
  min-height: var(--tap); padding: 0 44px 0 14px;
  font: 600 var(--t-h3)/1.4 var(--f-body);
  min-width: 280px; max-width: 100%;
  color: var(--ink); background: var(--paper-2);
  border: 1.5px solid var(--border-ui); border-radius: var(--radius);
  /* The native chevron is inconsistent across browsers and easy to miss at this
     size; an explicit one makes it obvious the field opens. Inline SVG, so no
     extra request and nothing for the CSP to allow. */
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%231F5C2E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}
.pricing-page .country-state { color: var(--ink-soft); flex: 1; min-width: 260px; }
.pricing-page .country-state strong { color: var(--ink); font-weight: 600; }

.pricing-page .notice {
  margin-top: 24px; padding: 16px 24px; border-radius: var(--radius);
  display: flex; gap: 12px; align-items: flex-start;
}
.pricing-page .notice svg { flex: none; margin-top: 4px; }
.pricing-page .notice-info { background: var(--veld-wash); color: var(--ink); }
/* "we don't produce your country's documents yet" — meaning, so it uses the
   amber that passes as TEXT (7.3:1 on paper), never the accent yellow. */
.pricing-page .notice-wait { background: #FBF2DF; color: #6B4E0A; border: 1px solid #C9A227; }
.pricing-page .notice a { color: inherit; font-weight: 600; text-underline-offset: 3px; }
/* The launch-offer banner lives in the page head, directly under the lead. It reuses the
   info-notice look so it reads as a fact about every plan, not a sale sticker. */
.pricing-page .head .launch { margin-top: 20px; max-width: 78ch; }

/* --- the three plans ---
   A tinted head (name, who it is for, price) over a white body (what you get,
   then the button). The two-part split is what makes a card read as an object
   rather than a bordered list. Cards stretch to equal height so the three
   buttons land on one line. */
.pricing-page .tiers { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-top: 16px; }
/* Four cards at 1fr each need the full 1040px shell to stay readable — the 24px name and the
   31px price were sized for three. Below that, two-by-two keeps every card a proper column
   rather than a sliver; the featured card is second, so it lands top-right, still above the fold. */
@media (max-width: 1099px) { .pricing-page .tiers { grid-template-columns: repeat(2, 1fr); } }
.pricing-page .tier {
  border: 1px solid var(--rule); border-radius: var(--radius); background: var(--paper-2);
  display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 1px 2px rgba(28,31,26,.05), 0 8px 20px rgba(28,31,26,.05);
}
.pricing-page .tier-head { padding: 24px 24px 16px; background: #FDFCF8; border-bottom: 1px solid var(--rule); }
.pricing-page .tier-body { padding: 24px; display: flex; flex-direction: column; flex: 1; }
/* The middle card carries the recommendation: full-weight green edge, a deeper
   shadow so it sits above the other two, and the brand tint in its head. */
.pricing-page .tier-featured {
  border: 2px solid var(--veld);
  box-shadow: 0 4px 10px rgba(31,92,46,.10), 0 18px 44px rgba(31,92,46,.18);
}
.pricing-page .tier-featured .tier-head { background: var(--veld-wash); border-bottom-color: #BFD5C4; }
.pricing-page .tier-flag {
  display: inline-block; margin-bottom: 8px; padding: 4px 12px; border-radius: 999px;
  background: var(--veld); color: #fff; font: 600 var(--t-micro)/1.4 var(--f-mono);
  letter-spacing: .06em; text-transform: uppercase;
}
.pricing-page .tier h3 { font-size: 24px; margin-bottom: 8px; }
.pricing-page .tier-for { color: var(--ink-soft); margin-bottom: 16px; min-height: 3.2em; }
/* The price is a figure, so it is set in the mono face with tabular numerals —
   the same treatment tag numbers and dates get everywhere else on the site. */
.pricing-page .tier-price {
  font: 600 31px/1.1 var(--f-mono); letter-spacing: -.01em;
  font-variant-numeric: tabular-nums; color: var(--veld);
}
.pricing-page .tier-per { font: 600 var(--t-body)/1.4 var(--f-body); letter-spacing: 0; color: var(--ink-soft); }
.pricing-page .tier-price.pending { font: 600 19px/1.3 var(--f-body); color: #6B4E0A; }
/* 3.2em = two lines at this size. Reserved so the head/body divider sits at the
   same height on the two plain cards whether the sub wraps or not. */
.pricing-page .tier-price-sub { color: var(--ink-soft); font-size: var(--t-small); margin-top: 8px; min-height: 3.2em; }
.pricing-page .tier ul { list-style: none; margin: 0 0 24px; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.pricing-page .tier li { display: flex; gap: 12px; align-items: flex-start; }
.pricing-page .tier li .tick { color: var(--veld); font-weight: 700; flex: none; }
.pricing-page .tier li .cross { color: var(--ink-soft); font-weight: 700; flex: none; }
.pricing-page .tier li.off { color: var(--ink-soft); }
/* margin-top:auto pushes the button to the bottom, so the three CTAs line up
   even when the feature lists are different lengths. */
.pricing-page .tier-cta {
  margin-top: auto; padding: 12px 16px; min-height: var(--tap);
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius); font-weight: 600; text-decoration: none;
}
.pricing-page .tier-cta-primary { background: var(--veld); color: #fff; }
.pricing-page .tier-cta-primary:hover { background: var(--veld-dark); }
.pricing-page .tier-cta-ghost { border: 1.5px solid var(--border-ui); color: var(--veld); }
.pricing-page .tier-cta-ghost:hover { background: var(--veld-wash); }
.pricing-page .note-names { color: var(--ink-soft); font-size: var(--t-small); margin-top: 16px; }

/* --- bands and boxes ---------------------------------------------------- */
.pricing-page .band { background: var(--veld-wash); margin-top: 64px; padding: 48px 0; border-top: 0; }
.pricing-page .band-grid { display: grid; grid-template-columns: 1.1fr 1fr; gap: 48px; align-items: start; }
.pricing-page .band ul { list-style: none; margin: 24px 0 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.pricing-page .band li { display: flex; gap: 12px; align-items: flex-start; }
.pricing-page .band li .tick { color: var(--veld); font-weight: 700; flex: none; }
.pricing-page .callout {
  background: var(--paper-2); border-radius: var(--radius); padding: 24px;
  border-left: 5px solid var(--veld); margin: 0;
}
.pricing-page .callout h3 { margin-bottom: 12px; }
.pricing-page .callout p + p { margin-top: 12px; }

.pricing-page .section { padding: 48px 0 0; }
.pricing-page .box {
  background: var(--paper-2); border: 1px solid var(--rule); border-left: 1px solid var(--rule);
  border-radius: var(--radius); padding: 24px; margin-top: 24px; font-size: var(--t-body);
}
.pricing-page .box p + p { margin-top: 12px; }
.pricing-page .box ul { margin: 12px 0 0; padding-left: 24px; list-style: disc; }
.pricing-page .box li + li { margin-top: 4px; }

.pricing-page .notes-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; margin-top: 24px; }
.pricing-page .note {
  border: 1px solid var(--rule); border-left: 1px solid var(--rule); background: none;
  border-radius: var(--radius); padding: 16px; margin: 0; color: var(--ink); font-size: var(--t-body);
}
.pricing-page .note h3 { font-size: var(--t-body); margin-bottom: 8px; }
.pricing-page .note p { color: var(--ink-soft); }

.pricing-page .final { padding: 48px 0 64px; }
.pricing-page .final p { margin-top: 16px; color: var(--ink-soft); max-width: 60ch; }
.pricing-page .final-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
/* This page uses .btn-primary / .btn-ghost (single dash), not the site's
   .btn--primary. The :not() is load-bearing: nav.js injects a shared
   .btn.btn--primary into this page's header, and without the guard this rule
   outranks .btn--primary (0,2,0 vs 0,1,0) and strips its background — leaving
   white text on paper, i.e. an invisible "Get the app" button. */
/* Layout only — it must NOT set a background. The legacy fallback that makes a
   bare .btn green uses :where(), so it carries zero specificity and the
   .btn-primary / .btn-ghost rules below already beat it. Adding `background:
   none` here instead outranked them (0,3,0 with the :not, against their 0,2,0)
   and blanked the page's own CTA. */
.pricing-page .btn:not([class*="btn--"]) {
  min-height: var(--tap); padding: 0 32px; display: inline-flex; align-items: center;
  border-radius: var(--radius); font-weight: 600; text-decoration: none;
}
.pricing-page .btn-primary { background: var(--veld); color: #fff; }
.pricing-page .btn-primary:hover { background: var(--veld-dark); }
.pricing-page .btn-ghost { border: 1.5px solid var(--border-ui); color: var(--veld); background: var(--veld-wash); }
.pricing-page .btn-ghost:hover { background: var(--paper-2); }

.pricing-page .foot-note { margin-top: 12px; font-size: var(--t-micro); max-width: 90ch; margin-inline: auto; }

@media (max-width: 699px) { .pricing-page .tiers { grid-template-columns: 1fr; } }
@media (max-width: 880px) {
  .pricing-page .band-grid { grid-template-columns: 1fr; gap: 24px; }
}
/* On a phone the header, the country bar and the documents notice stack, so the
   first card landed below the fold — nothing but preamble on open. Body text
   stays put: the size floor is not a space-saving lever. */
@media (max-width: 600px) {
  .pricing-page .head { padding: 16px 0 12px; }
  .pricing-page .head p { font-size: var(--t-body); }
  .pricing-page .country { padding: 16px; gap: 12px; }
  .pricing-page .notice { margin-top: 16px; padding: 12px 16px; }
  .pricing-page .tiers { margin-top: 16px; }
  .pricing-page .tier-head { padding: 16px 16px 12px; }
  .pricing-page .tier-body { padding: 16px; }
  /* Stacked cards size to their own content, so reserving space for a wrapped
     line just adds gaps that only existed to align three columns. */
  .pricing-page .tier-for, .pricing-page .tier-price-sub { min-height: 0; }
}
