/* AgroLiquid Fertilizer Recommendation -- wizard styling.

   COLOUR SOURCE: AgroLiquid Brand Standards, March 2026, p31. The corporate
   palette is FOUR colours, and p30 is explicit: "Use only the color references
   listed below in the AgroLiquid Color Palette chart."

     PANTONE 356   #007A33   corporate green
     PANTONE 362   #509E2F   secondary green
     Cool Grey 10  #62666A
     Cool Grey 1   #D9D9D6

   There is no navy in the palette. An earlier version of this file used
   #0F803F green and #0A2742 navy, both sampled from agroliquid.com rather than
   the standard -- the navy is a black scrim at partial opacity on the website
   header, not a brand colour at all.

   TYPOGRAPHY: p27 names Frutiger Next Pro (sans) and Sabon (serif) as the
   brand faces, neither licensed for web use here, and p28 offers Calibri and
   Garamond as the online substitutes.

   agroliquid.com does not use either. Measured live, it serves Teko 600 for
   display and Lato for body, with Exo 2 as a secondary -- so those are what a
   grower recognises as AgroLiquid, and they are what this app uses. The
   palette below still follows p31 to the letter, because that instruction is
   unambiguous ("use only the color references listed below") and #007A33 is
   visually indistinguishable from the #0F803F the site actually ships.

   Both choices are one line each if Marketing rules differently.

   STATUS COLOURS: the standard governs brand reproduction, not interface
   state, and a soil report needs low / optimal / high semantics. Rather than
   invent them these are drawn from the APPROVED PRODUCT palette (p34-p37) and
   are marked PENDING MARKETING CONFIRMATION, matching the FCIP console.        */

:root {
  /* The reader's text size, 0.9375 / 1.25 / 1.5. wizard.js sets it from the
     control in the masthead; this is the value before it has, and it is the
     normal size so the page does not paint large and then shrink. */
  --ui-scale: 0.9375;
  /* Step 3 nutrient cards: one tone per nutrient, from the approved product
     palette (Brand Standards p34-p37). -ink is for text and the symbol,
     -bg for the card head and pass tiles. */
  --tone-k-ink: #8A4B12;  --tone-k-bg: #FDF0DC;
  --tone-ca-ink: #155F8C; --tone-ca-bg: #E6F1FA;
  --tone-mg-ink: #3B7A22; --tone-mg-bg: #EDF5E8;
  --tone-s-ink: #7A5A10;  --tone-s-bg: #FBF3DD;
  --tone-p-ink: #8E1A1F;  --tone-p-bg: #FBECEC;
  --tone-na-ink: #4A4E52; --tone-na-bg: #F0F1F2;
  --tone-ink: var(--tone-na-ink);
  --tone-bg: var(--tone-na-bg);
  /* --- corporate palette, exact (p31) ----------------------------------- */
  --al-green-356: #007A33;
  --al-green-362: #509E2F;
  --al-grey-10: #62666A;
  --al-grey-1: #D9D9D6;

  /* --- semantic tokens -------------------------------------------------- */
  --green: var(--al-green-356);
  --green-accent: var(--al-green-362);
  /* Interaction tints, not brand colours: a darker 356 for button hover and a
     wash of it for panel fills. Derived, and labelled as derived. */
  --green-dark: #005E27;
  --green-light: #EAF3EC;
  --green-wash: #D7E8DD;

  --grey-900: #1F2224;
  --grey-600: var(--al-grey-10);
  /* Not a corporate colour and not for text: #8A8D90 measures 3.34:1 on white
     and 2.94:1 on the green cards, against the 4.5:1 normal-size text needs.
     Secondary text uses --grey-600 (Cool Grey 10, 5.79:1 and 5.11:1), which is
     both legible and actually in the palette. What is left here is disabled
     states, which 1.4.3 exempts, and a couple of rules, which are not text. */
  --grey-400: #8A8D90;
  --grey-200: var(--al-grey-1);
  --grey-100: #ECECEA;
  --grey-50: #F4F5F4;
  --white: #ffffff;

  /* Status. PENDING MARKETING CONFIRMATION -- sourced from the approved
     product palette so nothing here is invented:
       optimal   PANTONE 356   p31  #007A33
       high      PANTONE 354*  p37  #d59e28, darkened for text contrast
       low       PANTONE 7621  p34  #B01F25                                   */
  --low: #B01F25;
  --low-bg: #FBEDED;
  --optimal: var(--al-green-356);
  --optimal-bg: #EAF3EC;
  --high: #8A6414;
  --high-bg: #FBF3E2;

  /* The accent the step 5 mockup draws in yellow. There is no yellow in the
     corporate palette -- it has exactly four colours and none of them is one --
     so this is PANTONE 354 from the approved PRODUCT palette, the same source
     the status colours above come from, at its own value rather than the
     darkened text variant. PENDING MARKETING CONFIRMATION, like they are.
     Used for "chosen" and "carry on", never for a warning: --high is the
     warning, and a button that means "this is your plan" must not wear the
     colour that means "look at this". */
  --accent: #d59e28;
  --accent-ink: var(--grey-900);

  --font-sans: Lato, "Segoe UI", system-ui, sans-serif;
  --font-display: Teko, Lato, "Segoe UI", sans-serif;

  --space: 8px;
  --radius: 6px;
  --shadow: 0 1px 3px rgba(31, 34, 36, 0.12);
  --shadow-lg: 0 4px 16px rgba(31, 34, 36, 0.14);
}

* { box-sizing: border-box; }

/* Type is sized in rem against the browser's own base, so a grower who has set
   a larger default text size gets it. Nothing is capped with maximum-scale, so
   pinch zoom works too (WCAG 1.4.4). */
/* --ui-scale is the reader's text size (see TEXT_SIZES in wizard.js). Every
   size in this sheet is rem, so scaling the root moves type, spacing and
   controls together -- growing only the words would push them into the
   boxes around them. */
html { font-size: calc(100% * var(--ui-scale, 1)); -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--grey-900);
  background: var(--grey-50);
  /* Belt and braces against a stray wide child on a phone. */
  overflow-x: hidden;
}

/* Skip link: the first thing a keyboard user meets, hidden until focused. */
/* `hidden` must actually hide.
 *
 * The browser's own rule for it is [hidden] { display: none }, which any class
 * selector outranks -- so .wizard-nav { display: flex } quietly beat it and the
 * Back/Next bar stayed on screen through the sign-in gate. The step rail above
 * it disappeared correctly, because .steps-nav happens to set no display, and
 * that difference is what made it look like the gate was working.
 *
 * !important because the whole point of the attribute is that it wins. Without
 * this, hiding anything with a display rule silently does nothing. */
[hidden] { display: none !important; }

.skip-link {
  position: absolute; left: var(--space); top: calc(var(--space) * -8); z-index: 100;
  background: var(--green); color: var(--white); padding: 12px 20px;
  border-radius: 0 0 var(--radius) var(--radius); font-weight: 700;
  text-decoration: none; transition: top 0.15s;
}
.skip-link:focus { top: 0; }
/* The step container takes focus programmatically after the skip link and after
   a step change, purely so a screen reader lands there. It is not interactive,
   so a ring on it would only be confusing. */
#app:focus { outline: none; }

/* One visible focus style everywhere, dark enough to be seen on the green
   buttons as well as on white (WCAG 2.4.7, 2.4.11). */
:where(button, a, [tabindex]):focus-visible {
  outline: 3px solid var(--green-dark);
  outline-offset: 2px;
  border-radius: var(--radius);
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--grey-900);
  margin: 0 0 calc(var(--space) * 1.5);
  line-height: 1.15;
}
h4 {
  font-family: var(--font-sans); font-weight: 700; font-size: 1.0625rem;
  color: var(--grey-900); margin: 0 0 var(--space);
}

/* clamp() so headings shrink on a phone and grow on a laptop without a
   breakpoint for each. */
h1 { font-size: clamp(1.5rem, 1.15rem + 1.7vw, 2.125rem); }
h2 { font-size: clamp(1.375rem, 1.05rem + 1.5vw, 1.875rem); }
h3 { font-size: clamp(1.15rem, 1rem + 0.8vw, 1.5rem); }

p { margin: 0 0 var(--space); }

/* --- shell ------------------------------------------------------------ */

.masthead {
  background: var(--white);
  border-bottom: 3px solid var(--green);
  padding: calc(var(--space) * 1.25) calc(var(--space) * 3);
  display: flex;
  align-items: center;
  gap: calc(var(--space) * 3);
  flex-wrap: wrap;
}

/* p12 requires clear space around the logo; the gap and padding above provide
   it, and the logo is never scaled below the 0.25" minimum on screen. 48px is
   half an inch: the masthead was 100px tall at 62px, a tenth of a laptop
   screen spent before the work began (owner, October 2026). */
.masthead .brand-logo { display: block; height: 48px; width: auto; }
/* min-width:0 so the title block can shrink, and wrap so it does something
   useful when it has to. A flex item will not go below its content width
   without this, so at 200% text the masthead stopped wrapping and pushed the
   whole page sideways instead -- WCAG 1.4.4 asks for 200% without loss of
   content, and a heading off the right edge is loss of content. */
.masthead > div { min-width: 0; }
.masthead h1 {
  margin: 0; font-size: 1.5rem; color: var(--grey-900);
  overflow-wrap: break-word; hyphens: auto;
}
.masthead .tagline { color: var(--grey-600); font-size: 0.875rem; margin: 0; }

/* Who is signed in, and the way out. Pushed to the far end of the masthead,
   because it is not part of the work -- but it is on every screen, because a
   shared office machine left signed in as the last person is how one
   partner's quote ends up under another partner's name. */
.account {
  margin-left: auto; display: flex; align-items: center; gap: var(--space);
  min-width: 0;
}
.account-who {
  font-size: 0.875rem; color: var(--grey-600);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 16rem;
}

/* The language picker: a labelled select in the masthead, on the sign-in
   page only. It leaves once somebody is signed in, which gives the text size,
   the address and Sign out the row on a phone. */
.lang-pick { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.lang-pick-label { font-size: 0.875rem; color: var(--grey-600); font-weight: 400; }
.masthead .lang-pick .lang-select {
  width: auto; min-width: 8.5rem; min-height: 40px; padding: 6px 10px;
  font-size: 0.9375rem;
}
@media (max-width: 540px) {
  .masthead .lang-pick-label { display: none; }
}

/* The text size control, the same shape as the retail partner portal's: a
   pill of three A's, each drawn at the size it sets, the chosen one filled
   green. Buttons rather than a select because all three are visible at
   once and each shows what it does. */
.textsize {
  display: inline-flex; align-items: center; gap: 0.15rem;
  border: 1px solid var(--grey-200); border-radius: 999px;
  padding: 0.15rem; background: var(--white); flex-shrink: 0;
}
.textsize-label {
  font-size: 0.875rem; color: var(--grey-600);
  margin-right: 0.25rem; padding-left: 0.5rem;
}
.textsize-btn {
  min-width: 44px; min-height: 40px;
  border: none; border-radius: 999px; background: none;
  color: var(--grey-900); cursor: pointer; font-weight: 700; line-height: 1;
}
.textsize-btn[aria-pressed="true"] { background: var(--green); color: var(--white); }
.textsize-btn:hover:not([aria-pressed="true"]) { background: var(--grey-50); }
.textsize-btn:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
/* Drawn in px on purpose: these three show the sizes on offer, so they must
   not grow with the choice they set. */
.textsize-s { font-size: 14px; }
.textsize-m { font-size: 17px; }
.textsize-l { font-size: 20px; }
@media (max-width: 540px) {
  .textsize-label { display: none; }
  /* The app-wide button padding made each A 58px wide and the pill 179px,
     which on a phone left Sign out too little room and broke it over two
     lines (owner, October 2026). 44px is still the touch-target floor. */
  .textsize-btn { padding-left: 0; padding-right: 0; }
}

/* The gate in front of everything.
 *
 * A centred column (owner's request, October 2026): the logo, the mission
 * in one line, then the one decision on this screen. Narrow, because two
 * fields stretched across a desktop read as a form somebody forgot to lay
 * out, and dropped a little from the top so the card sits in the page
 * rather than clinging to the masthead. */
.panel.sign-in {
  max-width: 30rem; text-align: center;
  margin: calc(var(--space) * 4) auto 0;
  padding: calc(var(--space) * 3) calc(var(--space) * 2.5) calc(var(--space) * 2.5);
}
/* The brand mark, whole and with its clear space (Brand Standards p12). */
.sign-in-logo {
  display: block; height: 7.5rem; width: auto; max-width: 100%;
  margin: 0 auto calc(var(--space) * 1.5);
}
/* The mission on one line, the second half in green. */
.sign-in-headline {
  /* Scales with a phone's width, so the longest pledge ("Prosper through
     Commitment") keeps its margin inside the card at 360 px. */
  margin: 0 0 calc(var(--space) * 2.5); font-size: min(2.5rem, 8.2vw); line-height: 1.1;
  white-space: nowrap;
}
/* The owner's word rotation (October 2026), in the app's own heading
   face, size and colours, as the rest of the app sets its titles: the verb
   in the heading ink, the rest in green as "the Farmer" was, each word in a
   slot that clips it as it rolls. */
.animated-heading {
  display: flex; flex-flow: row nowrap; justify-content: center; align-items: baseline;
  gap: 0.22em; white-space: nowrap;
}
.animated-heading .slot {
  position: relative; display: block; width: max-content; overflow: hidden;
  line-height: 1.16; height: 1.16em; flex-shrink: 0;
}
.animated-heading .word {
  display: block; width: max-content; white-space: nowrap; will-change: translate, opacity;
}
.animated-heading .word.incoming { position: absolute; inset: 0 auto auto 0; }
.animated-heading #connector-slot, .animated-heading .noun-slot { color: var(--green); }
@media (prefers-reduced-motion: reduce) { .animated-heading .word { will-change: auto; } }
/* The form reads from the left, as a form does (owner's request, October
   2026): the logo and the mission above it stay centred. */
.sign-in-title { margin: 0; font-size: 1.75rem; line-height: 1.15; text-align: left; }

.sign-in-form { margin-top: calc(var(--space) * 2.5); }
/* Even air between the two fields. They were inheriting a margin meant for a
   grid of budget figures, which is tighter than two stacked text boxes want. */
.sign-in-form .sim-field { margin-bottom: calc(var(--space) * 2.5); }
.sign-in-form .sim-field { text-align: left; }
.sign-in-form .sim-field label { display: block; text-align: left; }
.sign-in-form .sim-field input { width: 100%; }
.sign-in-form .btn-primary {
  width: 100%; min-height: 48px; font-size: 1rem;
  margin-top: calc(var(--space) * 2);
}
/* The authenticator step (October 2026): numbered instructions, the QR code
   on white so every phone camera can read it, the setup key for a phone that
   cannot scan, and a quiet way back out. */
.mfa-steps { text-align: left; margin: 0 0 calc(var(--space) * 2); padding-left: 1.4em; }
.mfa-steps li { margin-bottom: calc(var(--space) * 0.75); }
.mfa-qr {
  display: block; margin: 0 auto calc(var(--space) * 2);
  width: 200px; height: 200px; background: var(--white);
  padding: calc(var(--space)); border: 1px solid var(--grey-200);
}
.mfa-key { text-align: left; overflow-wrap: anywhere; }
.mfa-key code { display: block; margin-top: calc(var(--space) * 0.5); font-size: 1rem; letter-spacing: 0.04em; }
.sign-in-form .mfa-cancel { width: 100%; min-height: 44px; margin-top: calc(var(--space) * 1.5); }

/* My account and Accounts (October 2026): pages outside the six steps. */
.account-panel { max-width: 960px; margin: 0 auto; }
.account-panel.accounts-wide { max-width: 1440px; }
.account-panel h2 { margin-top: calc(var(--space) * 2); }
.account-panel .sign-in-form { max-width: 420px; }
.account-panel #panel-back { margin-bottom: calc(var(--space)); }
.table-scroll { overflow-x: auto; }
.accounts-table { width: 100%; border-collapse: collapse; margin-top: calc(var(--space) * 2); }
.accounts-table th, .accounts-table td {
  text-align: left; padding: calc(var(--space)) calc(var(--space) * 1.25);
  border-bottom: 1px solid var(--grey-200); vertical-align: middle;
}
.accounts-table td:first-child { white-space: nowrap; }
.accounts-table select { min-width: 12rem; min-height: 40px; }

/* Who to ask, set below a rule rather than crowding the button. It is a
   footnote to the screen, not a third step in the form. */
.sign-in-note {
  margin: calc(var(--space) * 3) 0 0;
  padding-top: calc(var(--space) * 2);
  border-top: 1px solid var(--grey-200);
}

@media (max-width: 540px) {
  /* The address is the first thing to give up its space; the sign-out button
     is the thing somebody came to the masthead for. */
  .account-who { max-width: 8rem; }
  #sign-out { white-space: nowrap; flex-shrink: 0; padding-left: 12px; padding-right: 12px; }
  /* My account and Accounts (October 2026) made the row too long for a phone
     at large text. The row wraps instead of pushing the page sideways; each
     button keeps its words on one line. */
  .account { flex-wrap: wrap; justify-content: flex-end; row-gap: 6px; }
  #open-my-account, #open-accounts {
    white-space: nowrap; flex-shrink: 0; padding-left: 12px; padding-right: 12px;
  }
}

.shell {
  max-width: 70rem; margin: 0 auto;
  padding: clamp(var(--space), 1.5vw, calc(var(--space) * 2));
  /* Keep clear of a notch in landscape. */
  padding-left: max(clamp(var(--space), 1.5vw, calc(var(--space) * 2)), env(safe-area-inset-left));
  padding-right: max(clamp(var(--space), 1.5vw, calc(var(--space) * 2)), env(safe-area-inset-right));
}

/* --- step rail -------------------------------------------------------- */
/* Three shapes, one markup. Laptop gets the full six-across rail. Tablet and
   phone get a disclosure showing where you are, which opens the same list --
   six steps across a 375px screen would be six unreadable slivers.
   Back and Next at the foot of the page are the primary navigation on a phone
   and are always present, so the disclosure is for jumping, not for progress. */

.steps-nav { position: relative; margin: 0 0 calc(var(--space) * 2); }

.steps-toggle {
  display: none; width: 100%; text-align: left; align-items: center; gap: var(--space);
  background: var(--white); border: 1px solid var(--grey-200);
  border-left: 4px solid var(--green); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 10px calc(var(--space) * 2); font-weight: 400;
}
.steps-toggle-text { flex: 1; }
.steps-toggle-count {
  display: block; font-size: 0.875rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--grey-600); font-weight: 700;
}
.steps-toggle-title {
  display: block; font-family: var(--font-display); font-size: 1.375rem;
  font-weight: 600; color: var(--grey-900); line-height: 1.15;
}
.steps-toggle-caret {
  width: 0; height: 0; flex: 0 0 auto;
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-top: 8px solid var(--green); transition: transform 0.15s;
}
.steps-toggle[aria-expanded="true"] .steps-toggle-caret { transform: rotate(180deg); }
.steps-toggle:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }

