/* Design tokens carried over from the legacy Styles.html so the system still
   looks like itself. Nothing is loaded from a CDN — the legacy app pulled
   Bootstrap and Google Charts from third-party hosts, and the strict CSP this
   site ships would block them anyway. */
:root {
  --green:#2d6a4f; --green-mid:#52b788; --green-pale:#d8f3dc;
  --amber:#e9c46a; --red:#e76f51; --blue:#457b9d;
  --bg:#f4f6f4; --card:#fff; --line:#e3e6e3; --ink:#222; --muted:#777;
  --sidebar-w:236px; --topbar-h:54px; --radius:12px;
  --shadow:0 1px 5px rgba(0,0,0,.07);
}
* { box-sizing:border-box; }
html,body { margin:0; padding:0; }
body {
  background:var(--bg); color:var(--ink); font-size:14px;
  font-family:'Segoe UI', system-ui, -apple-system, sans-serif;
}
h1,h2,h3 { margin:0; }
button { font:inherit; }

/* ── Splash / login ─────────────────────────────────────────── */
.splash { min-height:100vh; display:flex; align-items:center; justify-content:center; }
.splash[hidden] { display:none; }
.splash-in { text-align:center; color:var(--muted); }
.splash-emoji { font-size:3rem; }
.spinner {
  width:28px; height:28px; margin:12px auto; border-radius:50%;
  border:3px solid var(--green-pale); border-top-color:var(--green);
  animation:spin .8s linear infinite;
}
@keyframes spin { to { transform:rotate(360deg); } }

.login-card {
  background:var(--card); padding:32px; border-radius:16px; box-shadow:var(--shadow);
  width:100%; max-width:390px; text-align:center;
}
.login-card h1 { font-size:1.35rem; color:var(--green); margin-top:6px; }
.login-card .sub { color:var(--muted); font-size:13px; margin:4px 0 18px; }
.login-card label { display:block; text-align:left; margin-bottom:14px; }
.login-card .fine { color:var(--muted); font-size:12px; margin:14px 0 0; }

