/* ---------------------------------------------------------------------------
   GROUNDTRUTH — the app shell
   Palette is the Avling brand token set, not a new one. Type is the system UI
   stack on purpose: this sits next to SMS on a farm-office Windows machine,
   and a statistics package has no display face.
   --------------------------------------------------------------------------- */
:root{
  --cream:#f2f0ea; --surf:#faf9f5; --raise:#ffffff;
  --ink:#22301a; --muted:#6f6a5e; --line:#e4e0d2; --line2:#d6d1bf;
  --forest:#2e5e1e; --forest-d:#234a16; --sage:#87a05c; --green-l:#cfe8c4;
  --gold:#c89a14; --amber:#a86e10; --amber-bg:#faf1dd;
  --taupe:#6b5749; --taupe-bg:#efe7de;
  /* Secondary body text — darker than --muted, lighter than --ink. It was
     already being used by three rules in this file and by nothing that
     defined it, so those three fell back to whatever they inherited and the
     distinction they were reaching for silently did not exist. */
  --brown2:#52503d;
  --red:#8b1a1a; --red-bg:#f2dcd6;
  --forest-bg:#e4efdb;
  --shadow:0 1px 2px rgba(34,48,26,.06), 0 4px 14px rgba(34,48,26,.05);
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:ui-monospace,"Cascadia Mono","SF Mono",Consolas,Menlo,monospace;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --cream:#12240b; --surf:#16340f; --raise:#1a2410;
    --ink:#e9efdd; --muted:#9aa88b; --line:#274a1a; --line2:#315a22;
    --forest:#8fc271; --forest-d:#a6d189; --sage:#87a05c; --green-l:#cfe8c4;
    --gold:#d9ae30; --amber:#d9a23e; --amber-bg:#2e2a12;
    --taupe:#c0a894; --taupe-bg:#2b241d;
    --brown2:#c8c4ae;
    --red:#e08a7a; --red-bg:#3a1a15;
    --forest-bg:#1f3d14;
    --shadow:0 1px 2px rgba(0,0,0,.3), 0 4px 14px rgba(0,0,0,.22);
  }
}
:root[data-theme="dark"]{
  --cream:#12240b; --surf:#16340f; --raise:#1a2410;
  --ink:#e9efdd; --muted:#9aa88b; --line:#274a1a; --line2:#315a22;
  --forest:#8fc271; --forest-d:#a6d189; --sage:#87a05c; --green-l:#cfe8c4;
  --gold:#d9ae30; --amber:#d9a23e; --amber-bg:#2e2a12;
  --taupe:#c0a894; --taupe-bg:#2b241d;
  --brown2:#c8c4ae;
  --red:#e08a7a; --red-bg:#3a1a15;
  --forest-bg:#1f3d14;
  --shadow:0 1px 2px rgba(0,0,0,.3), 0 4px 14px rgba(0,0,0,.22);
}

*{box-sizing:border-box}
body{
  margin:0; background:var(--cream); color:var(--ink);
  font:14px/1.5 var(--sans); -webkit-font-smoothing:antialiased;
}
button{font:inherit; color:inherit; cursor:pointer}
:focus-visible{outline:2px solid var(--forest); outline-offset:2px; border-radius:4px}
@media (prefers-reduced-motion:reduce){*{transition:none !important; animation:none !important}}

.num{font-family:var(--mono); font-variant-numeric:tabular-nums}
.lbl{
  font-size:10.5px; font-weight:700; letter-spacing:.09em; text-transform:uppercase;
  color:var(--muted);
}