.steps {
  display: flex; gap: 0; list-style: none; margin: 0;
  padding: 0; align-items: center;
  /* The rail and the banner under it are both children of .shell, so their
     boxes already share an edge. What did not line up was the INK: the first
     circle sat 8px in, behind the button's own padding. The row is pulled out
     by that much at each end so the circle and the banner share a left edge
     and the last label shares the right one. */
  --rail-pad: 8px;
  margin-inline: calc(var(--rail-pad) * -1);
}

/* Six equal shares of the width. Each cell is [circle][label][rule], and the
   rule takes whatever the label leaves -- so every circle sits at the start of
   its own sixth and they step across at an even pitch, whatever the labels
   happen to be called.
   min-width:0 on both the cell and the button is what lets a long label
   ellipsise instead of forcing the row wider than the shell. Without it a
   flex item refuses to shrink below its content and the page gains a
   horizontal scrollbar. */
.steps li {
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: center;
}
/* The last cell takes only what it needs, so the row ends on the label rather
   than on a sixth of trailing white. */
.steps li:last-child { flex: 0 1 auto; }
/* The connector gives room back to the labels rather than the other way
   round. At 16px "Review results" wanted 102px in a 101px cell and lost its
   last letters to an ellipsis -- one pixel, but one pixel is the difference
   between a word and a word with a full stop of dots after it. Six pixels of
   margin instead of ten buys eight per cell, which is slack rather than a
   coin toss. */
.steps li:not(:last-child)::after {
  content: ""; flex: 1 1 auto; min-width: 0.75rem; height: 1px;
  background: var(--grey-200); margin: 0 6px;
}

/* Each step is a button, so a step already visited can be jumped back to.
   Steps not yet reached stay inert -- they have nothing to show yet. */
.steps li button {
  display: flex; align-items: center; gap: 10px;
  padding: 6px var(--rail-pad); min-height: 44px; min-width: 0;
  background: none; border: 0; border-radius: 999px;
  font-size: 1rem; font-weight: 400; color: var(--grey-600);
  cursor: default; white-space: nowrap;
}
.steps li button:not(:disabled) { cursor: pointer; }
.steps li button:not(:disabled):hover .num { background: var(--grey-100); }
.steps li button:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }
/* Not --grey-400. A step you have not reached is still a step you are
   meant to read -- it is how you know what is coming -- and 3.34:1 made the
   back half of the rail look switched off. Cool Grey 10 is 5.79:1. The state
   is still carried, by the circle and by the weight of the label. */
.steps li button:disabled .step-label { color: var(--grey-600); }

/* Cool Grey 1 on the circle and Cool Grey 10 on the figure: both corporate,
   and the pair carries enough contrast at this size without reaching for a
   tint that is not in the palette. */
.steps li .num {
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 2rem; height: 2rem; border-radius: 50%;
  background: var(--grey-200); color: var(--grey-600);
  font-family: var(--font-sans); font-size: 0.9375rem; line-height: 1;
  font-weight: 700;
}
.steps li .step-label { overflow: hidden; text-overflow: ellipsis; }

/* --- breakpoints ------------------------------------------------------ */
/* phone  < 40rem (640px) | tablet 40rem-63.99rem | laptop >= 64rem (1024px) */

@media (max-width: 63.99rem) {
  .steps-toggle { display: flex; }
  /* One header per step. The collapsed menu said "Step 3 of 6" and the
     step's name, and the green banner under it said both again (design
     critique, October 2026). The menu keeps the count, which is what it is
     for; the banner keeps the heading and its sentence. */
  .steps-toggle { padding-top: 8px; padding-bottom: 8px; }
  .steps-toggle-title { display: none; }
  /* Specific enough to beat the banner's own rule further down. */
  .step-head .step-head-main .step-of { display: none; }
  /* Collapsed by default. display:none also takes the list out of the
     screen-reader tree, which is what a collapsed disclosure should do. */
  .steps {
    display: none;
    position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
    flex-direction: column; gap: 0;
    /* stretch, not the row's centre. align-items is a cross-axis rule, so the
       moment the rail turns into a column it stopped meaning "centre the
       circles on the line" and started meaning "make every row as wide as its
       own label, centred". That is what made the open menu ragged: six rows
       between 130 and 172 pixels wide, left edges scattered across 21, inside
       a 595-pixel box. */
    align-items: stretch;
    background: var(--white); border: 1px solid var(--grey-200);
    border-radius: var(--radius); box-shadow: var(--shadow-lg);
    /* svh, not vh, so the list is not hidden behind a phone's address bar. */
    max-height: min(70svh, 70vh); overflow-y: auto;
  }
  .steps-nav.is-open .steps { display: flex; }
  /* Stacked in a panel, so the connecting rules go and the rows are rows
     again. The circles stay -- they are the thing that was asked for, and a
     number in a circle reads the same in a column as in a row. */
  /* flex:0 0 auto on every row, last included. The row rail gives the first
     five 1 1 0 and the last 0 1 auto so the strip ends on its label; down a
     column that same pair means five rows sharing the height equally and a
     sixth sized to its content, which is why they were a pixel out. */
  .steps li, .steps li:last-child { flex: 0 0 auto; }
  .steps li { display: block; border-left: 3px solid var(--grey-200); box-shadow: none; }
  /* The connector moved to ::after when the rail became a row of cells;
     this suppression did not follow it, so five stray rules were still
     being drawn down the stacked menu. */
  .steps li:not(:last-child)::after { content: none; }
  .steps { margin-inline: 0; }
  /* border-TOP on the adjacent sibling, so each rule falls between two rows.
     On the bottom edge it drew under rows two to six: nothing between the
     first pair, and a stray line beneath the last row doubling the container's
     own border. */
  .steps li + li { border-top: 1px solid var(--grey-100); }
  .steps li.active { border-left-color: var(--green); background: var(--green-light); }
  .steps li.done { border-left-color: var(--green-accent); }
  .steps li button {
    width: 100%; border-radius: 0; padding: 15px calc(var(--space) * 2);
    white-space: normal;
  }
}

@media (min-width: 64rem) {
  /* Laptop: the full rail, always open, and the disclosure is irrelevant. */
  .steps { display: flex !important; }
}

/* Between the breakpoint and about 78rem the six labels and their circles are
   wider than the shell can hold. Rather than clip them or let the row push the
   page sideways, the type and the spacing come in first -- and only if that is
   still not enough do the labels go, leaving a row of numbered circles that is
   still a usable stepper. Tested at 1024, 1280 and 1440. */
@media (min-width: 64rem) and (max-width: 78rem) {
  /* The outdent has to track the padding or the row hangs past the banner
     by the difference -- 2px left and 8px right, before this. */
  .steps { --rail-pad: 6px; }
  .steps li button { font-size: 0.9375rem; gap: 8px; }
  .steps li .num { width: 1.75rem; height: 1.75rem; font-size: 0.875rem; }
  .steps li:not(:last-child)::after { margin: 0 4px; min-width: 0.5rem; }
}

@media (min-width: 64rem) and (max-width: 66rem) {
  /* The labels would be down to a few characters and an ellipsis by here,
     which tells a grower less than the number does. */
  .steps li .step-label { display: none; }
  .steps li { flex: 0 0 auto; }
  .steps li:not(:last-child)::after { flex: 1 1 auto; }
  .steps { justify-content: space-between; }
}
/* PANTONE 356 filled for where you are; behind you, the same 356 on white
   inside a PANTONE 362 ring.

   Done used to be white on a 362 fill, which measures 3.35:1 -- under the
   4.5:1 a tick at 15px needs. Both corporate greens are still here and the
   three states still read apart at a glance, by fill rather than by hue:
   solid where you are, ringed where you have been, grey where you have not.
   The ring is a graphical object at 3.35:1 against its 3:1, and the tick
   inside is 356 on white at 5.48:1. */
.steps li.active .num { background: var(--green); color: var(--white); }
.steps li.active .step-label { color: var(--grey-900); font-weight: 700; }
.steps li.done .num {
  background: var(--white); color: var(--green-dark);
  box-shadow: inset 0 0 0 2px var(--green-accent);
}
.steps li.done .step-label { color: var(--grey-900); }
/* The rule AFTER a finished step is green, so the trail reads as travelled
   rather than as six identical hops. On ::after now that the rule trails its
   own circle rather than leading the next one. */
.steps li.done::after { background: var(--green-accent); }

/* --- panels ----------------------------------------------------------- */

.panel {
  background: var(--white); border-radius: var(--radius);
  padding: calc(var(--space) * 2.25) calc(var(--space) * 2.5);
  box-shadow: var(--shadow); margin-bottom: calc(var(--space) * 1.5);
}
.panel > :last-child { margin-bottom: 0; }

.lede {
  font-size: 1.125rem; color: var(--grey-600); margin-bottom: calc(var(--space) * 2);
}

.headline {
  font-size: 1.25rem; font-weight: 700; color: var(--grey-900);
  background: var(--green-light); border-left: 4px solid var(--green);
  padding: calc(var(--space) * 2); border-radius: 0 var(--radius) var(--radius) 0;
  margin-bottom: calc(var(--space) * 3);
}

/* --- forms ------------------------------------------------------------ */

.field-grid {
  /* The column floor is in rem so it grows with the reader's text size. At
     168px it did not: with the type at 150% a "Choose a crop..." placeholder
     was 27px tall in a column sized for 16px type, and read "Choose a crc". */
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(10.5rem, 100%), 1fr));
  gap: calc(var(--space) * 1.5);
}
label { display: block; font-size: 0.9375rem; font-weight: 700; color: var(--grey-600); margin-bottom: 4px; }

/* Labels in a grid wrap at different lengths, so the controls under them start
   at different heights -- "Target yield" sat 21px above its neighbours the
   moment the NEEDED tag pushed the other two onto a second line. Reserving two
   lines for every label lines the controls up whatever the labels do.

   Reserved, not stretched. Making the label grow to fill its cell would look
   equivalent and break as soon as one cell has something BELOW its control --
   the legume note under "after last year's crop" -- because that cell would
   set the row height and every other control would be pushed past it. */
.field-grid label { min-height: 3.2em; }
label .unit { font-weight: 400; color: var(--grey-600); }

/* `password` was missing from this list until the sign-in screen existed, so
   that one field fell through to the browser default and sat two-thirds the
   height of the email box above it. Types are listed rather than styling bare
   `input`, because radios and checkboxes have their own sizing and would be
   stretched into slabs by the rules below. */
input[type="text"], input[type="number"], input[type="email"],
input[type="password"], select, textarea {
  width: 100%; padding: 10px 12px; min-height: 44px;
  /* The boundary is what says "this is a field you can type in", so 1.4.11
     asks it for 3:1. Cool Grey 1 managed 1.41:1 on white -- visible to us,
     not to everyone. --grey-400 is 3.34:1 and, now that it no longer paints
     text, this is the job it is actually good for. */
  border: 1px solid var(--grey-400);
  border-radius: var(--radius); font-family: inherit;
  /* 16px stops iOS Safari zooming the page when a field takes focus. The
     normal text size is 15px now, so the floor is said outright rather than
     left to the rem. */
  font-size: max(1rem, 16px);
  background: var(--white); color: var(--grey-900);
}
/* No up/down arrows in number fields: figures are typed (owner's request,
   October 2026). The field stays type="number", so a phone still offers its
   number keypad. */
input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--green); outline-offset: -1px; border-color: var(--green);
}
textarea { min-height: 84px; resize: vertical; }

.area-card {
  border: 1px solid var(--grey-200); border-radius: var(--radius);
  padding: calc(var(--space) * 2); margin-bottom: calc(var(--space) * 2);
}
.area-card header {
  display: flex; align-items: center; gap: var(--space); margin-bottom: calc(var(--space) * 2);
  flex-wrap: wrap;
}
.area-card header input[type="text"] { max-width: 100%; }
.area-card header input[type="number"] { max-width: 8rem; }

/* --- buttons ---------------------------------------------------------- */

/* 44x44 is the touch-target floor across the app, comfortably above the 24x24
   WCAG 2.2 AA minimum and the size a thumb actually needs. */
button {
  font-family: inherit; font-size: 1rem; font-weight: 700; cursor: pointer;
  border-radius: var(--radius); border: 1px solid transparent;
  padding: 11px 22px; min-height: 44px;
  transition: background 0.15s, border-color 0.15s;
}
.btn-primary { background: var(--green); color: var(--white); }
.btn-primary:hover:not(:disabled) { background: var(--green-dark); }
.btn-primary:disabled { background: var(--grey-200); color: var(--grey-400); cursor: not-allowed; }
.btn-secondary { background: var(--white); color: var(--grey-900); border-color: var(--grey-200); }
.btn-secondary:hover { border-color: var(--grey-900); }
.btn-ghost {
  background: transparent; color: var(--green); border-color: var(--green);
  padding: 10px 18px; font-size: 0.875rem; min-height: 44px;
}
.btn-ghost:hover { background: var(--green-light); }

/* Stuck to the window's foot: the October 2026 audit found Next below the
   fold on every one of the six steps, so leaving a step always meant
   scrolling to its end first. Slim, bordered, and over its own white so the
   content underneath stays readable as it slides past. */
.wizard-nav {
  display: flex; justify-content: space-between; align-items: center; gap: var(--space);
  padding: calc(var(--space) * 1.25) var(--space); flex-wrap: wrap;
  position: sticky; bottom: 0; z-index: 4;
  background: var(--white); border-top: 1px solid var(--grey-200);
  margin: 0 calc(var(--space) * -1);
}
.wizard-nav .spacer { flex: 1; }

/* The slim band steps 2-6 wear: title and counter, no art, a one-line lede.
   Step 1 keeps the full welcome banner. */
.step-head.is-slim { padding: calc(var(--space) * 1.25) calc(var(--space) * 3); align-items: center; }
.step-head.is-slim .step-of { font-size: 0.75rem; }
.step-head.is-slim .step-lede { font-size: 0.9375rem; margin: 2px 0 0; }

/* --- findings, and technical asides ----------------------------------- */
/* Step 2's agronomist list carries the readings that have no gauge of their
   own -- organic matter and CEC. The rest say the same thing under their own
   chart.

   .technical is left un-nested on purpose: step 3's balance table marks text
   as technical too, and while this rule was written as `.finding .technical`
   those usages matched nothing and rendered at full body weight. */

.finding {
  display: grid; grid-template-columns: 9.25rem 1fr; gap: calc(var(--space) * 2);
  padding: calc(var(--space) * 1.5) 0; border-bottom: 1px solid var(--grey-100);
  align-items: start;
}
.finding:last-child { border-bottom: none; }
.finding .name { font-weight: 700; color: var(--grey-900); }
.finding .value { font-size: 0.875rem; color: var(--grey-600); }
.finding .plain { margin: 0; }

.technical { font-size: 0.875rem; color: var(--grey-600); margin: 4px 0 0; }

.pill {
  display: inline-block; font-size: 0.8125rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; padding: 2px 8px; border-radius: 10px; margin-top: 4px;
}
.pill.low { background: var(--low-bg); color: var(--low); }
.pill.optimal { background: var(--optimal-bg); color: var(--optimal); }
.pill.high { background: var(--high-bg); color: var(--high); }

/* --- tables ----------------------------------------------------------- */

.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 0.9375rem; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--grey-100); }
th {
  font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--grey-600); background: var(--grey-50); font-weight: 700;
}
/* A header cell that carries a figure -- a row's own number, a total -- is
   read as a number, not as a column label, so it keeps the body floor. The
   13px above is for the upper-case labels across the top of a table. */
th.num, th.q-num, th.q-fill, tfoot th { font-size: 0.875rem; }
/* A nutrient or a product name is the label of its row, so it is a th with
   scope="row" -- that is what lets a reader say "Nitrogen, lbs per acre, 180"
   instead of reading 180 adrift. It is not a column heading though, and the
   rule above is written for column headings, so it is put back to looking
   exactly like the cell it has always looked like. */
tbody th {
  font-size: inherit; text-transform: none; letter-spacing: normal;
  color: inherit; background: none; font-weight: 400;
}
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tfoot td { font-weight: 700; border-top: 2px solid var(--grey-900); border-bottom: none; }
tr.total td { background: var(--green-light); }

/* --- charts ----------------------------------------------------------- */

.chart-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(18.75rem, 100%), 1fr));
  gap: calc(var(--space) * 3); margin-bottom: calc(var(--space) * 3);
}
.chart { text-align: center; }
.chart figcaption {
  font-size: 0.875rem; color: var(--grey-600); margin-top: var(--space);
}
svg { max-width: 100%; height: auto; }

/* A chart's labels live inside its viewBox, so scaling the chart scales the
   text with it -- at 320px a 13-unit label lands near 9px, and the exact size
   depends on the viewport, which makes it impossible to reason about.

   So the charts are NOT scaled. Each renders at its viewBox's own width, which
   makes every font-size inside it a literal CSS pixel size, and the box scrolls
   sideways when it will not fit. Charts are exactly the two-dimensional content
   WCAG 1.4.10 exempts from the reflow requirement. */
.chart-scroll {
  overflow-x: auto; overflow-y: hidden;
  margin-inline: calc(var(--space) * -1); padding-inline: var(--space);
}
.chart-scroll > svg { max-width: none; width: auto; height: auto; }

/* A chart that fills the panel instead of sitting at one fixed size.
   Text inside a viewBox scales with the drawing, so this cannot simply be
   width:100% -- on a 320px screen the labels would land near 5px. The floor
   keeps the drawing at its readable size and lets the wrapper scroll instead;
   the ceiling stops a wide panel blowing the labels up to headline size.
   Between them the chart is fluid, and its text stays roughly 13px to 20px. */
.chart-scroll > svg.chart-fluid {
  width: 100%; min-width: 38.75rem; max-width: 56.25rem; height: auto;
}
.chart-scroll:focus-visible,
.table-wrap:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

/* Step 2's gauges are drawn at their measured pixel width rather than scaled
   down from a fixed one (see gaugeMetrics in wizard.js), so they need no floor
   and can never overflow. width:100% here only confirms the width they were
   already drawn for; it is the base .chart-scroll > svg rule they must beat. */
.gauges .chart-scroll > svg.gauge-svg {
  width: 100%; height: auto; max-width: 56.25rem;
  /* Block, so it sits on the left edge with the sentence under it. Inline,
     it obeyed the centring .chart sets for its figcaption: on a panel wider
     than the 900px cap the whole drawing floated 62px to the right of its
     own note, and "Soil pH" stopped lining up with "pH: NOT OPTIMAL". */
  display: block;
}

/* --- which soil test to read ------------------------------------------- */

/* Sticky, because the point of it is the scroll. A grower two thirds of the
   way down one field's gauges can move to the next without going back up, and
   the bar tells them which field they are in while they read. */
.area-picker {
  position: sticky; top: 0; z-index: 3;
  margin: 0 0 calc(var(--space) * 2); padding: var(--space) 0;
  background: var(--white); border-bottom: 1px solid var(--grey-200);
}
.area-count { margin: 0 0 8px; font-size: 0.875rem; color: var(--grey-600); }
.area-count strong { color: var(--grey-900); }
.area-buttons { display: flex; flex-wrap: wrap; gap: 8px; }