/* ── Inputs ─────────────────────────────────────────────────── */
label > span { display:block; font-size:12.5px; color:#444; margin-bottom:4px; font-weight:500; }
input, select, textarea {
  width:100%; padding:9px 10px; font:inherit; font-size:13px;
  border:1px solid #ddd; border-radius:8px; background:#fff; color:inherit;
}
input:focus, select:focus, textarea:focus {
  outline:none; border-color:var(--green-mid); box-shadow:0 0 0 3px rgba(82,183,136,.18);
}
input[readonly] { background:#f6f6f6; }
textarea { resize:vertical; }

.btn {
  padding:9px 16px; border-radius:8px; border:1px solid transparent;
  cursor:pointer; font-size:13px; font-weight:600;
}
.btn:disabled { opacity:.5; cursor:not-allowed; }
.btn-green { background:var(--green); color:#fff; }
.btn-green:hover:not(:disabled) { background:#1b4332; }
.btn-ghost { background:#fff; border-color:#d5d9d5; color:#444; }
.btn-ghost:hover:not(:disabled) { background:var(--green-pale); color:var(--green); }
.btn-lg { width:100%; padding:12px; font-size:15px; }
.btn-sm { padding:6px 10px; font-size:12px; }
.icon-btn { background:none; border:none; font-size:1.3rem; cursor:pointer; padding:4px 8px; }
.link { background:none; border:none; color:var(--blue); cursor:pointer; padding:0 6px 0 0; font-size:12.5px; text-decoration:underline; }
.link-del { color:var(--red); }

/* ── Layout ─────────────────────────────────────────────────── */
#sidebar {
  position:fixed; inset:0 auto 0 0; width:var(--sidebar-w);
  background:#fff; border-right:1px solid var(--line);
  display:flex; flex-direction:column; z-index:40; overflow-y:auto;
}
.brand { display:flex; gap:10px; align-items:center; padding:14px 16px; border-bottom:1px solid var(--line); }
.brand-emoji { font-size:1.6rem; }
.brand b { display:block; font-size:.82rem; letter-spacing:.04em; color:var(--green); }
.brand small { color:var(--muted); font-size:10px; }
.who { padding:10px 16px; border-bottom:1px solid var(--line); font-size:12.5px; }
.who b { display:block; margin-bottom:4px; }
#nav { padding:8px 8px 16px; flex:1; }
.nav-sec { font-size:10px; color:#aaa; text-transform:uppercase; letter-spacing:.9px; padding:12px 10px 4px; }
.nav-btn {
  display:flex; align-items:center; gap:9px; width:100%; text-align:left;
  background:none; border:none; padding:9px 10px; border-radius:8px;
  cursor:pointer; color:#444; font-size:13.5px;
}
.nav-btn:hover { background:var(--green-pale); color:var(--green); }
.nav-btn.active { background:var(--green-pale); color:var(--green); font-weight:600; }
.nav-btn .ico { width:20px; text-align:center; }
.side-foot { padding:12px 16px; border-top:1px solid var(--line); display:grid; gap:8px; }

#topbar {
  position:fixed; top:0; left:var(--sidebar-w); right:0; height:var(--topbar-h);
  background:#fff; border-bottom:1px solid var(--line); box-shadow:var(--shadow);
  display:flex; align-items:center; gap:10px; padding:0 18px; z-index:30;
}
#topbar h2 { font-size:15px; color:var(--green); font-weight:600; }
#menuBtn { display:none; }
#main { margin-left:var(--sidebar-w); padding:calc(var(--topbar-h) + 20px) 22px 48px; }

/* ── Panels, KPIs, tables ───────────────────────────────────── */
.panel { background:var(--card); border-radius:var(--radius); box-shadow:var(--shadow); padding:18px 20px; margin-bottom:20px; }
.panel h3 { font-size:13px; color:#555; margin-bottom:14px; font-weight:600; }
.panel-head { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-bottom:12px; }
.panel-head h3 { margin:0; }
.panel-actions { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.panel-actions input[type=date] { width:auto; }
.grid-2 { display:grid; grid-template-columns:repeat(auto-fit,minmax(340px,1fr)); gap:20px; }

.kpi-row { display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:14px; margin-bottom:20px; }
.kpi { background:var(--card); border-radius:var(--radius); box-shadow:var(--shadow); padding:14px 16px; border-left:4px solid var(--green); }
.kpi.amber { border-color:var(--amber); } .kpi.blue { border-color:var(--blue); } .kpi.red { border-color:var(--red); }
.kpi-val { font-size:1.55rem; font-weight:700; line-height:1.15; }
.kpi-label { font-size:11.5px; color:var(--muted); margin-top:5px; }
.kpi-sub { font-size:11px; color:#999; margin-top:6px; }

.table-wrap { overflow-x:auto; max-height:520px; overflow-y:auto; border-radius:8px; border:1px solid var(--line); }
table { width:100%; border-collapse:collapse; font-size:12.5px; }
thead th {
  background:var(--green-pale); color:var(--green); font-weight:600; font-size:11.5px;
  text-align:left; padding:9px 10px; white-space:nowrap; position:sticky; top:0; z-index:2;
}
tbody td { padding:8px 10px; border-top:1px solid var(--line); vertical-align:top; }
tbody tr:hover { background:#fafbfa; }
th.num, td.num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
.empty { color:var(--muted); font-size:13px; padding:14px 2px; margin:0; }
.pager { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:12px; font-size:12.5px; color:var(--muted); }

/* ── Chips, alerts, charts ──────────────────────────────────── */
.chip { display:inline-block; font-size:10.5px; font-weight:600; padding:2px 7px; border-radius:20px; }
.chip-up { background:#d1fae5; color:#065f46; }
.chip-down { background:#fee2e2; color:#991b1b; }
.chip-flat { background:#eef1ee; color:#666; }
.chip-warn { background:#fff3cd; color:#8a6d1f; }
/* Outside growers' fields. Deliberately cool and quiet: this is a category,
   not a warning — their fruit is real, it just is not ours. */
.chip-outside { background:#e4ecf5; color:#37536f; }
.var-ok { color:var(--green); font-weight:600; }

/* ── Ours / outside ─────────────────────────────────────────── */
.sec-head { font-size:12px; font-weight:700; text-transform:uppercase;
  letter-spacing:.06em; color:var(--muted); margin:0 0 8px; }
.sec-head + .table-wrap, .sec-head + .kpi-row { margin-top:0; }
h6.sec-head ~ h6.sec-head { margin-top:22px; }
/* The outside tiles read as a footnote to the farm's own, never as a rival
   headline: same grid, lighter ground, no tone colours. */
.kpi-row-alt .kpi { background:#f6f8fa; border-color:#e2e8ef; }
.chk-group { font-size:11.5px; font-weight:700; text-transform:uppercase;
  letter-spacing:.05em; color:var(--muted); margin:10px 0 4px; }
.chk-group:first-child { margin-top:2px; }
.sec-bar { display:flex; align-items:center; justify-content:space-between; gap:12px; }
.sec-bar .sec-head { margin-bottom:8px; }

/* ── KPI report ─────────────────────────────────────────────── */
.kpi-wrap { max-height:min(66vh,620px); }
.kpi-table { font-size:11.5px; }
.kpi-table th, .kpi-table td { padding:5px 7px; white-space:nowrap; }
/* The name column stays put while the twelve months scroll under it. */
.kpi-table .kpi-name {
  position:sticky; left:0; z-index:3; background:#fff; text-align:left;
  min-width:210px; max-width:210px; white-space:normal; border-right:1px solid var(--line);
}
.kpi-table thead th.kpi-name { background:var(--green-pale); z-index:4; }
.kpi-table tbody tr:hover .kpi-name { background:#fafbfa; }
.kpi-cmt { font-size:10px; color:var(--muted); font-weight:400; margin-top:2px; line-height:1.35; }
.kpi-ytd { background:var(--green-pale); font-weight:600; }
.kpi-sec td {
  background:var(--green); color:#fff; font-weight:700; font-size:10.5px;
  letter-spacing:.07em; text-transform:uppercase; padding:6px 8px;
}
/* A row with no source is present but plainly not a measurement. */
.kpi-absent { color:#9aa39a; }
.kpi-absent .kpi-name { color:#8a938a; }
.kpi-notes { margin-top:14px; font-size:12px; color:var(--muted); }
.kpi-notes summary { cursor:pointer; font-weight:600; color:var(--green); }
.kpi-notes ul { margin:8px 0 0; padding-left:18px; line-height:1.6; }
.kpi-notes li { margin-bottom:5px; }

/* The typed-figure grid. Compact enough that a year fits without scrolling
   sideways more than the report itself does. */
.kpi-edit td { padding:3px 4px; }
.kpi-in {
  width:64px; padding:4px 5px; font-size:11.5px; text-align:right;
  font-variant-numeric:tabular-nums;
}
.kpi-edit .kpi-name { min-width:190px; max-width:190px; font-size:11.5px; }
/* Typed and derived read differently from measured: one is somebody's entry,
   the other is arithmetic over it. Neither is a measurement. */
.kpi-typed .kpi-name { color:#7a5c11; }
.kpi-derived .kpi-name { color:#37536f; font-style:italic; }
/* A loss, in parentheses as a ledger writes it. Red as well as bracketed,
   because these are the rows where the sign is the whole story. */
.kpi-neg { color:var(--red); }

/* ── Harvest liquidation ────────────────────────────────────── */
.lq-wrap { max-height:min(58vh,520px); }
.lq-table { font-size:12px; }
.lq-table th, .lq-table td { padding:5px 6px; }
.lq-table td { vertical-align:middle; }
.lq-sub { font-size:10.5px; color:var(--muted); margin-top:3px; line-height:1.5; }
.lq-in { width:72px; padding:5px 6px; font-size:12px; text-align:right;
  font-variant-numeric:tabular-nums; }
/* Zero is the only acceptable answer here, so it is the only one in green. */
.lq-un { font-variant-numeric:tabular-nums; }
/* A checkbox that needs a whole line to say what it means. */
.chk-block { display:flex; align-items:center; gap:8px; font-size:13px;
  font-weight:400; padding:9px 0; }
.chk-block input { width:auto; }
/* The liquidation grid wants ~1,100px; .form-card caps at 820, which is what
   pushed Kilos and Bruising off the right edge. Only the harvest form takes the
   extra room, and only while the grid is in it. */
/* Two classes, because this block sits above .form-card in the file and equal
   specificity would let the 820px cap win on source order. */
.form-card.form-card-wide { max-width:min(1280px, 100%); }
.lq-table { table-layout:fixed; width:100%; }
.lq-table th:first-child, .lq-table td:first-child { width:auto; min-width:146px; }
/* Eight number columns have to share what is left after the bunch name, and a
   1024px window leaves about 680px for the lot. Sized to fit there and allowed
   to breathe wider when the screen has room, rather than scrolling sideways. */
/* 58px is the floor that keeps eight columns inside a 1024px window; on a
   wider screen they grow rather than leaving the slack to the bunch name. */
.lq-table th.num, .lq-table td.num { width:clamp(58px, 7.5%, 104px); }
.lq-table th:nth-child(2), .lq-table td:nth-child(2) { width:56px; }
.lq-table th, .lq-table td { padding:5px 4px; }
/* Let a long heading wrap instead of forcing the column wider. */
.lq-table thead th { white-space:normal; font-size:10.5px; line-height:1.25; }
.lq-in { width:100%; max-width:52px; padding:5px 4px; }
.lq-uncell { background:#fafbfa; }
/* Captions live on every cell; on a wide screen the header row says them. */
.lq-table td[data-cap]::before { display:none; }

/* ═══════════════ Record Harvest on a phone ═══════════════
   Ten columns cannot be read at 380px, and a sideways scroll inside a
   data-entry form is how a bunch count ends up in the wrong box. Each cohort
   becomes its own card instead: the bunch named once across the top, then the
   figures as captioned boxes two to a line. Portrait, nothing off-screen, and
   the caption still visible while the number is being typed. */
@media (max-width:760px) {
  .lq-wrap { max-height:none; overflow:visible; border:0; border-radius:0; }
  .lq-table, .lq-table tbody, .lq-table tr, .lq-table td { display:block; width:auto; }
  .lq-table thead { display:none; }
  .lq-table { table-layout:auto; }

  .lq-table tbody tr {
    border:1px solid var(--line); border-radius:10px;
    padding:11px 12px 12px; margin-bottom:12px; background:#fff;
    display:grid; grid-template-columns:1fr 1fr; gap:9px 10px;
  }
  .lq-table tbody tr:hover { background:#fff; }

  /* The bunch, its due date and its status get the full width. */
  .lq-table td:first-child {
    grid-column:1 / -1; border:0; padding:0 0 2px;
    font-weight:600; font-size:13.5px; text-align:left;
  }
  .lq-table td { border:0; padding:0; text-align:left; }

  /* Caption above the box, so it survives the keyboard covering half the screen. */
  .lq-table td[data-cap]::before {
    display:block; content:attr(data-cap);
    font-size:10.5px; font-weight:700; letter-spacing:.04em;
    text-transform:uppercase; color:var(--muted); margin-bottom:3px;
  }
  .lq-in { max-width:none; width:100%; font-size:16px; padding:10px 11px; text-align:right; }

  /* Standing and Unaccounted are read, not typed. They sit side by side across
     the top of the card, because they are the two numbers that have to agree. */
  /* Name first, then the two numbers that have to agree, then the boxes.
     order:-1 put Standing above the bunch name, so you read a count before
     knowing what it counted. Equal orders keep DOM sequence, so the six entry
     boxes stay in the order the header lists them. */
  .lq-table tbody td { order:3; }
  .lq-table tbody td:first-child { order:0; }
  .lq-standing { order:1; }
  .lq-uncell { order:2; }
  .lq-standing, .lq-uncell {
    background:var(--green-pale); border-radius:8px; padding:7px 10px;
  }
  .lq-uncell { background:#fafbfa; }
  .lq-standing b, .lq-un { font-size:18px; }
}

.kpi-ytd.kpi-neg { color:var(--red); }
.var-warn { color:#d4a017; font-weight:600; }
.var-alert { color:var(--red); font-weight:600; }

.alert { padding:11px 14px; border-radius:8px; font-size:13px; margin-bottom:16px; }
.alert-err { background:#fdecea; color:#8e2a1c; }
.alert-warn { background:#fff3cd; color:#7a5c11; }

.chart-scroll { overflow-x:auto; }
.chart-scroll svg { display:block; }
svg .grid { stroke:#eceeec; stroke-width:1; }
svg .axis { fill:#999; font-size:10px; }
.legend { display:flex; flex-wrap:wrap; gap:6px 16px; justify-content:center; margin-top:10px; font-size:11.5px; color:#555; }
.lg { display:inline-flex; align-items:center; gap:5px; }
.lg i { width:10px; height:10px; border-radius:50%; display:inline-block; }
.share { display:flex; height:22px; border-radius:6px; overflow:hidden; background:#eee; }
.share .seg { height:100%; }
.loading-inline { color:var(--muted); font-size:13px; padding:16px 2px; }

/* ── Modal ──────────────────────────────────────────────────── */
.modal-overlay { position:fixed; inset:0; background:rgba(0,0,0,.4); z-index:80; display:flex; align-items:flex-start; justify-content:center; padding:36px 16px; overflow-y:auto; }
.modal { background:#fff; border-radius:14px; width:100%; box-shadow:0 12px 40px rgba(0,0,0,.22); }
.modal header { display:flex; align-items:center; justify-content:space-between; padding:16px 20px; border-bottom:1px solid var(--line); }
.modal header h3 { font-size:15px; color:var(--green); }
.modal .x { background:none; border:none; font-size:1.4rem; cursor:pointer; color:#999; line-height:1; }
.modal-body { padding:18px 20px; display:grid; gap:12px; }
.modal footer { display:flex; justify-content:flex-end; gap:10px; padding:14px 20px; border-top:1px solid var(--line); }
.frow { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.fnote { font-size:12px; color:var(--muted); margin:0; line-height:1.5; }
/* An empty picker is a question, not a failure — say what IS there. */
.fnote-warn { color:#7a5c11; background:#fff8e1; border-left:3px solid #e9c46a;
  padding:8px 10px; border-radius:0 6px 6px 0; }
.confirm-msg { font-size:13.5px; margin:0; line-height:1.6; }
.checkgrid { display:grid; grid-template-columns:repeat(auto-fill,minmax(88px,1fr)); gap:6px; }
.chk { display:flex; align-items:center; gap:6px; font-size:12.5px; font-weight:400; }
.chk input { width:auto; }
.grade-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:10px; }
.grade-col { background:#fafbfa; border:1px solid var(--line); border-radius:8px; padding:10px; }
.grade-col b { display:block; font-size:12px; color:var(--green); margin-bottom:6px; }
.grade-col label { margin-bottom:6px; }

/* ── Tabs (every page but Dashboard and Audit) ──────────────── */
.tabs { display:flex; flex-wrap:wrap; gap:2px; list-style:none; margin:0 0 18px; padding:0; border-bottom:1px solid var(--line); }
.tab {
  background:none; border:none; border-bottom:2px solid transparent;
  padding:9px 16px; font-size:13px; color:#555; cursor:pointer; margin-bottom:-1px;
}
.tab:hover { color:var(--green); }
.tab.active { color:var(--green); font-weight:600; border-bottom-color:var(--green); }
.tab-badge { background:#b3261e; color:#fff; border-radius:20px; font-size:10px; padding:1px 6px; margin-left:6px; }
.sub-tabs { margin-bottom:14px; }
.tab .count { background:#eef1ee; color:#666; border-radius:20px; font-size:10.5px; padding:1px 7px; margin-left:6px; }
.tab.active .count { background:var(--green-pale); color:var(--green); }

/* ── Inline entry forms ─────────────────────────────────────── */
.form-card { background:var(--card); border-radius:var(--radius); box-shadow:var(--shadow); padding:22px 24px; margin-bottom:20px; max-width:820px; }
.form-card h6 { font-size:13.5px; font-weight:700; color:var(--green); margin:0 0 16px; }
.entry-form { display:grid; gap:12px; }
.entry-form textarea { min-height:56px; }
.form-actions { display:flex; justify-content:flex-end; gap:10px; margin-top:18px; }
fieldset.sub { border:1px solid var(--line); border-radius:8px; padding:12px 14px; margin:4px 0; display:grid; gap:10px; }
fieldset.sub legend { font-size:12px; font-weight:600; color:var(--green); padding:0 6px; }
.checkgrid { display:grid; grid-template-columns:repeat(auto-fill,minmax(92px,1fr)); gap:6px; }
.checkgrid.wide { grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); }
.chk { display:flex; align-items:center; gap:6px; font-size:12.5px; font-weight:400; }
.chk input { width:auto; }
.input-row { display:grid; grid-template-columns:2fr 1fr 1fr 1fr auto; gap:8px; align-items:center; }
.cust-row { display:grid; grid-template-columns:1fr auto; gap:8px; align-items:end; }

/* ── QA / sale line grid ────────────────────────────────────── */
.qa-grid { border:1px solid var(--line); border-radius:8px; overflow:hidden; }
.qa-head, .qa-row { display:grid; grid-template-columns:120px 1fr 1fr 1fr; gap:8px; align-items:center; padding:7px 10px; }
.qa-head { background:var(--green-pale); font-size:11px; font-weight:600; color:var(--green); text-transform:uppercase; letter-spacing:.4px; }
.qa-head span { text-align:right; }
.qa-head span:first-child { text-align:left; }
.qa-row { border-top:1px solid var(--line); }
.qa-row b { font-size:12.5px; font-weight:600; }
.qa-row input { text-align:right; font-variant-numeric:tabular-nums; }
.qa-kilos { background:#f6f8f6; color:#666; }
.qa-total { background:#fafbfa; font-weight:700; }
.qa-total input { background:var(--green-pale); color:var(--green); font-weight:700; }
.price-bar { display:flex; flex-wrap:wrap; align-items:center; gap:14px; background:var(--green-pale); color:var(--green); border-radius:8px; padding:10px 14px; margin-bottom:18px; font-size:12.5px; }
.price-bar b { font-weight:700; }

/* ── Record cards ───────────────────────────────────────────── */
.rec-card { background:var(--card); border-radius:var(--radius); box-shadow:var(--shadow); padding:16px 18px; margin-bottom:20px; }
.rec-head { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-bottom:12px; }
.rec-head h6 { font-size:13px; font-weight:600; color:#555; margin:0; }
.rec-actions { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.rec-search { width:auto; min-width:190px; }
.rec-actions input[type=date] { width:auto; }
.chart-card { background:var(--card); border-radius:var(--radius); box-shadow:var(--shadow); padding:18px 20px; margin-bottom:20px; }
.chart-card h6 { font-size:13px; font-weight:600; color:#555; margin:0 0 14px; }
.chart-card h6.danger { color:var(--red); }
.confirm-tbl { width:100%; border-collapse:collapse; font-size:13px; }
.confirm-tbl th { text-align:left; padding:6px 10px 6px 0; color:#666; font-weight:500; width:42%; vertical-align:top; }
.confirm-tbl td { padding:6px 0; font-weight:600; }
.confirm-tbl tr + tr th, .confirm-tbl tr + tr td { border-top:1px solid var(--line); }
.alert-ok { background:var(--green-pale); color:var(--green); }
svg .bar-lbl { fill:#555; font-size:9.5px; font-weight:600; }
.brand-logo { width:38px; height:38px; object-fit:contain; flex-shrink:0; }
.login-logo { width:74px; height:74px; object-fit:contain; }
#topbarDate { margin-left:auto; font-size:12.5px; color:var(--muted); }

/* ── Edit history ───────────────────────────────────────────── */
.hist { border-left:3px solid var(--green-pale); padding:8px 0 8px 12px; margin-bottom:12px; }
.hist-head { font-size:11.5px; color:var(--muted); margin-bottom:5px; }
.hist-row { font-size:12.5px; line-height:1.7; }
.hist-row b { display:inline-block; min-width:130px; color:#555; font-weight:600; }
.hist-row s { color:#b3261e; opacity:.75; }
.hist-row em { color:var(--green); font-style:normal; font-weight:600; }

/* ── Toast / busy ───────────────────────────────────────────── */
.toast {
  position:fixed; top:18px; left:50%; transform:translate(-50%,-16px);
  background:var(--green); color:#fff; padding:11px 18px; border-radius:10px;
  font-size:13px; z-index:120; opacity:0; pointer-events:none;
  transition:opacity .2s, transform .2s; max-width:min(92vw,460px);
}
.toast.show { opacity:1; transform:translate(-50%,0); }
.toast-err { background:#b3261e; }
.busy { position:fixed; inset:0; background:rgba(255,255,255,.6); display:none; align-items:center; justify-content:center; gap:10px; z-index:110; flex-direction:column; color:var(--muted); }
.busy.show { display:flex; }

/* ── Small screens ──────────────────────────────────────────── */
@media (max-width:820px) {
  #sidebar { transform:translateX(-100%); transition:transform .22s; box-shadow:0 0 30px rgba(0,0,0,.2); }
  #sidebar.open { transform:none; }
  #topbar { left:0; }
  #menuBtn { display:inline-block; }
  #main { margin-left:0; padding:calc(var(--topbar-h) + 14px) 12px 40px; }
  .frow { grid-template-columns:1fr; }
  .kpi-val { font-size:1.3rem; }
}

/* ── Record Harvest: the chosen bunch's details ─────────────── */
.bag-info {
  background:#fafbfa; border:1px solid var(--line); border-left:3px solid var(--green-mid);
  border-radius:8px; padding:10px 12px; font-size:12.5px; color:#555; line-height:1.7;
}
.bag-info b { color:var(--ink); }
.bag-info b.over { color:var(--red); }

/* Weeks that had bunches come due with no harvest recorded against them.
   Amber rather than red: the data is recoverable, and the banner exists to
   prompt rather than to alarm. */
.missed-weeks { border-left:3px solid var(--amber); }
.missed-weeks .rec-head h6 { color:var(--ink); }
.missed-list { list-style:none; margin:0; padding:0; display:grid; gap:8px; }
.missed-list li {
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:8px 10px; border:1px solid var(--line); border-radius:8px;
  background:var(--bg); font-size:13px;
}
.missed-list .muted { color:var(--muted); }
.missed-list button { margin-left:auto; }
@media (max-width:640px) {
  .missed-list button { margin-left:0; }
}

/* A KPI tile that opens its own breakdown. It reads as a button — cursor,
   hover lift, and a visible focus ring, since it is keyboard-reachable. */
.kpi-click { cursor:pointer; transition:box-shadow .12s, transform .12s; }
.kpi-click:hover { box-shadow:0 3px 12px rgba(0,0,0,.12); transform:translateY(-1px); }
.kpi-click:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }
.kpi-click:active { transform:none; }
.kpi-more {
  float:right; font-size:10px; font-weight:600; letter-spacing:.03em;
  text-transform:uppercase; color:var(--blue); opacity:0; transition:opacity .12s;
}
.kpi-click:hover .kpi-more, .kpi-click:focus-visible .kpi-more { opacity:1; }
.kpi-busy { opacity:.6; pointer-events:none; }
/* Touch has no hover, so the affordance has to be permanent there. */
@media (hover:none) {
  .kpi-more { opacity:1; }
}

/* Rejects: one row per field, kilos box enabled only when the field is ticked. */
.reject-grid { border:1px solid var(--line); border-radius:8px; overflow:hidden; }
.reject-head, .reject-row {
  display:grid; grid-template-columns:minmax(8rem,1.4fr) 1fr 1fr;
  gap:10px; align-items:center; padding:7px 10px;
}
.reject-head {
  background:var(--bg); font-size:11px; font-weight:600; color:var(--muted);
  text-transform:uppercase; letter-spacing:.03em;
}
.reject-head span:not(:first-child), .reject-row > span { text-align:right; }
.reject-row { border-top:1px solid var(--line); }
.reject-row .muted { color:var(--muted); }
.reject-row input[type=number] { text-align:right; }
.reject-row input:disabled { background:var(--bg); color:var(--muted); }
/* Cell captions: silent on desktop where the header row names the columns. */
.rj-cell > i { display:none; }
.rj-cell > b { font-weight:600; }
.reject-row > .rj-derived { text-align:right; }

@media (max-width:560px) {
  .reject-head { display:none; }          /* the captions take over */
  .reject-row { grid-template-columns:1fr 1fr; row-gap:2px; align-items:end; }
  .reject-row label.chk { grid-column:1 / -1; font-weight:600; }
  .rj-cell > i {
    display:block; font-style:normal;
    font-size:10px; font-weight:600; letter-spacing:.03em; text-transform:uppercase;
    color:var(--muted); margin-bottom:2px; white-space:nowrap;
  }
  .reject-row > .rj-derived { text-align:left; }
  .reject-row > .rj-derived > b { font-size:15px; }
}

/* ═══════════════ Mobile: the entry grids ═══════════════
   Desktop keeps the four-column table — the header row names the columns and
   each cell's own caption stays hidden. Below 640px that layout gives an input
   about 60px, which is why amounts were being cut off mid-number. */
.qa-cell { display:block; min-width:0; }
.qa-cell > i { display:none; }

@media (max-width:640px) {
  .qa-head { display:none; }             /* the captions take over */

  /* Two columns, not four. The row's label and its derived figure share the
     first line; the boxes you actually type into get the second line to
     themselves and roughly double in width. */
  .qa-row {
    grid-template-columns:1fr 1fr;
    gap:2px 10px;
    padding:7px 10px;
    align-items:end;
  }
  /* The derived figure reads on one line with its caption rather than stacking,
     which is ~14px a row across six rows. */
  .qa-row > .qa-derived {
    display:flex; align-items:baseline; justify-content:flex-end; gap:6px;
  }
  .qa-row > .qa-derived > i { margin:0; }
  .qa-row > .qa-derived input { width:auto; min-width:0; flex:0 1 auto; }
  .qa-row > b        { order:1; align-self:center; font-size:13px; }
  .qa-row > .qa-derived { order:2; }
  .qa-row > .qa-cell:not(.qa-derived) { order:3; }
  .qa-row > span     { display:none; }   /* the TOTAL row's empty spacers */

  .qa-cell > i {
    display:block; font-style:normal;
    font-size:10px; font-weight:600; letter-spacing:.03em; text-transform:uppercase;
    color:var(--muted); margin-bottom:2px; white-space:nowrap;
  }
  /* The derived figure is a readout, not a box: no border, right-aligned, sized
     to be readable rather than editable. */
  .qa-row > .qa-derived > i { text-align:right; }
  .qa-row > .qa-derived input {
    border:none; background:none; padding:0; text-align:right;
    font-weight:700; font-size:15px; color:var(--ink);
  }
  .qa-total { background:var(--green-pale); }
  .qa-total .qa-derived input { color:var(--green); }
}

/* iOS zooms the page in when a focused input is under 16px, which then leaves
   the form scrolled sideways. 16px on the things you type into prevents it. */
@media (max-width:820px) {
  input, select, textarea { font-size:16px; padding:11px 12px; }
  .qa-row input { padding:8px 10px; }
  .entry-form { gap:14px; }
  /* A select holding a long option label must not stretch the page. */
  select { max-width:100%; text-overflow:ellipsis; }
  .form-card { padding:16px 14px; }
  .rec-card { padding:14px 12px; }
  /* Comfortable thumb targets. */
  .btn { padding:11px 16px; }
  .chk input[type=checkbox] { width:20px; height:20px; }
}

/* ═══════════════ Mobile: Farm Inputs Applied ═══════════════
   Desktop keeps the single five-column row and hides the cell captions. On a
   phone that row gave the input name about 110px, so "Chicken Dung" read as
   "Chi…" and the placeholders as "Qt" and "Ar". */
.fi-cell { display:block; min-width:0; }
.fi-cell > i { display:none; }

@media (max-width:640px) {
  .input-row {
    /* The name gets a line of its own — the longest is 33 characters and needs
       the full width. Everything else is a short number under a caption. */
    /* The unit is a SELECT and loses ~40px to its own chevron and padding,
       so it gets a wider column than the two number boxes beside it. */
    grid-template-columns:1fr 1.25fr 1fr auto;
    gap:4px 6px;
    align-items:end;
    padding:10px;
    border:1px solid var(--line);
    border-radius:8px;
    background:var(--bg);
  }
  .input-row + .input-row { margin-top:8px; }
  /* Both row AND column, by child position. Setting only the column left
     the rows to auto-placement, which scattered them over four lines. */
  .input-row > :nth-child(1) { grid-column:1 / -1; grid-row:1; }  /* the name */
  .input-row > :nth-child(2) { grid-column:1; grid-row:2; }       /* Qty      */
  .input-row > :nth-child(3) { grid-column:2; grid-row:2; }       /* Unit     */
  .input-row > :nth-child(4) { grid-column:3; grid-row:2; }       /* Amount   */
  /* Bottom-aligned like the boxes beside it, then nudged up so its text sits on
     their centre line rather than their baseline. */
  .input-row > :nth-child(5) { grid-column:4; grid-row:2; align-self:end; margin-bottom:6px; }
  /* Selects lose a lot of their width to the chevron; claw some back inside
     this row so "Liters" is not clipped to "L…". */
  .input-row select { padding-left:8px; padding-right:4px; }

  .fi-cell > i {
    display:block; font-style:normal;
    font-size:10px; font-weight:600; letter-spacing:.03em; text-transform:uppercase;
    color:var(--muted); margin-bottom:2px; white-space:nowrap;
  }
  /* The caption says it, so the placeholder repeating it just gets clipped. */
  .input-row input::placeholder { color:transparent; }
  .input-row input { text-align:right; }
}

/* "Keep me signed in" sits with the fields, not with the button. */
.login-remember {
  display:flex; align-items:center; gap:9px;
  margin:2px 0 4px; font-size:13px; color:#555; cursor:pointer;
}
.login-remember input { width:18px; height:18px; margin:0; flex:none; }
.login-remember span { user-select:none; }


/* A compact remove control. "remove" as a word cost 62px of a 330px row on a
   phone, which is most of a field. */
.btn-x {
  width:32px; height:32px; padding:0; flex:none;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:20px; line-height:1; color:var(--red);
  background:none; border:1px solid var(--line); border-radius:8px; cursor:pointer;
}
.btn-x:hover { background:#fdeeea; border-color:var(--red); }
.btn-x:focus-visible { outline:2px solid var(--red); outline-offset:2px; }

/* ── Total Area card ────────────────────────────────────────── */
/* The one tile in the KPI row with three figures instead of one: the land
   under crop and the land not yet, side by side, over their total. Everything
   here is scoped to .kpi-area, so the other six tiles in the row are untouched
   and the card keeps .kpi's ground, radius, shadow, padding and left rule. */
.kpi-area .area-split { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
/* min-width:0 or a long figure would push the right column off the card
   instead of staying in its half. */
.kpi-area .area-cell { min-width:0; }
.kpi-area .area-lab { font-size:11.5px; color:var(--muted); }
/* Smaller than .kpi-val below it: these two are the parts, the total is the
   headline. Sized to fit the 190px the kpi-row grid can shrink a card to. */
.kpi-area .area-val { font-size:1.05rem; font-weight:700; line-height:1.2; margin-top:3px; }
/* Spelled out, 'hectares' will not sit beside the figure in a card the grid has
   squeezed to 190px, so it is allowed to drop to its own line there and sits
   beside it everywhere else. Neither the figure nor the word ever breaks. */
.kpi-area .area-unit { font-size:.7em; font-weight:600; color:var(--muted); white-space:nowrap; }
/* The divider, and the figure the two above add up to. */
.kpi-area .area-total { margin-top:11px; padding-top:9px; border-top:1px solid var(--line); }
.kpi-area .area-total .kpi-label { margin-top:0; }
.kpi-area .area-total .kpi-val { margin-top:2px; }
/* ── Outside grower: the field picker ──────────────────────────────────────
   A tick list, not a multi-select. There are only ever a handful of outside
   fields and an admin needs to see at a glance which are ticked, which a
   collapsed <select multiple> hides.                                        */
.gf-pick { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 10px; }
.gf-opt {
  display: flex; align-items: center; gap: 8px; margin: 0;
  padding: 9px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--card); cursor: pointer; min-width: 132px;
}
.gf-opt:hover { border-color: var(--green); }
.gf-opt input { width: 16px; height: 16px; margin: 0; accent-color: var(--green); flex: none; }
.gf-opt span { display: flex; flex-direction: column; font-weight: 600; line-height: 1.25; }
.gf-opt small { font-weight: 400; color: var(--muted); font-size: .76rem; }
/* A ticked box is readable without squinting at the checkbox itself. */
.gf-opt:has(input:checked) { border-color: var(--green); background: var(--green-pale); }

@media (max-width: 760px) {
  .gf-pick { flex-direction: column; }
  .gf-opt { min-width: 0; }
}

/* ── The other door on the sign-in screen ──────────────────────────────────
   Quiet on purpose. Almost everybody signing in is staff; the grower link has
   to be findable without competing with the button they actually want.      */
.login-alt {
  display:flex; align-items:center; justify-content:center; gap:7px;
  flex-wrap:wrap; margin-top:14px; padding-top:14px;
  border-top:1px solid var(--line); font-size:.86rem; color:var(--muted);
}
.login-alt .link { font-size:.86rem; font-weight:600; }

/* ── The outside grower's whole application ────────────────────────────────
   He gets no sidebar, because there is nowhere else for him to go. One bar,
   his name, and the two things he can do to his own account.                */
.g-top {
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  flex-wrap:wrap; padding:12px 18px; background:var(--card);
  border-bottom:1px solid var(--line); position:sticky; top:0; z-index:5;
}
.g-brand { display:flex; align-items:center; gap:11px; min-width:0; }
.g-brand b { display:block; font-size:.9rem; letter-spacing:.3px; color:var(--green); }
/* His name, which can be long, and must not push the buttons off a phone. */
.g-brand small {
  display:block; font-size:.78rem; color:var(--muted);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:46vw;
}
.g-actions { display:flex; gap:8px; flex:none; }
#growerApp .content { padding:18px; max-width:1100px; margin:0 auto; }

@media (max-width:560px) {
  .g-top { padding:10px 14px; }
  #growerApp .content { padding:14px; }
  .g-brand small { max-width:38vw; }
}

/* ── A figure the form works out rather than asks for ──────────────────────
   Shaped like the inputs beside it so the row still reads as a row, but flat
   and greyed so nobody tries to type in it. <output>, not <input>, so the
   form collector never sends it — the database derives it from the same two
   dates and has the last word.                                             */
.computed {
  display:flex; align-items:center; min-height:42px;
  padding:9px 12px; border:1px dashed var(--line); border-radius:8px;
  background:var(--bg); color:var(--muted); font-weight:600; font-size:.92rem;
}

/* ── A pop-up saying no ────────────────────────────────────────────────────
   Clicking the backdrop no longer closes a dialog. Ignoring the click in
   silence reads as a frozen page, so the box gives a short nudge instead —
   enough to show the click landed, not so much that it feels like a telling
   off. Honours a reduced-motion preference by doing nothing at all, which is
   fine: the dialog staying put is the message.                             */
@keyframes modal-nudge {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-5px); }
  40%      { transform: translateX(5px); }
  60%      { transform: translateX(-3px); }
  80%      { transform: translateX(3px); }
}
.modal-nudge { animation: modal-nudge .3s ease; }
@media (prefers-reduced-motion: reduce) { .modal-nudge { animation: none; } }

/* ── The rent schedule grid ────────────────────────────────────────────────
   Thirteen money columns will not fit a laptop, let alone a phone, so the
   months scroll sideways and the year column stays pinned — scrolling to
   August is useless if you can no longer see which year you are in.       */
.rent-wrap { overflow-x:auto; }
.rent-grid { min-width:980px; border-collapse:separate; border-spacing:0; }
.rent-grid th, .rent-grid td { white-space:nowrap; }
.rent-grid .rent-yr {
  position:sticky; left:0; z-index:2; text-align:left; font-weight:700;
  background:var(--card); box-shadow:1px 0 0 var(--line);
}
.rent-grid thead .rent-yr { background:var(--green-pale); }
.rent-grid tfoot .rent-yr { background:var(--bg); }
/* A month with nothing due is not a zero — the contracts had not started, or
   had already ended. Greyed so the shape of the commitment is readable down
   the column rather than hidden among real figures. */
.rent-grid .rent-nil { color:var(--line); }
.rent-grid .rent-tot { font-weight:700; background:var(--bg); }
/* The month you are in, which is the first thing anybody looks for. */
.rent-grid .rent-now {
  font-weight:700; color:var(--green);
  box-shadow:inset 0 0 0 2px var(--green-mid); border-radius:6px;
}
.rent-grid tfoot td, .rent-grid tfoot th {
  border-top:2px solid var(--line); font-weight:700; background:var(--bg);
}

/* ── A rent figure you can open ────────────────────────────────────────────
   Every cell with money in it is clickable. Underlined on hover rather than
   coloured, because colour in this grid already means something — green is
   the month you are in — and a second meaning would muddy the first.      */
.rent-grid .rent-cell { cursor:pointer; }
.rent-grid .rent-cell:hover { background:var(--green-pale); text-decoration:underline; }
.rent-grid .rent-cell:focus-visible {
  outline:2px solid var(--green); outline-offset:-2px; border-radius:6px;
}
/* The year column keeps its own background while hovering a row's total. */
.rent-grid .rent-tot.rent-cell:hover { background:var(--green-pale); }

/* The section a typed figure belongs to, inside the KPI editor. Needed once
   the yield ratios became typeable: "AB" and "C" mean nothing in a flat list
   that also holds "Average Selling Price". */
.kpi-edit-head td {
  background: var(--green-pale); font-weight: 700; color: var(--green);
  font-size: .78rem; letter-spacing: .3px; text-transform: uppercase;
  padding-top: 10px; padding-bottom: 6px;
}