/* ---------- one header, everywhere ---------------------------------------- */
.hdr{
  position:sticky; top:0; z-index:40;
  display:flex; align-items:center; gap:14px;
  padding:0 16px; height:58px;
  background:linear-gradient(180deg,#1c3a12,#12240b); color:#e9efdd;
  border-bottom:1px solid #0d1b08;
}
.brand{display:flex; align-items:center; gap:10px; padding-right:15px; border-right:1px solid #2d5220}
.brand b{font-size:17px; letter-spacing:-.015em; font-weight:700}
.brand small{display:block; font-size:10.5px; letter-spacing:.075em; color:#9dba7f; text-transform:uppercase; margin-top:-1px}

/* The Avling wheat mark, traced from the logo file rather than redrawn — the
   silhouette matches the original to within its own antialiasing. As a path
   rather than a PNG it stays sharp at any size, costs no request, and takes its
   color from `color` so one mark serves a dark header and a light page.

   Sized in em off the wordmark beside it, so the two move together: the header
   was 52px with a 15px wordmark and a 19px glyph, and at that size the mark's
   three chevrons had nowhere to resolve — it read as a smudge rather than as
   anything. */
.brand .amark{height:1.95em; width:auto; flex:none; color:#87a05c; display:block}
/* The wordmark goes home. Every product on the web works this way, and it is
   the escape hatch somebody reaches for before they find a nav bar. */
a.brand{text-decoration:none; color:inherit}
a.brand:hover b{color:#fff}

/* ---------------------------------------------------------------------------
   THE NAV, ON EVERY SCREEN

   Four screens had four different headers and none of them had a way to any of
   the others. The trial board was the worst of it: reaching it meant reaching a
   dead end, and the only way out was the browser's back button — which is not
   a control anybody trusts in software they are evaluating.

   One block, defined once here and inlined into every built page, so a fifth
   screen cannot arrive without it.
   --------------------------------------------------------------------------- */
/* THE PRIMARY ACTION IS NOT ALLOWED TO HIDE.
   Every action on a field card used to appear on hover, so a prospect opening
   the screen for the first time saw eleven cards and no way into any of them.
   Nobody discovers a product by hovering. Laying out a trial is what this whole
   thing is for; it stays on the card. */
.card .go{
  display:inline-block; margin:0 0 8px; font-size:12.5px; font-weight:650;
  color:var(--forest); text-decoration:none;
}
.card .go:hover{text-decoration:underline}

.gtnav{display:flex; align-items:center; gap:2px}
.gtnav a{
  color:#cfe8c4; text-decoration:none; font-size:13px; font-weight:600;
  padding:6px 11px; border-radius:9px; white-space:nowrap;
}
.gtnav a:hover{background:rgba(255,255,255,.10); color:#fff}
.gtnav a[aria-current="page"]{background:#2e5e1e; color:#fff}
@media (max-width:900px){ .gtnav a{padding:6px 8px; font-size:12.5px} }

.orgpick{
  display:flex; align-items:center; gap:7px; background:rgba(255,255,255,.07);
  border:1px solid #2d5220; border-radius:8px; padding:5px 10px; color:#e9efdd;
}
.orgpick:hover{background:rgba(255,255,255,.13)}
.orgpick .car{color:#9dba7f; font-size:10px}

/* the season selector — one control, everything obeys it */
.seasons{display:flex; align-items:center; gap:2px; background:rgba(0,0,0,.24); border-radius:9px; padding:3px}
.seasons button{
  background:none; border:0; padding:4px 11px; border-radius:7px;
  color:#b7cfa2; font-size:12.5px; font-weight:600; font-family:var(--mono);
  font-variant-numeric:tabular-nums;
}
.seasons button:hover{color:#e9efdd}
.seasons button[aria-pressed="true"]{background:var(--gold); color:#1c3a12}

/* THE DESIGNER'S SEASON IS A FIELD, NOT A FILTER, so it is a select and it says
   what it is for. The board filters a list and keeps its segmented row; this
   sets the year a design IS, which is the year that reaches save_trial and
   every exported file name — and it has to hold seven years, because laying
   groundwork for the second and third season of a study means reaching past
   next year. */
.seasonpick{display:inline-flex; align-items:center; gap:7px;
  background:rgba(0,0,0,.24); border-radius:9px; padding:4px 5px 4px 11px;
  color:#b7cfa2; font-size:12px; font-weight:600; letter-spacing:.01em}
.seasonpick select{
  background:var(--gold); color:#1c3a12; border:0; border-radius:7px;
  padding:4px 8px; font:inherit; font-family:var(--mono); font-size:12.5px;
  font-weight:700; font-variant-numeric:tabular-nums; cursor:pointer}
.seasonpick select:focus-visible{outline:2px solid #e9efdd; outline-offset:1px}

.hdr .spacer{flex:1}
.srch{
  display:flex; align-items:center; gap:7px; width:210px;
  background:rgba(255,255,255,.07); border:1px solid #2d5220; border-radius:8px; padding:5px 10px;
}
.srch input{
  flex:1; min-width:0; background:none; border:0; color:#e9efdd; font:13px var(--sans); outline:none;
}
.srch input::placeholder{color:#7f9a68}
.who{
  width:28px; height:28px; border-radius:50%; background:var(--sage); color:#12240b;
  display:grid; place-items:center; font-weight:700; font-size:11.5px; border:0;
}
.who:hover{background:#9dba7f}

/* The account menu. It carried hardcoded initials and no handler on all four
   screens, so the only way out of the app was to find the one page that still
   had a Sign out button. Two people sharing a machine could not tell whose
   session they were in. Mounted from live.js — see mountAccountMenu(). */
.whomenu{
  position:fixed; z-index:60; min-width:200px; padding:6px;
  background:var(--raise); border:1px solid var(--line2); border-radius:12px;
  box-shadow:var(--shadow); color:var(--ink);
}
.whomenu[hidden]{display:none}
.whomenu .whoem{
  padding:7px 10px 8px; font-size:12px; color:var(--muted);
  border-bottom:1px solid var(--line); margin-bottom:4px;
  overflow:hidden; text-overflow:ellipsis;
}
.whomenu button{
  display:block; width:100%; text-align:left; background:none; border:0;
  padding:8px 10px; border-radius:8px; font:inherit; font-size:13px; font-weight:600;
  color:var(--ink);
}
.whomenu button:hover:not(:disabled){background:var(--cream)}
.whomenu button:disabled{color:var(--muted)}

/* ---------- the breadcrumb rail — the only door to the tree ---------------- */
.rail{
  position:sticky; top:52px; z-index:30;
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:9px 16px; background:var(--surf); border-bottom:1px solid var(--line);
}
.crumbs{display:flex; align-items:center; gap:5px; flex-wrap:wrap; background:none; border:1px solid transparent; border-radius:8px; padding:3px 8px 3px 6px; margin-left:-6px}
.crumbs:hover{border-color:var(--line2); background:var(--raise)}
/* The disclosure for the whole operations tree, and at 9px it read as
   punctuation rather than as a control — Mark's words were that it should
   "stand out as a potential action button". Bigger, and it TURNS: a triangle
   that points right when closed and down when open is the one disclosure
   convention everybody already knows, and it also means the control says which
   state it is in rather than only how to change it. */
.crumbs .tri{color:var(--forest); font-size:13px; line-height:1; margin-right:5px;
             display:inline-block; transform-origin:50% 50%}
.crumbs[aria-expanded="true"] .tri{transform:rotate(90deg)}
@media (prefers-reduced-motion: no-preference){
  .crumbs .tri{transition:transform .16s ease}
}
/* A control has a hit area. The row was a hover-only affordance, which on a
   touchpad is discovered by accident. */
.crumbs{cursor:pointer}
/* AND A LABEL DOES NOT. Three screens shipped this chip as a button with the
   disclosure triangle on it and nothing behind it, because it was copied from
   the one screen where it opens the operations tree. `flat` is the same crumb
   trail with none of the promises: no pointer, no hover frame, no triangle. */
.crumbs.flat{cursor:default}
.crumbs.flat:hover{border-color:transparent; background:none}
.crumbs.flat .tri{display:none}
.crumbs:focus-visible{outline:2px solid var(--forest); outline-offset:1px}
.crumbs span{font-size:13px}
.crumbs .sep{color:var(--line2)}
.crumbs .here{font-weight:650}
.rail .spacer{flex:1}

.seg{display:flex; border:1px solid var(--line2); border-radius:8px; overflow:hidden; background:var(--raise)}
.seg button{background:none; border:0; padding:5px 12px; font-size:12.5px; font-weight:600; color:var(--muted)}
.seg button[aria-pressed="true"]{background:var(--forest); color:#fff}
.seg button+button{border-left:1px solid var(--line2)}

.btn{
  background:var(--forest); color:#fff; border:0; border-radius:9px;
  padding:6px 14px; font-weight:600; font-size:13px;
}
.btn:hover{background:var(--forest-d)}
.btn.ghost{background:var(--raise); color:var(--ink); border:1px solid var(--line2)}
.btn.ghost:hover{background:var(--cream)}
/* A DISABLED BUTTON HAS TO LOOK DISABLED. .btn had no disabled state at all,
   so the designer's Save and Commit design sat over an empty canvas looking
   exactly as clickable as they do over a finished trial — which is the state
   Mark screenshotted. Hover is suppressed too: a control that lights up under
   the pointer and then does nothing is worse than one that never moved. */
.btn:disabled, .btn[disabled]{opacity:.45; cursor:default}
.btn:disabled:hover{background:var(--forest)}
.btn.ghost:disabled:hover{background:var(--raise)}
/* Some of these are links rather than buttons — "New trial" goes to the
   designer's field picker, and a link that navigates should BE a link: it
   opens in a new tab on a middle click, it says where it goes on hover, and
   the browser's back button behaves. */
a.btn{display:inline-flex; align-items:center; text-decoration:none; line-height:1.6}

/* ---------- summary before detail: five states, and they filter ------------ */
/* Full width, everywhere. This was capped at 1240px and centered, which the
   board and the map had each already overridden to `none` — so the cap was
   only still applying to the one screen nobody had got to, and the product
   used the screen differently depending on which tab you were on.

   For a card grid the cap is the wrong instinct anyway. A reading measure
   belongs on PROSE, and the prose on these screens carries its own (.foot-note
   is 70ch). The cards are a scanning surface: at 50-100 trials the thing that
   costs somebody time is scrolling, and a wider window should buy more COLUMNS
   rather than more margin. The grids below are all auto-fill/minmax, so that is
   exactly what it buys — the cards keep their size and there are simply more of
   them across. */
main{max-width:none; margin:0; padding:16px}
/* auto-FIT, not auto-fill, and now with a ceiling. auto-fit collapses the empty
   tracks and stretches what is left, which on a 32-inch monitor turned five
   summary tiles into five 500px slabs of mostly whitespace. A summary tile has
   a number and two short lines in it; past about 280px it stops looking like a
   tile and starts looking like a mistake. */
.states{display:grid; grid-template-columns:repeat(auto-fit,minmax(168px,280px));
        justify-content:start; gap:10px; margin-bottom:16px}
.state{
  text-align:left; background:var(--raise); border:1px solid var(--line);
  border-radius:12px; padding:11px 13px 10px; box-shadow:var(--shadow);
  display:flex; flex-direction:column; gap:3px; position:relative; overflow:hidden;
}
.state::before{content:""; position:absolute; inset:0 auto 0 0; width:3px; background:var(--tone,var(--muted))}
.state:hover{border-color:var(--line2)}
.state[aria-pressed="true"]{border-color:var(--tone,var(--forest)); box-shadow:0 0 0 2px color-mix(in srgb,var(--tone,var(--forest)) 22%,transparent)}
.state .n{font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:25px; font-weight:700; line-height:1; color:var(--tone,var(--ink))}
.state .t{font-size:12.5px; font-weight:650}
.state .d{font-size:11.5px; color:var(--muted); line-height:1.35}

/* ---------- the layer bar — 11 ticks, the DB's own order ------------------- */
.bar{display:flex; gap:2px; align-items:flex-end; height:16px}
.tick{
  flex:1; min-width:4px; height:9px; border-radius:2px;
  background:var(--line2); position:relative;
}
.tick[data-s="received"]{background:var(--forest); height:16px}
.tick[data-s="aware"]{background:var(--amber); height:13px}
.tick[data-s="blocked"]{background:var(--red); height:16px}
.tick[data-s="absent"]{background:transparent; border:1px dashed var(--taupe); height:9px}
.tick[data-s="not_due"]{background:var(--line2); height:6px}

/* ---------- the bar as a control, and the panel behind it ------------------
   Mark, 5 Sep: "this screen is fine the way it is, unless if you click on one
   it zooms it in to give a better explanation." So the bar keeps its size and
   its silence on the card, and says everything it has to say when asked. */
.barbtn{
  display:flex; gap:2px; align-items:flex-end; height:16px;
  width:100%; padding:0; border:0; background:none; cursor:pointer;
  border-radius:3px;
}
.barbtn:hover .tick{filter:brightness(.93)}
.barbtn:focus-visible{outline:2px solid var(--forest); outline-offset:3px}

/* ELEVEN STEPS IS A TALL LIST. Pinned head and foot with the steps scrolling
   between them, so the field's name and the one thing to do next are both on
   screen whatever the window height — the first version pushed the title off
   the top on a 950px display, which is most laptops. */
/* THE display GOES BEHIND [open], AND THAT IS NOT A DETAIL. A <dialog> is
   hidden by the browser's own `dialog:not([open]){display:none}`, which is a
   type selector and loses to any id. So `#lay{display:flex}` un-hid it: the
   panel sat at the bottom of every page, empty, with a Close button that did
   nothing — close() on a dialog that was never opened is a no-op. Shipped and
   found by Mark in a minute. Any dialog styled by id has to say [open]. */
#lay{max-width:660px; width:calc(100vw - 40px);
     max-height:min(86vh, 780px); overflow:hidden}
#lay[open]{display:flex; flex-direction:column}
#lay .steps{overflow:auto; flex:1 1 auto; min-height:0; margin-right:-4px; padding-right:4px}
#lay h4, #lay .lede, #lay .foot{flex:0 0 auto}
#lay h4{margin:0 0 6px; font-size:16.5px}
#lay .lede{margin:0 0 14px; font-size:13px; line-height:1.55; color:var(--brown2)}
#lay .steps{list-style:none; margin:0; padding:0; display:flex;
            flex-direction:column; gap:0}
#lay .step{display:flex; gap:11px; align-items:flex-start;
           padding:8px 2px; border-top:1px solid var(--line)}
#lay .step:first-child{border-top:0}
/* The tick is the SAME element as on the card, at the same size, so the row a
   person is reading and the square they clicked are recognizably one thing. */
#lay .step .tick{flex:0 0 10px; width:10px; margin-top:3px}
#lay .step .nm{font-size:13px; font-weight:650; color:var(--ink)}
#lay .step .nm em{font-style:normal; font-weight:500; font-size:11.5px;
                  color:var(--muted); margin-left:7px}
#lay .step .wh{font-size:12.5px; line-height:1.45; color:var(--brown2); margin-top:1px}
/* Grey the ones nothing is waiting on, so the two or three that DO want
   attention are the ones the eye lands on. */
#lay .step[data-s="not_due"] .nm, #lay .step[data-s="not_due"] .wh{opacity:.62}
#lay .foot{display:flex; align-items:center; justify-content:space-between;
           gap:14px; margin-top:16px; padding-top:13px; border-top:1px solid var(--line)}
#lay .foot .note{margin:0; font-size:13px; font-weight:650; color:var(--forest)}

/* ---------- and the eleven steps, NAMED above the ticks -------------------
   Mark, 5 Sep, having shown the table to Bill: "it just says layers... what
   are they and are they important?" A column of anonymous squares is a column
   that gets skipped.

   Rotated because eleven full words will not fit across 330px any other way,
   and abbreviations are the problem restated — "Anls" is no more readable to
   somebody seeing this for the first time than an unlabelled square. The head
   uses the SAME flex rule as .bar, so every word sits over its own tick and
   stays there at any column width.

   The two gaps split the season into what happens before it, during it and
   after it. That is not decoration: it is the reason the order is what it is,
   and it turns eleven ticks into three groups the eye can take in at once. */
.layhead{vertical-align:bottom}
.layhead .hlab{font-size:11px; text-transform:uppercase; letter-spacing:.06em;
               color:var(--muted); font-weight:650; margin-bottom:2px}
.bar.heads{height:62px; align-items:flex-end; gap:2px}
.bar.heads .hd{flex:1; min-width:4px; position:relative; height:100%}
.bar.heads .hd i{
  position:absolute; left:50%; bottom:1px; transform-origin:0 100%;
  transform:rotate(-52deg); white-space:nowrap;
  font-style:normal; font-size:10.5px; font-weight:600; letter-spacing:.01em;
  color:var(--brown2); line-height:1;
}
/* The season in three parts: before it, during it, after it. Applied to the
   heads and the ticks alike so a word never drifts off its own square. */
.bar > :nth-child(5), .bar > :nth-child(9){margin-left:9px}

/* ---------- chips: color NEVER alone — glyph + word ----------------------- */
.chip{
  display:inline-flex; align-items:center; gap:5px; border-radius:999px;
  padding:2px 9px 2px 7px; font-size:11.5px; font-weight:650; white-space:nowrap;
  border:1px solid transparent;
}
.chip .g{font-size:10px; line-height:1}
.chip.ok{background:var(--forest-bg); color:var(--forest); border-color:color-mix(in srgb,var(--forest) 26%,transparent)}
.chip.aware{background:var(--amber-bg); color:var(--amber); border-color:color-mix(in srgb,var(--amber) 30%,transparent)}
.chip.blocked{background:var(--red-bg); color:var(--red); border-color:color-mix(in srgb,var(--red) 32%,transparent)}
.chip.absent{background:var(--taupe-bg); color:var(--taupe); border-color:color-mix(in srgb,var(--taupe) 30%,transparent)}
.chip.idle{background:transparent; color:var(--muted); border-color:var(--line2)}

/* ---------- cards (default) ------------------------------------------------ */
.cards{display:grid; grid-template-columns:repeat(auto-fill,minmax(310px,1fr)); gap:12px}
.card{
  background:var(--raise); border:1px solid var(--line); border-radius:14px;
  box-shadow:var(--shadow); overflow:hidden; display:flex; flex-direction:column;
  text-align:left; padding:0; width:100%;
}
.card:hover{border-color:var(--sage)}
.card.dim{opacity:.5}
.card .top{display:flex; gap:11px; padding:12px 13px 9px}
.plan{width:62px; height:62px; flex:none; border-radius:9px; background:var(--cream); border:1px solid var(--line); overflow:hidden}
.card h3{margin:0; font-size:14.5px; font-weight:660; letter-spacing:-.01em}
.card .sub{font-size:11.5px; color:var(--muted); margin-top:2px}
.card .acres{font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:11.5px; color:var(--muted)}
.card .mid{padding:0 13px 10px; display:flex; flex-wrap:wrap; gap:5px}
.card .foot{
  margin-top:auto; padding:9px 13px 11px; border-top:1px solid var(--line);
  background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--cream) 55%,transparent));
}
.card .foot .row{display:flex; align-items:center; justify-content:space-between; margin-bottom:6px}

/* ---------- table (one click away) ----------------------------------------- */
.tblwrap{overflow-x:auto; background:var(--raise); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow)}
table{border-collapse:collapse; width:100%; min-width:860px}
th{
  text-align:left; padding:8px 11px; font-size:10.5px; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase; color:var(--muted);
  border-bottom:1px solid var(--line2); background:var(--surf); position:sticky; top:0;
}
td{padding:8px 11px; border-bottom:1px solid var(--line); font-size:13px; vertical-align:middle}
tbody tr:hover{background:var(--surf)}
tbody tr.dim{opacity:.5}
td.n{font-family:var(--mono); font-variant-numeric:tabular-nums; text-align:right}
th.n{text-align:right}
.fname{font-weight:620}
.fsub{font-size:11px; color:var(--muted)}

/* ---------- the tree drawer ------------------------------------------------ */
.scrim{position:fixed; inset:0; background:rgba(18,36,11,.42); z-index:50; opacity:0; pointer-events:none; transition:opacity .16s}
.scrim.on{opacity:1; pointer-events:auto}
.drawer{
  position:fixed; inset:0 auto 0 0; width:min(330px,86vw); z-index:60;
  background:var(--surf); border-right:1px solid var(--line2);
  transform:translateX(-102%); transition:transform .2s cubic-bezier(.3,.7,.3,1);
  display:flex; flex-direction:column;
}
.drawer.on{transform:none}
.drawer header{display:flex; align-items:center; gap:8px; padding:12px 14px; border-bottom:1px solid var(--line)}
.drawer header b{flex:1; font-size:13.5px}
.xbtn{background:none; border:1px solid var(--line2); border-radius:7px; width:26px; height:26px; line-height:1; color:var(--muted)}
.tree{overflow:auto; padding:8px 6px 16px; flex:1}
.node{display:block; width:100%; text-align:left; background:none; border:0; border-radius:7px; padding:5px 8px; font-size:13px; display:flex; align-items:center; gap:7px}
.node:hover{background:var(--cream)}
.node.sel{background:var(--forest-bg); color:var(--forest); font-weight:650}
.node .ic{width:15px; text-align:center; color:var(--sage); font-size:11px}
.node .ct{margin-left:auto; font-family:var(--mono); font-size:11px; color:var(--muted)}

/* --- adding to the operation ----------------------------------------------
   The + sits on the row it adds INTO, so "add a farm" is a click on the
   grower it belongs to and there is nothing to pick afterwards. It appears on
   hover and on keyboard focus — always-on plus signs down every row turn a
   tree you read into a form you fill in — but `opacity` alone would leave it
   clickable while invisible, so it is visibility too. */
.treerow{display:flex; align-items:center; gap:2px}
.treerow .node{flex:1; min-width:0}
.addbtn{flex:none; width:24px; height:24px; border-radius:7px; border:1px solid transparent;
        background:none; color:var(--muted); font-size:15px; line-height:1; cursor:pointer;
        visibility:hidden; opacity:0}
.treerow:hover .addbtn, .addbtn:focus-visible{visibility:visible; opacity:1}
.addbtn:hover{background:var(--forest-bg); border-color:var(--forest); color:var(--forest)}
.addbtn:focus-visible{outline:2px solid var(--forest); outline-offset:1px}
/* A level nobody may add to shows no plus at all, rather than one that argues.
   The right to restructure is checked in the database; this only stops the
   screen offering what it knows will be refused. */
.treerow.locked .addbtn{display:none}

/* THE HANDOVER, next to the plus. Same size, same quietness, same hover rule:
   it is used once a season per grower, not every time somebody opens the
   drawer. It is NOT suppressed on a locked row — somebody who may not
   restructure a customer may still owe that customer their files. */
.handbtn{flex:none; width:24px; height:24px; border-radius:7px;
         border:1px solid transparent; background:none; color:var(--muted);
         font-size:14px; line-height:1; text-decoration:none;
         display:inline-grid; place-items:center; visibility:hidden; opacity:0}
.treerow:hover .handbtn, .handbtn:focus-visible{visibility:visible; opacity:1}
.handbtn:hover{background:var(--forest-bg); border-color:var(--forest); color:var(--forest)}
.handbtn:focus-visible{outline:2px solid var(--forest); outline-offset:1px}

.treeadd{display:flex; gap:6px; padding:6px 8px 2px 30px}
.treeadd input{flex:1; min-width:0; font:inherit; font-size:12.5px; color:var(--ink);
        background:var(--raise); border:1px solid var(--line); border-radius:8px; padding:5px 8px}
.treeadd input:focus{outline:0; border-color:var(--forest); box-shadow:0 0 0 3px rgba(46,94,30,.15)}
.treeadd button{font:inherit; font-size:12.5px; padding:5px 10px; border-radius:8px;
        border:1px solid var(--forest); background:var(--forest); color:#fff; cursor:pointer}
.treemsg{font-size:12px; padding:2px 8px 6px 30px; color:var(--red)}
.treemsg.ok{color:var(--forest)}
.lvl1{padding-left:20px} .lvl2{padding-left:36px} .lvl3{padding-left:52px}

/* ---------- selection and bulk -------------------------------------------- */
.ck{
  appearance:none; width:16px; height:16px; flex:none; border-radius:4px;
  border:1.5px solid color-mix(in srgb,var(--muted) 60%,transparent);
  background:var(--raise); cursor:pointer; margin:0;
  display:grid; place-content:center;
}
.ck:checked{background:var(--forest); border-color:var(--forest)}
.ck:checked::after{content:"✓"; color:#fff; font-size:11px; line-height:1; font-weight:700}
.ck:indeterminate{background:var(--sage); border-color:var(--sage)}
.ck:indeterminate::after{content:"–"; color:#fff; font-size:12px; line-height:1; font-weight:800}

.card{cursor:pointer}
.card .top{position:relative}
.card .ck{position:absolute; top:1px; right:0}
.card:has(.ck:checked){border-color:var(--forest); box-shadow:0 0 0 2px color-mix(in srgb,var(--forest) 20%,transparent)}
tr:has(.ck:checked){background:color-mix(in srgb,var(--forest) 7%,transparent)}

.bulkbar{
  position:sticky; top:100px; z-index:25; display:flex; align-items:center; gap:10px;
  flex-wrap:wrap; padding:9px 12px; margin-bottom:12px; border-radius:12px;
  background:var(--raise); border:1px solid var(--forest); box-shadow:var(--shadow);
}
.bulkbar[hidden]{display:none}
.bulkbar .cnt{font-weight:700; font-family:var(--mono); font-variant-numeric:tabular-nums}
.bulkbar .lnk{background:none; border:0; color:var(--forest); font-weight:650; font-size:12.5px; text-decoration:underline; padding:2px 0}
.bulkbar .acts{display:flex; gap:6px; flex-wrap:wrap; margin-left:auto}
.bulkbar .acts button{
  background:var(--surf); border:1px solid var(--line2); border-radius:8px;
  padding:5px 11px; font-size:12.5px; font-weight:600;
}
.bulkbar .acts button:hover{border-color:var(--forest); color:var(--forest)}

dialog{
  border:1px solid var(--line2); border-radius:14px; padding:0; max-width:520px; width:92vw;
  background:var(--raise); color:var(--ink); box-shadow:0 18px 50px rgba(18,36,11,.3);
}
dialog::backdrop{background:rgba(18,36,11,.45)}
dialog h4{margin:0; padding:14px 16px 10px; font-size:15px; border-bottom:1px solid var(--line)}
dialog .body{padding:14px 16px}
dialog .lede{font-size:14.5px; font-weight:600; margin:0 0 10px}
dialog .why{border:1px solid var(--line); border-radius:10px; overflow:hidden}
dialog .why div{display:flex; gap:9px; padding:7px 11px; font-size:12.5px; border-top:1px solid var(--line)}
dialog .why div:first-child{border-top:0}
dialog .why b{font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--amber); flex:none}
dialog .why span{color:var(--muted)}
dialog .names{display:block; color:var(--ink); font-size:11.5px; margin-top:2px}
dialog .warn{
  margin:10px 0 0; padding:8px 11px; border-radius:9px; font-size:12.5px;
  background:var(--amber-bg); color:var(--amber); border:1px solid color-mix(in srgb,var(--amber) 28%,transparent);
}
dialog footer{display:flex; gap:8px; justify-content:flex-end; padding:12px 16px; border-top:1px solid var(--line); background:var(--surf); border-radius:0 0 13px 13px}

.toast{
  position:fixed; left:50%; bottom:22px; transform:translateX(-50%);
  display:flex; align-items:center; gap:12px; z-index:70;
  background:#12240b; color:#e9efdd; border-radius:11px; padding:10px 14px;
  box-shadow:0 10px 30px rgba(0,0,0,.3); font-size:13px;
}
.toast[hidden]{display:none}
.toast button{background:none; border:0; color:var(--gold); font-weight:700; text-decoration:underline}

.empty{padding:34px 16px; text-align:center; color:var(--muted)}
.legend{display:flex; flex-wrap:wrap; gap:12px; align-items:center; margin:14px 2px 0; font-size:11.5px; color:var(--muted)}
.legend b{color:var(--ink); font-weight:650}
.foot-note{margin:18px 2px 6px; font-size:11.5px; color:var(--muted); max-width:70ch; line-height:1.6}

/* The hidden attribute has to WIN. `[hidden]` is display:none in the UA
   stylesheet, and any later rule that sets display — .rail{display:flex},
   .seasons{display:flex} — beats it on specificity and order. So an element
   marked hidden stays on screen, which is how a sign-in page ended up showing
   a fictional org's breadcrumb above the sign-in box. */
[hidden]{display:none !important}

/* ---------------------------------------------------------------------------
   THE SIGN-IN GATE

   Deliberately plain. A farm office in February on a slow connection does not
   need a hero image, and every element that can be half-loaded is one more way
   for the page to look broken when it is only slow. The one thing that IS
   styled carefully is the error line: it is where the page says the true thing
   about what went wrong, and a message nobody can read is the same as no
   message.
   --------------------------------------------------------------------------- */
#gate{display:grid; place-items:center; min-height:70vh; padding:24px}
.gate{width:min(380px,100%); background:var(--surf); border:1px solid var(--line);
      border-radius:16px; padding:26px 24px 22px; box-shadow:var(--shadow);
      display:flex; flex-direction:column; gap:12px}
.gate h1{margin:0; font-size:20px; letter-spacing:-.015em}
.gate .lede{margin:0 0 6px; color:var(--muted); font-size:13.5px}
.gate label{display:flex; flex-direction:column; gap:5px; font-size:12px;
            font-weight:600; color:var(--muted)}
.gate input{font:inherit; font-weight:400; color:var(--ink); padding:9px 11px;
            border:1px solid var(--line2); border-radius:10px; background:var(--raise)}
.gate input:focus-visible{outline:2px solid var(--forest); outline-offset:1px;
                          border-color:var(--forest)}
.gate button[type=submit]{background:var(--forest); color:#fff; border:0;
  border-radius:11px; padding:10px 14px; font:inherit; font-weight:600; cursor:pointer}
.gate button[type=submit]:hover{background:var(--forest-d)}
.gate button[type=submit]:disabled{opacity:.55; cursor:default}
.gate .linkish{background:none; border:0; color:var(--forest); font:inherit;
  font-size:12.5px; text-decoration:underline; cursor:pointer; padding:2px}
.gate .err{margin:2px 0 0; font-size:12.5px; color:var(--red); line-height:1.45}
/* The "you belong to nothing yet" card, which is the same card as the sign-in
   one and deliberately so: it is the same moment, one step further along.
   .gtwho rather than .who — .who is the round account initials button forty
   lines up, and reusing it here would inherit a 28px circle and clip the
   address it exists to show. The third such collision in this stylesheet, and
   the reason the build guards against them. */
.gate .gtwho{margin:0; font-size:13.5px; font-weight:600; color:var(--ink);
  background:var(--raise); border:1px solid var(--line2); border-radius:10px;
  padding:9px 11px; overflow-wrap:anywhere}
.gate button[data-out]{background:none; border:1px solid var(--line2); color:var(--ink);
  border-radius:11px; padding:9px 14px; font:inherit; font-weight:600; cursor:pointer;
  align-self:flex-start}
.gate button[data-out]:hover{border-color:var(--forest); color:var(--forest)}
.err{color:var(--red); padding:14px; font-size:13.5px; line-height:1.5}

/* ---------------------------------------------------------------------------
   HELP, AND THE FIRST-RUN PANEL

   Mark asked for both. One stylesheet entry rather than eight, for the reason
   the markup is one module: help that looks different on every screen reads as
   eight products, and the whole point of it is that somebody who has learnt to
   look in one corner keeps looking there.
   --------------------------------------------------------------------------- */
.helpbtn{
  width:26px; height:26px; flex:none; border-radius:999px; cursor:pointer;
  border:1px solid #4b7038; background:transparent; color:#cfe8c4;
  font:inherit; font-size:13px; font-weight:700; line-height:1;
  display:inline-flex; align-items:center; justify-content:center;
}
.helpbtn:hover{background:rgba(255,255,255,.12); color:#fff}
.helpbtn[aria-expanded="true"]{background:var(--forest); border-color:var(--forest); color:#fff}
/* On a light header — the Machines and Products screens put it beside a dark
   bar, the Fields rail does not — the same button has to stay visible. */
.rail .helpbtn,.mfoot .helpbtn{border-color:var(--line2); color:var(--muted)}
.rail .helpbtn:hover,.mfoot .helpbtn:hover{background:var(--surf); color:var(--ink)}

.helpwrap{
  position:fixed; inset:0; z-index:60; background:rgba(18,36,11,.42);
  display:flex; justify-content:flex-end;
}
.helpbox{
  width:min(30rem,100%); height:100%; overflow:auto; background:var(--raise);
  border-left:1px solid var(--line2); box-shadow:-12px 0 40px rgba(0,0,0,.16);
  display:flex; flex-direction:column;
}
.helphead{
  display:flex; align-items:center; gap:10px; padding:13px 16px;
  border-bottom:1px solid var(--line2); background:var(--surf);
  position:sticky; top:0; z-index:1;
}
.helphead b{flex:1; font-size:15px; letter-spacing:-.015em}
.helpx{
  border:1px solid var(--line2); background:var(--raise); color:var(--muted);
  border-radius:8px; width:28px; height:28px; font:inherit; font-size:16px;
  line-height:1; cursor:pointer;
}
.helpx:hover{border-color:var(--forest); color:var(--forest)}
.helpbody{padding:15px 16px 40px}
.helpwhat{margin:0 0 14px; font-size:13.5px; line-height:1.6}
.helpsteps{margin:0 0 14px; padding:0; list-style:none; counter-reset:hs}
.helpsteps li{
  counter-increment:hs; position:relative; padding:0 0 11px 26px;
  font-size:13px; line-height:1.55;
}
.helpsteps li::before{
  content:counter(hs); position:absolute; left:0; top:1px;
  font-family:var(--mono); font-size:11px; font-weight:700; color:var(--forest);
}
.helpsteps b{display:block; font-weight:650}
.helpsteps span{color:var(--muted)}
.helpwont,.helpnext{
  margin:0 0 10px; font-size:12.5px; line-height:1.55; color:var(--muted);
  padding:9px 11px; border-radius:10px; background:var(--surf);
  border:1px solid var(--line);
}
.helpwont b,.helpnext b{color:var(--ink)}

/* --- the first-run panel --------------------------------------------------- */
.startbox{
  border:1px solid var(--line2); border-left:4px solid var(--forest);
  border-radius:0 14px 14px 0; background:var(--raise); box-shadow:var(--shadow);
  padding:16px 18px 14px; margin:0 0 16px;
}
.starthead{display:flex; gap:12px; align-items:flex-start}
.starthead h2{margin:0 0 3px; font-size:16px; letter-spacing:-.015em}
.starthead p{margin:0; font-size:13px; color:var(--muted); line-height:1.55; max-width:60ch}
.startx{
  margin-left:auto; flex:none; border:1px solid var(--line2); background:var(--surf);
  color:var(--muted); border-radius:8px; padding:4px 10px; font:inherit;
  font-size:12px; cursor:pointer;
}
.startx:hover{border-color:var(--forest); color:var(--forest)}
.startsteps{
  margin:14px 0 0; padding:0; list-style:none; counter-reset:ss;
  display:grid; gap:10px;
}
.startsteps li{
  counter-increment:ss; position:relative; padding:10px 12px 10px 34px;
  border:1px solid var(--line); border-radius:11px; background:var(--surf);
  font-size:13px; line-height:1.55;
}
.startsteps li::before{
  content:counter(ss); position:absolute; left:12px; top:10px;
  font-family:var(--mono); font-size:12px; font-weight:700; color:var(--forest);
}
.startsteps b{display:block; font-weight:650; margin-bottom:1px}
.startsteps span{color:var(--muted); display:block; max-width:70ch}
/* ---------------------------------------------------------------------------
   .btn-tiny — THE SMALL CONTROL, DEFINED ONCE

   THE BUG THIS FIXES. Thirteen pages use this class and only four defined it,
   each in its own <style> block, because it was copied from page to page rather
   than put here. On the other nine the buttons fell back to the browser's
   default chrome — which is why the Machines screen looked like it had seven
   dead grey chips along the bottom. They were not dead: the click handler is
   there and switching kind redraws the whole list. Nothing on the button said
   which one was chosen, and on an empty account the only other thing that
   changes is one word in a heading, so pressing them looked like doing nothing.

   THE PRESSED STATE IS THE POINT. A toggle with no selected state is not a
   toggle, it is seven identical buttons. Every other selector in this stylesheet
   — the season picker, the segmented control, the layer states — styles
   [aria-pressed="true"], and this one was the exception nobody noticed because
   the class had no rule at all to hang it off.

   The four pages that already define it keep their own version: their <style>
   comes after this file and wins. This is for the nine that had nothing.
   --------------------------------------------------------------------------- */
.btn-tiny{
  font:inherit; font-size:12px; font-weight:600; padding:4px 10px;
  border-radius:8px; cursor:pointer; line-height:1.4;
  border:1px solid var(--line2); background:var(--surf); color:var(--muted);
}
.btn-tiny:hover{border-color:var(--forest); color:var(--forest)}
.btn-tiny[aria-pressed="true"]{
  background:var(--forest); border-color:var(--forest); color:#fff;
}
.btn-tiny[aria-pressed="true"]:hover{background:var(--forest-d); color:#fff}
.btn-tiny:disabled{opacity:.45; cursor:default}

/* Its own class rather than .btn-tiny: that one now lives here, but this panel
   wants a link that reads as the primary move rather than a small control, and
   the first build of it rendered five blue underlined links inside a green
   card. */
.startsteps a.startgo{
  margin-top:8px; display:inline-block; text-decoration:none;
  background:var(--forest); color:#fff; border-radius:9px;
  padding:5px 11px; font-size:12.5px; font-weight:600;
}
.startsteps a.startgo:hover{background:#234a16; color:#fff}
.starthint{margin:12px 0 0; font-size:11.5px; color:var(--muted); line-height:1.5; max-width:70ch}

/* The stale-save dialog. Its own look rather than the confirm dialog's, because
   it is not a confirmation — there is nothing here to agree to. */
dialog.stale{
  border:1px solid var(--line2); border-radius:14px; padding:0; max-width:34rem;
  background:var(--raise); color:var(--ink); box-shadow:0 18px 60px rgba(0,0,0,.28);
}
dialog.stale::backdrop{background:rgba(18,36,11,.42)}
dialog.stale h4{margin:0; padding:14px 17px 0; font-size:15px; letter-spacing:-.015em}
dialog.stale p{margin:8px 17px 0; font-size:13px; line-height:1.6}
dialog.stale p.hint{color:var(--muted); font-size:12.5px}
dialog.stale footer{
  display:flex; gap:9px; justify-content:flex-end; margin-top:15px;
  padding:12px 17px; background:var(--surf); border-top:1px solid var(--line2);
  border-radius:0 0 14px 14px;
}

/* ---------------------------------------------------------------------------
   THE TRIAL SHEET

   Screen-hidden, print-visible. The rule that shapes all of it: this is read
   standing up, in a cab, by somebody about to do something irreversible — so
   the picture is big, the numbers are large enough to read at arm's length,
   and nothing on it needs a second pass.
   --------------------------------------------------------------------------- */
.sheet.trialsheet{display:none}
/* SCOPED TO body.sheeting, AND THAT IS NOT TIDINESS. app.css is on every
   screen, so an unscoped `@media print { body > * { display:none } }` would
   make Ctrl-P print a blank page from the Fields screen, the board and the
   protocol editor — a rule that breaks printing everywhere in order to fix it
   in one place. The designer adds the class before it prints and takes it off
   afterwards. */
@media print{
  /* Everything else goes. A print stylesheet that only ADDS is a print
     stylesheet that puts a navigation bar on somebody's clipboard. */
  body.sheeting > *{display:none !important}
  /* NAMED, NOT `.sheet`. Both sheets are listed rather than the rule being
     loosened to the shared class, because what is printable is a decision per
     document: a future `.sheet` that is a screen preview would start printing
     itself the day it was written, and the failure is a blank or a doubled page
     on somebody's clipboard rather than an error anybody sees. */
  body.sheeting > .sheet.trialsheet{display:block !important}
  body.sheeting > .sheet.mixsheet{display:block !important}
  /* PORTRAIT, AND IT WAS LANDSCAPE FIRST. Landscape is the instinct — the map
     is the biggest thing on here and a field is wide. But the sheet is a
     STACK: picture, then the numbers, then the table, then the blanks, and on
     a landscape page that stack ran to two pages with the map squeezed to a
     36mm strip to make it fit. A trial sheet that is two pages is a trial
     sheet whose second page stays in the truck. Portrait fits it on one and
     leaves the map 118mm — and a clipboard is portrait anyway. */
  /* LETTER, NAMED. `size:portrait` alone leaves the paper to the machine, and
     on a printer defaulted to A4 the sheet is laid out for a page two tenths of
     an inch narrower than the one it was measured against. Naming the size is
     what makes "it fits" mean the same thing in every office. */
  /* AND THE MARGIN GOES IN THE DOCUMENT, not in the page box — the same fix the
     protocol sheet already carries, which this one should have had at the same
     time and did not. `@page{margin}` belongs to the browser's print dialog,
     and somebody who has ever chosen "Margins: None" — for a label, a boarding
     pass — has chosen it for every document since. The trial sheet then prints
     edge to edge: reported twice, and the second time it was still doing it
     because only half the fix had shipped.

     Zero here so the two cannot add up when the dialog is left on Default, and
     the white space on `body` where nothing outside this file can take it away.
     Unscoped, because a printed page from ANY screen in this app wants margins,
     and a sheet is one page by design so there is no page two to leave
     marginless. */
  @page{size:letter portrait; margin:0}
  /* AND THE WHITE SPACE GOES ON THE SHEET, NOT ON THE ROOT — WHICH IS THE
     SECOND VERSION OF THIS RULE.

     The first put it on `:root`, unscoped, on the reasoning that a printed page
     from any screen in this app wants margins. It measured perfectly on the
     trial sheet and it silently doubled the protocol sheet's: that document
     carries its own `.sheet{padding:0 15mm}` and cancels the root with
     `html, body{padding:0 !important}` — and `:root` is 0,1,0 to `html`'s
     0,0,1, so app.css won the tie and the two paddings stacked. Measured: side
     margins 1.14in where the document asks for 0.59in, on every page.

     A stylesheet has no way to know which documents manage their own margins,
     so it must not set margins for documents it does not own. This one owns the
     trial sheet — which is one page by design, so padding on the sheet itself
     is the whole story; the protocol sheet's margins have to live in a repeated
     thead/tfoot precisely because it is not. */
  /* SIDE MARGINS HERE, VERTICAL MARGINS IN THE REPEATING HEAD AND FOOT.
     The first version put all four on this element, on the reasoning that the
     sheet is one page by design. With a real plot map and four treatments it is
     two — measured — and padding on the sheet then gives page one a top margin
     and page two ink 0.03in from the trim edge. Left and right are safe here
     because every page has the same left and right; top and bottom are not,
     so they live in `table.page`'s thead and tfoot, which the browser repeats
     on every printed page. Same fix, same reason, as the protocol sheet. */
  .sheet.trialsheet, .sheet.mixsheet{padding:0 14mm !important; margin:0 !important;
    box-shadow:none}
  /* The wrapper is scaffolding, not a table: no borders, no padding, and none
     of the cell styling further down. `> ` at every step so the sheet's real
     tables, which are nested inside this one's cell, are untouched. */
  .trialsheet table.page, .mixsheet table.page{width:100%; min-width:0; border-collapse:collapse;
    table-layout:auto}
  .trialsheet table.page > thead, .mixsheet table.page > thead{display:table-header-group}
  .trialsheet table.page > tfoot, .mixsheet table.page > tfoot{display:table-footer-group}
  .trialsheet table.page > thead > tr > td,
  .trialsheet table.page > tbody > tr > td,
  .trialsheet table.page > tfoot > tr > td{padding:0; border:0; background:none}
  .trialsheet .runhead, .trialsheet .runfoot{display:flex; gap:10px;
    align-items:baseline; font-size:8.5px; letter-spacing:.04em; color:#666}
  .trialsheet .runhead .sp, .trialsheet .runfoot .sp{flex:1}
  .trialsheet .runhead .nm{font-weight:700; color:#111; font-size:9.5px}
  .trialsheet .runhead .tag{font-size:7.5px; letter-spacing:.11em;
    text-transform:uppercase; font-weight:750}
  /* THE VERTICAL PAGE MARGINS. They are padding on these two rows rather than
     on the sheet, because these two rows are the ones that repeat. */
  .trialsheet .runhead{padding:12mm 0 4px; margin:0 0 8px; border-bottom:.5pt solid #ddd}
  .trialsheet .runfoot{padding:5px 0 12mm; margin:10px 0 0; border-top:.5pt solid #ddd}
  /* THE SAME PAGE FRAME FOR THE MIX SHEET. Repeated rather than shared,
     for the reason given where the print rule lists both sheets by name: what
     is printable, and how its running head is built, is a decision per
     document. The trial sheet has been measured onto one page twice and must
     not move because a second document wanted a different header. */
  .mixsheet table.page > thead > tr > td,
  .mixsheet table.page > tbody > tr > td,
  .mixsheet table.page > tfoot > tr > td{padding:0; border:0; background:none}
  .mixsheet .runhead, .mixsheet .runfoot{display:flex; gap:10px;
    align-items:baseline; font-size:8.5px; letter-spacing:.04em; color:#666}
  .mixsheet .runhead .sp, .mixsheet .runfoot .sp{flex:1}
  .mixsheet .runhead .nm{font-weight:700; color:#111; font-size:9.5px}
  .mixsheet .runhead .tag{font-size:7.5px; letter-spacing:.11em;
    text-transform:uppercase; font-weight:750}
  /* THE VERTICAL PAGE MARGINS. They are padding on these two rows rather than
     on the sheet, because these two rows are the ones that repeat. */
  .mixsheet .runhead{padding:12mm 0 4px; margin:0 0 8px; border-bottom:.5pt solid #ddd}
  .mixsheet .runfoot{padding:5px 0 12mm; margin:10px 0 0; border-top:.5pt solid #ddd}
  /* html AS WELL AS body, and unscoped on purpose. The app's warm grey ground
     is on the root element, and with backgrounds printing it comes out as a
     slab of ink over whatever part of the last page a document does not fill —
     which is the first thing anybody notices about a printed report. Paper is
     white for every page of this app, not just the sheets; the scoping above
     exists to stop `display:none` blanking other screens, and a background has
     no such hazard. */
  /* `:root` rather than `html`, and that is not style. build-shell refuses a
     page that declares the same bare selector as app.css, because two `html`
     rules merge silently and the page inherits half of what it meant to set.
     `:root` is the same element and does not collide. */
  :root, body{background:#fff !important}
  body.sheeting{color:#111 !important}

  .trialsheet{font:12px/1.45 -apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
  .trialsheet header{display:flex; align-items:flex-start; gap:16px;
    border-bottom:2px solid #2e5e1e; padding-bottom:7px}
  .trialsheet h1{margin:0; font-size:26px; letter-spacing:-.02em}
  .trialsheet .where{margin:2px 0 0; font-size:13px; color:#333}
  .trialsheet header .right{margin-left:auto; text-align:right}
  .trialsheet header .right p{margin:0; font-size:13px}
  .trialsheet .sub{color:#555; font-size:11.5px}

  .trialsheet figure.map{margin:9px 0; border:1px solid #ccc; padding:4px; page-break-inside:avoid}
  /* NO MINIMUM ON THE IMAGE. A field is wide and short, so a map forced to
     60mm tall is letterboxed inside it — the drawing shrinks to the middle and
     two thick bands of white eat a third of the page, which is what "still
     looks tight" was: the sheet was not tight, the map was hogging it. The
     picture takes the height its own shape asks for, up to a ceiling. */
  .trialsheet figure.map img{display:block; width:100%; height:auto; max-height:118mm;
    object-fit:contain}
  /* The minimum belongs to the EMPTY state only. A box with a caption in it
     reads as "no map"; two hairlines four pixels apart read as a broken page. */
  .trialsheet figure.map.nomap{min-height:46mm; display:grid; place-items:center}
  .trialsheet figure.map.nomap p{margin:26px; text-align:center; color:#777}

  .trialsheet h2{font-size:12px; letter-spacing:.08em; text-transform:uppercase;
    margin:11px 0 5px; color:#2e5e1e}
  .trialsheet section{page-break-inside:avoid}

  /* WHAT IS IN THE TANK, under the treatment name it belongs to. Its own line
     rather than a parenthesis, because a three-product mix is longer than the
     name and would push the rate column off the paper; smaller and grey so the
     table still scans down the names. The tank-mix order is the label order and
     is preserved by everything upstream, so this is a run of text and not a
     list — it reads the way it is said at the tank. */
  .trialsheet .mix{display:block; font-size:9.5px; line-height:1.35; color:#444;
    margin-top:1px}

  .trialsheet ul.facts{display:flex; flex-wrap:wrap; gap:0; margin:0; padding:0; list-style:none;
    border:1px solid #ddd}
  .trialsheet ul.facts li{flex:1 1 16%; padding:5px 9px; border-right:1px solid #eee}
  .trialsheet ul.facts li:last-child{border-right:0}
  .trialsheet ul.facts span{display:block; font-size:10px; text-transform:uppercase;
    letter-spacing:.06em; color:#666}
  .trialsheet ul.facts b{font-size:15px; font-variant-numeric:tabular-nums}

  .trialsheet .passes{margin:7px 0 0; padding:5px 9px; font-size:12.5px; border-left:3px solid #2e5e1e}
  .trialsheet .passes.bad{border-left-color:#8b1a1a; font-weight:600}
  .trialsheet .warns p{margin:6px 0 0; padding:5px 9px; font-size:11.5px;
    border-left:3px solid #8b1a1a; background:#f7ecea}
  .trialsheet .rule{margin:7px 0 0; font-size:11.5px; color:#333}

  /* WHICH COPY THIS IS, AND IT IS ON EVERY COPY INCLUDING THE OPEN ONE.
     A blind sheet that announces itself and an open sheet that says nothing are
     only distinguishable to somebody holding both. The color is the warning:
     the OPEN copy is the dangerous one to hand out, so it gets the red rail,
     and the blind copy — the safe one — gets forest. That is the opposite of
     the instinct to make the redacted thing look alarming, and it is the right
     way round: the mistake this stops is handing a scout the full copy. */
  .trialsheet .viewtag{margin:8px 0 0; padding:5px 9px; font-size:11.5px;
    border:1px solid #ddd; border-left:3px solid #6b5749; background:#faf9f5}
  .trialsheet .viewtag b{text-transform:uppercase; letter-spacing:.08em;
    font-size:10px; margin-right:6px}
  .trialsheet .viewtag.open{border-left-color:#8b1a1a}
  .trialsheet .viewtag.coded{border-left-color:#a86e10}
  .trialsheet .viewtag.blind{border-left-color:#2e5e1e}

  /* --- the rows a scout writes in -----------------------------------------
     Sized in percentages like every other printed table here: a print table
     whose columns are sized by their contents grows past the paper the first
     time somebody writes a long note into one.

     The measure headers are blank and ruled, because this sheet does not know
     what is being scored and a fixed set of columns would be wrong for most
     trials. Row height is set in millimetres rather than left to the text,
     since the cells are empty and a pen needs somewhere to go. */
  .trialsheet .plotrec{margin-top:7mm}
  .trialsheet .plotrec .sub{margin:0 0 3mm; font-size:8.6pt; color:#52503d}
  .trialsheet .plotrec .when{display:flex; gap:6mm; margin:0 0 3mm}
  .trialsheet .plotrec .when label{flex:1; font-size:8pt; color:#52503d}
  .trialsheet .plotrec .when span{display:block; border-bottom:1px solid #999;
    height:6mm}
  .trialsheet table.rec{width:100%; max-width:100%; min-width:0;
    table-layout:fixed; border-collapse:collapse}
  .trialsheet table.rec col.pl{width:13%}
  .trialsheet table.rec col.m{width:19%}
  .trialsheet table.rec col.nt{width:30%}
  .trialsheet table.rec th{border-bottom:1.2px solid #22301a; font-size:8.4pt;
    text-align:left; padding:1.5mm 1.5mm; vertical-align:bottom}
  /* A header cell the scout writes the measure into. */
  .trialsheet table.rec th.w{border-bottom:1.2px solid #22301a; height:7mm}
  .trialsheet table.rec td{border-bottom:.4px solid #b9b6a8; height:7.5mm;
    padding:0 1.5mm; font-size:9pt}
  /* A hairline every fifth row: twenty-four unbroken rows is a grid somebody
     loses their place in, and losing your place is a score against the wrong
     plot. */
  .trialsheet table.rec tr.nth td{border-bottom:1px solid #6f6a5e}
  /* Left, not right. `td.n` elsewhere on this sheet is a column of figures to
     compare; this is a label somebody reads down the edge of the page while
     their other hand is on a plot. */
  .trialsheet table.rec td.n{font-variant-numeric:tabular-nums; font-weight:650;
    text-align:left}
  .trialsheet .plotrec.none .rule{margin-top:6mm}

/* --- the blind, on the screen where it is made and broken ------------------
   0014 built unblind_events and nothing ever wrote to it, so printing a blind
   copy left the same trace as printing an open one: none. 0051 records both
   halves and web/blind.js turns them into the one sentence a report may print.

   Forest while the blind still holds, taupe once it has ended — and
   deliberately NOT red. Ending a blind when scoring is finished is the normal,
   correct thing to do, and coloring it as a fault would teach people to avoid
   recording it, which is the one outcome worse than not having the feature. */
.blindclaim{margin:6px 0 0; padding:6px 10px; border-radius:9px;
  border-left:3px solid var(--line2); background:var(--surf)}
.blindclaim.open{border-left-color:var(--forest)}
.blindclaim.closed{border-left-color:var(--taupe)}
.revealask{margin:8px 0 0; padding:11px 12px; border-radius:11px;
  background:var(--amber-bg);
  border:1px solid color-mix(in srgb, var(--amber) 30%, transparent)}
.revealask p{margin:0 0 8px; font-size:12.5px; line-height:1.5; max-width:64ch}
.revealask .row{display:flex; gap:7px; align-items:center; flex-wrap:wrap}
.revealask input{font:inherit; font-size:13px; padding:6px 9px; border-radius:9px;
  border:1px solid var(--line2); background:var(--raise); color:var(--ink);
  min-width:240px}

  /* Same argument as the protocol sheet. With the default `auto` layout a
     table is at least as wide as its contents want to be and `width:100%` is
     only a request — one long product name and the table grows past the paper
     and the right-hand column is cut off, silently, because nothing on screen
     is 8.5 inches wide. `fixed` makes the content wrap to the columns instead. */
  /* THE RESET THAT WAS MISSING, AND IT IS THE WHOLE "IT LOOKS TIGHT" REPORT.

     app.css gives bare tables `min-width:860px`, because the app's tables are
     wide data grids that scroll sideways. A printed page does not scroll. At
     8.5 inches with real margins the content box is about 710px, so an 860px
     minimum is 150px off the paper — every time, on every trial sheet ever
     printed, and invisible on screen because on screen the sheet is
     display:none and the window is 1400px wide.

     The protocol sheet has carried this same reset since it was written; the
     trial sheet never got it. tools/printfit.mjs now refuses a printed document
     that inherits a pixel floor without resetting it, so the next sheet cannot
     be added without one. */
  .trialsheet table{min-width:0; width:100%; max-width:100%; table-layout:fixed;
    border-collapse:collapse; font-size:12px}
  .trialsheet th, .trialsheet td{overflow-wrap:break-word}
  /* THE COLUMNS, SIZED FOR WHAT IS IN THEM. Only the treatment name runs long,
     so it takes the slack — but not ALL the slack: leaving it implicit gave it
     half the page and pushed rate, unit and plots into a huddle against the
     right edge with an acre of white between them and the name. Every column is
     named here so the row reads as one line rather than as two clumps. */
  .trialsheet table col.code{width:9%}
  .trialsheet table col.trt{width:43%}
  .trialsheet table col.rate{width:13%}
  .trialsheet table col.unit{width:20%}
  .trialsheet table col.plots{width:15%}
  /* THE BLIND COPY'S TABLE IS THREE COLUMNS, NOT FIVE WITH TWO EMPTIED.
     A blind sheet may name what is being compared and may not say which plot
     got it, so the code and plot-count columns are absent rather than blank —
     a row of em dashes reads as a rendering fault, and on a sheet that has
     already had to be reported as "tight" it spends a fifth of the paper on
     nothing. */
  .trialsheet table.compare col.trt{width:60%}
  .trialsheet table.compare col.rate{width:16%}
  .trialsheet table.compare col.unit{width:24%}
  .trialsheet table.compare td:nth-child(3),
  .trialsheet table.compare th:nth-child(3){padding-left:6px}
  /* Rate against unit is one fact written in two cells, so they sit together:
     the number ends on the right of its column and the unit starts on the left
     of the next. */
  .trialsheet td.n, .trialsheet th.n{padding-right:6px}
  .trialsheet td:nth-child(4), .trialsheet th:nth-child(4){padding-left:6px}
  .trialsheet th,.trialsheet td{border:1px solid #ddd; padding:4px 7px; text-align:left}
  .trialsheet th{background:#f2f0ea; font-size:10.5px; text-transform:uppercase;
    letter-spacing:.05em}
  .trialsheet .n{text-align:right; font-variant-numeric:tabular-nums}

  .trialsheet .fill{display:flex; gap:10px; margin:0 0 8px}
  .trialsheet .fill label{flex:1}
  .trialsheet label{display:block; font-size:10px; text-transform:uppercase;
    letter-spacing:.06em; color:#666}
  .trialsheet label span{display:block; border-bottom:1px solid #999; height:19px; margin-top:2px}
  .trialsheet label.notes span{height:34px}

  /* The sheet's <footer> became `table.page`'s tfoot when the sheet learned to
     run to two pages — a footer inside the flow prints once, at the end of the
     last page, which on a two-page sheet leaves page one unidentified. Nothing
     declares <footer> here any more; the rule is gone rather than left to rot,
     because a dead selector is a thing somebody later styles and wonders why
     nothing moves. */

  /* --- THE MIX SHEET --------------------------------------------------------
     It borrows the trial sheet's page frame — the running head and foot on
     `table.page` are what let either of them run long — and nothing else. The
     shared selectors below are listed against BOTH classes rather than being
     loosened to `.sheet`, because a rule that applies to every sheet is a rule
     that changes the trial sheet the next time this one needs something, and
     the trial sheet has been measured onto one page twice. */
  .mixsheet header{display:flex; justify-content:space-between; align-items:flex-start;
    gap:12px; margin:0 0 8px}
  .mixsheet h1{font-size:19px; margin:0; letter-spacing:-.01em}
  .mixsheet .where{margin:2px 0 0; color:#555; font-size:11.5px}
  .mixsheet .right{text-align:right; font-size:11.5px}
  .mixsheet .right p{margin:0}
  .mixsheet .right .sub{color:#666; font-size:10.5px}
  .mixsheet h2{font-size:12px; letter-spacing:.08em; text-transform:uppercase;
    margin:12px 0 5px; color:#2e5e1e}
  .mixsheet .viewtag{font-size:11px; margin:0 0 10px; padding:5px 8px;
    border:1px solid #ddd; background:#f6f6f4}
  .mixsheet .rule{font-size:10.5px; color:#555; line-height:1.45; margin:6px 0 0}
  .mixsheet .rule.bad{color:#8b1a1a}

  /* A TREATMENT IS ONE UNBROKEN BLOCK. Its loads split across a page break
     would put a part-load row on its own under a header it cannot be read
     against, which is the exact comparison this sheet exists to make. */
  .mixsheet .mixblk{page-break-inside:avoid; margin:0 0 14px}

  .mixsheet ul.facts{display:flex; flex-wrap:wrap; gap:0; margin:0; padding:0;
    list-style:none; border:1px solid #ddd}
  .mixsheet ul.facts li{flex:1 1 16%; padding:5px 9px; border-right:1px solid #eee}
  .mixsheet ul.facts li:last-child{border-right:0}
  .mixsheet ul.facts span{display:block; font-size:10px; text-transform:uppercase;
    letter-spacing:.06em; color:#666}
  .mixsheet ul.facts b{font-size:15px; font-variant-numeric:tabular-nums}

  /* The recipe as the label states it, above the arithmetic derived from it.
     Both are on the sheet on purpose: the numbers in the table are what gets
     poured, and this is what they can be checked against. */
  .mixsheet ul.recipe{display:flex; flex-wrap:wrap; gap:0 18px; margin:7px 0 0;
    padding:0; list-style:none; font-size:11.5px}
  .mixsheet ul.recipe li{white-space:nowrap}
  .mixsheet ul.recipe span{color:#555; font-variant-numeric:tabular-nums}

  /* COLUMNS IN PERCENTAGES, like every other printed table here: a print table
     sized by its contents grows past the paper the first time somebody names a
     product something long. The three fixed columns are narrow and the products
     share what is left, however many there are. */
  .mixsheet table.loads{width:100%; border-collapse:collapse; margin-top:8px;
    font-size:11px; table-layout:fixed}
  .mixsheet table.loads col.ld{width:9%}
  .mixsheet table.loads col.ac{width:11%}
  .mixsheet table.loads col.gl{width:14%}
  .mixsheet table.loads th, .mixsheet table.loads td{
    border:1px solid #ddd; padding:4px 6px; text-align:left;
    overflow-wrap:anywhere}
  .mixsheet table.loads th{background:#f2f2ef; font-size:10px;
    text-transform:uppercase; letter-spacing:.05em; color:#555}
  .mixsheet table.loads .n{text-align:right; font-variant-numeric:tabular-nums}
  .mixsheet table.loads tfoot td{background:#f8f8f6; font-weight:700}
  .mixsheet table.loads .gap{color:#8b1a1a; font-size:10px; font-weight:600}

  /* THE PART LOAD, MARKED. It is the row that gets filled like the one above it
     if nothing distinguishes them, so it is distinguished on the paper and not
     only in a sentence underneath. */
  .mixsheet table.loads tr.part td{background:#faf1dd}
  .mixsheet table.loads tr.part em{font-style:normal; font-size:9.5px;
    text-transform:uppercase; letter-spacing:.05em; color:#a86e10; font-weight:700}
  .mixsheet .rule.part{color:#7a5210}

  .mixsheet table.rinseck{width:100%; border-collapse:collapse; margin-top:7px;
    font-size:11px; table-layout:fixed}
  .mixsheet table.rinseck th, .mixsheet table.rinseck td{
    border:1px solid #ddd; padding:5px 6px; text-align:left}
  .mixsheet table.rinseck th{background:#f2f2ef; font-size:10px;
    text-transform:uppercase; letter-spacing:.05em; color:#555}
  /* The checkbox cell only. Scoped to td because the same class on the header
     printed "RINSED" at 14px in a table whose every other heading is 10px. */
  .mixsheet table.rinseck td.n{text-align:center; font-size:14px}
  .mixsheet table.rinseck th.n{text-align:center}
  /* Written on, not read. Tall enough for a pen and a gloved hand. */
  .mixsheet table.rinseck td.fill{height:20px}
  .mixsheet .rinse, .mixsheet .mixdone{page-break-inside:avoid}
}