/* The same picker above step 1's soil-test cards. The strip and the cards
   share one wrapper, because a sticky element only sticks as far as its
   parent reaches: wrapped on its own it would scroll away with the first
   card. */
.sample-tabs > .area-picker {
  padding-inline: calc(var(--space) * 1.5); border-radius: var(--radius);
}
/* Explicit, because a rule that sets display on .panel would otherwise win
   over the attribute and show every card. */
.sample-card[hidden] { display: none; }
.area-pick {
  padding: 6px 14px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--grey-200); background: var(--white);
  font-size: 0.875rem; font-weight: 600; color: var(--grey-900);
  transition: border-color 0.15s, background 0.15s;
}
.area-pick:hover { border-color: var(--green); }
.area-pick:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }
.area-pick.is-on {
  background: var(--green); border-color: var(--green); color: var(--white);
}

@media (prefers-reduced-motion: reduce) {
  .area-pick { transition: none; }
}

/* ------------------------------------------- step 4: the grower's plans

   A grower with ten sampled zones arranges them into the plans their farm is
   actually run as. Each plan is one row: its name is the button that selects
   it, what it covers is written underneath, and Remove sits outside the
   button so a thumb aiming for one cannot land on the other. */
.plan-groups { list-style: none; margin: 0 0 var(--space); padding: 0; }
.plan-group {
  display: flex; align-items: stretch; gap: var(--space);
  margin-bottom: var(--space);
}
/* The whole row is the target, not the name inside it. 2.5.8 wants 24x24 and
   a row of ground a grower picks between wants considerably more than that. */
.plan-group-pick {
  flex: 1; min-width: 0; text-align: left; cursor: pointer;
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 14px; border-radius: var(--radius);
  border: 1px solid var(--grey-200); background: var(--white);
  color: var(--grey-900); font: inherit;
  transition: border-color 0.15s, background 0.15s;
}
.plan-group-pick:hover { border-color: var(--green); }
.plan-group-pick:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }
.plan-group-pick strong { font-size: 1rem; }
.plan-group-sub { font-size: 0.875rem; color: var(--grey-600); }
/* The areas themselves, smaller and allowed to wrap. Seven zone names is a
   long line, and it is the line that catches a plan named for the wrong
   ground -- so it is shown in full rather than trimmed to fit. */
.plan-group-areas {
  font-size: 0.875rem; color: var(--grey-600); overflow-wrap: anywhere;
}
.plan-group.is-on .plan-group-pick {
  border-color: var(--green); background: var(--green-light);
  box-shadow: inset 3px 0 0 var(--green);
}
.plan-group.is-on .plan-group-sub,
.plan-group.is-on .plan-group-areas { color: var(--grey-900); }
.plan-group-drop { flex: 0 0 auto; align-self: center; }

.plan-group-actions {
  display: flex; flex-wrap: wrap; gap: var(--space); align-items: center;
  margin-top: calc(var(--space) * 2);
}
.plan-builder { margin-top: calc(var(--space) * 2); margin-bottom: 0; }
.plan-areas { border: 0; padding: 0; margin: 0 0 calc(var(--space) * 2); }
.plan-areas legend {
  padding: 0; margin-bottom: var(--space);
  font-weight: 700; color: var(--grey-900);
}
.plan-areas legend .muted { font-weight: 400; margin-left: 6px; }
/* As many columns as the names will take. Ten zones down a single column is a
   scroll on a laptop; two or three across is the whole list at once. */
.plan-area-list {
  display: grid; gap: 0 calc(var(--space) * 2);
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.plan-area-list .choice { margin-bottom: 0; }
.plan-builder-fields {
  display: grid; gap: var(--space) calc(var(--space) * 3);
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  align-items: start;
}
.plan-builder-fields .sim-field { margin-bottom: 0; }
.plan-builder-fields input { width: 100%; }

@media (prefers-reduced-motion: reduce) {
  .plan-group-pick { transition: none; }
}

/* ------------------------------ step 5: pricing each of those plans

   One plan is priced at a time, so the strip is both the way between them and
   the running answer: which are settled, what each costs, which still needs a
   decision. */
/* Drawn as a strip of folder tabs, the way the owner's mockup draws the
   product preferences, and the same strip on steps 4 and 5. */
.plan-tabs {
  display: flex; gap: 6px; margin: 12px 0 0; overflow-x: auto;
  border-bottom: 1px solid var(--grey-200);
}
.plan-tab {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; padding: 8px 16px; min-height: 44px; cursor: pointer;
  border: 1px solid var(--grey-200); border-bottom: 0; border-radius: 8px 8px 0 0;
  background: var(--grey-50); color: var(--grey-600); font: inherit; font-weight: 600;
  white-space: nowrap; transition: background 0.15s;
}
.plan-tab:hover { color: var(--grey-900); }
.plan-tab:focus-visible { outline: 3px solid var(--green); outline-offset: -3px; }
.plan-tab strong { font-size: 1rem; }
.plan-tab-sub { font-size: 0.8125rem; font-weight: 400; color: var(--grey-600); }
.plan-tab-cost { font-weight: 700; color: var(--green-dark); font-variant-numeric: tabular-nums; }
/* Not a price. A plan nobody has chosen from has no cost yet, and showing a
   dash or a zero there would read as one. */
.plan-tab-cost.is-open { font-weight: 600; color: var(--high); }
.plan-tab.is-on {
  background: var(--white); color: var(--grey-900);
  box-shadow: inset 0 3px 0 var(--green);
}
.plan-tab.is-on .plan-tab-sub { color: var(--grey-900); }
/* The placement panel's own strip, drawn like the plan tabs but named apart
   from them: step 5's tests count .plan-tab to know how many plans there are. */
.place-tab, .trip-tab {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; padding: 8px 16px; min-height: 44px; cursor: pointer;
  border: 1px solid var(--grey-200); border-bottom: 0; border-radius: 8px 8px 0 0;
  background: var(--grey-50); color: var(--grey-600); font: inherit; font-weight: 600;
  white-space: nowrap; transition: background 0.15s;
}
.place-tab:hover, .trip-tab:hover { color: var(--grey-900); }
.place-tab:focus-visible, .trip-tab:focus-visible { outline: 3px solid var(--green); outline-offset: -3px; }
.place-tab strong, .trip-tab strong { font-size: 1rem; }
.place-tab.is-on, .trip-tab.is-on {
  background: var(--white); color: var(--grey-900);
  box-shadow: inset 0 3px 0 var(--green);
}
.place-tab.is-on .plan-tab-sub, .trip-tab.is-on .plan-tab-sub { color: var(--grey-900); }

/* Where each plan stands, on its tab: two or three words in a pill, the
   way a course card carries its state (owner's request, October 2026). */
.plan-chip {
  display: inline-block; margin-top: 2px; padding: 1px 8px; border-radius: 999px;
  font-size: 0.75rem; font-weight: 700; line-height: 1.5; white-space: nowrap;
}
.plan-chip.is-good { background: var(--green-light); color: var(--green-dark); }
.plan-chip.is-bad { background: color-mix(in srgb, var(--high) 12%, var(--white)); color: var(--high); }
.plan-chip.is-quiet { background: var(--grey-100); color: var(--grey-600); }
.plan-chip.is-busy { background: var(--grey-100); color: var(--grey-600); font-style: italic; }

/* --- step 5's section bar ----------------------------------------------- */

/* Slim and sticky under the banner: which step this is, and the step's
   sections to jump to, each marked done or current. A confirmed section
   folds to its head and tabs, so the next one is in reach without
   scrolling (owner's request, October 2026). */
.step-bar {
  position: sticky; top: 0; z-index: 3;
  display: flex; align-items: center; gap: 6px; overflow-x: auto;
  margin: calc(var(--space) * -1.5) calc(var(--space) * -2.5) calc(var(--space) * 1.5);
  padding: 8px calc(var(--space) * 2.5);
  background: var(--white); border-bottom: 1px solid var(--grey-200);
}
.step-bar-step {
  margin-right: 6px; color: var(--grey-600); font-size: 0.8125rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap;
}
.step-bar-link {
  font: inherit; font-size: 0.9375rem; font-weight: 600; padding: 6px 14px; min-height: 36px;
  border-radius: 999px; border: 1px solid var(--grey-200); background: var(--white);
  color: var(--grey-600); cursor: pointer; white-space: nowrap;
}
.step-bar-link:hover { color: var(--grey-900); border-color: var(--grey-400); }
.step-bar-link.is-done { color: var(--green-dark); background: var(--green-light); border-color: var(--green-light); }
.step-bar-link.is-current { color: var(--white); background: var(--green); border-color: var(--green); }
.step-bar-link:disabled { opacity: 0.5; cursor: default; }
.step-bar-link:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }
/* A section the bar lands on stops under the bar, not behind it. */
#customize-panel, #placement-panel, #pass-panel, #farm-total { scroll-margin-top: 4rem; }
.section-state { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
.prefs-panel.is-collapsed .plan-tabs { border-bottom: 0; }
@media (max-width: 540px) {
  .step-bar-step { display: none; }
  .step-bar { margin-left: calc(var(--space) * -1.5); margin-right: calc(var(--space) * -1.5);
    padding-left: calc(var(--space) * 1.5); padding-right: calc(var(--space) * 1.5); }
}

/* The figure that actually leaves the grower's account. Each plan above is an
   answer to its own budget; this is the one they are deciding about. */
.farm-total {
  background: var(--white); border: 2px solid var(--green);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: calc(var(--space) * 2.5); margin-top: calc(var(--space) * 2);
}
.farm-total .order-head { margin-bottom: var(--space); }
.farm-total table { font-variant-numeric: tabular-nums; }

/* The acreage, where the plans already settled it. A box here would be a
   second answer to a question the grower has answered on step 4. */
.sim-readout-label {
  display: block; margin-bottom: 6px; font-weight: 700;
  color: var(--grey-900); font-size: 0.9375rem;
}
/* Figures in Lato: a condensed face makes a 1 and a 7, or a 3 and an 8, too
   alike to trust at a glance (design critique, October 2026). Teko stays
   for headings. */
.sim-readout {
  margin: 0; font-family: var(--font-sans); font-weight: 700;
  font-size: 1.3125rem; color: var(--grey-900); line-height: 1.25;
}
.sim-readout .unit {
  display: block; font-family: var(--font-sans); font-size: 0.875rem;
  font-weight: 400; color: var(--grey-600);
}

/* Each plan's quote, headed by the plan it is for. */
.quote-plan-head {
  margin: calc(var(--space) * 3) 0 var(--space);
  padding-top: var(--space); border-top: 2px solid var(--grey-200);
}
.quote-plan-head .muted { display: block; font-weight: 400; margin-top: 2px; }
.quote-plan-head:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
/* The one total under all of them. Heavier than the quotes above it, because
   it is the figure a signature goes against. */
.quote-grand { margin-top: calc(var(--space) * 3); }
.quote-grand table { border-top: 2px solid var(--green); }

@media (prefers-reduced-motion: reduce) {
  .plan-tab { transition: none; }
}

/* Six gauges in a row down the page. A hairline between them so they read as
   six readings of the same kind rather than one long drawing. */
.gauges { margin: 0 0 calc(var(--space) * 4); }

/* The rule falls between blocks, not between a chart and the sentence about
   it: each gauge and its note are one thing to read. */
.gauges .gauge-block + .gauge-block {
  border-top: 1px solid var(--grey-100); padding-top: var(--space);
  margin-top: var(--space);
}

/* What an agronomist would say about this reading, under the reading. Held to
   a readable measure and left-aligned with the gauge's own label. */
/* Left-aligned with the gauge above it, not centred under it. `margin: auto`
   centres the block, which left the sentence floating in from the chart's own
   left edge and reading as though it belonged to nothing.

   The type follows the chart's own width, not the viewport: cqi is a percent of
   this block's inline size, so a gauge that is narrow because the panel is
   narrow gets a note sized for the same room. A viewport-based clamp would go
   on growing while the chart beside it stayed put.

   The measure is the gauge's own width, the same 56.25rem cap the drawing
   wears, so the sentence runs to the gauge's right edge and wraps there. It
   was capped at 78ch, which on a laptop wrapped the words a hundred pixels
   short of the "Target" label above them and left a blank strip down the
   right (owner, October 2026). */
.gauge-block { container-type: inline-size; }
/* .chart sets text-align:center for its figcaption, and the note inherited it
   -- so the box was left-aligned while the words inside it were not. */
.gauge-note {
  text-align: left;
  margin: 10px 0 0; max-width: 56.25rem; line-height: 1.5; color: var(--grey-900);
  font-size: clamp(0.875rem, 0.78rem + 0.5cqi, 1.125rem);
}
.gauge-note .pill { margin-right: 2px; vertical-align: 1px; font-size: 0.85em; }

/* The letter's Summary: one bullet per finding, the pill saying the degree.
   Same three pills as step 2, so a reading is the same colour everywhere. */
/* No bullets: the pill is the marker. A dot beside a coloured pill was two
   markers for one thing, and the agronomist said as much. */
.findings-summary { margin: 4px 0 0; }
.findings-area { margin: 12px 0 2px; }
.finding-line { margin: 6px 0; line-height: 1.5; }
.finding-line .pill { margin-right: 6px; vertical-align: 1px; }

/* One paragraph per pass. Nothing more than the letter's own text rhythm:
   the sentences carry the structure, so the markup does not have to. */
.application-pass { margin: 8px 0; line-height: 1.55; }

/* --- step 3: nutrient cards ------------------------------------------- */
/* One card per nutrient, read in the order a grower acts: product, rate,
   field total, then when. The arithmetic sits behind the details. */
.nutrient-eyebrow {
  margin: var(--space) 0 2px; font-size: 0.875rem;
  text-transform: uppercase; letter-spacing: .04em; color: var(--grey-600);
}
.nutrient-title { margin: 0 0 4px; font-size: 1.75rem; color: var(--grey-900); }
.nutrient-cards { display: grid; gap: var(--space); margin-top: var(--space); }
.nutrient-card {
  border: 1px solid var(--grey-200); border-radius: 12px; overflow: hidden;
  background: var(--white);
}
.nutrient-card-head {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px;
  background: var(--tone-bg);
}
.nutrient-card-head h4 { margin: 0; font-size: 1.25rem; color: var(--grey-900); }
.nutrient-card-sub { margin: 2px 0 0; font-size: 0.875rem; color: var(--tone-ink); }
.nutrient-symbol {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 2.375rem; height: 2.375rem; border-radius: 8px;
  background: var(--tone-ink); color: var(--white);
  font-size: 1.25rem; font-weight: 700;
}
.tone-ca .nutrient-symbol { border-radius: 50%; }
.nutrient-card-body { padding: 12px 16px 10px; }
.nutrient-figures {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
  gap: 10px 24px; align-items: start;
}
.nutrient-figures > div + div { border-left: 1px solid var(--grey-200); padding-left: 24px; }
@media (max-width: 540px) {
  .nutrient-figures > div + div { border-left: 0; padding-left: 0; }
}
.figure-cap { margin: 0; font-size: 0.875rem; color: var(--grey-600); }
.figure-big {
  margin: 2px 0 0; font-size: 2.25rem; line-height: 1.1; color: var(--grey-900);
  font-variant-numeric: tabular-nums;
}
.figure-mid {
  margin: 4px 0 0; font-size: 1.5rem; line-height: 1.15; color: var(--grey-900);
  font-variant-numeric: tabular-nums;
}
.figure-big span, .figure-mid span { font-size: 1rem; color: var(--grey-600); }
.figure-note { margin: 2px 0 0; font-size: 0.875rem; color: var(--grey-600); }
.nutrient-split-head, .nutrient-one-pass {
  margin: 12px 0 6px; font-weight: 600; color: var(--grey-900);
}
.pass-tiles {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(min(9.5rem, 100%), 1fr));
}
.pass-tile {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border-radius: 10px;
  background: var(--tone-bg); color: var(--tone-ink);
}
/* The pass number, its rate and the unit on one line; the timing under it. */
.pass-label { display: flex; align-items: baseline; gap: 6px; }
.pass-label .pass-num { align-self: center; }
.pass-num {
  display: grid; place-items: center; width: 1.4rem; height: 1.4rem; border-radius: 50%;
  background: var(--tone-ink); color: var(--white); font-size: 0.8125rem; font-weight: 700;
}
.pass-rate { font-size: 1.5rem; line-height: 1.1; font-variant-numeric: tabular-nums; }
.pass-unit { font-size: 0.875rem; }
.pass-when { font-size: 0.875rem; }
.nutrient-card .amendment-pick { display: block; margin: 0; max-width: 28rem; }
.nutrient-pick select { width: 100%; }
.nutrient-pick .technical { margin: 4px 0 0; }
.amendment-pick-label { display: block; font-size: 0.875rem; color: var(--grey-600); margin-bottom: 4px; }
.nutrient-details { margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--grey-200); }
.nutrient-details summary {
  cursor: pointer; color: var(--grey-900); text-decoration: underline;
  text-underline-offset: 3px; font-size: 0.9375rem;
}
.nutrient-details ul { margin: 8px 0 4px; padding-left: 1.1em; }
.nutrient-details li { margin: 4px 0; line-height: 1.5; color: var(--grey-900); }
.tone-k { --tone-ink: var(--tone-k-ink); --tone-bg: var(--tone-k-bg); }
.tone-ca { --tone-ink: var(--tone-ca-ink); --tone-bg: var(--tone-ca-bg); }
.tone-mg { --tone-ink: var(--tone-mg-ink); --tone-bg: var(--tone-mg-bg); }
.tone-s { --tone-ink: var(--tone-s-ink); --tone-bg: var(--tone-s-bg); }
.tone-p { --tone-ink: var(--tone-p-ink); --tone-bg: var(--tone-p-bg); }
.tone-na { --tone-ink: var(--tone-na-ink); --tone-bg: var(--tone-na-bg); }
.gauge-tech { color: var(--grey-600); }

/* A field the plan is built on that the report did not give us. The app's
   existing "worth knowing" gold rather than a new yellow, so it sits in the
   same vocabulary as the warn notices instead of giving a warm colour a second
   meaning.

   Typed with the element and the attribute, not as a bare class: the base field
   rule is input[type="number"], which outranks a lone .is-needed and wins
   silently -- the class lands on the element and nothing happens.

   Never colour alone. The label carries a word, the field carries a title, and
   the notice above the card names them all in a sentence.

   The same gold marks step 1's crop questions once a report has been read, so
   "the plan needs this from you" looks the same wherever it appears rather
   than having to be learned twice. Red stays reserved for a field that has
   already stopped someone -- see .is-missing. */
input[type="number"].is-needed, select.is-needed {
  background: var(--high-bg); border-color: var(--high); border-width: 2px;
}
input[type="number"].is-needed:focus, select.is-needed:focus {
  outline: 3px solid var(--high); outline-offset: 1px;
}
.needed-tag {
  margin-left: 6px; padding: 1px 6px; border-radius: 3px;
  background: var(--high-bg); color: var(--high);
  font-size: 0.8125rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; vertical-align: 1px;
}

/* Blanks that are not key inputs are counted, not coloured -- on an ordinary
   report there are a dozen of them and they are mostly tests the lab never
   ran. */
.unread-key {
  font-size: 0.875rem; color: var(--grey-600); margin: 12px 0 0;
}


/* --- notices ---------------------------------------------------------- */

.notice {
  padding: calc(var(--space) * 1.5) calc(var(--space) * 2); border-radius: var(--radius);
  margin-bottom: calc(var(--space) * 1.5); font-size: 0.9375rem; border-left: 4px solid;
}
.notice.info { background: var(--green-light); border-color: var(--green); }
.notice.warn { background: var(--high-bg); border-color: var(--high); }
.notice.alert { background: var(--low-bg); border-color: var(--low); }
.notice ul { margin: var(--space) 0 0; padding-left: 20px; }

.question {
  border: 2px solid var(--green); border-radius: var(--radius);
  padding: calc(var(--space) * 2.5); margin-bottom: calc(var(--space) * 2);
  background: var(--white);
}
.question h4 { color: var(--grey-900); margin-bottom: var(--space); }
.choice { display: flex; gap: var(--space); align-items: flex-start; margin-bottom: calc(var(--space) * 1.5); }
/* 2.5.8 Target Size: a default radio is 13x13, under the 24x24 minimum. */
.choice input[type="radio"], .choice input[type="checkbox"] {
  width: 24px; height: 24px; min-width: 24px; margin: 10px 0 0; flex: 0 0 auto;
  accent-color: var(--green); cursor: pointer;
}
/* The label is the real target and it is the full row, so the whole 44px-tall
   block responds to a thumb, not just the control. */
.choice { min-height: 44px; }
.choice label { cursor: pointer; flex: 1; padding: 10px 0; }
.choice label { font-size: 0.9375rem; font-weight: 400; color: var(--grey-900); margin: 0; }
.choice label strong { display: block; color: var(--grey-900); }

.muted { color: var(--grey-600); font-size: 0.875rem; }
/* The line that tells a grower what this step wants from them. It was 13px
   .muted -- the same size as a footnote -- for the most important sentence on
   the step. */
.entry-intro { color: var(--grey-600); font-size: 1rem; line-height: 1.55; }
.spinner { color: var(--grey-600); font-style: italic; }

/* A panel that is waiting for an answer, and says nothing until the wait is
   worth mentioning.
 *
 * `both` is what makes this work: the animation's opening frame applies
 * BEFORE the delay as well, so the panel is invisible for the first 400ms and
 * only then fades in. Most waits here finish inside that -- an analysis is
 * two tenths of a second on the deployment -- so nothing is drawn at all, and
 * something never drawn cannot flicker.
 *
 * 400ms because below it a person reads an indicator as a glitch rather than
 * as progress, and above it they start to wonder whether anything is
 * happening. */
.step-loading {
  animation: settle-in 0.18s ease 0.4s both;
}
@keyframes settle-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  /* The delay is timing, not motion, and it is the part that does the work --
     so it stays. Only the fade goes. */
  .step-loading { animation-duration: 0.01s; }
}

/* --- figures ---------------------------------------------------------- */
/* Borrowed from the Composition Guarantee panel on agroliquid.com: the number
   that matters set large and green, its supporting detail small and grey
   underneath. In a table of a dozen values that is the difference between
   scanning and reading. */

.figure { display: block; font-family: var(--font-sans); font-weight: 700;
  font-size: 1.375rem; line-height: 1.15; color: var(--green); font-variant-numeric: tabular-nums; }
.figure-unit { font-size: 0.875rem; color: var(--grey-600); font-family: var(--font-sans);
  font-weight: 400; margin-left: 3px; }
.figure-sub { display: block; font-size: 0.875rem; color: var(--grey-600); margin-top: 2px; }
.figure.is-zero { color: var(--grey-600); }

/* --- step header ------------------------------------------------------ */
/* Each step opens on a green band, so the six steps read as distinct places
   rather than one long scroll of white panels. */

.step-head {
  background: var(--green); color: var(--white);
  border-radius: var(--radius) var(--radius) 0 0;
  padding: calc(var(--space) * 1.5) calc(var(--space) * 2.5);
  margin: calc(var(--space) * -2.25) calc(var(--space) * -2.5) calc(var(--space) * 1.5);
  display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: var(--space) calc(var(--space) * 3);
  position: relative; overflow: hidden;
}
/* Out of the flow, so it cannot push the title around or take a turn in the
   tab order; behind it, so nothing it does can touch the contrast of white on
   PANTONE 356. It is cropped by the banner's own rounded corner. */
.step-head-art {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(34%, 17rem); height: 100%;
  pointer-events: none; z-index: 0;
}
/* How wide the picture may be, and so where the words must stop. */
.step-head { --step-art-w: min(38%, 20rem); }
/* The step's picture (owner's graphics, October 2026): the whole picture,
   as tall as the band allows at its own 3:1 shape and never cropped, against
   the right-hand end. Drawn as vector art with no background, so it stays
   sharp at any size and sits straight on the banner's own green. */
.step-head-art.step-art {
  left: auto; width: min(var(--step-art-w), 100%); height: 100%;
  object-fit: contain; object-position: right center;
}
.step-head-main, .step-head .step-head-aside { position: relative; z-index: 1; }
/* Below this the banner is one column of words and there is no room beside
   them; a motif underneath the title is just a thing in the way. */
@media (max-width: 47.99rem) {
  .step-head-art { display: none; }
}
/* And where it is shown, the words stop before it starts. The art is out of
   the flow, so nothing makes room for it on its own -- the titles are all
   short, but steps four to six carry a full sentence of lede in this block and
   it would have run underneath. */
@media (min-width: 48rem) {
  .step-head-main { max-width: calc(100% - min(34%, 17rem)); }
  .step-head.with-art .step-head-main { max-width: calc(100% - var(--step-art-w)); }
}
.step-head-main { flex: 1 1 22rem; min-width: 0; }
.step-head h2 { color: var(--white); margin: 0; }

/* Something the step needs done without being the work of the step. On step 1
   that is the retail price list: it has to be collected here, because both
   files are to hand at the same moment and step 5 cannot cost anything
   without it -- but it is not what this step is about, and giving it a section
   of its own in the body put it in the way of the soil test. */
/* Smaller than the heading beside it, and sitting on the bottom edge of the
   banner rather than level with the title. It is something to do, not what
   this step is about, and at the title's size and height it read as a second
   heading. */
/* Sits toward the bottom edge of the banner rather than on the heading's own
   baseline. The negative margin is the point of it: flex-end puts the aside's
   bottom level with the heading's, and this drops it into the banner's own
   padding so it reads as a footer to the bar instead of a second title. */
/* 0.92, not 0.82. Softened white over PANTONE 356 composites to 4.22:1 at
   0.82, under the 4.5 this size needs -- and it survived an earlier pass
   because the checker read the declared colour and ignored its alpha. The same
   0.92 the lede uses comes out at 4.89 and still sits back from the title. */
.step-head .step-of { display: block; font-size: 0.875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: rgba(255,255,255,0.92); font-family: var(--font-sans);
  font-weight: 700; margin-bottom: 2px; }
.step-head .step-lede { margin: 6px 0 0; color: rgba(255,255,255,0.92); font-size: 1rem; }

/* --- product cards --------------------------------------------------- */
/* The program, one card per product, each carrying its label analysis. A
   grower can see what they are buying rather than only how much of it. */

.product-cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16.25rem, 100%), 1fr));
  gap: calc(var(--space) * 2);
}
.product-card {
  border: 1px solid var(--grey-200); border-top: 3px solid var(--green);
  border-radius: var(--radius); padding: calc(var(--space) * 2);
  background: var(--white); display: flex; flex-direction: column; gap: 6px;
}
.product-card .name {
  font-family: var(--font-display); font-size: 1.4375rem; font-weight: 600;
  color: var(--grey-900); line-height: 1.1;
}
.product-card .rate { display: flex; align-items: baseline; gap: 6px; }
.product-card .why { font-size: 0.875rem; color: var(--grey-600); margin: 4px 0 0; }
.product-card .analysis {
  margin-top: auto; padding-top: var(--space); border-top: 1px solid var(--grey-100);
  font-size: 0.875rem; color: var(--grey-600);
}
.product-card .analysis dl { display: grid; grid-template-columns: 1fr auto;
  gap: 2px 10px; margin: 4px 0 0; }
.product-card .analysis dt { color: var(--grey-600); }
.product-card .analysis dd { margin: 0; text-align: right; font-weight: 700;
  color: var(--grey-900); font-variant-numeric: tabular-nums; }
.product-card .double-duty {
  margin-top: var(--space); font-size: 0.875rem; color: var(--green);
  font-weight: 700;
}

/* --- optimal range markers -------------------------------------------- */
/* The dotted verticals bounding the optimal range, on the pH scale and on
   every bar. Drawn dark rather than in a status colour: they are where the
   value ought to be, not a judgement on where it is. */
.range-line {
  stroke: var(--grey-900); stroke-width: 2; stroke-dasharray: 3 4;
  stroke-linecap: round;
}

/* --- budgeting -------------------------------------------------------- */
/* The headline figures on step 5. A budget, what the target costs and the gap
   between them are three numbers that only mean anything together, so they sit
   in one row and wrap rather than stacking into a list. */

.figure-row {
  display: grid; gap: var(--space);
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
}
.figure-block {
  background: var(--green-light); border-left: 4px solid var(--green);
  border-radius: var(--radius); padding: calc(var(--space) * 1.5);
}
.figure-label {
  display: block; font-size: 0.8125rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--grey-600);
}
.figure-block .figure {
  display: block; font-family: var(--font-sans); font-weight: 900;
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2rem); line-height: 1.1;
  color: var(--green-dark); margin: 2px 0 4px;
}
.figure-block .figure-sub { color: var(--grey-600); }

/* The plan being recommended has to be findable at a glance among the tiers. */
.product-card.is-chosen {
  border-color: var(--green); border-width: 2px;
  box-shadow: 0 0 0 3px var(--green-wash);
}


/* --- crop context on step 1 -------------------------------------------- */
/* Set apart from the three entry choices above it by a rule rather than a box,
   because it is a second question on the same step, not a second panel. */

.crop-context {
  margin-top: calc(var(--space) * 3);
  padding-top: calc(var(--space) * 2.5);
  border-top: 1px solid var(--grey-100);
}

/* These three are read across a desk, often on a laptop in a truck, by people
   who are not twenty-five. They are also the answers everything downstream is
   built on, so they are set a step larger than the rest of the form -- and the
   labels take the body colour rather than the muted grey, which did as much to
   make them hard to read as the size did. Scoped to this grid: the soil boxes
   below are a dense table of twenty numbers and do not want the same
   treatment. */
.crop-context label { font-size: 1.0625rem; color: var(--grey-900); }
.crop-context select,
.crop-context input[type="number"],
.crop-context input[type="text"] {
  font-size: 1.125rem; min-height: 48px;
}


/* --- required fields --------------------------------------------------- */
/* The asterisk is the sighted convention; what a screen reader actually
   announces is the control's own required attribute, and aria-invalid once the
   grower has tried to move on. Red arrives only at that same moment -- an
   untouched form in red is telling someone off for not having started -- and
   is never the only signal, since the message names each field in words. */

.req {
  color: var(--low); font-weight: 700; text-decoration: none;
  border: 0; cursor: help; margin-left: 1px;
}
/* The asterisk and the NEEDED badge on their own line beneath the label. Put
   there on purpose: the badge is wide enough to wrap a longer question anyway,
   so letting it fall where it lands made three fields in a row disagree about
   where their marks sat. */
.label-marks { display: block; margin-top: 2px; }
.label-marks .req { margin-left: 0; }
.label-marks .needed-tag { margin-left: 6px; }
select.is-missing, input.is-missing {
  border-color: var(--low); border-width: 2px;
  background: var(--low-bg);
}
select.is-missing:focus, input.is-missing:focus {
  outline: 3px solid var(--low); outline-offset: 1px;
}


/* The two files step 1 asks for, each named and set apart from the other
   without being a panel of its own -- it is the same question asked twice, not
   two steps. The rule goes BETWEEN them rather than above each, so the first
   one does not sit under a line separating it from the heading it belongs
   to. */
/* General sibling, not adjacent: the two hidden file inputs sit between the
   soil test section and the pricing one, and an adjacent selector would miss a
   rule the eye still expects to see between two subjects. */
.entry-section ~ .entry-section {
  margin-top: calc(var(--space) * 3); padding-top: calc(var(--space) * 3);
  border-top: 1px solid var(--grey-200);
}
/* The section heading and the card titles under it are the same kind of
   thing said at two levels -- "Soil test", then the three ways to give one --
   so they are set the same. h4's sans at 1.0625rem made the heading look like
   a label for the cards rather than the name of the section they belong to. */
/* The crop questions are their own subject and were the only block on the step
   without a name over them. */
.crop-context-head {
  font-family: var(--font-sans); font-size: 1.125rem; font-weight: 700;
  color: var(--grey-900); margin: 0 0 calc(var(--space) * 1.5);
}

.entry-section-head {
  margin: 0 0 4px;
  font-family: var(--font-display); font-size: 1.375rem; font-weight: 600;
  line-height: 1.15; color: var(--green-dark);
}

/* What step 5 says about the prices it used. A line, not a panel: by the time
   anyone reads it the file is in, and all it has to do is name it. */
.priced-at {
  margin: 0 0 calc(var(--space) * 2); font-size: 0.875rem; color: var(--grey-600);
}
.priced-at strong { color: var(--grey-900); }
.priced-at .btn-ghost { font-size: 0.875rem; padding: 0 4px; }

/* --- step 5: budget, packages, order ------------------------------------ */
/* Three bands down the page: the figures everything is worked out from, the
   packages to choose between, and the order that comes out of the choice. Each
   the full width, because the packages are meant to be read across and
   compared and a sidebar was taking a third of the room they needed. */

.sim {
  display: grid; gap: calc(var(--space) * 2); margin-top: calc(var(--space) * 2);
  grid-template-columns: 1fr;
  align-items: start;
}
/* A grid item will not go below its own min-content width unless it is told
   it may, and the panels here hold tables wider than a phone. Without this the
   single column was sized to the widest table and the whole page scrolled
   sideways at 320px (WCAG 1.4.10); with it the panels fit and the tables
   scroll inside their own boxes, which is what .table-wrap is for. */
.sim > * { min-width: 0; }

.budget-panel {
  background: var(--white); border: 1px solid var(--grey-200);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: calc(var(--space) * 2.5);
}
.sim-field { margin-bottom: calc(var(--space) * 2); }
.sim-field label { margin-bottom: 6px; }

/* The figures the packages are worked out from, in a row. auto-fit rather than
   a fixed four, so the row becomes two and then one as the window narrows
   without a breakpoint per shape. */
.budget-fields {
  display: grid; gap: var(--space) calc(var(--space) * 2);
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  align-items: start;
}
.budget-fields .sim-field { margin-bottom: 0; }
/* A box sized to what goes in it. The grid column is as wide as the label,
   and a budget stretched across it was a 234px field holding three digits --
   which reads as a field somebody forgot to fill rather than one holding a
   number. The value sits next to its currency sign and the unit stays at the
   right edge, so the space that is left is between them and looks deliberate. */
.budget-fields .sim-input { max-width: 11rem; }
.budget-fields .sim-input input { text-align: left; }
.budget-fields .sim-input .unit { margin-left: auto; }

/* Label and box each on their own shared row across all the fields. Without
   this a label that wraps pushes its box out of line with the others, and a
   row of input boxes that do not line up reads as a mistake before it reads as
   anything else. */
@supports (grid-template-rows: subgrid) {
  .budget-fields { grid-auto-rows: auto; }
  .budget-fields .sim-field {
    display: grid; grid-template-rows: subgrid; grid-row: span 2;
  }
}

/* The box, its currency sign and its unit as one control.
 *
 * Without this the three are three stacked blocks: the $ on one line, a
 * full-width box under it, and /acre under that -- so four fields in a row
 * come out at four different heights, each one several times wider than the
 * number in it. An input is as wide as its widest plausible value and no
 * wider; a budget is four figures and a rate is three.
 */
.sim-input {
  display: flex; align-items: center; gap: 6px;
  border: 1px solid var(--grey-200); border-radius: var(--radius);
  background: var(--white); padding-right: 10px;
}
.sim-input input {
  border: 0; box-shadow: none; text-align: right; font-weight: 700;
  min-width: 0; width: 100%; font-variant-numeric: tabular-nums;
}
.sim-input input:focus { outline: none; }
.sim-input:focus-within { outline: 3px solid var(--green-dark); outline-offset: 2px; }
.sim-input .prefix { padding-left: 10px; font-weight: 700; color: var(--grey-600); }
.sim-input .unit { white-space: nowrap; color: var(--grey-600); font-size: 0.875rem; }

/* Where the crop price came from, under the row of figures it belongs to. */
.budget-fields + .price-note { margin: var(--space) 0 0; }

/* What the budget splits into, on one line under the fields it comes from. */
.budget-split {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 6px;
  margin: calc(var(--space) * 2) 0 0; font-size: 0.875rem; color: var(--grey-600);
}
.budget-split strong { font-variant-numeric: tabular-nums; font-size: 1rem; }
.budget-split .is-available { color: var(--green-dark); }
.budget-split .is-held { color: var(--grey-900); margin-left: var(--space); }
.budget-split .budget-live { margin-left: auto; font-style: italic; }

.sim-empty {
  min-height: 12rem; display: flex; align-items: center; justify-content: center;
  text-align: center; color: var(--grey-600);
}
.sim-empty p { max-width: 26rem; margin: 0; }

/* --- the plan card ------------------------------------------------------- */
.order-head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 8px var(--space);
  margin-bottom: calc(var(--space) * 1.5);
}
.order-head h4 { margin: 0; }
.order-note { font-size: 0.875rem; color: var(--grey-600); }

/* The card is the step's panel itself, laid out like step 4's Product
   Preferences: the plan's name and a line about it, the money, the rows. */
.plan-body { display: flex; flex-direction: column; gap: 4px; }
.plan-money-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px;
  margin: 4px 0 12px;
}
.plan-price {
  font-size: 1.5rem; font-weight: 700; color: var(--grey-900);
  font-variant-numeric: tabular-nums;
}
.plan-per { font-size: 0.875rem; font-weight: 600; color: var(--grey-600); }

/* Empty while the money is updating, or on an unpriced run where there is no
   budget to be inside or outside of. An empty line box has no height, so a
   zero-width space gives it exactly one line of this element's own text and
   the card does not jump as the answer comes and goes. */
.plan-fit { padding-top: 2px; font-size: 0.875rem; }
.plan-fit:empty::after { content: "\200B"; }
.plan-fit.ok { color: var(--grey-600); }
/* Amber, not red. Over budget is a fact about the plan, not an error the
   grower has made, and it is still one they may keep. */
.plan-fit.over { color: var(--high); font-weight: 600; }

/* --- the card's editable product list ------------------------------------ */

/* One product per line, down the panel. Two columns fitted more on a screen
   and made the plan something to scan across; a plan is a list of what goes in
   the tank, and it reads down. */
.order-lines { display: flex; flex-direction: column; }
/* One product a row, in the same three columns as the preferences on step 4:
   the mark, the name with its cost, the rate. Marks of every proportion share
   one column, so the names line up under each other. On a narrow screen the
   three stack; a rate control squeezed to a few pixels is not usable, and a
   row that pushes the page sideways is worse. */
.order-line {
  display: grid; grid-template-columns: 110px 1fr auto; align-items: center;
  gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--grey-100);
}
.order-line:last-child { border-bottom: 0; }
.order-line-words { display: flex; flex-direction: column; min-width: 0; }
.order-line-words strong { display: block; color: var(--green-dark); font-size: 0.9375rem; }
.order-line-words em { font-style: normal; font-size: 0.875rem; color: var(--grey-600); }
.product-badge { height: 1.75rem; width: auto; flex-shrink: 0; }
/* In a table cell the mark sits inline before the name, on the text's own
   middle, a touch smaller so a row of them does not swell the table. */
th .product-badge, td .product-badge {
  height: 1.4rem; vertical-align: middle; margin-right: 8px;
}
/* A zeroed line stays on the card until the evaluation records the removal,
   greyed and struck so it reads as a decision rather than a product. */
.order-line.is-removed .order-line-words strong {
  color: var(--grey-600); text-decoration: line-through;
}
@media (max-width: 640px) {
  /* minmax(0, ...) so a row's widest part cannot stretch the column past the
     screen, and the rate box gives up width before the page scrolls sideways
     (at the largest text size it did, October 2026). */
  .order-line { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .stepper .step-value { width: auto; flex: 1 1 0; min-width: 4.5rem; }
  /* The unit drops under the box on the narrowest screens rather than off
     the edge. */
  .stepper { flex-wrap: wrap; row-gap: 2px; }
}
.line-removed { color: var(--high); }
/* A line with product on hand reads recommended, taken off, to buy: the
   subtraction in the warning gold, the figure to buy in full weight. */
.order-line-words .line-off { color: var(--high); }
.order-line-words .line-buy {
  display: block; margin-top: 2px; font-size: 0.9375rem; font-weight: 700;
  color: var(--grey-900);
}

/* The on-hand list under the lines: what the grower said they have, and what
   the server made of it. */
.on-hand {
  margin: 10px 0 0; padding: 8px 12px;
  border: 1px solid var(--grey-200); border-radius: 10px;
}
.on-hand-line {
  display: flex; align-items: center; gap: 12px; padding: 8px 0;
  border-top: 1px solid var(--grey-100);
}
.on-hand-line:first-of-type { border-top: 0; }
.on-hand-words { flex: 1 1 auto; min-width: 0; }
.on-hand-words strong { color: var(--green-dark); }
.on-hand-words em {
  font-style: normal; color: var(--grey-600); font-size: 0.875rem; margin-left: 6px;
}
.on-hand-detail {
  display: block; margin-top: 2px; font-size: 0.875rem; line-height: 1.45;
  color: var(--grey-900);
}
.onhand-remove { padding: 4px 10px; min-height: 36px; font-size: 1.125rem; line-height: 1; }

/* Minus, number, plus. A rate is set in tenths of a gallon and typed about as
   often as it is nudged, so the middle of the control is a real field. */
.stepper { display: flex; align-items: stretch; gap: 4px; }
/* Outlined and neutral: these nudge a rate, and filled in PANTONE 354, the
   palette's warning colour, they read as a warning (design critique,
   October 2026). */
.stepper button {
  width: 32px; border-radius: 6px; cursor: pointer; font: inherit; font-weight: 700;
  background: var(--white); border: 1px solid var(--grey-400);
  color: var(--grey-900);
}
.stepper button:hover { background: var(--grey-50); border-color: var(--grey-900); }
.stepper button:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }
.stepper .step-value {
  width: 7.5rem; min-height: 36px; padding: 4px 8px; text-align: right;
  font-variant-numeric: tabular-nums; font-weight: 700;
}
/* The unit inside the control, so the number is never read as a bare figure.
   These are gallons per acre and the difference between 20 and 20 gal/acre is
   the difference between a quantity and a guess. */
.stepper .step-unit {
  align-self: center; font-size: 0.875rem; color: var(--grey-600);
  white-space: nowrap;
  /* One width for gal/acre and ton/acre, so every row's stepper starts at
     the same place (owner's spec: controls aligned, October 2026). */
  min-width: 4.25em;
}

/* --- where each product goes ---------------------------------------------- */

/* Step 5's placement table, from the owner's mockup and review (October
   2026): the product's mark and plan rate first, a picture of each
   placement over its column, a figure box per pass with All here under it,
   what is left at the end, and a row that turns green once it adds up.
   Fixed layout, so the passes share the width evenly; narrower than the
   table needs, it scrolls with the product column held in place. */

/* One colour per pass, from the approved product palette (Brand Standards
   p34-p37): the column's picture, its boxes and its share of a row's bar
   read as one thing. */
.pass-in_furrow { --pass: var(--al-green-356); }
.pass-two_by_two { --pass: var(--al-green-362); }
.pass-surface_band { --pass: #7B7CBB; }
.pass-broadcast { --pass: var(--al-grey-10); }
.pass-sidedress { --pass: var(--accent); }
.pass-foliar { --pass: var(--green-dark); }
.pass-fertigation { --pass: #1F89CA; }

.place-top {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; flex-wrap: wrap;
}
.place-units { font-size: 0.9375rem; color: var(--grey-600); }
.place-hint { margin: 2px 0 12px; font-size: 0.9375rem; color: var(--grey-600); line-height: 1.45; }
.place-wrap { margin: 4px 0 10px; }
.place-table {
  width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0;
}
.place-col-product { width: 16rem; }
.place-col-left { width: 6.5rem; }
.place-table th { text-transform: none; letter-spacing: normal; }
.place-table th, .place-table td {
  padding: 10px 6px; border-bottom: 1px solid var(--grey-100); vertical-align: middle;
}
.place-table thead th {
  background: var(--grey-50); color: var(--grey-900); font-size: 0.9375rem;
  font-weight: 700; line-height: 1.25; text-align: center; white-space: normal;
  vertical-align: bottom;
}
.place-table thead th.place-head-product,
.place-table thead th.place-left-head { color: var(--grey-600); font-weight: 600; }
.place-table thead th.place-head-product { text-align: left; }
.place-table thead th.place-left-head { text-align: right; }
.place-head { box-shadow: inset 0 -4px 0 var(--pass); transition: background-color 0.2s; }
.place-art { display: block; color: var(--pass); }
.place-art svg { display: block; width: 56px; height: 37px; margin: 0 auto 6px; }
.place-head-words { display: block; }
.place-table tbody th { text-align: left; font-weight: 400; background: var(--white); }
.place-table tbody th, .place-table thead th:first-child {
  position: sticky; left: 0; z-index: 1;
}
/* The plan card's mark and name, side by side as its lines have them: the
   same balanced 110 x 40 mark (.prefs-mark) with the name beside it, on the
   placement table and the pass cards alike (owner's request, October 2026). */
.mark-stack {
  display: grid; grid-template-columns: 110px minmax(0, 1fr); align-items: center;
  gap: 12px; min-width: 0;
}
.mark-stack .mark-name {
  display: block; color: var(--green-dark); font-size: 0.9375rem; line-height: 1.25;
  overflow-wrap: anywhere;
}
/* A product with no mark: the name takes the mark's place. */
.mark-stack .mark-name:first-child { grid-column: 1 / -1; }
/* In the placement table the cell is a table header, whose own mark rule
   (.table-wrap th .name.is-mark img) outranks .prefs-mark's. Said again
   from the block so the balanced size holds there too. */
.mark-stack .prefs-mark .name.is-mark img {
  width: var(--mark-w); height: var(--mark-h); max-width: 110px; max-height: 40px;
}
.place-plan-rate {
  display: block; margin-top: 4px; font-size: 0.9375rem; color: var(--grey-600);
  font-variant-numeric: tabular-nums;
}
.place-bar {
  display: flex; height: 7px; margin-top: 6px; max-width: 9.5rem;
  border-radius: 4px; overflow: hidden; background: var(--grey-200);
}
.place-bar-seg { display: block; height: 100%; background: var(--pass); transition: width 0.25s ease; }
.place-cell { position: relative; text-align: center; transition: background-color 0.2s; }
/* Each pass column on a faint wash of its own colour, with a hairline between
   columns, so the columns read apart (owner's request, October 2026). */
.place-table tbody .place-cell, .place-table tbody .place-closed {
  background: color-mix(in srgb, var(--pass, var(--green)) 7%, var(--white));
  border-left: 1px solid var(--grey-200);
}
.place-table tbody .place-left { border-left: 1px solid var(--grey-200); }
.place-table thead .place-head { border-left: 1px solid var(--grey-200); }
/* The recommended cells: a dot in the pass colour at the cell's corner,
   and the reason as its title. */
.place-seed-dot {
  position: absolute; top: 5px; right: 5px; width: 9px; height: 9px;
  border-radius: 50%; background: var(--pass, var(--green));
  box-shadow: 0 0 0 2px var(--white);
}
.place-seed-dot.is-inline {
  position: static; display: inline-block; vertical-align: middle; margin-right: 4px;
  background: var(--green); box-shadow: none;
}
.place-cell.is-seeded { cursor: help; }
.place-cell.is-seeded .place-value { cursor: text; }
.place-value {
  width: 100%; max-width: 6rem; min-height: 42px; padding: 4px 8px;
  font: inherit; font-size: 1.0625rem; font-variant-numeric: tabular-nums; text-align: right;
  color: var(--grey-600); background: var(--white);
  border: 1px solid var(--grey-200); border-radius: 8px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.place-value.has-value {
  color: var(--grey-900); font-weight: 700; border-color: var(--pass);
  box-shadow: inset 4px 0 0 var(--pass);
}
.place-value:hover { border-color: var(--grey-400); }
.place-value:focus-visible { outline: 3px solid var(--pass); outline-offset: 1px; }
/* More than the row has free, while it is being typed: when the box is
   left, the difference comes from the row's other passes. */
.place-value.is-taking { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.place-all {
  display: block; margin: 5px auto 0; padding: 2px 10px; min-height: 26px;
  font: inherit; font-size: 0.8125rem; font-weight: 700; cursor: pointer;
  color: var(--pass); background: var(--white);
  border: 1px solid var(--pass); border-radius: 999px;
}
.place-all:hover { background: var(--pass); color: var(--white); }
.place-all:focus-visible { outline: 3px solid var(--pass); outline-offset: 2px; }
/* Shown on the row being pointed at or typed in, and always on a touch
   screen, which has no pointer to point with: a pill under every box of a
   seven-pass table is a wall of them. */
.place-all { opacity: 0; transition: opacity 0.15s; }
.place-table tbody tr:hover .place-all,
.place-table tbody tr:focus-within .place-all { opacity: 1; }
@media (hover: none) { .place-all { opacity: 1; } }
/* Already all here: kept in the layout so the row does not jump. */
.place-all.is-full { visibility: hidden; }
.place-closed { text-align: center; color: var(--grey-600); font-size: 0.875rem; }
.place-left {
  text-align: right; white-space: nowrap; font-size: 1rem;
  font-variant-numeric: tabular-nums;
}
.place-short { color: var(--high); font-weight: 700; }
.place-ok { color: var(--green-dark); font-weight: 700; }
.place-tick {
  display: inline-grid; place-items: center; width: 1.3em; height: 1.3em;
  border-radius: 50%; background: var(--green); color: var(--white); font-size: 0.8em;
}
.place-table tbody th, .place-table tbody td { transition: background-color 0.3s; }
/* A finished row is said by its tick in Remaining, not by a green wash over
   the row: the columns carry the colour here (owner's request, October 2026). */
.place-table tr.is-placed .place-tick { animation: place-pop 0.35s ease-out; }
@keyframes place-pop { 0% { transform: scale(0.4); } 70% { transform: scale(1.2); } 100% { transform: scale(1); } }
/* The row and column being typed in. */
.place-table tbody tr:focus-within th { box-shadow: inset 4px 0 0 var(--green); }
.place-table .is-lit { background: color-mix(in srgb, var(--pass, var(--green)) 16%, var(--white)); }
.place-progress { display: flex; flex-direction: column; gap: 6px; min-width: 12rem; }
.place-progress-words { color: var(--grey-600); font-size: 1rem; }
.place-progress.is-done .place-progress-words { color: var(--green-dark); font-weight: 700; }
.place-progress-track {
  display: block; height: 7px; width: 100%; max-width: 16rem;
  border-radius: 4px; background: var(--grey-200); overflow: hidden;
}
.place-progress-fill {
  display: block; height: 100%; background: var(--green); transition: width 0.3s ease;
}
.place-confirmed { color: var(--green-dark); font-weight: 700; white-space: nowrap; }
/* Under a shared Confirm on step 5: which plan is holding it shut, when it is
   one whose tab may not be open. A whole row of the foot, under the button. */
.confirm-why {
  flex-basis: 100%; margin: 0; text-align: right;
  color: var(--grey-600); font-size: 0.875rem;
}
/* On a phone the buttons wrap to the left, so the line follows them. */
@media (max-width: 540px) {
  .confirm-why { text-align: left; }
}
@media (prefers-reduced-motion: reduce) {
  .place-bar-seg, .place-progress-fill, .place-cell, .place-head,
  .place-table tbody th, .place-table tbody td, .place-all { transition: none; }
  .place-table tr.is-placed .place-tick { animation: none; }
}

/* --- customize the pass --------------------------------------------------- */

/* Step 5's pass board (owner's request, October 2026): the trips a plan is
   made in, grouped by when they go on, one card per trip in its placement's
   colour, each product with its volume, the tank against the rig. */
/* Each season in its own box, so where one ends and the next begins is
   seen, not inferred (owner's request, October 2026). */
.trip-season {
  margin: 14px 0 0; padding: 12px 14px 4px; background: var(--grey-50);
  border: 1px solid var(--grey-200); border-radius: 12px;
}
.trip-season-head {
  margin: 0 0 10px; padding-bottom: 8px; border-bottom: 1px solid var(--grey-200);
  font-size: 0.9375rem; font-weight: 700; color: var(--grey-900);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.trip-group { margin-bottom: 12px; }
/* Wide enough for a line to run across: mark, name, figure and All here. */
.trip-cards {
  display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(30rem, 1fr));
}
.trip-card {
  display: flex; flex-direction: column; gap: 8px; padding: 12px 14px;
  background: var(--white); border: 1px solid var(--grey-200); border-radius: 10px;
  box-shadow: inset 0 4px 0 var(--pass);
}
.trip-card.is-over { border-color: var(--high); }
.trip-head { display: flex; align-items: center; gap: 10px; }
.trip-art { color: var(--pass); flex: none; }
.trip-art svg { display: block; width: 48px; height: 32px; }
.trip-title { display: flex; flex-direction: column; min-width: 0; }
.trip-label { color: var(--grey-900); font-size: 1.0625rem; line-height: 1.25; }
.trip-when { color: var(--grey-600); font-size: 0.875rem; }
.trip-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
/* One line across: the mark, the name, the figure, and All here (owner's
   request, October 2026). */
.trip-line {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center;
  gap: 4px 10px; padding-top: 8px; border-top: 1px solid var(--grey-100);
}
.trip-line:first-child { border-top: 0; padding-top: 0; }
.trip-product { min-width: 0; }
.trip-figure { display: flex; align-items: center; gap: 6px; }
.trip-figure .trip-value {
  flex: none; width: 5.5rem; min-height: 40px; padding: 4px 8px; font: inherit;
  font-size: 1.0625rem; font-variant-numeric: tabular-nums; text-align: right;
  color: var(--grey-600); background: var(--white);
  border: 1px solid var(--grey-200); border-radius: 8px;
}
.trip-value.has-value { color: var(--grey-900); font-weight: 700; border-color: var(--pass); }
.trip-value:focus-visible { outline: 3px solid var(--pass); outline-offset: 1px; }
.trip-unit { color: var(--grey-600); font-size: 0.875rem; white-space: nowrap; }
.trip-all {
  justify-self: end; padding: 2px 10px; min-height: 26px;
  font: inherit; font-size: 0.8125rem; font-weight: 700; cursor: pointer;
  color: var(--pass); background: var(--white); border: 1px solid var(--pass); border-radius: 999px;
}
.trip-all:hover { background: var(--pass); color: var(--white); }
.trip-all.is-full { visibility: hidden; }
.trip-tank { display: flex; flex-direction: column; gap: 4px; }
.trip-tank-words { color: var(--grey-600); font-size: 0.9375rem; font-variant-numeric: tabular-nums; }
.trip-tank-track { display: block; height: 7px; border-radius: 4px; background: var(--grey-200); overflow: hidden; }
.trip-tank-fill { display: block; height: 100%; background: var(--pass); transition: width 0.25s ease; }
.trip-card.is-over .trip-tank-fill { background: var(--high); }
.trip-alert { margin: 0; color: var(--high); font-weight: 700; font-size: 0.9375rem; line-height: 1.4; }
.trip-foot { display: flex; justify-content: flex-end; }
.trip-group-foot {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  flex-wrap: wrap; margin-top: 8px;
}
.trip-left { display: flex; flex-direction: column; gap: 2px; }
.trip-left.is-short { color: var(--high); font-weight: 700; }
@media (prefers-reduced-motion: reduce) { .trip-tank-fill { transition: none; } }
/* On a phone the mark and name take a row of their own, and the figure
   and All here sit under them. */
@media (max-width: 540px) {
  .trip-cards { grid-template-columns: 1fr; }
  .trip-line { grid-template-columns: minmax(0, 1fr) auto; }
  .trip-product { grid-column: 1 / -1; }
  .place-col-product { width: 12rem; }
  .place-table .mark-stack { grid-template-columns: 1fr; gap: 2px; }
  .place-table .mark-stack .mark-name { grid-column: auto; }
}

/* --- the card's framing -------------------------------------------------- */

.plan-money { margin-bottom: 2px; }
.plan-updating {
  color: var(--grey-600); font-style: italic;
  font-size: 1.125rem; font-weight: 600;
}

/* The add row under the product list: the same row control the declarations
   used, now feeding the card itself. The button is the dashed bar of the
   owner's mockup, full width, so adding reads as part of the list. */
.card-adds {
  margin: 4px 0 var(--space);
  display: flex; flex-direction: column; gap: 10px; align-items: stretch;
}
.btn-add {
  width: 100%; min-height: 48px; cursor: pointer; font: inherit; font-weight: 700;
  border: 1px dashed var(--grey-400); border-radius: 10px;
  background: var(--grey-50); color: var(--green-dark);
}
.btn-add:hover { border-color: var(--green); background: var(--green-light); }
.btn-add:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }


/* --- option cards ------------------------------------------------------ */

.option-cards {
  display: grid; gap: var(--space); margin-top: calc(var(--space) * 2);
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.option-card {
  background: var(--white); border: 1px solid var(--grey-200);
  border-radius: var(--radius); padding: calc(var(--space) * 2);
  display: flex; flex-direction: column; gap: var(--space);
}
.option-card.is-chosen {
  border-color: var(--green); border-width: 2px; background: var(--green-light);
}
.option-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.option-head .dot {
  width: 12px; height: 12px; border-radius: 50%; flex: 0 0 auto;
}
.option-head .name { font-weight: 700; font-size: 1.0625rem; }

.option-figures { display: grid; gap: 4px; margin: 0; }
.option-figures dt {
  font-size: 0.875rem; color: var(--grey-600); font-weight: 700;
}
.option-figures dd { margin: 0 0 6px; }
.option-figures dd strong { font-size: 1.125rem; }
.option-figures .figure-sub { display: block; }

.option-products { border-top: 1px solid var(--grey-100); padding-top: var(--space); }
.option-products strong {
  display: block; font-size: 0.8125rem; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--grey-600); margin-bottom: 6px;
}
.option-products ul { list-style: none; margin: 0; padding: 0; }
.option-products li {
  display: flex; justify-content: space-between; gap: var(--space);
  font-size: 0.875rem; padding: 3px 0;
}
.option-products .rate {
  font-variant-numeric: tabular-nums; color: var(--grey-600); white-space: nowrap;
}


/* --- intro ------------------------------------------------------------ */
/* Shown on step 1 only. It orients someone arriving for the first time; on
   every later step it would be something already read and in the way. */

/* Sits under the step heading now rather than in a panel of its own, so it
   loses the left rule and the outer padding it needed when it was the only
   thing on the page. It is context for the step, not a banner above it. */
.intro { margin: 0 0 calc(var(--space) * 1.5); }
/* The measure is capped for readability, but the cap is in ch and the type is
   fluid, so the two move together: the line stays about the same number of
   characters while the block grows to fill the panel.

   Fixing the size at 18px was what made this look wrong on a wide screen. The
   cap then worked out to 710px inside a 1020px panel -- a third of the width
   empty on the right, reading as a left-aligned block that had failed to
   stretch. Narrow screens looked fine only because the panel was the binding
   constraint there and the cap never came into play.

   The two numbers were picked by measuring, not guessing. 26px against 64ch
   fills 94% of the panel at 67 characters a line -- the same measure the 18px
   version read at, so it gained the width without becoming a longer read. Note
   that ch is the width of a zero, which is wider than average text: 64ch buys
   about 67 characters, not 64. Raising the cap alone to fill the space would
   have taken the line to 90 characters.

   Now that it sits beneath the step heading the ceiling comes down: at 26px it
   was larger than the h2 above it, so the paragraph shouted over the heading it
   was explaining. The floor and the measure are unchanged. */
.intro p {
  margin: 0;
  font-size: 1rem;
  line-height: 1.5; color: var(--grey-900);
  /* No measure cap. It had one -- 64ch, for readability -- and on a wide
     panel that left the right third empty beside three lines of text, which
     read as a layout that had not been finished rather than as a column.
     The panel's own width is the measure now, as it is for everything else
     on the step. */
}

/* --- entry choices --------------------------------------------------- */
/* Three ways to get the numbers in, given equal weight. None is a fallback
   for the others: a lab PDF, a printout on the workshop bench and a figure
   read down the phone are all normal ways for a soil test to arrive. */

/* Two documents, one set of controls. The tab strip is the section's heading
   as well as its switch, which is most of what consolidating them bought: the
   two headings, two descriptions and two pairs of cards filled the screen
   before the step had asked for anything. */
.upload-switch {
  display: inline-flex; gap: 4px; padding: 4px; margin: 0 0 calc(var(--space) * 1.5);
  background: var(--green-light); border: 1px solid #C6DFCE;
  border-radius: var(--radius); flex-wrap: wrap;
}
/* Lato, not Teko. The condensed face is what gives the headings their
   character, and exactly what makes a 16px tab harder to scan -- narrow
   letterforms cost most at the sizes you read rather than look at. Teko also
   has no weight between 600 and nothing, where Lato's 700 reads as the medium
   emphasis a tab wants. */
.upload-switch button {
  background: transparent; border: 1px solid transparent; color: var(--green-dark);
  font-family: var(--font-sans); font-size: 1rem; font-weight: 700;
  /* 44, not the 40 this first had: the app's stated touch-target floor is 44
     and a tab is the one control on this step you cannot get anywhere without
     pressing. */
  padding: 11px 22px; min-height: 46px;
}
.upload-switch button:hover:not([aria-selected="true"]):not(:disabled) {
  background: var(--green-wash);
}
/* PANTONE 356 on the chosen tab, not the pale card border. Which tab is
   selected was carried by a white fill on a pale green strip -- 1.13:1 -- and
   a 1.41:1 edge, which is a state you have to already know about to see. */
.upload-switch button[aria-selected="true"] {
  background: var(--white); border-color: var(--green); box-shadow: var(--shadow);
}
.upload-switch button:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }
/* Disabled only while a report is being read, when leaving would unmount the
   progress scene. It should read as "not now", not as "never". */
.upload-switch button:disabled { opacity: 0.55; cursor: default; }
.upload-switch .tick { color: var(--green); }

.entry-choices {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16.875rem, 100%), 1fr));
  gap: calc(var(--space) * 2);
}

/* One tile, one control. The whole card is the button: the big icon and the
   words beside it are what a thumb lands on, so there is no second button to
   find under a paragraph, and the card is a third of the height it was
   (owner's request, October 2026). White, with an edge and the shadow doing
   the separating, so the green icon and words keep their contrast. */
.entry-choice {
  display: block; padding: 0;
  background: var(--white);
  border: 1px solid var(--grey-200); border-radius: var(--radius);
  box-shadow: var(--shadow);
  transition: border-color 0.15s, background 0.15s;
}
/* Dashed all the time, not only mid-drag. A dashed edge is the one convention
   everybody already reads as "you can drop something here", and the tile says
   so in words -- there is no reason to make the grower start dragging to find
   out the words were true. */
.entry-choice.is-droppable {
  border: 2px dashed var(--green-accent);
}
.entry-choice.is-active {
  border: 2px solid var(--green); background: var(--green-light);
  box-shadow: inset 0 0 0 1px var(--green), var(--shadow);
}
.entry-choice.is-dragover {
  border-color: var(--green); background: var(--green-light);
}

@media (prefers-reduced-motion: reduce) {
  .entry-choice { transition: none; }
}

.entry-tile {
  display: grid; grid-template-columns: auto 1fr; align-items: center;
  gap: calc(var(--space) * 1.5); width: 100%; min-height: 108px;
  padding: calc(var(--space) * 1.5) calc(var(--space) * 2);
  background: transparent; border: 0; border-radius: inherit;
  text-align: left; cursor: pointer; font: inherit; color: inherit;
}
.entry-tile:hover { background: var(--green-light); }
.entry-tile:focus-visible { outline: 3px solid var(--green); outline-offset: -4px; }

.entry-choice-icon { color: var(--green); line-height: 0; }
.entry-choice-icon svg { width: 72px; height: 72px; display: block; }
.entry-choice-words { display: flex; flex-direction: column; gap: 4px; min-width: 0; }

/* Off the condensed face: the tile is a thing to press, not a masthead. */
.entry-choice-title {
  font-family: var(--font-sans); font-size: 1.375rem; font-weight: 700;
  line-height: 1.2; color: var(--green-dark);
}
.entry-choice.is-active .entry-choice-title::after {
  content: " \2713"; color: var(--green);
}
.entry-choice-detail { font-size: 0.9375rem; color: var(--grey-600); line-height: 1.45; }

/* Typing the numbers in is a real way for a soil test to arrive, but it is the
   slowest one. It reads as a link under the two cards rather than as a third
   card competing with them for the same glance. */
.entry-manual-row { text-align: center; margin: calc(var(--space) * 1.5) 0 0; }
.entry-manual {
  display: inline-flex; align-items: center; gap: var(--space);
  background: none; border: 1px solid transparent; color: var(--green-dark);
  font-size: 1rem; padding: 12px 18px;
}
.entry-manual:hover { background: var(--green-light); text-decoration: underline; }
.entry-manual:focus-visible { outline: 3px solid var(--green); outline-offset: 2px; }
.entry-manual.is-active { background: var(--green-wash); border-color: var(--green); }
.entry-manual svg { width: 18px; height: 18px; flex: none; }

/* --- camera ----------------------------------------------------------- */
/* A live preview, because `capture="environment"` on a file input is only a
   hint to mobile browsers -- desktop ignores it and opens a file dialog. The
   only way to reach a webcam is getUserMedia. */

.camera { margin-top: calc(var(--space) * 2); }
.camera-stage {
  position: relative; background: #1F2224; border-radius: var(--radius);
  overflow: hidden; max-width: 45rem; margin: 0 auto;
}
.camera-stage video, .camera-stage img {
  display: block; width: 100%; height: auto; max-height: 62vh;
  object-fit: contain; background: #1F2224;
}

/* Corner marks, so a grower knows to fill the frame with the report. Framing
   is most of what decides whether the OCR can read it. */
.camera-guide { position: absolute; inset: 6%; pointer-events: none; }
.camera-guide span {
  position: absolute; width: 34px; height: 34px;
  border: 3px solid rgba(255, 255, 255, 0.85);
}
.camera-guide span:nth-child(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.camera-guide span:nth-child(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; }
.camera-guide span:nth-child(3) { bottom: 0; left: 0; border-right: 0; border-top: 0; }
.camera-guide span:nth-child(4) { bottom: 0; right: 0; border-left: 0; border-top: 0; }

.camera-controls {
  display: flex; gap: var(--space); justify-content: center; align-items: center;
  flex-wrap: wrap; margin-top: calc(var(--space) * 2);
}
.camera-hint {
  text-align: center; color: var(--grey-600); font-size: 0.9375rem;
  margin: var(--space) auto 0; max-width: 35rem;
}
.camera-resolution { color: var(--grey-600); font-size: 0.875rem; }
/* The photos kept so far in one trip to the camera, read together. */
.camera-taken {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space);
  list-style: none; padding: 0; margin: var(--space) auto 0; max-width: 45rem;
}
.camera-taken li {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  font-size: 0.875rem; color: var(--grey-600);
}
.camera-taken img {
  width: 72px; height: 96px; object-fit: cover; border-radius: 4px;
  border: 1px solid var(--grey-200); background: var(--grey-900);
}
.camera-taken .btn-ghost { padding: 2px 6px; font-size: 0.8125rem; }
/* Sits in the control row beside Cancel, so it has to read as one control and
   not as a stray label that happens to precede a menu. */
.camera-pick {
  display: inline-flex; align-items: center; gap: calc(var(--space) / 2);
  color: var(--grey-600); font-size: 0.875rem; white-space: nowrap;
}
.camera-pick select { width: auto; margin: 0; max-width: 16rem; }

/* --- fieldwork progress ----------------------------------------------- */
/* A tractor pulling an AgroLiquid tank, applying liquid fertilizer as it goes.
   One pass takes five seconds, matching MIN_READ_MS in the wizard, so reading a
   report always shows a full run rather than a flicker.

   The sprayer loops rather than filling to 100%. Reading takes under a second
   for a clean PDF and up to a minute for ten OCR'd pages, and the server cannot
   report progress mid-parse, so a percentage would be invented.

   The crop behind the boom is taller and greener than the crop ahead of it,
   which is the whole point of the product. */

/* Everything here is proportional so the whole scene scales to whatever width
   it is given -- 520px on a laptop, about 280px on a 320px phone -- instead of
   overflowing or being cut off. aspect-ratio keeps the tractor from squashing. */
.fieldwork {
  position: relative; width: 100%; max-width: 32.5rem; aspect-ratio: 520 / 140;
  margin: 0 auto; overflow: hidden;
}
.fieldwork .base { position: absolute; inset: 0; }

/* The fed strip grows behind the sprayer. It is clipped rather than resized:
   a strip that changed width would squash the field inside it instead of
   revealing it, and clip-path takes a percentage, so it stays fluid. */
.fieldwork .fed {
  position: absolute; inset: 0;
  clip-path: inset(0 100% 0 0);
  animation: fieldwork-feed 5s linear infinite;
}
.fieldwork .fed-inner { width: 100%; height: 100%; }
.fieldwork svg { display: block; width: 100%; height: 100%; }

/* 186/520 of the scene, so the tractor keeps its size relative to the field.
   The 186 matches the rig's viewBox exactly -- change one and the other has to
   follow, or the tractor scales against the crop. */
.fieldwork .rig {
  position: absolute; top: 0; left: 0; width: 35.7692%; height: 100%;
  animation: fieldwork-drive 5s linear infinite;
}

/* The spinning group is nested INSIDE a group that carries the translate.
   A CSS transform replaces an SVG transform attribute outright, so rotating
   the positioned group directly would drop each wheel back to the origin and
   out of the viewBox -- which is exactly what it did. */
.fieldwork .wheel {
  transform-box: fill-box; transform-origin: center;
  animation: fieldwork-spin 0.85s linear infinite;
}

/* Each nozzle pulses on its own beat, so the boom reads as four nozzles
   working rather than one shape blinking. transform-box is what makes
   "top center" mean the nozzle tip: without it the origin is the whole SVG
   viewport and the fan grows from the wrong end. */
.fieldwork .spray {
  transform-box: fill-box; transform-origin: top center;
  animation: fieldwork-spray 1s ease-in infinite;
}
.fieldwork .spray-2 { animation-delay: 0.25s; }
.fieldwork .spray-3 { animation-delay: 0.5s; }
.fieldwork .spray-4 { animation-delay: 0.75s; }

@keyframes fieldwork-feed {
  from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); }
}
/* Starts fully off the left edge and leaves fully off the right. */
@keyframes fieldwork-drive { from { left: -35.7692%; } to { left: 100%; } }
@keyframes fieldwork-spin  { from { transform: rotate(0); } to { transform: rotate(360deg); } }
@keyframes fieldwork-spray {
  0%   { opacity: 0;    transform: scaleY(0.4); }
  30%  { opacity: 0.95; transform: scaleY(1); }
  100% { opacity: 0;    transform: scaleY(1.12); }
}

.fieldwork-caption {
  text-align: center; color: var(--grey-600); font-size: 0.9375rem; margin-top: var(--space);
}
.fieldwork-caption .detail {
  display: block; color: var(--grey-600); font-size: 0.875rem; margin-top: 4px;
}
.fieldwork-caption .motto {
  display: block; margin-top: var(--space);
  font-family: var(--font-sans); font-size: clamp(1.0625rem, 1rem + 0.6vw, 1.25rem);
  letter-spacing: 0.06em; color: var(--grey-900);
}

@media (prefers-reduced-motion: reduce) {
  .fieldwork .fed { animation: none; clip-path: inset(0 46.1538% 0 0); }
  .fieldwork .rig { animation: none; left: 36.5%; }
  .fieldwork .wheel { animation: none; }
  .fieldwork .spray { animation: none; opacity: 0.9; }
}


/* Announced, not shown. The card group needs a name a screen reader can read
   out when it enters the radio group, and there is a visible heading right
   above it, so repeating it on screen would be noise. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}


/* --- retail pricing --------------------------------------------------- */
/* The cards themselves are step 1's, unchanged -- this is the same question
   asked twice on one screen, so it is asked the same way both times. What is
   left here is only what belongs to pricing and not to the soil test. */

/* Notes about the price list, under the banner that loaded it. Small and
   quiet: a list of products left out is worth knowing and is not an alarm, and
   at notice size it shouted over the step it sits above. An error keeps a
   coloured edge, because that one does need finding. */
.pricing-messages { margin: 0 0 var(--space); }
.pricing-message {
  margin: 0 0 6px; font-size: 0.875rem; line-height: 1.5; color: var(--grey-600);
  padding-left: var(--space); border-left: 3px solid var(--grey-200);
}
.pricing-message:last-child { margin-bottom: 0; }
.pricing-message strong { color: var(--grey-900); }
.pricing-message.is-alert { border-left-color: var(--low); }
.pricing-message .pricing-template { margin-left: 4px; white-space: nowrap; }

/* Stacked, not side by side: uploading is the thing to do, and the template is
   what you need only the first time. Two buttons in a row read as a choice
   between equals. */
.pricing-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }

.pricing-template {
  font-size: 0.875rem; color: var(--green); text-decoration: underline;
}
.pricing-template:hover { color: var(--grey-900); }

/* --- step 5: validation, the budget sum, and choosing a plan ----------- */

/* An inline message under the field it is about, rather than a summary
   somewhere else -- the grower should not have to work out which box the
   complaint refers to. */
/* Provenance for the seeded crop price. Quiet, because it is a footnote to a
   field the grower is expected to overwrite -- but present, because a figure
   with no source is just a number someone typed. */
.price-note {
  font-size: 0.875rem; color: var(--grey-600); margin: 6px 0 0; line-height: 1.45;
}

.sim-error {
  color: var(--low); font-size: 0.875rem; font-weight: 600;
  margin: 6px 0 0;
}

/* The three figures the plan is built from. A grower who disagrees with the
   answer usually disagrees with one of these, so they are checkable. */
.sim-summary {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 12px;
  margin: 18px 0 4px; padding-top: 14px;
  border-top: 1px solid var(--grey-100); font-size: 0.875rem;
}
.sim-summary dt { color: var(--grey-600); }
.sim-summary dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.sim-summary dd span { color: var(--grey-600); font-size: 0.875rem; }
.sim-summary dd.strong { font-weight: 700; color: var(--grey-900); }

/* The cards are a radio group now: the whole card is the label, so the target
   is the card and not a 13px circle. The radio stays in the DOM rather than
   being replaced by a styled div, because that is what makes arrow keys move
   between options and a screen reader call it a choice. */
.option-cards { border: 0; padding: 0; margin: 0; }
.option-cards legend { padding: 0; }
.option-card { cursor: pointer; }
.option-card input[type="radio"] {
  width: 18px; height: 18px; margin: 0 8px 0 0; accent-color: var(--green);
  flex: none;
}
.option-card.is-picked {
  border-color: var(--green); border-width: 2px;
  box-shadow: 0 0 0 3px rgba(0, 122, 51, 0.12);
}
.option-card:focus-within {
  outline: 3px solid var(--green); outline-offset: 2px;
}

/* When a product goes on matters as much as how much of it does. */
.option-products .timing {
  display: block; color: var(--grey-600); font-size: 0.875rem; font-weight: 400;
}
.option-unmet {
  margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--grey-100);
}
.option-unmet strong { color: var(--low); font-size: 0.875rem; }
.option-unmet ul { margin: 4px 0 0; padding-left: 16px; }
.option-unmet li { font-size: 0.875rem; }

/* A disabled Next has to look disabled, or it reads as an unresponsive app. */
[data-nav="next"]:disabled {
  opacity: 0.45; cursor: not-allowed;
}


/* --- the estimated quote ----------------------------------------------- */
/* A working document for a retail partner, not a summary for a grower: figures
   line up in columns, quantities are in the units they are ordered in, and two
   columns are left blank for the partner's own pricing. Tabular numerals so a
   column of money reads as a column. */

.quote {
  width: 100%; border-collapse: collapse; font-size: 0.875rem; margin-top: 14px;
}
.quote th, .quote td {
  padding: 8px 10px; border-bottom: 1px solid var(--grey-100); text-align: left;
  vertical-align: top;
}
.quote thead th {
  font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--grey-600); border-bottom: 2px solid var(--grey-400);
}
.quote .q-num, .quote td.q-num { text-align: right; font-variant-numeric: tabular-nums; }
.quote .q-name { font-weight: 600; }
.quote .q-sub {
  display: block; font-weight: 400; font-size: 0.875rem; color: var(--grey-600);
}
.quote tr.q-group th {
  background: var(--grey-50, #F5F6F5); font-size: 0.8125rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--grey-600);
}
.quote tfoot th { text-align: right; font-weight: 600; }
.quote tfoot td { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.quote tr.q-total th, .quote tr.q-total td {
  border-top: 2px solid var(--grey-900); border-bottom: none;
  font-size: 1rem; padding-top: 12px;
}

/* The columns the retail partner fills in. Ruled, not empty -- a blank cell
   reads as missing data, a ruled one reads as somewhere to write. */
.quote .q-fill {
  min-width: 6.5rem; background: repeating-linear-gradient(
    to bottom, transparent, transparent calc(100% - 1px),
    var(--grey-100) calc(100% - 1px), var(--grey-100) 100%);
}
.quote thead .q-fill { background: none; color: var(--grey-600); font-style: italic; }
/* Said in words, not as a number, so it cannot be read as a price of zero. */
.quote .q-uncosted { color: var(--grey-600); font-style: italic; font-weight: 400; }

.quote-terms {
  font-size: 0.875rem; color: var(--grey-600); line-height: 1.5;
  margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--grey-100);
}
.quote-signoff {
  display: flex; flex-wrap: wrap; gap: 28px; margin-top: 28px;
  font-size: 0.875rem; color: var(--grey-600);
}
.quote-signoff > div { flex: 1 1 12rem; }
.quote-signoff .q-rule {
  display: block; border-bottom: 1px solid var(--grey-400);
  height: 2.2rem; margin-bottom: 4px;
}

/* Printed, this is what gets ordered from, so it must not break across a page
   mid-table and must not carry the screen's scroll box. */
@media print {
  .quote-section { break-inside: avoid-page; }
  .quote-section .table-wrap { overflow: visible; }
  .quote { font-size: 0.75rem; }
}


/* --- letter ----------------------------------------------------------- */

.letter { font-size: 1rem; }
.letter .letterhead {
  display: flex; justify-content: space-between; gap: calc(var(--space) * 2);
  border-bottom: 3px solid var(--green); padding-bottom: calc(var(--space) * 2);
  margin-bottom: calc(var(--space) * 3); flex-wrap: wrap;
}
/* Prepared for, prepared by, date -- as blocks with quiet headings rather than
   an unlabelled stack of lines. Left-aligned inside a right-aligned column, so
   the block sits on the right of the page but its own lines start together;
   ragged-left text is hard to read even three lines of it. */
.letter .meta {
  display: flex; gap: calc(var(--space) * 2.5); flex-wrap: wrap;
  font-size: 0.875rem; color: var(--grey-900);
}
.letter .meta-block { display: flex; flex-direction: column; gap: 1px; }
.letter .meta-label {
  font-size: 0.8125rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--grey-600); margin-bottom: 2px;
}
.letter .meta-block strong { font-weight: 700; }

/* --- who the letter is for ---------------------------------------------- */

/* Two by two: four short answers about two parties, paired on a line each. It
   is capped so the boxes do not stretch to the width of the panel, where a
   name gets a text field wide enough for a paragraph. */
/* Three across, and three deliberately.
   These are five answers about two parties -- who the plan is for, and who it
   is from -- and a fifth field broke the two-by-two that used to carry that:
   auto-fit put Address beside Retail Partner, which reads as one pair and is
   two halves of different ones, and left Prepared by stranded on a row of its
   own. Three explicit columns put the grower's three on the first row and the
   sender's two on the second, so the grouping is the layout.

   align-items:start because one of the five carries a line of help under it.
   Stretching the rest to match left a hole under Grower Name the size of that
   sentence. */
/* Step 6's details: a card per party. The grower's card is the wider of the
   two because it holds three answers to the partner's two. Titles in the
   display face and the banners' green, the same as step 4's subtitle, so the
   two cards read as parts of the step rather than as a form dropped on it. */
.letter-details .party-grid {
  display: grid; gap: calc(var(--space) * 2);
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  align-items: start;
}
@media (max-width: 63.99rem) {
  .letter-details .party-grid { grid-template-columns: 1fr; }
}
.party {
  border: 1px solid var(--grey-200); border-radius: 10px; background: var(--white);
  padding: calc(var(--space) * 1.5) calc(var(--space) * 2) calc(var(--space) * 2);
}
.party-title {
  margin: 0 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--grey-100);
  font-family: var(--font-display); font-weight: 600; font-size: 1.375rem;
  line-height: 1.15; letter-spacing: 0.01em; color: var(--green-dark);
}
.party-fields {
  display: grid; gap: calc(var(--space) * 1.5) calc(var(--space) * 2);
  grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
  align-items: start;
}
.party-fields .party-wide { grid-column: 1 / -1; }
/* The labels here are single words on one line, so they need none of the
   min-height the soil form uses to keep wrapped labels aligned. */
.letter-details label { min-height: 0; }
.letter section { margin-bottom: calc(var(--space) * 3); }
.signoff { margin-top: calc(var(--space) * 3); font-size: 0.9375rem; }

/* --- print ------------------------------------------------------------ */

@media print {
  body { background: var(--white); font-size: 11pt; }
  .masthead, .steps, .wizard-nav, .no-print { display: none !important; }
  .shell { max-width: none; padding: 0; }
  .panel { box-shadow: none; padding: 0; margin-bottom: 16pt; page-break-inside: avoid; }
  .letter .letterhead { border-bottom-color: #000; }
  table { font-size: 10pt; }
  h2, h3 { page-break-after: avoid; }
}

/* Phone: stack anything still sitting in two columns, and give the content
   back the room the padding was taking. */
@media (max-width: 39.99rem) {
  .finding { grid-template-columns: 1fr; gap: 4px; }
  .panel { padding: calc(var(--space) * 2); }
  .camera-controls button { flex: 1 1 100%; }
  /* The camera names are long and the row is one column wide down here, so the
     menu takes the width rather than pushing the row sideways. */
  /* min-width on the label as well as the menu inside it: the label is itself
     a flex item of the control row, and without this its intrinsic width --
     "Camera:" plus a long device name -- refused to shrink and hung 21px off
     each end of a centred row. */
  /* Full width and split evenly: at this size the strip is the only thing
     naming the two documents, so neither name may be the one that wraps. */
  .upload-switch { display: flex; width: 100%; }
  .upload-switch button { flex: 1 1 0; min-width: 0; padding: 9px 8px; font-size: 0.9375rem; }

  .camera-pick { flex: 1 1 100%; min-width: 0; }
  .camera-pick select { flex: 1 1 auto; max-width: none; min-width: 0; }
  .camera-guide span { width: 24px; height: 24px; }

  /* Back and Next are the whole of navigation on a phone, so they stay on
     screen rather than at the foot of a long step. Sticky, not fixed, so the
     buttons still print and still sit in the normal tab order. */
  /* Scrolling has to stop short of the bar below, or tabbing into a field
     near the foot of a step puts that field behind it -- WCAG 2.4.11, and
     measured: five of the twenty-four fields on step 1 were covered
     completely, by the whole 44px of their height. scroll-padding is obeyed
     by the browser's own focus scrolling as well as by scrollIntoView, so one
     declaration covers both. */
  html { scroll-padding-bottom: 6.5rem; }

  .wizard-nav {
    position: sticky; bottom: 0; z-index: 30;
    margin-inline: calc(var(--space) * -1); padding-inline: var(--space);
    padding-bottom: max(var(--space), env(safe-area-inset-bottom));
    background: var(--grey-50);
    box-shadow: 0 -1px 0 var(--grey-200), 0 -8px 16px -12px rgba(0, 0, 0, 0.35);
  }
  .wizard-nav .spacer { display: none; }
  .wizard-nav button { flex: 1 1 0; }
}

/* Tablet and up: Back and Next sit at their natural size at the two ends. */
@media (min-width: 40rem) {
  .wizard-nav button { flex: 0 0 auto; }
}

/* Landscape on a phone leaves very little height. Trim the vertical rhythm so
   the step heading and the first control are both on screen at once. */
@media (max-height: 30rem) and (orientation: landscape) {
  .step-head { padding-block: var(--space); }
  .panel { padding-block: calc(var(--space) * 1.5); }
  .camera-stage video, .camera-stage img { max-height: 46vh; }
}

/* --- the card's add-product row ------------------------------------------ */
/* Product wide, the rest only as wide as they need. Collapses to one control
   per line on a phone rather than five squeezed boxes. */
.supply-row {
  display: grid; gap: 8px; align-items: center;
  grid-template-columns: minmax(10rem, 2fr) 6rem 6.5rem 10rem auto;
}
@media (max-width: 720px) {
  .supply-row { grid-template-columns: 1fr 1fr; }
  .supply-row .supply-product { grid-column: 1 / -1; }
}
.supply-row select, .supply-row input {
  padding: 7px 8px; border: 1px solid var(--grey-200); border-radius: 6px;
  font: inherit; font-size: 0.875rem; background: #fff; min-width: 0;
}
.supply-row input { font-variant-numeric: tabular-nums; }
.supply-remove { padding: 4px 8px; font-size: 1.125rem; line-height: 1; }

.supply-warn {
  margin: 8px 0 0; font-size: 0.875rem; color: var(--grey-900);
  border-left: 3px solid var(--accent); padding-left: 10px;
}
/* A product the price sheet has no price for: said where its price would be,
   in the warning ink, never read as free (owner's request, October 2026). */
.price-missing { color: var(--high); font-weight: 700; font-style: normal; }

/* The one line of context the progress scene does not carry itself: why a
   photograph takes longer than a PDF with a text layer. */
.read-detail { margin: 10px 0 0; font-size: 0.875rem; }

/* --- step 3: the amendment cell ------------------------------------------ */
/* The middle column now carries a product name, a rate, sometimes a split
   schedule and sometimes a picker. At 375px three columns sharing the viewport
   squeezed that select to 92px, which is not wide enough to read "Muriate of
   potash - 826 lb/acre" in. A floor on the table makes .table-wrap scroll
   instead, which is what it is there for. */

/* The "what to spread" column carries a product, a rate, sometimes a split
   schedule and sometimes a choice, so it needs room to breathe in a way the
   bare number it replaced did not. */
.amendment-analysis { font-size: .875rem; margin-top: 2px; }

.amendment-passes {
  margin: 10px 0 0; padding-left: 1.1rem; font-size: .9375rem;
}
.amendment-passes li { margin-bottom: 4px; }
.amendment-passes .muted { display: block; font-size: .875rem; }

.amendment-pick { display: block; margin-top: 10px; }
.amendment-pick select {
  width: 100%; max-width: 22rem; min-height: 44px;
  padding: .4rem .6rem; font: inherit;
  border: 1px solid var(--grey-400); border-radius: 6px;
  background: #fff; color: var(--grey-900);
}
.amendment-pick select:focus-visible {
  outline: 3px solid var(--green); outline-offset: 1px;
}

/* --- step 4: which product supplies each nutrient ------------------------- */
.product-choices {
  display: grid; gap: 16px; margin-top: 18px;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}
.product-choice label { display: block; margin-bottom: 6px; }
.product-choice select {
  width: 100%; min-height: 44px; padding: .4rem .6rem; font: inherit;
  border: 1px solid var(--grey-400); border-radius: 6px;
  background: #fff; color: var(--grey-900);
}
.product-choice select:focus-visible {
  outline: 3px solid var(--green); outline-offset: 1px;
}
.product-choice .technical { margin: 6px 0 0; }

/* --- step 4: which pass the liquid goes on ------------------------------- */
.placement-choice { max-width: 22rem; margin-top: 18px; }
.placement-choice label { display: block; margin-bottom: 6px; }
.placement-choice select {
  width: 100%; min-height: 44px; padding: .4rem .6rem; font: inherit;
  border: 1px solid var(--grey-400); border-radius: 6px;
  background: #fff; color: var(--grey-900);
}
.placement-choice select:focus-visible {
  outline: 3px solid var(--green); outline-offset: 1px;
}
.placement-choice .technical { margin: 6px 0 0; }

/* --- step 4: choosing the placement, then the products ------------------- */

/* The placement picker reuses the plan-area multi-select: same fieldset, same
   24x24 boxes in a 44px row, same "select all" ghost button. A grower who has
   grouped their ground has already met this control once. */
.placement-pick .choice label strong { margin-bottom: 2px; }
.placement-pick .choice label .pill { margin-left: 6px; vertical-align: 1px; }
.placement-pick .choice label .technical { margin: 2px 0 0; }
.placement-pick legend .req { margin-left: 2px; }

/* Why a product is not on the list. Closed by default -- it is the answer to
   a question most growers will not ask, and the ones who do ask it are owed
   more than silence. */
.product-unavailable { margin-top: 8px; font-size: 0.875rem; }
.product-unavailable summary {
  cursor: pointer; color: var(--grey-600); padding: 6px 0; min-height: 24px;
}
.product-unavailable summary:focus-visible {
  outline: 3px solid var(--green); outline-offset: 2px; border-radius: 4px;
}
.product-unavailable ul { margin: 4px 0 0; padding-left: 1.1rem; color: var(--grey-600); }
.product-unavailable li { margin-bottom: 4px; }

/* A nutrient with nothing that can supply it where the plan is going. Not an
   error -- ask for a foliar-only corn plan and there is genuinely no nitrogen
   product -- but not something to show as an empty box either. */
.is-missing-note { color: var(--low); font-weight: 600; }

/* --- a product named by its own mark --------------------------------------
   Brand standards: where an AgroLiquid product is named, it is named with its
   own logo. The artwork is the brand folder's, trimmed and scaled by height
   only -- never recoloured, never stretched, so `width: auto` here is load
   bearing rather than tidy.

   The name stays in the markup behind it. It is what a screen reader reads,
   what a browser find-on-page matches, and what is left if the image does not
   load. */
.product-card .name.is-mark { display: block; margin-bottom: 4px; }
.product-card .name.is-mark img {
  height: 34px; width: auto; max-width: 100%;
  object-fit: contain; object-position: left center;
  display: block;
}
/* A mark is artwork, not type, so it does not get the uppercasing the rest of
   the card's headings carry. */
.product-card .name.is-mark { text-transform: none; letter-spacing: normal; }

@media (max-width: 600px) {
  .product-card .name.is-mark img { height: 30px; }
}

/* What the rig can put down. Inside the card of the placement it belongs to,
   under the category's name, because each placement is its own rig and asks
   for its own gallons; it was one field under all the cards, which could only
   describe one rig. The card is a 19rem column with 12px of padding, so the
   field runs its full width and the label takes the detail labels' size. The
   control keeps its 44px height and 1rem type. */
.capacity-field {
  display: grid; grid-template-areas: "l l" "i u"; grid-template-columns: 1fr auto;
  margin: 2px 0 8px;
}
.capacity-field label {
  grid-area: l; display: block; margin-bottom: 4px;
  font-size: 0.875rem; font-weight: 600;
}
/* The unit sits inside the control's border rather than beside the label, so
   the field reads as one thing. */
.capacity-field input {
  grid-area: i; width: 100%; min-width: 0; min-height: 44px; font-size: 1rem;
  border-top-right-radius: 0; border-bottom-right-radius: 0;
}
.capacity-field .unit {
  grid-area: u; display: flex; align-items: center; padding: 0 12px;
  border: 1px solid var(--grey-200); border-left: 0;
  border-top-right-radius: 6px; border-bottom-right-radius: 6px;
  background: var(--grey-50); color: var(--grey-600); font-size: 0.875rem;
}
/* Soybean row spacing, under In-furrow (agronomist, 20 October 2026): the
   gallons field's label, the select at the control's full width. */
.row-spacing-field { display: block; }
.row-spacing-field select { width: 100%; min-height: 44px; font-size: 1rem; }

/* Categories, with their details indented beneath the one that is ticked.
   Two columns of categories on a wide screen; the details stay under their
   own parent rather than flowing into the next column. */
.placement-cats {
  display: grid; gap: 0 calc(var(--space) * 2);
  grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr));
  align-items: start;
}
.placement-cat { break-inside: avoid; margin-bottom: var(--space); }
.placement-cat .choice { margin-bottom: 0; }
.placement-details {
  margin: 2px 0 0 34px; padding-left: var(--space);
  border-left: 2px solid var(--grey-200);
}
.placement-details .choice { min-height: 36px; margin-bottom: 0; }
.placement-details .choice label { font-size: 0.875rem; padding: 6px 0; }
.placement-details .choice input[type="checkbox"] { margin-top: 6px; }

/* Which pass a split line goes in. Only ever on a product the plan has
   deliberately put in two places, so it marks the exception rather than
   labelling every card with something they all share. */
.product-card .placement-tag { display: inline-block; margin-bottom: 6px; }

/* --- how will you apply fertilizer, on step 1 ----------------------------
   A card per placement, stacked, with the details of a chosen one folded out
   inside its own card. Cards rather than a plain list because a category and
   the details under it are one answer, and a flat list of checkboxes gives a
   sub-item the same weight as the thing it belongs to. */
/* Its own section under "About your crop", not a field in the crop row:
   eleven placements is a question, not a form control, and squeezing it into
   a grid cell made step 1 a scroll. */
.placement-block { margin-top: calc(var(--space) * 2); }

/* Columns rather than a grid, so the eleven fit on a screen without holes.
   A grid puts them in rows, and one card folded open sets the height of its
   whole row -- so opening In-furrow left Banded placement and Surface banded
   floating above a hand's width of nothing. Columns flow instead: a tall card
   pushes what follows it down its own column and leaves the others alone. */
.placement-cards {
  columns: 19rem auto; column-gap: 8px;
}
.placement-cards > * {
  break-inside: avoid; -webkit-column-break-inside: avoid;
  margin-bottom: 8px;
}
.placement-card {
  border: 1px solid var(--grey-200); border-radius: 8px;
  padding: 4px 12px; background: #fff;
}
.placement-card.is-on { border-color: var(--green); }
.placement-card .pill { margin-left: 8px; vertical-align: 1px; }

/* The category and its link on one line. */
.placement-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.placement-head .choice { margin-bottom: 0; min-width: 0; }
.placement-head .choice label { font-weight: 600; }
.placement-more {
  flex: none; border: 0; background: none; padding: 4px 0; cursor: pointer;
  font: inherit; font-size: 0.875rem; color: var(--green);
  text-decoration: underline; text-underline-offset: 2px;
}
.placement-more:hover { text-decoration-thickness: 2px; }
.placement-more:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; border-radius: 3px; }
/* What was chosen under a closed drawer, so closing it hides nothing. */
.placement-chosen { margin: 0 0 6px; font-size: 0.875rem; color: var(--grey-600); }

/* The details under a placement: the rig behind it. Behind a link by
   request, so the section lists the categories and the gallons and nothing
   else until somebody wants to say more. Which drawers are open lives in the
   page's state, not here, so a tick that redraws the step does not shut
   them. */
.placement-details {
  margin: 0; padding: 2px 0 4px 10px;
  border-top: 1px solid var(--grey-200);
}
.placement-details[hidden] { display: none; }
.placement-details .choice { min-height: 34px; margin-bottom: 0; }
.placement-details .choice label { font-size: 0.875rem; font-weight: 400; padding: 5px 0; }
.placement-details .choice input[type="checkbox"] { margin-top: 5px; }

.placement-block.is-missing .placement-cards { outline: 2px solid var(--low); outline-offset: 6px; border-radius: 10px; }

/* ------------------------------ step 4: asked in order

   "More information" opens from a link, the way step 3's details do: what
   explains a plan is a click away, and what is crossed stays on screen. */
.more-info { margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--grey-200); }
.more-info summary {
  cursor: pointer; color: var(--grey-900); text-decoration: underline;
  text-underline-offset: 3px; font-size: 0.9375rem;
}
.more-info-body { margin-top: 8px; }
.more-info-body p { margin: 0 0 6px; line-height: 1.5; }
.more-info-body ul { margin: 0 0 4px; padding-left: 1.1em; }
.more-info-body li { margin: 4px 0; line-height: 1.5; }
.placement-summary .more-info { margin-top: 12px; }

/* One plan's products, under its name, where there are several plans. */
.product-choice-plan + .product-choice-plan {
  margin-top: calc(var(--space) * 2); padding-top: var(--space);
  border-top: 1px solid var(--grey-200);
}
.product-choice-head { margin: 0 0 8px; font-size: 1.125rem; }
.product-choice-head .muted { font-weight: 400; font-size: 0.9375rem; }
/* The chosen product's own mark above its select, so the choice reads as the
   product rather than as a line of text. */
.product-choice-mark { margin: 2px 0 6px; min-height: 28px; }
.product-choice-mark .name.is-mark { display: block; }
.product-choice-mark .name.is-mark img {
  height: 28px; width: auto; max-width: 100%; display: block;
  object-fit: contain; object-position: left center;
}
.product-choice-mark .name:not(.is-mark) { font-weight: 600; color: var(--grey-900); }
.products-confirmed { margin: 0; font-weight: 600; color: var(--green-dark); }

/* The plan's table names each product by its mark, as the cards do. */
.table-wrap th .name.is-mark { display: block; margin-bottom: 2px; }
.table-wrap th .name.is-mark img {
  height: 26px; width: auto; max-width: 11rem; display: block;
  object-fit: contain; object-position: left center;
}
/* The nutrient a product is there for, under its mark, as step 4's table
   shows it. */
.doc-table .doc-row-nutrient {
  display: block; margin-top: 2px; font-size: 0.8125rem; font-weight: 400;
}
/* In the letter's tables the mark stands in for the name, in the row's own
   cell, a touch smaller than a card's. */
.doc-table th .name.is-mark, .doc-table td .name.is-mark { display: block; margin: 0; }
.doc-table th .name.is-mark img, .doc-table td .name.is-mark img {
  height: 22px; width: auto; max-width: 9rem; display: block;
  object-fit: contain; object-position: left center;
}
.table-wrap th .name + .muted { display: block; }

/* ------------------------------ step 6: the plan document

   The October 2026 template set in the app's own tokens: the two findings
   cards, the green eyebrow, the dark table heads, the pale banner cards with
   flat art, the gold left-border callouts and the dark total band. Nothing
   here is smaller than 0.875rem -- this is a document a grower reads. */

.doc-head {
  border-bottom: 1px solid var(--grey-200);
  padding-bottom: calc(var(--space) * 1.5); margin-bottom: calc(var(--space) * 2);
}
.doc-title {
  margin: 0; color: var(--green-dark); font-size: 2rem; line-height: 1.1;
}
.doc-date { margin: 4px 0 0; color: var(--grey-600); font-size: 0.9375rem; }
/* The opening paragraph: under the head's rule, before the first section. */
.doc-intro { margin: 0; line-height: 1.55; }
/* Each section opens on a rule of its own with air above it, so one does not
   run into the next (owner's request, October 2026). */
.doc section {
  margin: 0 0 calc(var(--space) * 3); padding-top: calc(var(--space) * 2.5);
  border-top: 2px solid var(--grey-200);
}
.doc-section-title {
  margin: 0 0 var(--space); font-size: 1.5rem;
  color: var(--green-dark);
}

/* The Good / Needs attention pair. */
.doc-cards {
  display: grid; gap: calc(var(--space) * 2);
  /* Never wider than the page: at the largest text size 16rem is wider than a
     phone, and the pair pushed the letter sideways. */
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  margin-bottom: calc(var(--space) * 2);
}
.doc-card { border-radius: 10px; padding: calc(var(--space) * 2); }
.doc-card.good { background: var(--green-light); }
.doc-card.attention { background: var(--high-bg); }
.doc-card-title {
  display: flex; align-items: center; gap: 10px; margin: 0 0 10px;
  font-size: 1.25rem; font-weight: 700;
}
.doc-card.good .doc-card-title { color: var(--green); }
.doc-card.attention .doc-card-title { color: var(--high); }
.doc-badge { display: inline-flex; width: 24px; flex: none; }
.doc-badge svg { width: 100%; height: auto; display: block; }
.doc-fact { margin: 0 0 6px; font-size: 0.9375rem; line-height: 1.5; }

/* The green strap line under the cards. */
.doc-eyebrow {
  margin: 0 0 4px; color: var(--green); font-weight: 700; font-size: 0.875rem;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.doc-strap { margin: calc(var(--space) * 2) 0 var(--space); }

/* Dark-headed tables: the soil table, the planting table and the quote. */
.doc-table {
  width: 100%; border-collapse: collapse; font-size: 0.9375rem;
  margin: var(--space) 0;
}
.doc-table thead th {
  background: var(--green-dark); color: var(--white); text-align: left;
  font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.04em;
  padding: 10px 12px;
}
.doc-table th, .doc-table td { padding: 10px 12px; text-align: left; }
.doc-table tbody th { font-weight: 400; }
.doc-table tbody tr:nth-child(odd) { background: var(--green-light); }
.doc-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.doc-table td.v { font-weight: 700; }

.doc-reco { margin: calc(var(--space) * 2) 0 var(--space); line-height: 1.55; }
.doc-footnote { margin: 0; color: var(--grey-600); font-size: 0.875rem; }

/* Step 2's gauges in the report: a block per sampled area, two to a row,
   each drawn to its column. Under them, the crop need each plan was fed
   from, as a table in the soil table's clothes. */
/* Each area and each plan is a card of its own, so two blocks of gauges or
   two plans' tables do not read as one long run. */
.doc-area {
  margin: calc(var(--space) * 2) 0;
  padding: calc(var(--space) * 1.5) calc(var(--space) * 2);
  border: 1px solid var(--grey-200); border-radius: 10px; background: var(--white);
}
.doc-area-head {
  margin: 0 0 10px; padding-bottom: 8px; border-bottom: 1px solid var(--grey-100);
  font-size: 1.125rem; color: var(--green-dark);
}
.doc-area-head .muted { font-weight: 400; font-size: 0.9375rem; }
.doc-gauges {
  display: grid; gap: 4px 24px;
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
}
.doc-gauges .gauge-block { container-type: normal; }
.doc-gauges .chart-scroll { margin-inline: 0; padding-inline: 0; overflow: visible; }
.doc-gauges .chart-scroll > svg.gauge-svg {
  width: 100%; max-width: 100%; height: auto; display: block;
}
.doc-sub-title {
  margin: calc(var(--space) * 3) 0 6px; padding-top: calc(var(--space) * 2);
  border-top: 1px dashed var(--grey-200); font-size: 1.25rem; color: var(--green-dark);
}
/* Each plan's block of the quote opens on a heavy rule, so the eye finds
   where one plan ends and the next begins. */
.doc-quote tr.doc-sub th, .doc-quote tr.doc-sub td {
  border-top: 3px solid var(--green-dark);
}
.doc-weights-intro { margin: 0 0 var(--space); line-height: 1.55; }
.doc-weights { margin: 0 0 10px; color: var(--grey-600); font-size: 0.9375rem; }

/* The pale banner card that opens each treatment section. */
.doc-banner {
  display: flex; align-items: center; gap: calc(var(--space) * 3);
  flex-wrap: wrap; background: var(--green-light); border-radius: 10px;
  padding: calc(var(--space) * 2) calc(var(--space) * 2.5);
  margin: var(--space) 0 calc(var(--space) * 1.5);
}
.doc-art { flex: none; width: 130px; }
.doc-art svg { width: 100%; height: auto; display: block; }
.doc-mark {
  flex: none; background: var(--white); border-radius: 8px; padding: 8px 14px;
}
.doc-mark .name.is-mark img {
  height: 40px; width: auto; max-width: 11rem; display: block;
  object-fit: contain; object-position: left center;
}
.doc-mark .name:not(.is-mark) { font-weight: 700; font-size: 1.0625rem; }
.doc-banner-words { flex: 1 1 14rem; min-width: 0; }
.doc-headline { margin: 2px 0; font-size: 1.125rem; font-weight: 700; }
.doc-subline { margin: 2px 0 0; color: var(--grey-600); font-size: 0.9375rem; }
.doc-rate-big { margin: 2px 0; font-size: 1.625rem; font-weight: 800; }
.doc-pass-words { line-height: 1.55; }

/* The two-column list the Fall program is read from. */
.doc-list { margin: var(--space) 0; }
/* The plan's own notes, printed after the quote (agronomist, 21 October 2026). */
.doc-plan-notes { margin: 8px 0 0 18px; padding: 0; }
.doc-plan-notes li { margin: 4px 0; }
.doc-list-row {
  display: flex; justify-content: space-between; gap: calc(var(--space) * 2);
  padding: 10px 14px; font-size: 0.9375rem;
}
.doc-list-row:nth-child(odd) { background: var(--green-light); }
.doc-list-row strong { text-align: right; white-space: nowrap; }
/* On a phone the figure may wrap rather than push the letter sideways:
   "826 lb/acre in 3 passes" at the largest text is wider than a 320 px
   screen (October 2026). */
@media (max-width: 640px) {
  .doc-list-row { flex-wrap: wrap; gap: 4px calc(var(--space) * 2); }
  .doc-list-row strong { white-space: normal; }
}

/* The gold left-border callout: To finalize / Before loading. */
.doc-callout {
  border-left: 4px solid var(--accent); background: var(--high-bg);
  border-radius: 0 8px 8px 0; padding: 12px 16px;
  margin: calc(var(--space) * 1.5) 0; font-size: 0.9375rem; line-height: 1.55;
}

/* The foliar card, with the leaf. */
.doc-foliar {
  display: flex; gap: calc(var(--space) * 2); background: var(--high-bg);
  border-radius: 10px; padding: calc(var(--space) * 2);
  margin-top: calc(var(--space) * 1.5);
}
.doc-art-leaf { width: 36px; }
.doc-foliar .doc-headline { margin: 0 0 4px; }
.doc-foliar-words { margin: 0; font-size: 0.9375rem; line-height: 1.5; }

/* The quote table's tinted per-plan subtotal row. */
.doc-quote tr.doc-sub th, .doc-quote tr.doc-sub td {
  background: var(--green-light); font-weight: 700; font-size: 0.9375rem;
}
.doc-sub-meta { display: block; font-weight: 400; color: var(--grey-600); }
.doc-uncosted { color: var(--grey-600); font-style: italic; font-weight: 400; }

/* The dark total band. */
.doc-band {
  display: flex; justify-content: space-between; align-items: center;
  gap: calc(var(--space) * 2); flex-wrap: wrap; background: var(--green-dark);
  color: var(--white); border-radius: 10px;
  padding: calc(var(--space) * 2) calc(var(--space) * 3);
  margin: calc(var(--space) * 2) 0;
}
.doc-band-label {
  margin: 0 0 4px; font-size: 0.875rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.doc-band-avg { margin: 0; font-size: 0.9375rem; }
.doc-band-total {
  font-size: 2.25rem; font-weight: 800; font-variant-numeric: tabular-nums;
}

.doc-note { color: var(--grey-600); font-size: 0.9375rem; }
.doc-estimate { line-height: 1.55; font-size: 0.9375rem; }
.doc-citations { color: var(--grey-600); font-size: 0.875rem; }
.doc-footer {
  display: flex; justify-content: space-between; gap: calc(var(--space) * 2);
  flex-wrap: wrap; border-top: 1px solid var(--grey-200);
  margin-top: calc(var(--space) * 2); padding-top: var(--space);
  color: var(--grey-600); font-size: 0.875rem; text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* The gold callout as rows rather than one paragraph: a lead, small group
   heads, short bulleted lines, and the must-line last. */
.doc-callout-rows .doc-callout-lead { margin: 0 0 6px; }
.doc-callout-rows .doc-callout-head {
  margin: 10px 0 2px; font-weight: 700; color: var(--grey-900);
}
.doc-callout-rows ul { margin: 0; padding-left: 1.15em; }
.doc-callout-rows li { margin: 3px 0; line-height: 1.45; }
.doc-callout-rows .doc-callout-must { margin: 10px 0 0; }
/* The same small group head inside a "More information" link's notes. */
.more-info-body .doc-callout-head { margin: 8px 0 2px; font-weight: 700; color: var(--grey-900); }

/* ------------------------------ step 4: product preferences, per plan tab */
.prefs-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.prefs-title { display: flex; align-items: center; gap: 10px; margin: 0; }
.prefs-leaf, .prefs-auto { display: inline-flex; flex: 0 0 auto; }
.prefs-leaf svg, .prefs-auto svg { width: 26px; height: 26px; display: block; }
.prefs-auto svg { opacity: 0.75; }
.prefs-body { padding-top: 12px; }
.prefs-plan { margin: 0; font-size: 1.375rem; }
.prefs-need { border: 1px solid var(--grey-200); border-radius: 10px; padding: 8px 12px;
  margin-bottom: 10px; }
.prefs-need-head { margin: 2px 0 4px; font-weight: 700; color: var(--grey-900); }
.prefs-row { display: grid; grid-template-columns: 110px 1fr auto; align-items: center;
  gap: 12px; padding: 8px 0; border-top: 1px solid var(--grey-100); }
.prefs-row:first-of-type { border-top: 0; }
.prefs-row.is-avoided .prefs-name, .prefs-row.is-avoided .prefs-mark { opacity: 0.45; }
/* Every logo in a 110 x 40 area, balanced: each is given the same visible
   area, its proportions kept, so a long wordmark and a compact mark read as
   the same size (owner's spec, October 2026). The size comes from markSize
   in the wizard; object-fit is the guard against any stretch. */
.prefs-mark { display: flex; align-items: center; width: 110px; min-height: 40px; }
/* Defaults; the wizard sets each logo's own on the element, which wins. */
.name.is-mark { --mark-w: auto; --mark-h: 26px; }
.prefs-mark .name.is-mark img {
  width: var(--mark-w); height: var(--mark-h); max-width: 110px; max-height: 40px;
  display: block; object-fit: contain; object-position: left center;
}
.prefs-mark .name:not(.is-mark) { display: none; }
.prefs-name { display: flex; flex-direction: column; min-width: 0; }
.prefs-name .technical { margin: 2px 0 0; }
.seg-group { display: inline-flex; border: 1px solid var(--grey-200); border-radius: 8px;
  overflow: hidden; }
.seg { min-height: 40px; padding: 0 16px; border: 0; border-left: 1px solid var(--grey-200);
  background: var(--white); color: var(--grey-900); font: inherit; font-size: 0.9375rem;
  cursor: pointer; white-space: nowrap; }
.seg:first-child { border-left: 0; }
.seg.is-on[data-pref="preferred"] { background: var(--green); color: var(--white); font-weight: 700; }
.seg.is-on[data-pref="acceptable"] { background: var(--green-light); font-weight: 700; }
.seg.is-on[data-pref="avoid"] { background: var(--grey-100); font-weight: 700; }
.seg:disabled { cursor: not-allowed; color: var(--grey-400); }
.seg:focus-visible { outline: 3px solid var(--green); outline-offset: -3px; }
.prefs-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--grey-200); flex-wrap: wrap; }
.prefs-foot-plan { display: inline-flex; align-items: center; gap: 8px; color: var(--grey-600); }
.prefs-foot-plan .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); }
.prefs-foot-actions { display: inline-flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-left: auto; }
@media (max-width: 640px) {
  .prefs-row { grid-template-columns: 1fr; gap: 6px; }
  .seg { padding: 0 10px; }
}

/* The maker's label and Safety Data Sheet beside a Monty's product: on the
   add row, on the order line, and in the step 6 quote. Small, because they
   are reference beside the decision, not the decision. */
.doc-links { display: inline-flex; flex-wrap: wrap; gap: 4px 12px; font-size: 0.8125rem; }
.doc-links a { color: var(--green-dark); font-weight: 600; text-decoration: underline; }
.doc-links a:hover, .doc-links a:focus-visible { color: var(--green); }
.supply-row .doc-links { grid-column: 1 / -1; }
.order-line-words .doc-links { margin-top: 2px; }
.doc-table td .doc-links { display: flex; margin-top: 4px; }
@media print { .doc-links { display: none; } }

/* The password rules on My account and "Choose a new password" (owner,
   2026-10-23): each line ticks as it is met while the person types. */
.password-rules-title { margin: 0 0 calc(var(--space) * 0.5); font-weight: 600; text-align: left; }
.password-meter {
  --met: 0; /* share of rules met, 0 to 1; the page updates it as people type */
  height: 8px; border-radius: 4px; background: var(--grey-100); overflow: hidden;
}
.password-meter-fill {
  display: block; height: 100%; width: calc(var(--met) * 100%);
  background: #C77700; transition: width 0.2s ease, background-color 0.2s ease;
}
.password-meter[data-complete="yes"] .password-meter-fill { background: var(--green); }
.password-meter-label { margin: 4px 0 calc(var(--space) * 0.5); text-align: left; font-size: 0.9em; }
.password-rules { list-style: none; padding: 0; margin: 0 0 calc(var(--space) * 1.5); text-align: left; }
.password-rules li { padding: 4px 0 4px 2em; position: relative; color: var(--grey-600); }
/* An empty box that fills green with a white tick when the rule is met. */
.password-rules li::before {
  content: ""; position: absolute; left: 0.2em; top: 0.4em;
  width: 1.15em; height: 1.15em; box-sizing: border-box; border-radius: 3px;
  border: 2px solid var(--grey-400); background: transparent;
  color: #fff; font-size: 0.9em; font-weight: 700; line-height: 1em; text-align: center;
}
.password-rules li[data-met="yes"] { color: var(--grey-900); }
.password-rules li[data-met="yes"]::before { content: "\2713"; background: var(--green); border-color: var(--green); }
.password-rules li[data-met="later"]::before { border-style: dashed; }
@media (prefers-reduced-motion: reduce) { .password-meter-fill { transition: none; } }

/* Accounts page: company and usage columns (owner, 2026-10-23). */
.accounts-table .company-edit { display: flex; gap: 6px; align-items: center; }
.accounts-table .company-input { min-width: 11em; }
.accounts-table .usage-count { white-space: nowrap; }
.accounts-table .usage-count .muted { display: block; font-size: 0.85em; }
