/* =============================================================================
   COMPONENTS. Built only from tokens.css — no literal colours, sizes or
   durations below this line. Loaded after tokens.css and base.css.
   ========================================================================== */


/* ---------- KPI strip ---------- */

/* Flex, so a row that does not divide evenly stretches to the edge rather
   than leaving a grey cell where the gap colour shows through. */
.kpis {
  display: flex; flex-wrap: wrap;
  gap: 1px; background: var(--border);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  overflow: hidden; margin-bottom: var(--space-6);
}
.kpi { flex: 1 1 150px; background: var(--surface); padding: var(--space-4) var(--space-5); }
.kpi.hi { background: var(--accent-tint); }
.kpi .v { font-size: var(--text-xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); }
.kpi .v small { font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--ink-faint); }
.kpi .v.over { color: var(--caution); }
.kpi .l { font-size: var(--text-xs); color: var(--ink-muted); margin-top: var(--space-1); }

/* ---------- controls ---------- */

.controls { display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap; margin-bottom: var(--space-4); }
.segmented { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); color: var(--ink-muted); }
.segmented > span { letter-spacing: var(--tracking-label); text-transform: uppercase; font-size: var(--text-2xs); }
.segmented a {
  padding: var(--space-2) var(--space-4); border: 1px solid var(--border);
  border-radius: var(--radius-pill); text-decoration: none;
  color: var(--ink-muted); background: var(--surface);
  transition: border-color var(--motion) var(--ease), color var(--motion) var(--ease);
}
.segmented a:hover { border-color: var(--accent); color: var(--accent); }
.segmented a[aria-current="true"] {
  background: var(--selected); border-color: var(--selected);
  color: var(--selected-ink); font-weight: var(--weight-semi);
}

/* ---------- the board ---------- */

.board { border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; background: var(--surface); }
.b-head, .brow {
  display: grid;
  grid-template-columns: minmax(200px, 2fr) 88px 140px minmax(148px, 1.35fr) minmax(160px, 1.5fr) 120px;
  gap: var(--space-4); align-items: center;
  padding: var(--space-3) var(--space-5);
}
.b-head {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--ink-faint); background: var(--ground); border-bottom: 1px solid var(--border);
}
.b-head span:nth-child(2), .brow .qty { text-align: right; }
.b-head span:last-child, .brow .spread { text-align: right; }
.brow {
  min-height: var(--board-row-min);
  border-bottom: 1px solid var(--border-soft);
  text-decoration: none; color: inherit; font-size: var(--text-md);
  transition: background var(--motion-fast) var(--ease);
}
.brow:hover { background: var(--accent-tint); }
.brow:active { background: var(--accent-tint-strong); }
.brow[aria-current="true"] { background: var(--accent-tint-strong); }
.brow .farm b { font-weight: var(--weight-semi); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.brow .farm .mono { font-size: var(--text-xs); color: var(--ink-faint); }
.brow .qty small { display: block; color: var(--ink-faint); font-size: var(--text-xs); }

.vcell { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); min-width: 0; }
/* The variety group and the variety: a category, said as words — the group in
   the ink, the variety muted beside it. Not chips: they mark nothing to act on. */
.vgchip { max-width: 100%; font-size: var(--text-xs); font-weight: var(--weight-bold); color: var(--ink);
          white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vgchip.unsure { color: var(--ink-muted); }
.vgchip.unknown { color: var(--ink-faint); font-weight: var(--weight-semi); }
.vchip { font-size: var(--text-xs); color: var(--ink-faint); }

.belief { display: flex; align-items: center; gap: var(--space-3); }
.bgauge { flex: 1; height: 6px; background: var(--border-soft); border-radius: var(--radius-pill); overflow: hidden; min-width: 40px; }
.bgauge > i { display: block; height: 6px; }
.belief .pct { font-size: var(--text-sm); font-weight: var(--weight-semi); }
.belief .conf { font-size: var(--text-2xs); color: var(--ink-faint); }

.lanechip { display: flex; align-items: baseline; gap: var(--space-3); font-size: var(--text-sm); }
.lanechip .to { display: block; font-size: var(--text-2xs); color: var(--ink-faint); }
.spread { font-size: var(--text-sm); font-weight: var(--weight-semi); color: var(--positive); }
.spread.neg { color: var(--ink-faint); font-weight: var(--weight-normal); }

.splitchips { display: inline-flex; gap: var(--space-1); flex-wrap: wrap; justify-content: flex-end; }
.placed-chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: var(--text-xs); font-weight: var(--weight-semi);
  color: var(--positive); background: var(--positive-tint);
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
}
.splitn { display: block; margin-top: var(--space-1); color: var(--ink-faint); font-size: var(--text-2xs); }

.ghead {
  display: flex; align-items: baseline; gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  background: var(--ground);
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
  font-size: var(--text-sm);
}
.ghead .gt { font-weight: var(--weight-bold); letter-spacing: .04em; text-transform: uppercase; font-size: var(--text-xs); }
.ghead .gcode { font-size: var(--text-2xs); color: var(--ink-faint); font-weight: var(--weight-semi); letter-spacing: 0; }
.ghead .gnote {
  text-transform: none; letter-spacing: 0; font-weight: var(--weight-medium);
  font-size: var(--text-2xs); color: var(--ink-faint); font-style: italic;
}
.ghead .gs { color: var(--ink-muted); margin-left: auto; font-size: var(--text-xs); }
.gfill.ok { color: var(--positive); }
.gfill.warn { color: var(--caution); }
.gfill.over { color: var(--critical); font-weight: var(--weight-bold); }

.empty { padding: var(--space-8) var(--space-5); text-align: center; color: var(--ink-muted); }

/* ---------- the drawer ---------- */

.scrim { position: fixed; inset: 0; background: var(--scrim); z-index: var(--layer-scrim); }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(560px, 94vw);
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-panel);
  overflow-y: auto; z-index: var(--layer-panel);
}
.d-in { padding: var(--space-5) var(--space-6) var(--space-8); }
.d-head { display: flex; align-items: flex-start; gap: var(--space-4); }
.d-head h2 { font-size: var(--text-xl); }
.d-meta { font-size: var(--text-xs); color: var(--ink-faint); margin-top: var(--space-1); }
.d-close {
  margin-left: auto; font-size: var(--text-lg); line-height: 1; color: var(--ink-muted);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3); text-decoration: none;
}
.d-close { display: inline-flex; align-items: center; transition: background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease); }
.d-close:hover { background: var(--surface-sunk); color: var(--ink); border-color: var(--border-strong); }

.d-sec { margin-top: var(--space-6); }
.d-sec > h3 {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint);
  margin-bottom: var(--space-4);
}

.hero { display: flex; align-items: baseline; gap: var(--space-5); }
.hero .big { font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); line-height: 1; }
.hero .sub { font-size: var(--text-sm); color: var(--ink-muted); }

.basis { display: grid; gap: var(--space-1); margin-top: var(--space-4); }
.basis-row { display: grid; grid-template-columns: 140px 1fr 42px; gap: var(--space-3); align-items: center; font-size: var(--text-sm); }
.basis-row .bar { height: 6px; background: var(--border-soft); border-radius: var(--radius-pill); overflow: hidden; }
.basis-row .bar > i { display: block; height: 6px; background: var(--accent); }

.belief-form { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-4); }
.belief-form input[type="range"] { flex: 1; accent-color: var(--accent); min-width: 0; }
.belief-form output { font-size: var(--text-lg); font-weight: var(--weight-bold); min-width: 3.2em; text-align: right; }
.belief-q { font-size: var(--text-sm); color: var(--ink-muted); }

/* ---------- allocation summary ---------- */

.alloc {
  background: var(--ground); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: var(--space-4);
}
.alloc-line { display: flex; align-items: baseline; gap: var(--space-3); font-size: var(--text-sm); flex-wrap: wrap; }
.alloc-sub { font-size: var(--text-xs); color: var(--ink-faint); }
.alloc-state { margin-left: auto; font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--ink-muted); }
.alloc-state.done { color: var(--positive); }

/* The track: a fixed-height row of explicitly-sized segments. Heights are set
   on the segments themselves, not inherited from the container, so a segment
   can never stretch beyond the track. */
.alloc-track {
  display: flex; gap: 2px;
  height: var(--track-height); min-height: var(--track-height); max-height: var(--track-height);
  margin-top: var(--space-3); overflow: hidden;
  border-radius: var(--radius-sm); background: var(--border-soft);
}
.alloc-seg {
  display: block; height: var(--track-height); max-height: var(--track-height);
  min-width: 2px; background: var(--accent); border-radius: var(--radius-sm);
}
.alloc-seg--1 { background: var(--series-1); }
.alloc-seg--2 { background: var(--series-2); }
.alloc-seg--3 { background: var(--series-3); }
.alloc-seg--free { background: transparent; }

.alloc-actions { display: flex; gap: var(--space-3); margin-top: var(--space-4); align-items: center; }

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

/* Five states, every button: rest, hover, pressed, disabled, and busy. Busy is
   Turbo's own — it marks a submitting form aria-busy and disables the button
   that sent it, so the spinner below is CSS and a double submit is impossible.
   Padding is 2:1, horizontal to vertical. */
.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  font-size: var(--text-xs); font-weight: var(--weight-semi); line-height: 1.3;
  padding: 6px var(--space-4); border-radius: var(--radius-md);
  border: 1px solid transparent; text-decoration: none; white-space: nowrap;
  transition: background var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease),
              color var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease);
}
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; }
.btn .icon { --icon-size: 14px; }
.btn--primary { background: var(--accent); color: var(--ink-on-accent); }
.btn--primary:hover:not(:disabled) { background: var(--accent-hover); }
/* Quiet is the secondary action: outline, and a NEUTRAL hover. It used to turn
   red on hover, which told you "Edit" and "close" were dangerous. Red is kept
   for the things that are: see --danger. */
.btn--quiet { border-color: var(--border); color: var(--ink-muted); background: var(--surface); }
.btn--quiet:hover:not(:disabled) { border-color: var(--border-strong); color: var(--ink); background: var(--surface-sunk); }
.btn--danger:hover:not(:disabled) { border-color: var(--critical); color: var(--critical); background: var(--critical-tint); }
.btn--link { color: var(--accent); text-decoration: underline; padding: 0; border: 0; }
.btn--link:hover { color: var(--accent-hover); }

form[aria-busy="true"] .btn:disabled,
form[aria-busy="true"] .step:disabled,
form[aria-busy="true"] .chip:disabled { opacity: 1; color: transparent !important; }
form[aria-busy="true"] .btn:disabled::after,
form[aria-busy="true"] .step:disabled::after,
form[aria-busy="true"] .chip:disabled::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid var(--ink-faint); border-right-color: transparent;
  animation: spin .6s linear infinite;
}
form[aria-busy="true"] .btn--primary:disabled::after { border-color: var(--ink-on-accent); border-right-color: transparent; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  form[aria-busy="true"] .btn:disabled::after, form[aria-busy="true"] .step:disabled::after,
  form[aria-busy="true"] .chip:disabled::after { animation-duration: 1.6s; }
}

/* ---------- lanes ----------
   A list, not thirteen cards. One line each: market, what the next reefer is
   worth, and the two buttons. The stats live in the comparison table below.   */

.lanes {
  list-style: none; margin: var(--space-4) 0 0; padding: 0;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  overflow: hidden; background: var(--surface);
}
.lane { border-bottom: 1px solid var(--border-soft); }
.lane:last-child { border-bottom: 0; }

.lane-row {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto auto auto;
  align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  min-height: 40px;
}
.lane-name { font-weight: var(--weight-semi); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lane-marks { display: flex; gap: var(--space-2); align-items: center; }
.lane-rate { font-size: var(--text-md); font-weight: var(--weight-bold); white-space: nowrap; }
.lane-rate em { font-style: normal; font-size: var(--text-2xs); font-weight: var(--weight-normal); color: var(--ink-faint); }

/* the desk's pick — the one warm note on the screen */
.lane--suggested { background: var(--suggest-tint); }
.lane--cold .lane-name, .lane--cold .lane-rate { color: var(--ink-faint); }

.lane-step { display: flex; align-items: center; gap: var(--space-2); }
.step {
  position: relative;
  width: var(--control-size); height: var(--control-size); flex: none;
  font-size: var(--text-lg); font-weight: var(--weight-bold); line-height: 1;
  border: 1px solid var(--accent); color: var(--accent);
  border-radius: var(--radius-md); display: grid; place-items: center;
  transition: background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease);
}
.step:hover:not(:disabled) { background: var(--accent); color: var(--ink-on-accent); }
.step:active:not(:disabled) { transform: scale(.94); }
.step:disabled { border-color: var(--border); color: var(--border-strong); }
.step-n {
  min-width: 1.6em; text-align: center;
  font-size: var(--text-md); font-weight: var(--weight-bold);
}

.lane-extra { padding: 0 var(--space-4) var(--space-2); margin-left: calc(26px + var(--space-3)); }
.lane-note {
  padding: 0 var(--space-4) var(--space-3);
  margin-left: calc(26px + var(--space-3));
  font-size: var(--text-sm); color: var(--ink-muted); line-height: var(--leading-normal);
}

/* the reason prompt — asks, never blocks */
.reason {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  background: var(--caution-tint); border-top: 1px solid var(--suggest-border);
  font-size: var(--text-2xs); color: var(--ink-muted);
}
.reason strong { color: var(--ink); font-weight: var(--weight-semi); }
.chip {
  position: relative;
  font-size: var(--text-2xs); font-weight: var(--weight-semi);
  transition: background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease);
  border: 1px solid var(--accent); color: var(--accent);
  border-radius: var(--radius-pill); padding: var(--space-1) var(--space-3);
  background: var(--surface);
}
.chip:hover { background: var(--accent); color: var(--ink-on-accent); }
.chip--skip { border-color: var(--border-strong); color: var(--ink-muted); }
.chip--skip:hover { background: var(--ink-muted); color: var(--ink-on-dark); border-color: var(--ink-muted); }

/* ---------- compare every lane ---------- */

.lane-table { margin-top: var(--space-4); }
.lane-table > summary {
  font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--accent);
  padding: var(--space-2) 0;
}
.lane-table table { width: 100%; border-collapse: collapse; font-size: var(--text-xs); }
.lane-table th, .lane-table td {
  padding: var(--space-2) var(--space-3); text-align: left;
  border-bottom: 1px solid var(--border-soft); white-space: nowrap;
}
.lane-table thead th {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint); font-weight: var(--weight-semi);
}
.lane-table tbody th { font-weight: var(--weight-semi); }
.lane-table .num { text-align: right; }
.lane-table tr.is-cold { color: var(--ink-faint); }
.lane-table td.over { color: var(--critical); font-weight: var(--weight-bold); }
.lane-table td.warn { color: var(--caution); }

/* ---------- badges ---------- */

.badge {
  font-size: var(--text-2xs); font-weight: var(--weight-semi);
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
  background: var(--surface-sunk); color: var(--ink-muted); white-space: nowrap;
}
.badge--warn { background: var(--caution-tint); color: var(--caution); }
.badge--bad { background: var(--critical-tint); color: var(--critical); }
.badge--suggest { background: var(--suggest-strong); color: var(--suggest); }

/* ---------- the week's decisions ---------- */

.ledger {
  margin-top: var(--space-6); border: 1px solid var(--border);
  border-radius: var(--radius-lg); background: var(--surface); overflow: hidden;
}
.ledger > h3 {
  padding: var(--space-4) var(--space-5); font-size: var(--text-xs);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--ink-faint); border-bottom: 1px solid var(--border);
}
.dlog-row {
  display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap;
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border-soft); font-size: var(--text-sm);
}
.dlog-row .rm { margin-left: auto; color: var(--ink-faint); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0 var(--space-3); }
.dlog-row .rm { position: relative; min-height: 22px; display: inline-flex; align-items: center; transition: background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease); }
.dlog-row .rm:hover:not(:disabled) { border-color: var(--critical); color: var(--critical); background: var(--critical-tint); }
.fill-label { padding: var(--space-4) var(--space-5) var(--space-1); font-size: var(--text-xs); color: var(--ink-faint); }
.fill-row {
  display: grid; grid-template-columns: 160px 1fr 200px; gap: var(--space-4);
  align-items: center; padding: var(--space-1) var(--space-5); font-size: var(--text-sm);
}
.fill-row .bar { height: 8px; background: var(--border-soft); border-radius: var(--radius-pill); overflow: hidden; }
.fill-row .bar > i { display: block; height: 8px; }
.fill-row .st { text-align: right; color: var(--ink-muted); }
.fill-row .st.over { color: var(--critical); font-weight: var(--weight-bold); }

/* ---------- weeks index ---------- */

.wlist { border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; background: var(--surface); }
.wrow {
  display: grid; grid-template-columns: 120px 1fr 160px; gap: var(--space-4);
  padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border-soft);
  text-decoration: none; color: inherit;
}
.wrow:hover { background: var(--accent-tint); }

/* ---------- auth ---------- */

.auth { max-width: 380px; margin: var(--space-8) auto; }
.auth h1 { font-size: var(--text-xl); margin-bottom: var(--space-1); }
.auth p.lede { color: var(--ink-muted); font-size: var(--text-sm); margin-bottom: var(--space-6); }
.auth .field { display: grid; gap: var(--space-2); margin-bottom: var(--space-4); }
.auth label { font-size: var(--text-xs); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-faint); }
.auth input[type="email"], .auth input[type="password"], .auth input[type="text"] {
  padding: var(--space-3) var(--space-4); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface);
}
.auth .actions { display: flex; align-items: center; gap: var(--space-5); margin-top: var(--space-5); }
.auth .btn { font-size: var(--text-sm); padding: var(--space-3) var(--space-6); }
.auth .muted-links { margin-top: var(--space-5); font-size: var(--text-sm); }
.auth .error {
  background: var(--critical-tint); color: var(--critical);
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  font-size: var(--text-sm); margin-bottom: var(--space-5);
}

/* The board's six columns need about 950px; below that a row stacks. */
@media (max-width: 1000px) {
  .b-head { display: none; }
  .brow { grid-template-columns: 1fr 1fr; gap: var(--space-1) var(--space-4); }
  .brow .farm { grid-column: 1 / -1; }
  .brow .qty, .brow .spread { text-align: left; }
  .fill-row { grid-template-columns: 1fr; }
}

/* ---------- how the week panned out ---------- */

.score {
  margin-top: var(--space-6); border: 1px solid var(--border);
  border-radius: var(--radius-lg); background: var(--surface); overflow: hidden;
}
.score-head {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border);
}
.score-head h3 { font-size: var(--text-md); }
.score-head .btn { margin-left: auto; }
.score-empty { padding: var(--space-6) var(--space-5); color: var(--ink-muted); font-size: var(--text-sm); }

.score-cells {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px; background: var(--border);
}
.score-cells .cell { background: var(--surface); padding: var(--space-4) var(--space-5); }
.score-cells .n { font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); line-height: 1; }
.score-cells .l { font-size: var(--text-xs); color: var(--ink-muted); margin-top: var(--space-2); }

.score-verdict { padding: var(--space-5); font-size: var(--text-md); line-height: var(--leading-normal); }
.score-note {
  padding: 0 var(--space-5) var(--space-4); font-size: var(--text-sm);
  color: var(--ink-muted); line-height: var(--leading-normal);
}
.score-note--quiet { font-size: var(--text-xs); color: var(--ink-faint); padding-top: var(--space-3); border-top: 1px solid var(--border-soft); }

.surprises { padding: var(--space-3) var(--space-5) var(--space-4); }
.surprises h4 {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: var(--space-3);
}
.surprise {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: var(--space-4);
  align-items: baseline; padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border-soft);
  text-decoration: none; color: inherit; font-size: var(--text-sm);
}
.surprise:last-child { border-bottom: 0; }
.surprise:hover { color: var(--accent); }
.s-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.s-move { color: var(--ink-muted); font-size: var(--text-xs); }
.s-gap { font-weight: var(--weight-bold); }
.s-gap.good { color: var(--positive); }
.s-gap.bad { color: var(--critical); }

.ledger-actions {
  display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap;
  padding: var(--space-4) var(--space-5); border-top: 1px solid var(--border-soft);
}
.ledger-hint { font-size: var(--text-xs); color: var(--ink-faint); }

/* ---------- the answer sheet, in the drawer ---------- */

.reveal { margin-top: var(--space-6); border-top: 1px solid var(--border); padding-top: var(--space-4); }
.reveal > summary {
  font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--accent);
  padding: var(--space-2) 0;
}
.reveal-body { padding-top: var(--space-2); }
.reveal-verdict { font-size: var(--text-sm); line-height: var(--leading-normal); }
.reveal-note { font-size: var(--text-xs); color: var(--ink-muted); margin-top: var(--space-3); line-height: var(--leading-normal); }
.reveal-table { width: 100%; border-collapse: collapse; font-size: var(--text-xs); margin-top: var(--space-4); }
.reveal-table th, .reveal-table td {
  padding: var(--space-2) var(--space-3); text-align: left;
  border-bottom: 1px solid var(--border-soft); white-space: nowrap;
}
.reveal-table thead th {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint); font-weight: var(--weight-semi);
}
.reveal-table .num { text-align: right; }

/* =============================================================================
   MULTI-COMMODITY — added when the desk stopped being grapes-only.

   Three jobs here, in order of how much they matter:
     1. Say WHICH commodity you're standing at, always, in the chrome.
     2. Say what this board CANNOT tell you, without shouting it every time.
     3. Give the eye something to hold onto — icons, and a paid-share bar that
        makes "how settled is this?" readable at a glance rather than in prose.
   ========================================================================== */

/* ---------- icons ----------
   Inlined SVG so it inherits currentColor and sits on the text baseline like a
   letter. One size variable, overridable per call; stroke thinned slightly from
   Lucide's 2px because everything here renders at 13–16px, where 2px reads
   heavy against Archivo.                                                      */

.icon {
  display: inline-flex; flex: none; vertical-align: -.14em;
  width: var(--icon-size, 1.05em); height: var(--icon-size, 1.05em);
  color: currentColor;
}
.icon > svg { width: 100%; height: 100%; stroke-width: 1.75; }

/* ---------- feedback ----------
   Every action on this desk says what it did, so this region is morphed on
   every placement rather than only surviving a redirect. Drawn as toasts: see
   THE INVISIBLE UI at the end of this file.                                   */

.flashes:empty { display: none; }

/* ---------- page heads ---------- */

.page-head { margin-bottom: var(--space-6); max-width: 62ch; }
.page-head h1 { display: flex; align-items: center; gap: var(--space-3); font-size: var(--text-xl); }
.page-head h1 .icon { --icon-size: 20px; color: var(--accent); }
.page-head p { margin-top: var(--space-3); color: var(--ink-muted); font-size: var(--text-sm); line-height: var(--leading-normal); }
.page-foot { margin-top: var(--space-6); font-size: var(--text-xs); color: var(--ink-faint); line-height: var(--leading-normal); }
.linky { color: var(--accent); font-weight: var(--weight-semi); }

/* ---------- the commodity picker ---------- */

.cgroup { margin-bottom: var(--space-7); }
.cgroup > h2 {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint);
  margin-bottom: var(--space-3);
}
.cgroup > h2 .icon { --icon-size: 15px; }
.cg-n { margin-left: auto; font-weight: var(--weight-normal); }

.clist { border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; background: var(--surface); }
.crow {
  display: grid; align-items: center; gap: var(--space-4);
  grid-template-columns: 24px minmax(0, 1.4fr) 78px 92px 108px 132px 18px;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-soft);
  text-decoration: none; color: inherit;
}
.crow:hover { background: var(--accent-tint); }
.crow--thin { background: var(--critical-tint); }
.crow--thin:hover { background: var(--critical-strong-tint); }
.c-icon .icon { --icon-size: 20px; color: var(--accent); }
.c-name b { font-weight: var(--weight-semi); }
.c-code { font-size: var(--text-2xs); color: var(--ink-faint); margin-left: var(--space-2); }
.c-unsure { display: block; font-size: var(--text-2xs); color: var(--caution); }
.c-vol, .c-weeks, .c-reefer { font-size: var(--text-sm); font-weight: var(--weight-semi); text-align: right; }
.c-vol small, .c-weeks small, .c-reefer small { display: block; font-size: var(--text-2xs); font-weight: var(--weight-normal); color: var(--ink-faint); }
.c-span { display: block; font-size: var(--text-2xs); font-weight: var(--weight-normal); color: var(--ink-faint); }
.c-est { font-size: var(--text-2xs); color: var(--caution); font-weight: var(--weight-semi); }
.c-paid { display: grid; gap: var(--space-2); }
.paidbar { display: block; height: 6px; background: var(--border-soft); border-radius: var(--radius-pill); overflow: hidden; }
.paidbar > i { display: block; height: 6px; }
.paidn { font-size: var(--text-2xs); color: var(--ink-faint); }
.c-go .icon { --icon-size: 16px; color: var(--border-strong); }
.crow:hover .c-go .icon { color: var(--accent); }

.ccaveats {
  display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5);
  padding: 0 var(--space-5) var(--space-4);
  margin-top: calc(-1 * var(--space-2));
  border-bottom: 1px solid var(--border-soft);
}
.ccav { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-2xs); color: var(--caution); }

/* ---------- what the desk can't tell you ----------
   Collapsed by default. The summary always names the worst of it, so the
   disclosure invites you to read the detail rather than being the only hint
   that any exists. It opens by itself only when the caveat changes what the
   board MEANS rather than qualifying it.                                      */

.notes {
  margin-bottom: var(--space-4);
  border: 1px solid var(--suggest-border); border-radius: var(--radius-md);
  background: var(--caution-tint);
}
.notes--serious { border-color: var(--critical); background: var(--critical-tint); }
.notes > summary {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm); cursor: pointer; list-style: none;
  color: var(--caution);
}
.notes--serious > summary { color: var(--critical); }
.notes > summary::-webkit-details-marker { display: none; }
.notes > summary .icon { --icon-size: 15px; }
.notes-lead { font-weight: var(--weight-semi); }
.notes-more {
  margin-left: auto; font-size: var(--text-2xs); color: var(--ink-faint);
  letter-spacing: var(--tracking-label); text-transform: uppercase; white-space: nowrap;
}
.notes[open] .notes-more { visibility: hidden; }
.notes-list {
  margin: 0; padding: 0 var(--space-5) var(--space-4) calc(var(--space-5) + var(--space-5));
  font-size: var(--text-sm); color: var(--ink-muted); line-height: var(--leading-normal);
}
.notes-list li { margin-bottom: var(--space-2); }
.notes-list li:last-child { margin-bottom: 0; }

/* ---------- commodity on the board ---------- */

.kpi--commodity { background: var(--surface-sunk); }
.kpi--commodity .v {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-md); letter-spacing: 0;
}
.kpi--commodity .v .icon { --icon-size: 18px; color: var(--accent); }

.d-cm { display: block; margin-top: var(--space-1); color: var(--ink-muted); }
.d-cm .icon { --icon-size: 12px; }

/* a lane with real volume and no realized price: shown, never ranked as zero */
.lane--unpriced { background: var(--surface-sunk); }
.lane--unpriced .lane-name { color: var(--ink-muted); }
.lane-nil { color: var(--ink-faint); font-weight: var(--weight-normal); }
.lane-via { display: block; font-size: var(--text-2xs); font-weight: var(--weight-normal); color: var(--ink-faint); }
.lane-badges {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  padding: 0 var(--space-4) var(--space-3);
  margin-left: calc(26px + var(--space-3));
}
.badge .icon { --icon-size: 11px; vertical-align: -.1em; }
.unpriced-tag {
  font-size: var(--text-2xs); font-weight: var(--weight-semi);
  color: var(--critical); margin-left: var(--space-2);
}
.lane-table tr.is-unpriced { color: var(--ink-faint); background: var(--surface-sunk); }
.lt-foot { padding: var(--space-3) var(--space-3) 0; font-size: var(--text-2xs); color: var(--ink-faint); line-height: var(--leading-normal); }

.wrow { align-items: center; grid-template-columns: 120px 1fr minmax(200px, auto) 18px; }
.wseason {
  padding: var(--space-3) var(--space-4) var(--space-2); background: var(--surface-sunk);
  border-bottom: 1px solid var(--border); font-size: var(--text-2xs);
  letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-faint);
  position: sticky; top: 0; z-index: 1;
}
/* A week with nothing in it is ordinary, not broken: quiet, still clickable. */
.wrow--quiet { color: var(--ink-faint); }
.wrow--quiet strong { font-weight: var(--weight-normal); }
.wrow--now { background: var(--accent-tint); }
.w-now { color: var(--accent); margin-left: var(--space-2); font-size: var(--text-2xs);
         text-transform: uppercase; letter-spacing: var(--tracking-label); }
.w-holds { display: flex; gap: var(--space-3); justify-content: flex-end; align-items: baseline;
           flex-wrap: wrap; font-size: var(--text-xs); }
.w-packed { font-weight: var(--weight-semi); }
.w-booked { color: var(--accent); font-weight: var(--weight-semi); }
.w-go .icon { --icon-size: 15px; color: var(--border-strong); }
.wrow:hover .w-go .icon { color: var(--accent); }

.step .icon { --icon-size: 14px; }
.d-close .icon { --icon-size: 15px; }
.dlog-row .rm .icon { --icon-size: 13px; }
.ledger > h3 .icon, .score-head h3 .icon { --icon-size: 14px; vertical-align: -.15em; }

@media (max-width: 1080px) {
  .crow { grid-template-columns: 22px minmax(0, 1fr) 74px 108px 18px; }
  .c-weeks, .c-reefer { display: none; }
}

/* ---------- lanes with no realized price ----------
   Collapsed behind one explanation instead of repeating it per row. The reason
   is a property of the commodity, not of the lane, so it is stated once above
   the group; each row then carries only what is specific to it — how much has
   already gone there.                                                        */

.unpriced { margin-top: var(--space-3); }
.unpriced > summary {
  display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4); cursor: pointer; list-style: none;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-md);
  font-size: var(--text-sm); color: var(--ink-muted); background: var(--surface-sunk);
}
.unpriced > summary::-webkit-details-marker { display: none; }
.unpriced > summary:hover { border-color: var(--accent); }
.unpriced > summary .icon { --icon-size: 14px; }
.up-hint { font-size: var(--text-xs); color: var(--ink-faint); }
.unpriced[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.lanes--unpriced { margin-top: 0; border-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; }
.lane-sent { font-size: var(--text-2xs); color: var(--ink-faint); white-space: nowrap; }

/* ---------- the book ahead ----------
   The forward half of the decision: what is forecast for the week being locked
   and for the weeks after it. One row tall, never collapsed — it is context for
   the decision rather than a footnote to it.                                  */

.book {
  margin-top: var(--space-6); padding: var(--space-4) var(--space-5);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.book-head { display: flex; align-items: baseline; gap: var(--space-5); flex-wrap: wrap; }
.book-head h3 {
  font-size: var(--text-xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint); white-space: nowrap;
}
.book-head h3 .icon { --icon-size: 13px; vertical-align: -.15em; }
.book-now { font-size: var(--text-sm); color: var(--ink-muted); }
.book-gap { font-weight: var(--weight-semi); }
.book-gap.over { color: var(--caution); }
.book-gap.under { color: var(--accent); }
.book-note { font-size: var(--text-xs); color: var(--ink-faint); font-style: italic; }

.book-bars {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: var(--space-2); align-items: end;
  list-style: none; margin: var(--space-4) 0 0; padding: 0;
  height: 92px;
}
/* Four rows, always — the demand figure renders empty rather than absent, or a
   column without demand drops a row and its week label climbs out of line. */
.book-col { height: 100%; }
/* Every column is its week: one link, six rows, always all six — a cell that
   renders empty rather than absent keeps the week labels on one line. */
.book-link {
  display: grid; grid-template-rows: 1fr auto auto auto auto auto; gap: var(--space-1); height: 100%;
  padding: var(--space-1); border-radius: var(--radius-md); color: inherit; text-decoration: none;
  transition: background var(--motion-fast) var(--ease);
}
.book-link:hover { background: var(--surface-sunk); }
.book-link:hover .book-wk { color: var(--accent); }
.book-col.is-here .book-link { background: var(--accent-tint); }
.book-col .wstate { justify-self: center; }
.book-col.is-done .book-bar > i { background: var(--border-strong); }
.book-dec { font-size: var(--text-2xs); color: var(--ink-muted); text-align: center; min-height: 1.2em; }
.bn--stock { color: inherit; text-decoration: none; }
.bn--stock:hover b { color: var(--accent); }
.book-bar {
  position: relative;           /* the demand marker is positioned against this */
  display: flex; align-items: flex-end; height: 100%;
  background: var(--surface-sunk); border-radius: var(--radius-sm);
}
.book-bar > i { display: block; width: 100%; background: var(--accent-border); border-radius: var(--radius-sm); }
.book-col.is-here .book-bar > i { background: var(--accent); }
.book-col.is-here .book-wk { color: var(--accent); font-weight: var(--weight-bold); }
.book-qty { font-size: var(--text-2xs); color: var(--ink-muted); text-align: center; }
.book-wk { font-size: var(--text-2xs); color: var(--ink-faint); text-align: center; }
.book-foot { margin-top: var(--space-3); font-size: var(--text-xs); color: var(--ink-faint); }

.crow { grid-template-columns: 24px minmax(0, 1.3fr) 70px 84px 100px 118px 118px 18px; }
.c-fcast { display: grid; gap: var(--space-2); }
.c-nobook { color: var(--caution); }
@media (max-width: 1240px) {
  .crow { grid-template-columns: 22px minmax(0, 1fr) 70px 110px 110px 18px; }
  .c-weeks, .c-reefer { display: none; }
}
.book-now { display: flex; align-items: baseline; gap: var(--space-4); flex-wrap: wrap; }
.bn { white-space: nowrap; }
.bn em { font-style: normal; font-size: var(--text-xs); color: var(--ink-faint); }

/* ---------- the admin area ----------
   Deliberately plain. Nothing here is part of making a decision, so it should
   look like administration and not compete with the board.                    */


.atable { border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; background: var(--surface); }
.arow {
  display: grid; align-items: center; gap: var(--space-4);
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.2fr) 90px 150px 80px minmax(0, auto);
  padding: var(--space-3) var(--space-5); border-bottom: 1px solid var(--border-soft);
  font-size: var(--text-sm);
}
.atable--cmdty .arow {
  grid-template-columns: 24px minmax(0, 1.2fr) 60px 110px 80px 60px 60px 92px auto;
}
/* Accounts: the buttons differ by row (resend for the invited, Unlock, no
   delete on your own), so each row sizing its own `auto` column shifted every
   column before it. The rows share the table's tracks instead (subgrid), and
   the widest row's buttons set the column for all. */
.atable--users { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.2fr) 90px 150px 80px auto; }
.atable--users .arow { grid-column: 1 / -1; grid-template-columns: subgrid; }
.arow--head {
  background: var(--surface-sunk); color: var(--ink-faint);
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label); text-transform: uppercase;
}
.arow:last-child { border-bottom: 0; }
.arow.is-locked { background: var(--critical-tint); }
.arow.is-off { opacity: .55; }
.arow .num { text-align: right; }
.a-dim { color: var(--ink-faint); font-size: var(--text-xs); }
.a-dim small { display: block; }
.a-actions { display: flex; gap: var(--space-2); justify-content: flex-end; flex-wrap: wrap; }
.btn--tiny { font-size: var(--text-2xs); padding: var(--space-1) var(--space-3); }
.acav {
  display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5);
  padding: 0 var(--space-5) var(--space-3); border-bottom: 1px solid var(--border-soft);
}

.aform { max-width: 520px; }
.aform .field { display: grid; gap: var(--space-2); margin-bottom: var(--space-5); }
.aform .field--check { grid-template-columns: auto 1fr; align-items: center; gap: var(--space-3); }
.aform .field--check .hint { grid-column: 1 / -1; }
.aform label {
  font-size: var(--text-xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint);
}
.aform .field--check label { text-transform: none; letter-spacing: 0; font-size: var(--text-sm); color: var(--ink); }

/* Email settings: a followed dashboard's days and hour, one row each. A day
   is a toggle in a segmented strip; a row with none ticked is not followed,
   so its hour fades. */
.mail-when { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); padding: var(--space-2) 0; border-top: 1px solid var(--border-soft); }
.mail-when-name { flex: 1 1 160px; min-width: 0; font-size: var(--text-sm); color: var(--ink); }
.daypick { display: inline-flex; padding: 2px; gap: 2px; background: var(--surface-sunk); border: 1px solid var(--border); border-radius: var(--radius-md); }
.aform .daypick label { position: relative; display: inline-grid; place-items: center; width: 28px; height: 26px; border-radius: var(--radius-sm);
  font-size: var(--text-xs); letter-spacing: 0; text-transform: none; color: var(--ink-muted); cursor: pointer; }
.daypick input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.aform .daypick label:hover { color: var(--ink); }
.aform .daypick label:has(:checked) { background: var(--surface-raised); color: var(--ink); font-weight: var(--weight-semi); box-shadow: var(--shadow-card); }
.daypick label:has(:focus-visible) { outline: 2px solid var(--accent); outline-offset: 1px; }
.aform .field .mail-when select { padding: var(--space-1) var(--space-2); font-size: var(--text-sm); }
.aform .mail-when:not(:has(:checked)) select { color: var(--ink-faint); }
.aform input[type="text"], .aform input[type="email"] {
  padding: var(--space-3) var(--space-4); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface); font-size: var(--text-md);
}
.aform input:disabled { background: var(--surface-sunk); color: var(--ink-faint); }
.aform .hint { font-size: var(--text-xs); color: var(--ink-faint); line-height: var(--leading-normal); }
.aform .actions { display: flex; align-items: center; gap: var(--space-5); }
.aform input[type="submit"] { font-size: var(--text-md); }
.aform .error {
  background: var(--critical-tint); color: var(--critical);
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  font-size: var(--text-sm); margin-bottom: var(--space-5);
}

/* ---------- weather over the growing district ----------
   A month at a glance: one narrow column per day, the packing week picked out,
   and only the days that are actually extreme carrying a mark.              */

.wx {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 1px; list-style: none; margin: var(--space-4) 0 0; padding: 0;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  overflow: hidden; background: var(--border-soft);
}
.wx-day {
  display: grid; gap: 1px; padding: var(--space-2) 0;
  background: var(--surface); text-align: center;
}
/* No `.wx-day.is-week` background and no `.is-hot` / `.is-wet` here any more.
   Those three predate the bands, and the first was doing real damage: at two
   classes it outranked every single-class `.wx-day--*` tone below, so the seven
   days of the packing week — the only days anyone is actually deciding about —
   were painted a flat green and lost their heat colour entirely. A 38° day
   inside the week looked exactly like a 27° one. The week is marked by its
   underline now, which is what the rule further down always intended. */
.wx-d { font-size: var(--text-2xs); color: var(--ink-faint); }
.wx-t { font-size: var(--text-xs); font-weight: var(--weight-semi); }
.wx-r { font-size: var(--text-2xs); color: var(--ink-faint); }
.wx-mark { height: 13px; }
.wx-mark .icon { --icon-size: 11px; }
.wx-note { margin-top: var(--space-3); font-size: var(--text-sm); color: var(--ink-muted); line-height: var(--leading-normal); }
.wx-note em { font-style: normal; color: var(--ink-faint); }

/* The month, named only where it changes, so "29 30 31 1 2 3" says which 1. */
.wx-mo { font-style: normal; margin-left: .25em; opacity: .75; }

/* A day that has not happened yet. Hatched rather than tinted, so it reads as
   "not a record" without competing with the band colour underneath it. */
.wx-day.is-forecast {
  background-image: repeating-linear-gradient(
    -45deg, transparent 0 3px, var(--border-soft) 3px 4px);
}
.wx-day.is-forecast .wx-t { font-weight: var(--weight-normal); }

/* The finding rides on one line; "warm for here" is the half of the old
   district paragraph worth keeping above the fold. */
.wx-tag {
  display: inline-block; margin-left: .15em; padding: 0 var(--space-2);
  border-radius: var(--radius-sm); background: var(--surface-sunk);
  color: var(--ink-muted); font-size: var(--text-xs); white-space: nowrap;
}

/* …and the arithmetic behind it, the source, and the bands themselves fold in
   here. Same disclosure idiom as `.reveal` and `.stockbox`. */
/* The same disclosure the drawer already uses twice — `.reveal` and
   `.lane-table`. An uppercase label here read as a fourth SECTION HEADING
   rather than a control you can open, which is the one thing a toggle must not
   look like. */
.wx-how { margin-top: var(--space-2); }
.wx-how > summary {
  cursor: pointer; padding: var(--space-2) 0;
  font-size: var(--text-xs); color: var(--ink-faint);
}
.wx-how > summary:hover { color: var(--ink-muted); }
.wx-how-body {
  margin-top: var(--space-2); padding: var(--space-3) var(--space-4);
  background: var(--surface-sunk); border-radius: var(--radius-md);
  font-size: var(--text-2xs); color: var(--ink-faint); line-height: var(--leading-normal);
}
.wx-how-body p { margin: 0 0 var(--space-3); }
.wx-how-body p:last-child { margin-bottom: 0; }
.wx-bands { margin-bottom: var(--space-2) !important; }
.wx-key { list-style: none; margin: 0 0 var(--space-3); padding: 0; display: grid; gap: var(--space-1); }
.wx-key li { display: flex; align-items: center; gap: var(--space-2); }

/* The key has to be drawn in the STRIP's colours, not the admin screen's.
   `.bandswatch` was reused here first and showed a solid red disc for Extreme
   against a pale pink day — a legend that doesn't match what it explains is
   worse than none, so these mirror `.wx-day--*` exactly. */
.wx-sw {
  flex: none; width: 22px; height: 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface);
}
.wx-sw--caution  { background: var(--caution-tint); }
.wx-sw--warning  { background: var(--critical-tint); }
.wx-sw--critical { background: var(--critical-strong-tint); }
.wx-src { margin-top: var(--space-2); font-size: var(--text-2xs); color: var(--ink-faint); line-height: var(--leading-normal); }
.wx-none { display: flex; gap: var(--space-3); font-size: var(--text-sm); color: var(--ink-muted); line-height: var(--leading-normal); margin-top: var(--space-3); }
.wx-none .icon { --icon-size: 14px; flex: none; margin-top: .2em; }

/* ---------- elsewhere, and the two new pages ---------- */


.afilters {
  display: flex; align-items: flex-end; gap: var(--space-4); flex-wrap: wrap;
  margin-bottom: var(--space-5); padding: var(--space-4) var(--space-5);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.afilter { display: grid; gap: var(--space-2); }
.afilter label {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint);
}
.afilter select {
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface); font-size: var(--text-sm);
}
.afilters .btn { margin-left: auto; }

.atable--alloc .arow {
  grid-template-columns: 110px minmax(0, 1.3fr) 110px minmax(0, 1fr) 60px 70px 60px 74px 110px;
}
.atable--decisions .arow {
  grid-template-columns: 110px minmax(0, 1.3fr) 110px minmax(0, 1.2fr) 60px 70px minmax(0, 1fr);
}
.alloc-h { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; font-size: var(--text-lg);
           margin: var(--space-7) 0 var(--space-4); }
.alloc-h .icon { --icon-size: 17px; color: var(--accent); }
.alloc-h .a-dim { font-size: var(--text-xs); font-weight: var(--weight-normal); }
.atable--sail .arow {
  grid-template-columns: 130px minmax(0, 1.4fr) 100px 80px 70px 60px minmax(0, 1fr);
}
.arow small { display: block; font-size: var(--text-2xs); }

/* Packed stock is not a caveat — it wears the same shape and its own class, so
   "this board carries no caveats" stays a true statement. */
.stockbox {
  margin-bottom: var(--space-4);
  border: 1px solid var(--accent-border); border-radius: var(--radius-md);
  background: var(--accent-tint);
}
.stockbox > summary {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); cursor: pointer; list-style: none;
  font-size: var(--text-sm); color: var(--accent);
}
.stockbox > summary::-webkit-details-marker { display: none; }
.stockbox > summary .icon { --icon-size: 15px; }
.stock-lead { font-weight: var(--weight-semi); }
.stock-more {
  margin-left: auto; font-size: var(--text-2xs); color: var(--ink-faint);
  letter-spacing: var(--tracking-label); text-transform: uppercase; white-space: nowrap;
}
.stockbox[open] .stock-more { visibility: hidden; }
.stock { padding: 0 var(--space-5) var(--space-3); }
.stock-row {
  display: grid; gap: var(--space-4); align-items: baseline;
  grid-template-columns: 100px 80px minmax(0, 1fr) 70px 60px 120px;
  padding: var(--space-2) 0; border-bottom: 1px solid var(--border-soft);
  font-size: var(--text-sm);
}
.stock-row--head {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-faint);
}
.stock-row .num { text-align: right; }
.stock-row:last-child { border-bottom: 0; }

@media (max-width: 1080px) {
  .atable--alloc .arow, .atable--sail .arow, .atable--decisions .arow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stock-row { grid-template-columns: 1fr 1fr; }
}

/* ---------- weather bands ----------
   The strip shades by BAND now rather than flagging every notable day the same,
   so a 41° day stops looking like a 33° one. Tones come from the semantic layer
   and nowhere else — `neutral` and `caution` shade, `warning` and `critical`
   also earn a mark.                                                           */

.wx-day--neutral  { background: var(--surface); }
.wx-day--caution  { background: var(--caution-tint); }
.wx-day--warning  { background: var(--critical-tint); }
.wx-day--critical { background: var(--critical-strong-tint); }
.wx-day--warning .wx-mark  { color: var(--caution); }
.wx-day--critical .wx-mark { color: var(--critical); }
.wx-day--critical .wx-t    { font-weight: var(--weight-bold); color: var(--critical); }
/* The packing week still wins the background, so you can always see which days
   are the ones being locked. */
.wx-day.is-week { box-shadow: inset 0 -3px 0 var(--accent); }

.bandswatch {
  display: block; width: 14px; height: 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}
.bandswatch--neutral  { background: var(--surface-sunk); }
.bandswatch--caution  { background: var(--caution-tint); border-color: var(--suggest-border); }
.bandswatch--warning  { background: var(--critical-tint); border-color: var(--critical); }
.bandswatch--critical { background: var(--critical); border-color: var(--critical); }

.atable--bands .arow      { grid-template-columns: 20px minmax(0, 1fr) 80px 220px minmax(0, 1fr) 20px; }
.atable--bandedit .arow   { grid-template-columns: 20px minmax(0, 1.1fr) 110px minmax(0, 1fr) minmax(0, 1fr); }
.atable--bandedit input[type="text"],
.atable--bandedit input[type="number"],
.atable--bandedit select {
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface); font-size: var(--text-sm);
  width: 100%;
}
.atable--bandedit .arow.has-error { background: var(--critical-tint); }
.bandbound { display: flex; align-items: center; gap: var(--space-2); }
.bandbound input { max-width: 76px; }
.aform--bands { max-width: none; }
.aform--bands .actions { margin-top: var(--space-5); }

.bandactions {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  margin-top: var(--space-3);
}

/* ---------- the market-code dictionary ----------
   One screen, every code, saved in one go. The untold rows are tinted because
   they are the ones costing somebody clarity — their fruit reads as awaiting a
   decision, which is the safe answer and may be the wrong one.            */

/* `.aform` is 520px wide because every other admin form is one column. This one
   is a dictionary of twenty-two rows and seven columns, so it opts out. */
.aform--codes { max-width: none; }
.atable--codes .arow {
  grid-template-columns: 64px 76px minmax(0, 1.3fr) minmax(0, 1fr) 128px minmax(0, 0.9fr) 130px;
  gap: var(--space-3);
}
/* What the demand book already knows about a code — the customer and the sales
   region behind it. Most of the dictionary is confirming this rather than
   working it out, so it sits beside the field it answers. */
.mc-hint { font-size: var(--text-2xs); line-height: var(--leading-normal); }
.mc-hint b { color: var(--ink); }
.mc-dole { display: block; }
.atable--codes input[type="text"], .atable--codes select {
  width: 100%; font-size: var(--text-xs); padding: var(--space-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface);
}
.atable--codes .arow.is-untold { background: var(--caution-tint); }
.akey { margin-top: var(--space-6); font-size: var(--text-xs); color: var(--ink-muted); }
.akey h2 { font-size: var(--text-2xs); letter-spacing: var(--tracking-label);
           text-transform: uppercase; color: var(--ink-faint); margin-bottom: var(--space-2); }
.akey ul { margin: 0; padding-left: var(--space-5); display: grid; gap: var(--space-1); }

/* ---------- the Thursday lock clock ----------
   The desk exists to serve a deadline and never said when it was. Three facts
   in the chrome: the week it is now, the week being locked, and how long is
   left — with the board's own anchor saying whether you are looking at the live
   decision or replaying a finished one.                                     */

.lockclock {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-1) var(--space-3); margin-left: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface); white-space: nowrap;
  /* The top bar is dark and its text is light. On this white panel that light
     text is all but invisible — the countdown rendered as pale grey on white
     the first time it was drawn. Set the ink explicitly rather than inherit. */
  color: var(--ink);
}
.lockclock .lc-l { font-size: var(--text-2xs); color: var(--ink-faint);
                   text-transform: uppercase; letter-spacing: var(--tracking-label); }
.lockclock b { font-size: var(--text-sm); }
.lockclock .lc-date { font-size: var(--text-2xs); color: var(--ink-faint); margin-left: .35em; }
.lc-now, .lc-lock { display: flex; align-items: baseline; gap: var(--space-2);
                    color: inherit; text-decoration: none; border-radius: var(--radius-sm); }
a.lc-now:hover, a.lc-lock:hover { background: var(--surface-sunk); }
a.lc-now:hover b, a.lc-lock:hover b { text-decoration: underline; }
.lc-sep { width: 1px; align-self: stretch; background: var(--border); }
.lockclock.is-imminent { border-color: var(--critical); background: var(--critical-tint); }
.lockclock.is-imminent .lc-left { color: var(--critical); font-weight: var(--weight-bold); }

/* ---------- which side of the deadline a week is on ----------
   One chip, in the chrome and on the page alike. Loudest for the week being
   locked, quietest for the ones already shipped, and dashed for the weeks
   ahead of the lock — drawn but not yet firm, like a forecast day on the
   weather strip.                                                            */

.wstate {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 0 var(--space-2); border: 1px solid transparent; border-radius: var(--radius-pill);
  font-size: var(--text-2xs); font-weight: var(--weight-semi); line-height: var(--leading-normal);
  text-transform: uppercase; letter-spacing: var(--tracking-label); white-space: nowrap;
  background: var(--surface-sunk); color: var(--ink-muted);
}
.wstate .icon { --icon-size: 11px; }
.wstate--locking  { background: var(--accent); color: var(--ink-on-accent); }
.wstate--packing  { background: var(--accent-tint); color: var(--accent); border-color: var(--accent-border); }
.wstate--locked   { background: var(--surface-sunk); color: var(--ink-muted); border-color: var(--border-strong); }
.wstate--done     { background: var(--surface-sunk); color: var(--ink-faint); }
.wstate--planning { background: var(--surface); color: var(--ink-muted); border-color: var(--border-strong); border-style: dashed; }

@media (max-width: 1440px) { .lockclock .lc-date { display: none; } }

/* ---------- a row that has not been decided ----------
   No market, no price, no upside — just the size of the decision. See
   blocks/_row for why: a board of model answers reads as settled work.     */

.brow .needs { display: flex; align-items: baseline; gap: var(--space-2); }
.brow .needs b { color: var(--ink); }
.brow .undecided {
  color: var(--ink-faint); font-size: var(--text-2xs);
  text-transform: uppercase; letter-spacing: var(--tracking-label);
}

/* ---------- the fruit diary ----------
   What the inspectors said about this block's EARLIER weeks: the grade it was
   packed at, the defect that caused a downgrade, and what somebody wrote. The
   evidence behind the belief, so it sits directly under it.               */

.dy-lead { font-size: var(--text-sm); color: var(--ink-muted); line-height: var(--leading-normal); }
.dy-defects { display: inline; }

.dy-week { padding: var(--space-3) 0; border-bottom: 1px solid var(--border-soft); }
.dy-week:last-child { border-bottom: 0; }
.dy-week--latest { margin-top: var(--space-3); border-top: 1px solid var(--border-soft); }
.dy-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.dy-row { margin-top: var(--space-2); font-size: var(--text-2xs); color: var(--ink-faint); }
.dy-l { text-transform: uppercase; letter-spacing: var(--tracking-label); margin-right: var(--space-2); }

/* The grade split. Q is the only good one, so it is the only green one. */
.dy-bar {
  display: inline-flex; flex: 1; min-width: 90px; max-width: 220px; height: 8px;
  border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-sunk);
}
.dy-g { display: block; height: 100%; }
.dy-g--q  { background: var(--accent); }
.dy-g--p  { background: var(--caution); }
.dy-g--q2 { background: var(--suggest-border, var(--caution)); opacity: .7; }
.dy-g--s  { background: var(--critical); }
.dy-pct { font-size: var(--text-2xs); color: var(--ink-faint); white-space: nowrap; }

/* A person writing to another person — the part worth actually reading, so it
   is set as quoted prose rather than as another field. */
.dy-note {
  margin-top: var(--space-2); padding: var(--space-2) var(--space-3);
  background: var(--surface-sunk); border-radius: var(--radius-sm); font-size: var(--text-xs);
  color: var(--ink-muted); line-height: var(--leading-normal);
}

.dy-more { margin-top: var(--space-2); }
.dy-more > summary { cursor: pointer; padding: var(--space-2) 0;
                     font-size: var(--text-xs); color: var(--ink-faint); }
.dy-more > summary:hover { color: var(--ink-muted); }
.dy-more-body { padding: 0 var(--space-4); background: var(--surface-sunk);
                border-radius: var(--radius-md); }

/* ---------- the grower record ----------
   A forward line's whole history for this grower's variety, as two aligned
   tables: a season per row, then a week per row. Numbers right-aligned in
   tabular figures; defects and the comment on one muted line beneath, cut
   with an ellipsis (whole on hover). Under 640px the markets drop to their own
   line rather than squeeze, so nothing overflows at phone width; the folded
   weeks scroll inside their own box so a long record does not push the
   decision off the panel.                                                  */

.rec { display: grid; font-size: var(--text-xs); color: var(--ink-muted); min-width: 0; }
.rec-row { display: grid; grid-template-columns: var(--rec-cols); gap: var(--space-1) var(--space-3); align-items: baseline;
           padding: var(--space-2) 0; border-bottom: 1px solid var(--border-soft); min-width: 0; }
.rec-row:last-child { border-bottom: 0; }
.rec-row > * { min-width: 0; }
.rec .num { text-align: right; }
.rec-head { padding-top: 0; font-size: var(--text-2xs); font-weight: var(--weight-semi); color: var(--ink-faint);
            text-transform: uppercase; letter-spacing: var(--tracking-label); }
.rec--seasons { --rec-cols: 84px 32px 60px 44px 48px minmax(0, 1fr); }
.rec--weeks { --rec-cols: 56px 52px 40px minmax(0, 1fr) minmax(0, 120px); margin-top: var(--space-4); }
.rec-season b { color: var(--ink); font-weight: var(--weight-semi); }
.rec-season small { display: block; font-size: var(--text-2xs); color: var(--ink-faint); white-space: nowrap; }
.rec-mkts { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-2); }
.rec-grade { display: flex; align-items: center; gap: var(--space-2); }
.rec-grade .dy-bar { min-width: 40px; max-width: 120px; height: 6px; }
.rec-sub { grid-column: 1 / -1; display: flex; align-items: baseline; gap: var(--space-2); min-width: 0;
           color: var(--ink-faint); font-size: var(--text-2xs); white-space: nowrap; overflow: hidden; }
.rec-sub > * { flex: none; }
.rec-sub > .gr-note-line { flex: 1 1 auto; }
.rec-ico { display: inline-flex; align-items: center; gap: 2px; }
.rec-ico .icon { --icon-size: 11px; }
.gr-wk { font-weight: var(--weight-semi); color: var(--ink); }
.gr-defects { overflow: hidden; text-overflow: ellipsis; }
.gr-note-line.dy-note { margin: 0; padding-left: var(--space-2); min-width: 0; line-height: inherit;
                        font-size: var(--text-2xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gr-note { margin: var(--space-2) 0 0; display: flex; align-items: center; gap: var(--space-1); }
.gr-note .icon { --icon-size: 11px; }
.gr-weeks--more { max-height: 320px; overflow-y: auto; padding-right: var(--space-2); margin-top: 0; }
@media (max-width: 640px) {
  .rec-row { column-gap: var(--space-3); }
  .rec--seasons { --rec-cols: 76px 28px 56px 40px 52px; }
  .rec--weeks { --rec-cols: 56px 52px 40px minmax(0, 1fr); }
  .rec-head { letter-spacing: 0; }
  .rec-row > .rec-mkts { grid-column: 1 / -1; }
  .rec-head > .rec-mkts { display: none; }
}

/* ---------- packed stock, by depot ----------
   A packhouse is where fruit physically is, so that is the grouping. The flat
   (depot × variety × market) list put DURBAN on nineteen non-adjacent rows and
   could not answer "what is standing in Durban".                            */

.depot { border-bottom: 1px solid var(--border); }
.depot:last-child { border-bottom: 0; }
.depot-head {
  display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4); background: var(--surface-sunk);
}
.depot-head b { font-size: var(--text-sm); }
.depot-n { font-size: var(--text-sm); color: var(--ink-muted); }
.depot-rows { padding: 0 var(--space-4); }
.depot-row {
  display: grid; align-items: center; gap: var(--space-3);
  grid-template-columns: 56px minmax(0, 1fr) 70px 90px 130px 90px;
  padding: var(--space-2) 0; border-bottom: 1px solid var(--border-soft);
  font-size: var(--text-xs);
}
.depot-row:last-child { border-bottom: 0; }
.depot-row .num { text-align: right; }
.depot-aim { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.depot-row.is-owing { background: var(--caution-tint); }

/* A name nobody has confirmed — matched from the code alone. Dotted rather than
   labelled, because "inferred from RU" on forty-six consecutive rows is noise;
   the panel's footer says it once and the tooltip says it per row. */
.depot-aim b.is-inferred { border-bottom: 1px dotted var(--ink-faint); cursor: help; }
.stock-owing { color: var(--caution); }

@media (max-width: 900px) {
  .depot-row { grid-template-columns: 50px minmax(0, 1fr) 60px; }
  .depot-when, .depot-row .num:nth-of-type(2) { display: none; }
}

/* ---------- a week with nothing in it ----------
   Not an error. Stepping through the season walks off the end of a packing
   window in two clicks, and a 404 there blames the planner for the calendar. */

/* The board is a week behind and looks exactly like a board that is not. */
.stalebar {
  display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap;
  margin: 0 0 var(--space-4); padding: var(--space-3) var(--space-4);
  border: 1px solid var(--critical); border-radius: var(--radius-md);
  background: var(--critical-tint); color: var(--ink); font-size: var(--text-sm);
  line-height: var(--leading-normal);
}
.stalebar .icon { color: var(--critical); --icon-size: 15px; align-self: center; }
.stalebar b { font-weight: var(--weight-semi); }

/* The demand book — who is waiting for a week's fruit. Shown on the board and,
   more often, on a week the book has reached before the fruit has. */
.dbook { margin-top: var(--space-6); border: 1px solid var(--border);
         border-radius: var(--radius-lg); background: var(--surface); padding: var(--space-5); }
.dbook-h { display: flex; align-items: center; gap: var(--space-3); font-size: var(--text-md);
           margin: 0 0 var(--space-4); }
.dbook-total { margin-left: auto; font-size: var(--text-xs); color: var(--ink-faint);
               font-weight: var(--weight-normal); }
.dbook-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.dbook-row { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(80px, 1fr) 150px;
             align-items: center; gap: var(--space-3); font-size: var(--text-sm); }
.dbook-who { display: flex; flex-direction: column; line-height: 1.25; }
.dbook-who .a-dim { font-size: var(--text-2xs); }
.dbook-mkt { display: flex; align-items: baseline; gap: var(--space-2); font-size: var(--text-xs); }
.dbook-untold { color: var(--critical); text-decoration: underline dotted; }
.dbook-bar { display: block; height: 6px; border-radius: 3px; background: var(--border); overflow: hidden; }
.dbook-bar i { display: block; height: 100%; background: var(--accent); }
.dbook-qty { text-align: right; color: var(--ink-muted); }
.dbook-qty b { color: var(--ink); font-weight: var(--weight-semi); }
.dbook-qty small { display: block; font-size: var(--text-2xs); color: var(--ink-faint); }
.dbook-foot { margin: var(--space-4) 0 0; font-size: var(--text-xs); color: var(--ink-faint); }
@media (max-width: 640px) {
  .dbook-row { grid-template-columns: minmax(0, 1fr) auto; }
  .dbook-mkt, .dbook-bar { grid-column: 1 / -1; }
}

/* A week the book has reached and the fruit has not. */
.ew-chip--booked { border-style: dashed; }

.ew-actions { display: flex; align-items: center; gap: var(--space-4);
              flex-wrap: wrap; margin-top: var(--space-6); }
.ew-weeks { margin-top: var(--space-7); display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.ew-l { width: 100%; font-size: var(--text-2xs); color: var(--ink-faint);
        text-transform: uppercase; letter-spacing: var(--tracking-label); margin-bottom: var(--space-1); }
.ew-chip {
  display: inline-flex; align-items: baseline; gap: var(--space-2);
  padding: var(--space-1) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface); font-size: var(--text-xs);
}
.ew-chip:hover { border-color: var(--accent); }


/* Weather by district, for a week with no block to hang it on. The drawer's
   strip, one line per district and a little tighter. */
.rwx { margin-top: var(--space-6); border: 1px solid var(--border);
       border-radius: var(--radius-lg); background: var(--surface); padding: var(--space-5); }
.rwx-h { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
         font-size: var(--text-md); margin: 0 0 var(--space-2); }
.rwx-h .icon { --icon-size: 15px; align-self: center; }
.rwx-sub { font-size: var(--text-xs); font-weight: var(--weight-normal); color: var(--ink-faint); }
.rwx-row { margin-top: var(--space-4); }
.rwx-name { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; font-size: var(--text-sm); }
.wx--compact { margin-top: var(--space-2); }
.wx--compact .wx-day { padding: var(--space-1) 0; }


/* ---------- demand, drawn on the supply bars ----------
   The first forward demand this desk has ever held. Drawn ON the supply bar
   rather than beside it: the question is whether the fruit covers what has been
   promised, and two separate bars make that a comparison rather than an answer. */

/* Sits slightly proud of the bar so it reads as a level marked ON it, and is
   never clipped — which is why `.book-bar` no longer hides its overflow. */
.book-want {
  position: absolute; left: -3px; right: -3px; height: 2px;
  background: var(--ink); border-radius: 1px;
}
.book-want.is-over { background: var(--critical); height: 3px; }
.book-col.is-short .book-bar i { background: var(--critical-tint); }
.book-demand {
  display: flex; align-items: center; justify-content: center; gap: 3px;
  font-size: var(--text-2xs); color: var(--ink-muted); min-height: 1em;
}
.book-demand .k-demand { display: block; width: 9px; height: 2px; background: var(--ink); flex: none; }
.book-demand.is-over .k-demand { background: var(--critical); height: 3px; }
.book-demand.is-over { color: var(--critical); font-weight: var(--weight-semi); }

.book-demandline {
  display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap;
  margin-top: var(--space-3); font-size: var(--text-xs); color: var(--ink-muted);
}
.book-key { display: inline-flex; align-items: center; gap: var(--space-2); }
.book-key i { display: block; width: 14px; }
.book-key .k-supply { height: 9px; background: var(--accent-tint-strong); border-radius: 2px; }
.book-key .k-demand { height: 2px; background: var(--ink); }
.book-over { color: var(--critical); }
/* Said, not implied: a week with no lines has no demand, not missing data. */
.book-nodemand { color: var(--ink-muted); font-weight: var(--weight-semi); }

/* =============================================================================
   THE INVISIBLE UI — added when the desk was reviewed against docs/ui-guide.md.

   The states a screen has that you do not see until you need them: a menu that
   closes when you click away, a tooltip that appears at once (and on a tap), a
   confirmation that a copy worked, a toast that says what a click did without
   shoving the board down the page, and a drawer that arrives rather than
   appears. Every one of these degrades to something that still works without
   JavaScript — the native `title`, a <details> that stays open, a flash that
   stays put.
   ========================================================================== */

/* ---------- disclosure ----------
   One chevron for every "show me more", turning as it opens. Drawn with two
   borders rather than a text triangle, so it is the same shape in every font
   and takes the colour of the words beside it. */

.lane-table > summary, .reveal > summary, .wx-how > summary, .dy-more > summary {
  display: inline-flex; align-items: center; gap: var(--space-2);
  border-radius: var(--radius-sm); transition: color var(--motion-fast) var(--ease);
}
.lane-table > summary:hover, .reveal > summary:hover { color: var(--accent-hover); }
.lane-table > summary::before, .reveal > summary::before,
.wx-how > summary::before, .dy-more > summary::before,
.notes > summary::after, .stockbox > summary::after, .unpriced > summary::after {
  content: ""; flex: none; width: 6px; height: 6px; margin: 0 2px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--motion) var(--ease);
}
.lane-table[open] > summary::before, .reveal[open] > summary::before,
.wx-how[open] > summary::before, .dy-more[open] > summary::before { transform: rotate(45deg); }
/* The panels that already say "what the desk can't tell you" get the chevron at
   the end instead, pointing down, and up once open. */
.notes > summary::after, .stockbox > summary::after, .unpriced > summary::after {
  transform: rotate(45deg); margin: 0 var(--space-1) 3px var(--space-2); opacity: .7;
}
.unpriced > summary::after { margin-left: auto; align-self: center; }
.notes[open] > summary::after, .stockbox[open] > summary::after, .unpriced[open] > summary::after {
  transform: rotate(-135deg); margin-bottom: -3px;
}
.notes > summary:hover .notes-lead, .stockbox > summary:hover .stock-lead { text-decoration: underline; text-underline-offset: 3px; }

/* ---------- toasts ----------
   Every action on this desk says what it did. It used to say it in a bar under
   the top bar, which pushed the whole board down by a line on every single
   placement. A toast floats: bottom left, clear of the drawer on the right, and
   it goes away by itself unless it is bad news. */

.flashes {
  position: fixed; left: var(--space-6); bottom: var(--space-6); z-index: var(--layer-toast);
  display: grid; gap: var(--space-2); max-width: min(440px, calc(100vw - 2 * var(--space-5)));
  pointer-events: none;
}
.flash {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  width: fit-content; max-width: 100%;
  background: var(--surface-raised); color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
  font-size: var(--text-sm); line-height: var(--leading-normal);
  animation: toast-in var(--motion) var(--ease) both;
}
.flash > .icon { --icon-size: 16px; flex: none; color: var(--positive); margin-top: 2px; }
.flash-alert { background: var(--surface-raised); color: var(--ink); border-color: var(--critical); }
.flash-alert > .icon { color: var(--critical); }
.flash > .flash-msg { flex: 1; min-width: 0; }
.flash-x {
  flex: none; display: inline-grid; place-items: center; width: 22px; height: 22px;
  border-radius: var(--radius-sm); color: var(--ink-faint);
  transition: background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease);
}
.flash-x:hover { background: var(--surface-sunk); color: var(--ink); }
.flash-x .icon { --icon-size: 13px; }
.flash.is-leaving { animation: toast-out var(--motion) var(--ease) both; }
@keyframes toast-in  { from { opacity: 0; transform: translateY(8px); } }
@keyframes toast-out { to   { opacity: 0; transform: translateY(8px); } }

/* ---------- tooltips ----------
   The desk explains itself in `title` attributes — dozens of them, most of them
   the only place a number's meaning is written down. A native title waits a
   second and never appears on a tablet, so the script lifts each one into this
   bubble at once, on hover, focus or tap. Without the script, the title stays. */

.tip {
  position: fixed; z-index: var(--layer-tooltip); pointer-events: none;
  max-width: 300px; padding: var(--space-2) var(--space-3);
  background: var(--tooltip-bg); color: var(--tooltip-ink);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
  font-size: var(--text-xs); line-height: 1.4; font-weight: var(--weight-medium);
  opacity: 0; transform: translateY(2px);
  transition: opacity var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease);
}
.tip.is-on { opacity: 1; transform: none; }
/* Something that explains itself on hover says so: a dotted underline on words,
   a help cursor on everything. */
[data-tip] { cursor: help; }
a[data-tip], button[data-tip], summary[data-tip], .brow [data-tip] { cursor: pointer; }

/* ---------- copy ----------
   A copy button that only changes colour leaves you wondering whether it
   worked. This one says "Copied", in a chip that rises out of it. */

.copy {
  position: relative; display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 0 var(--space-1); margin-left: var(--space-1); border-radius: var(--radius-sm);
  color: var(--ink-faint); vertical-align: baseline;
  transition: background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease);
}
.copy .icon { --icon-size: 12px; }
.copy:hover { background: var(--surface-sunk); color: var(--ink); }
.copy[hidden] { display: none; }
.copied {
  position: absolute; left: 50%; bottom: calc(100% + 4px); transform: translateX(-50%);
  padding: 2px var(--space-2); border-radius: var(--radius-sm);
  background: var(--tooltip-bg); color: var(--tooltip-ink);
  font-size: var(--text-2xs); font-weight: var(--weight-semi); white-space: nowrap;
  animation: copied 1.4s var(--ease) both; pointer-events: none;
}
@keyframes copied {
  0% { opacity: 0; transform: translate(-50%, 4px); }
  15%, 75% { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, -2px); }
}

/* ---------- a country ----------
   Markets were flag emoji. Windows draws those as two bare letters, Dole's desks
   run Windows, and emoji are not an icon set anyway — so a country is its code,
   in a small chip that looks the same everywhere and lines up in a column. */

.cc {
  display: inline-block; min-width: 2.3em; padding: 1px 3px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-sunk); color: var(--ink-muted);
  font-size: 10px; font-weight: var(--weight-semi); line-height: 1.3; letter-spacing: .04em;
  text-align: center; vertical-align: 1px; white-space: nowrap;
}
.placed-chip .cc { border-color: transparent; background: transparent; color: inherit; min-width: 0; padding: 0; margin-right: 2px; }
.cs-item.is-on .cc, .lanechip .cc { background: var(--surface); }

/* Small glyphs that were characters (✓ ⚠ ⚑) are icons now. */
.glyph { --icon-size: 11px; vertical-align: -1px; }
.glyph--good { color: var(--positive); }
.glyph--warn { color: var(--caution); }
.glyph--bad  { color: var(--critical); }

/* ---------- the drawer arrives ----------
   Opening a block is a real navigation, so this plays once; placing a reefer
   morphs the page in place, so it does not replay on every click. */

.drawer { animation: drawer-in var(--motion) var(--ease) both; }
.scrim  { animation: fade-in var(--motion) var(--ease) both; }
@keyframes drawer-in { from { transform: translateX(24px); opacity: 0; } }
@keyframes fade-in   { from { opacity: 0; } }
.drawer:focus { outline: none; }

/* Turbo's own progress bar, in the desk's colour rather than Turbo's blue. */
.turbo-progress-bar { height: 2px; background: var(--accent); }

/* ---------- small press states the rows were missing ---------- */

.segmented a:active, .ew-chip:active { transform: translateY(1px); }
.wrow, .crow, .surprise, .ew-chip, .segmented a, .cs-item, .unpriced > summary {
  transition: background var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease),
              color var(--motion-fast) var(--ease);
}
.wrow:active, .crow:active { background: var(--accent-tint-strong); }
.lane { transition: background var(--motion) var(--ease); }

/* ---------- admin page pieces the market-code screen asked for ----------
   It used five classes that were never defined, so its heading, its warning and
   its save row rendered as bare text. */

.anote {
  display: flex; gap: var(--space-3); align-items: flex-start;
  margin-bottom: var(--space-5); padding: var(--space-3) var(--space-4);
  border: 1px solid var(--suggest-border); border-radius: var(--radius-md);
  background: var(--caution-tint); font-size: var(--text-sm); line-height: var(--leading-normal);
}
.anote > .icon { color: var(--caution); --icon-size: 15px; margin-top: 3px; }
.aactions { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-5); flex-wrap: wrap; }

/* ---------- signing in ---------- */

.auth { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
        padding: var(--space-7); box-shadow: var(--shadow-card); }
.auth-brand { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-6);
              font-weight: var(--weight-black); letter-spacing: var(--tracking-tight); }
.auth-brand .icon { --icon-size: 20px; color: var(--accent); }
.auth-brand small { display: block; font-weight: var(--weight-medium); font-size: var(--text-2xs);
                    letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-faint); }
.auth input[type="email"], .auth input[type="password"], .auth input[type="text"] { width: 100%; font-size: var(--text-md); }
.auth .check { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--ink-muted); }
.auth .check label { text-transform: none; letter-spacing: 0; font-size: var(--text-sm); color: var(--ink-muted); }
.auth .muted-links a { color: var(--ink-muted); }
.auth .muted-links a:hover { color: var(--accent); }

/* ---------- touch ----------
   Nothing on the desk is ONLY on hover, but the hover washes stick after a tap
   on a tablet, which reads as "still selected". Keep them for pointers. */
@media (hover: none) {
  .brow:hover, .wrow:hover, .crow:hover { background: inherit; }
  .brow[aria-current="true"] { background: var(--accent-tint-strong); }
  .wrow--now:hover { background: var(--accent-tint); }
}

@media (max-width: 640px) {
  /* A lane on a phone: the badges drop to their own line, so the price and the
     stepper never get pushed out of the drawer. */
  .lane-row { grid-template-columns: 26px minmax(0, 1fr) auto auto; row-gap: var(--space-1); }
  .lane-marks { grid-column: 2 / -1; grid-row: 2; flex-wrap: wrap; }

  /* Menus span the screen, just under what opened them (a fixed box with no
     `top` stays at its static position). */

  /* The picker keeps the name, the volume and the way in; the two bars are on
     the commodity's own board, one tap away. */
  .crow { grid-template-columns: 22px minmax(0, 1fr) 64px 16px; gap: var(--space-3); padding: var(--space-3) var(--space-4); }
  .c-weeks, .c-reefer, .c-fcast, .c-paid { display: none; }
  .ccaveats { padding: 0 var(--space-4) var(--space-3); }

  /* A week: its number, then its dates over what it holds. */
  .wrow { grid-template-columns: 52px minmax(0, 1fr) 16px; gap: var(--space-1) var(--space-3); padding: var(--space-3) var(--space-4); }
  .wrow > :nth-child(2) { grid-column: 2; }
  .wrow .w-holds { grid-column: 2; justify-content: flex-start; }
  .wrow .w-go { grid-column: 3; grid-row: 1 / span 2; }

  /* Admin tables scroll inside their own box rather than the page. */
  .atable { overflow-x: auto; }
  .atable:not(.atable--alloc, .atable--sail, .atable--decisions) .arow { min-width: 620px; }
  /* Subgridded rows can't be wider than the table's tracks: give the tracks the width. */
  .atable--users { grid-template-columns: minmax(110px, 1.1fr) minmax(150px, 1.2fr) 90px 150px 80px auto; }
  .atable.atable--users .arow { min-width: 0; }

  .flashes { left: var(--space-4); right: var(--space-4); bottom: var(--space-4); max-width: none; }
}

/* ---------- the week's rows, from Dole's estimate book ---------- */

/* A forecast, set apart from the block rows that are fact: its own panel, its
   own heading, and a dashed rule under the head so the two tables never read
   as one list. Tokens only, so both themes follow. */
.elines { margin-top: var(--space-6); border: 1px solid var(--border);
          border-radius: var(--radius-lg); background: var(--surface); overflow: hidden; }
.elines-h { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
            font-size: var(--text-md); margin: 0; padding: var(--space-4) var(--space-5); }
.elines-h .icon { --icon-size: 15px; align-self: center; }
.elines-sub { font-size: var(--text-xs); font-weight: var(--weight-normal); color: var(--ink-faint); }
.elines-sub b { color: var(--ink); font-weight: var(--weight-semi); }
.elines > .wx-src { padding: 0 var(--space-5) var(--space-4); }
.el-head, .el-row {
  display: grid;
  grid-template-columns: minmax(170px, 1.6fr) 120px minmax(90px, 1fr) 100px 100px minmax(170px, 1.5fr) 120px;
  gap: var(--space-4); align-items: center; padding: var(--space-3) var(--space-5);
}
.el-head {
  font-size: var(--text-2xs); letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--ink-faint); background: var(--ground);
  border-top: 1px solid var(--border); border-bottom: 1px dashed var(--border-strong);
}
.el-head .num, .el-row .num { text-align: right; }
.el-row {
  border-bottom: 1px solid var(--border-soft); text-decoration: none; color: inherit;
  font-size: var(--text-sm); transition: background var(--motion-fast) var(--ease);
}
.el-row:last-child { border-bottom: 0; }
.el-row:hover { background: var(--accent-tint); }
.el-row:active { background: var(--accent-tint-strong); }
.el-row[aria-current="true"] { background: var(--accent-tint-strong); }
.el-grower { min-width: 0; }
.el-grower b { display: block; font-weight: var(--weight-semi); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.el-grower .mono { font-size: var(--text-2xs); color: var(--ink-faint); }
.el-area { font-size: var(--text-xs); color: var(--ink-muted); }
.el-row .num small { color: var(--ink-faint); font-size: var(--text-2xs); }
.el-row .num .el-beside { display: block; }
.el-evidence { display: flex; align-items: center; gap: var(--space-3); min-width: 0; flex-wrap: wrap; }
.el-evidence .bgauge { flex: 0 0 56px; }
.el-evidence .pct { font-weight: var(--weight-semi); }
.el-tag { font-size: var(--text-2xs); color: var(--ink-faint); }
.el-none { font-size: var(--text-xs); color: var(--ink-faint); font-style: italic; }
@media (hover: none) {
  .el-row:hover { background: inherit; }
  .el-row[aria-current="true"] { background: var(--accent-tint-strong); }
}

/* The estimate line's drawer: the line's own figures as a row of named facts. */
.el-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: var(--space-3);
            margin: 0; }
.el-facts dt { font-size: var(--text-2xs); color: var(--ink-faint); text-transform: uppercase;
               letter-spacing: var(--tracking-label); }
.el-facts dd { margin: var(--space-1) 0 0; font-size: var(--text-sm); color: var(--ink-muted); }
.el-facts dd b { font-size: var(--text-lg); color: var(--ink); }
.el-from { margin: var(--space-3) 0 0; font-size: var(--text-xs); color: var(--ink-faint); }

/* ---------- the team's decisions ----------
   The record of what was decided, in the drawer and on the week. Tokens only,
   so both themes follow; the one warm note is the over-estimate warning,
   because over is allowed and must still be seen. */

/* On an estimate row: "12 / 22 plt" and a chip per market or customer. */
.el-decided { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-1); }
.el-decided.is-over > .mono { color: var(--caution); }
.el-chips { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-1); }
.el-chip { font-size: var(--text-2xs); padding: 0 var(--space-2); border-radius: var(--radius-pill);
           background: var(--accent-tint); color: var(--accent); white-space: nowrap; }
.el-chip b { font-weight: var(--weight-semi); }

.depot-decide { text-align: right; }

/* The drawer's decision section. */
.dec > h3 { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.dec-tally { margin-left: auto; font-size: var(--text-xs); font-weight: var(--weight-normal);
             color: var(--ink-muted); text-transform: none; letter-spacing: 0; }
.dec-tally b { color: var(--ink); font-weight: var(--weight-semi); }
.dec-meter { height: 6px; background: var(--border-soft); border-radius: var(--radius-pill); overflow: hidden;
             margin-bottom: var(--space-4); }
.dec-meter > i { display: block; height: 6px; background: var(--accent); transition: width var(--motion) var(--ease); }
.dec-meter.is-over > i { background: var(--caution); }
.dec-warn { display: flex; gap: var(--space-2); align-items: flex-start; font-size: var(--text-xs);
            color: var(--caution); background: var(--caution-tint); border-radius: var(--radius-md);
            padding: var(--space-3) var(--space-4); margin-bottom: var(--space-4); }
.dec-warn .icon { --icon-size: 14px; flex: none; margin-top: 1px; }
.dec-none { font-size: var(--text-sm); color: var(--ink-faint); margin-bottom: var(--space-4); }
.dec-none--week { padding: 0 var(--space-5) var(--space-5); margin: 0; }

.dec-list { list-style: none; margin: 0 0 var(--space-4); padding: 0; border: 1px solid var(--border);
            border-radius: var(--radius-md); }
.dec-row { display: grid; grid-template-columns: 76px minmax(0, 1fr) auto; gap: var(--space-1) var(--space-4);
           align-items: center; padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border-soft);
           font-size: var(--text-sm); }
.dec-row:last-child { border-bottom: 0; }
.dec-row.is-editing { background: var(--accent-tint-strong); }
.dec-plt small { display: block; font-size: var(--text-2xs); color: var(--ink-faint); }
.dec-to { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; min-width: 0; }
.dec-cust { color: var(--ink-muted); }
.dec-who { grid-column: 2; font-size: var(--text-2xs); display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.dec-actions { grid-column: 3; grid-row: 1 / span 2; display: flex; gap: var(--space-2); }

.dec-suggest { margin-bottom: var(--space-5); }
.dec-suggest h4 { margin-bottom: var(--space-2); }
.dec-suggest ul { list-style: none; margin: 0; padding: 0; }
/* The customer's name has a line to itself: squeezed into a fifth of a 560px
   drawer beside the market, the shortfall and the price, "Northsea Fresh"
   read "Northsea ...", and the name is the one thing a suggestion is about. */
.dec-sug { display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto;
           grid-template-areas: "who who who act" "mkt short rate act";
           gap: var(--space-1) var(--space-3); align-items: center; padding: var(--space-2) 0;
           border-bottom: 1px solid var(--border-soft); font-size: var(--text-xs); }
.dec-sug-who { grid-area: who; min-width: 0; display: flex; gap: var(--space-2); align-items: baseline; flex-wrap: wrap; }
.dec-sug-who b { font-weight: var(--weight-semi); font-size: var(--text-sm); }
.dec-sug-mkt { grid-area: mkt; min-width: 0; color: var(--ink-muted); }
.dec-sug-short { grid-area: short; }
.dec-sug-rate { grid-area: rate; }
.dec-sug > .btn { grid-area: act; }
.dec-sug-short, .dec-sug-rate { text-align: right; }
.dec-suggest .wx-src { margin-top: var(--space-2); }

.dec-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4);
            padding: var(--space-4); background: var(--surface-sunk); border-radius: var(--radius-md); }
.dec-editing { grid-column: 1 / -1; display: flex; gap: var(--space-2); align-items: center;
               font-size: var(--text-xs); color: var(--ink-muted); }
.dec-editing .icon { --icon-size: 13px; }
.dec-field { display: grid; gap: var(--space-1); align-content: start; min-width: 0; }
.dec-field--wide { grid-column: 1 / -1; }
.dec-field label { font-size: var(--text-2xs); letter-spacing: var(--tracking-label); text-transform: uppercase;
                   color: var(--ink-faint); }
.dec-field input, .dec-field select { width: 100%; font-size: var(--text-sm); padding: var(--space-2) var(--space-3);
                                      border-radius: var(--radius-sm); min-width: 0; }
.dec-inline { display: flex; align-items: center; gap: var(--space-3); font-size: var(--text-xs); }
.dec-field--plt input { width: 7em; flex: none; }
.dec-hint { font-size: var(--text-2xs); color: var(--ink-faint); }
.dec-submit { grid-column: 1 / -1; display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

.dec-log { margin-top: var(--space-4); }
.dec-log > summary { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs);
                     color: var(--ink-muted); font-weight: var(--weight-semi); }
.dec-log > summary .icon { --icon-size: 13px; }
.dec-log > summary:hover { color: var(--ink); }
.dec-log ol { list-style: none; margin: var(--space-3) 0 0; padding: 0 0 0 var(--space-4);
              border-left: 2px solid var(--border); display: grid; gap: var(--space-2); }
.dec-log li { display: flex; gap: var(--space-3); align-items: baseline; flex-wrap: wrap; font-size: var(--text-xs); }
.dec-log li.is-gone > span:nth-child(2) { color: var(--ink-faint); }

@media (max-width: 640px) {
  .dec-form { grid-template-columns: 1fr; }
  .dec-row { grid-template-columns: 64px minmax(0, 1fr); }
  .dec-actions { grid-column: 1 / -1; grid-row: auto; }
  .dec-sug { grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "who who act" "mkt rate act"; }
  .dec-sug-short { display: none; }
}

/* The week's decisions: the summary, the fill, and the ledger. */
.decbox { margin-top: var(--space-6); border: 1px solid var(--border); border-radius: var(--radius-lg);
          background: var(--surface); overflow: hidden; }
.decbox-h { display: flex; align-items: center; gap: var(--space-3) var(--space-4); flex-wrap: wrap;
            padding: var(--space-4) var(--space-5); }
.decbox-h h2 { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-md); }
.decbox-h h2 .icon { --icon-size: 15px; }
.decbox-sub { font-size: var(--text-xs); color: var(--ink-faint); display: inline-flex; gap: var(--space-2);
              align-items: baseline; flex-wrap: wrap; }
.decbox-sub b { color: var(--ink); font-weight: var(--weight-semi); }
.decbox-actions { margin-left: auto; display: flex; gap: var(--space-3); flex-wrap: wrap; }


.dledger { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.dledger-row { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.3fr) 80px minmax(0, 1fr) minmax(0, 1fr);
               gap: var(--space-4); align-items: center; padding: var(--space-3) var(--space-5);
               border-bottom: 1px solid var(--border-soft); font-size: var(--text-sm); }
.dledger-row:last-child { border-bottom: 0; }
.dledger-what { min-width: 0; display: flex; gap: var(--space-2); align-items: baseline; flex-wrap: wrap; }
.dledger-name { font-weight: var(--weight-semi); color: var(--ink); text-decoration: none; }
.dledger-name:hover { color: var(--accent); text-decoration: underline; }
.dledger-to { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; min-width: 0; }
.dledger-to .icon { --icon-size: 13px; color: var(--ink-faint); }
.dledger-n { text-align: right; }
.dledger-n small { display: block; font-size: var(--text-2xs); color: var(--ink-faint); }
.dledger-who { font-size: var(--text-xs); }
.dledger-flags { display: flex; gap: var(--space-2); justify-content: flex-end; flex-wrap: wrap; }
.dledger-flags .icon { --icon-size: 11px; }

@media (max-width: 900px) {
  .dledger-row { grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-1) var(--space-3); }
  .dledger-to, .dledger-who, .dledger-flags { grid-column: 1 / -1; justify-content: flex-start; }
  .dledger-n { grid-column: 2; grid-row: 1; }
  .decbox-actions { margin-left: 0; }
}

/* =============================================================================
   THE WEEK, ONE SCREEN — docs/FORWARD_WEEK_PLAN.md, phase 4.

   The glance, who is waiting, the rows, the decisions, the context: in that
   order for every state, with the state deciding only which figure is lit.
   Tokens only, so both themes follow; the dark theme gets depth from the
   surfaces being lighter than the ground, not from shadows.
   ========================================================================== */

.week { min-width: 0; }
.week > * + * { margin-top: var(--space-6); }
.week > .stalebar + .glance { margin-top: 0; }

/* A section heading inside the week, for the panels that hold other panels. */
.wsec-h { display: flex; align-items: baseline; gap: var(--space-2) var(--space-4); flex-wrap: wrap;
          margin-bottom: var(--space-4); }
.wsec-h h2 { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-md); }
.wsec-h h2 .icon { --icon-size: 15px; }
.wsec-sub { font-size: var(--text-xs); color: var(--ink-faint); }
.wsec-sub b { color: var(--ink); font-weight: var(--weight-semi); }

/* ---------- at a glance ---------- */

.glance { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
          padding: var(--space-5); box-shadow: var(--shadow-card); }
.gl-state { display: flex; align-items: center; gap: var(--space-2) var(--space-3); flex-wrap: wrap;
            font-size: var(--text-xs); color: var(--ink-faint); margin-bottom: var(--space-3); }
.gl-state .icon { --icon-size: 13px; vertical-align: -.15em; }
.gl-clock { color: var(--ink-muted); }
.gl-clock b { color: var(--ink); font-weight: var(--weight-semi); }
.glance--locking .gl-clock { color: var(--accent); }
.glance h1 { font-size: var(--text-xl); letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); }
.wk-lead { margin-top: var(--space-3); font-size: var(--text-sm); color: var(--ink-muted);
           line-height: var(--leading-normal); max-width: 88ch; }

/* The figures: one band, labels small and above, the number the thing. */
/* Flex rather than a grid, so however many figures a state carries (six or
   seven) the last row stretches to the edge instead of leaving a grey hole. */
.gl-figs { display: flex; flex-wrap: wrap; gap: 1px;
           margin: var(--space-5) 0 0; background: var(--border-soft);
           border: 1px solid var(--border-soft); border-radius: var(--radius-md); overflow: hidden; }
.gl-fig { flex: 1 1 128px; background: var(--surface); padding: var(--space-3) var(--space-4); min-width: 0; }
.gl-fig--wide { flex: 2 1 240px; }
.gl-fig dt { font-size: var(--text-2xs); color: var(--ink-faint); text-transform: uppercase;
             letter-spacing: var(--tracking-label); line-height: 1.3; }
.gl-fig dt .a-dim { text-transform: none; letter-spacing: 0; }
.gl-fig dd { margin: var(--space-1) 0 0; font-size: var(--text-xs); color: var(--ink-muted); }
.gl-fig dd b { font-size: var(--text-lg); color: var(--ink); font-weight: var(--weight-bold);
               letter-spacing: var(--tracking-tight); }
.gl-fig dd small { display: block; font-size: var(--text-2xs); color: var(--ink-faint); }
.gl-fig.is-hi { background: var(--accent-tint); }
.gl-fig.is-open dd b { color: var(--caution); }
.gl-fig--gap.is-short dd b { color: var(--critical); }
.gl-meter { display: block; height: 6px; margin: var(--space-2) 0 var(--space-2); background: var(--border-soft);
            border-radius: var(--radius-pill); overflow: hidden; }
.gl-meter > i { display: block; height: 100%; background: var(--accent); transition: width var(--motion) var(--ease); }
.gl-meter.is-over > i { background: var(--caution); }
.gl-fig dd .gl-extra { margin-top: var(--space-1); }
.gl-actions { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
              margin-top: var(--space-4); font-size: var(--text-xs); }

/* ---------- who is waiting, and how full ---------- */

.week .dbook { margin-top: var(--space-6); }
.dbook-total b { color: var(--ink); font-weight: var(--weight-semi); }
.dbook-total b.is-open { color: var(--caution); }
.dbook-mkt { flex-wrap: wrap; min-width: 0; }
.dbook-code { display: inline-flex; gap: var(--space-1); align-items: baseline; }
.dbook-who { min-width: 0; }
.dbook-bar { height: 8px; border-radius: var(--radius-pill); background: var(--border-soft); }
.dbook-bar i { transition: width var(--motion) var(--ease); }
.dbook-row.is-full .dbook-bar i { background: var(--positive); }
.dbook-row.is-over .dbook-bar i { background: var(--caution); }
.week--locking .dbook-row.is-none .dbook-qty b { color: var(--critical); }
.dbook-row.is-extra .dbook-bar { background: transparent; border: 1px dashed var(--border-strong); }
.dbook-row.is-extra .dbook-bar i { background: transparent; }
.dbook-row.is-full, .dbook-row.is-over { opacity: .82; }
.dbook-list { gap: var(--space-3); }
/* In the line drawer the same rows carry one figure, not a fill. */
.drawer .dbook-row { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(50px, 1fr) 72px; }
.drawer .dbook-qty { color: var(--ink); font-weight: var(--weight-semi); }

/* ---------- the rows ---------- */

/* A line whose estimate is fully decided goes quiet; on the locking week a
   line with nothing decided says so in words ("to decide"). */
.el-row.is-decided { color: var(--ink-muted); }
.el-row.is-decided .el-grower b { font-weight: var(--weight-medium); }
.el-row.is-decided .bgauge, .el-row.is-decided .vgchip { opacity: .6; }
.el-todo { font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--caution); }
.el-meter { display: block; width: 72px; height: 4px; background: var(--border-soft);
            border-radius: var(--radius-pill); overflow: hidden; }
.el-meter > i { display: block; height: 100%; background: var(--accent); }
.el-decided.is-over .el-meter > i { background: var(--caution); }

.week .stockbox { margin-top: var(--space-4); }
.history { border-top: 1px dashed var(--border-strong); padding-top: var(--space-5); }
.history > .kpis { margin-bottom: var(--space-4); }

/* ---------- context ---------- */

.context > * + * { margin-top: var(--space-4); }
.ctxbox { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
.ctxbox.rwx { padding: 0; margin-top: 0; }
.context > .ctxbox.rwx { margin-top: var(--space-4); }
.context > .ctxbox.rwx:first-child { margin-top: 0; }
.ctxbox > summary { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-4) var(--space-5);
                    cursor: pointer; list-style: none; border-radius: var(--radius-lg);
                    transition: background var(--motion-fast) var(--ease); }
.ctxbox > summary::-webkit-details-marker { display: none; }
.ctxbox > summary:hover { background: var(--surface-sunk); }
.ctxbox > summary .rwx-h { margin: 0; min-width: 0; flex: 1; }
.ctxbox > summary::after { content: ""; flex: none; width: 6px; height: 6px; margin: 0 var(--space-1) 3px;
                           border-right: 1.5px solid var(--ink-faint); border-bottom: 1.5px solid var(--ink-faint);
                           transform: rotate(45deg); transition: transform var(--motion) var(--ease); }
.ctxbox[open] > summary::after { transform: rotate(-135deg); margin-bottom: -3px; }
.ctxbox[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.ctx-more { flex: none; font-size: var(--text-2xs); color: var(--ink-faint); text-transform: uppercase;
            letter-spacing: var(--tracking-label); }
.ctxbox > :not(summary) { margin-left: var(--space-5); margin-right: var(--space-5); }
.ctxbox > :last-child:not(summary) { margin-bottom: var(--space-4); }
.rwx-sub b { color: var(--ink-muted); font-weight: var(--weight-semi); }

.ships { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border-soft); }
.ship { display: grid; grid-template-columns: 96px minmax(0, 1fr) 120px 110px; gap: var(--space-3);
        align-items: baseline; padding: var(--space-2) 0; border-bottom: 1px solid var(--border-soft);
        font-size: var(--text-xs); }
.ship-when b { font-weight: var(--weight-semi); }
.ship-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ship .num { text-align: right; color: var(--ink-muted); }
.shipbox .wx-src { margin-top: var(--space-3); }

.nbrs-all { margin-top: var(--space-4); }
.nbrs-all > summary { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-xs);
                      font-weight: var(--weight-semi); color: var(--ink-muted); cursor: pointer; list-style: none; }
.nbrs-all > summary::-webkit-details-marker { display: none; }
.nbrs-all > summary .icon { --icon-size: 13px; transition: transform var(--motion) var(--ease); }
.nbrs-all[open] > summary .icon { transform: rotate(90deg); }
.nbrs-all > summary:hover { color: var(--ink); }
.nbrs-all .ew-weeks { margin-top: var(--space-4); }
.book .ew-actions { margin-top: var(--space-4); }

@media (max-width: 900px) {
  .ship { grid-template-columns: 76px minmax(0, 1fr) auto; }
  .ship-where { display: none; }
}
@media (max-width: 640px) {
  .glance { padding: var(--space-4); }
  .glance h1 { font-size: var(--text-lg); }
  .gl-fig { flex-basis: 40%; }
  .gl-fig--wide { flex-basis: 100%; }
  .ctxbox > summary { padding: var(--space-3) var(--space-4); }
  .ctxbox > :not(summary) { margin-left: var(--space-4); margin-right: var(--space-4); }
  .ctx-more { display: none; }
  .dbook-row { row-gap: var(--space-1); }
  .dbook-row .dbook-who { grid-column: 1; grid-row: 1; }
  .dbook-row .dbook-qty { grid-column: 2; grid-row: 1 / span 2; align-self: start; }
  .dbook-row .dbook-mkt { grid-column: 1; grid-row: 2; }
  .dbook-row .dbook-bar { grid-row: 3; }
}

/* A phone is 390px and nine weeks of bars or fifteen days of weather do not
   fit in it at a readable size. Each strip scrolls inside its own box instead
   of pushing the whole page sideways. */
@media (max-width: 640px) {
  .book-bars { grid-auto-columns: minmax(44px, 1fr); overflow-x: auto; overflow-y: visible;
               height: auto; padding-top: var(--space-1); }
  .wx { grid-auto-columns: minmax(30px, 1fr); overflow-x: auto; }
  .book-head { gap: var(--space-2) var(--space-4); }
  .book-key { display: block; }
  .book-key i { display: inline-block; vertical-align: middle; margin-right: var(--space-2); }
}

/* --- Contour inspections ------------------------------------------------------
   One card per inspection, in a drawer (Inspection.for_line / for_block) and on
   its own page (/inspections/:id). Findings are a small definition grid like .el-facts; a card the
   source no longer carries is quieter and says so, rather than vanishing.   */

.insp-list { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: grid; gap: var(--space-3); }
.insp { min-width: 0; }
.insp-list .insp { padding-top: var(--space-3); border-top: 1px solid var(--border-soft); }
.insp.is-withdrawn { opacity: .75; }
.insp-head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; font-size: var(--text-sm); }
.insp-type { font-weight: var(--weight-semi); color: var(--ink); }
.insp-why { font-size: var(--text-2xs); color: var(--accent); background: var(--accent-tint);
  border: 1px solid var(--accent-border); border-radius: var(--radius-pill); padding: 0 var(--space-3); }
.insp-gone { font-size: var(--text-2xs); color: var(--caution); background: var(--caution-tint);
  border-radius: var(--radius-pill); padding: 0 var(--space-3); }
.insp-where { margin: var(--space-2) 0 0; font-size: var(--text-xs); color: var(--ink-muted); }
.insp-finds { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: var(--space-3);
  margin: var(--space-3) 0 0; }
.insp-finds dt { font-size: var(--text-2xs); color: var(--ink-faint); text-transform: uppercase;
  letter-spacing: var(--tracking-label); }
.insp-finds dd { margin: var(--space-1) 0 0; font-size: var(--text-sm); color: var(--ink); overflow-wrap: anywhere; }
.insp-finds--all { padding: var(--space-3); background: var(--surface-sunk); border-radius: var(--radius-md); }
.insp-foot { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); font-size: var(--text-xs); }
.insp-foot .linky { display: inline-flex; align-items: center; gap: var(--space-2); overflow-wrap: anywhere; }
.insp-foot .icon { --icon-size: 12px; }
.insp-photos { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: var(--space-3); }
.insp-photos li { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.insp-photo { display: block; aspect-ratio: 4 / 3; border-radius: var(--radius-sm); overflow: hidden;
  background: var(--surface-sunk); border: 1px solid var(--border-soft); }
.insp-photo img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform .2s ease; }
.insp-photo:hover img, .insp-photo:focus-visible img { transform: scale(1.04); }
.insp-photo:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.insp-cap { margin: 0; color: var(--ink-faint); font-style: italic; overflow-wrap: anywhere; }
.insp-lapsed { margin: 0; display: flex; align-items: center; gap: var(--space-2); }
.insp-more { margin-top: var(--space-3); }
.insp-more > summary { list-style: none; width: max-content; }
.insp-more > summary::-webkit-details-marker { display: none; }
.insp-more[open] > summary { color: var(--ink); }
.el-facts--book { margin-top: var(--space-4); }
.el-facts--book dd { overflow-wrap: anywhere; }

/* --- certifications and MRL results ------------------------------------------
   A chip per scheme on an estimate row and in the drawers, toned by the
   combined standing (Certification#standing): quiet green when certified, the
   caution tint when expiring, pending or no longer listed, critical when
   expired, suspended or an MRL exceedance. Tokens only, so both themes follow. */

.cert-chip { display: inline-flex; align-items: center; gap: var(--space-1); white-space: nowrap;
  font-size: var(--text-2xs); font-weight: var(--weight-semi); padding: 0 var(--space-2);
  border-radius: var(--radius-pill); border: 1px solid var(--border); color: var(--ink-muted);
  background: var(--surface-sunk); }
.cert-chip .icon { --icon-size: 11px; }
.cert-chip--warn { color: var(--caution); background: var(--caution-tint); border-color: var(--suggest-border); }
.cert-chip--bad { color: var(--critical); background: var(--critical-tint); border-color: var(--critical-strong-tint); }
.cert-chip--sm { font-weight: var(--weight-medium); }

.el-certs { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-1); }

.cert-list, .mrl-list { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: grid; gap: var(--space-2); }
.cert-row, .mrl-row { display: grid; grid-template-columns: 56px minmax(0, 1fr) auto; gap: var(--space-3);
  align-items: baseline; padding-top: var(--space-2); border-top: 1px solid var(--border-soft); font-size: var(--text-sm); }
.cert-row:first-child, .mrl-row:first-child { border-top: 0; padding-top: 0; }
.mrl-row { grid-template-columns: 84px minmax(0, 1fr) auto; }
.cert-what, .mrl-what { min-width: 0; }
.mrl-row.is-exceed .mrl-what b { color: var(--critical); }

.atable--certs .arow {
  grid-template-columns: 96px minmax(0, 2.2fr) 110px minmax(0, 1fr) 84px 130px 72px;
}
.atable--mrl .arow {
  grid-template-columns: 96px minmax(0, 1.1fr) minmax(0, 1fr) 100px 100px 130px minmax(0, 1fr) 90px;
}
.atable--schemes .arow {
  grid-template-columns: 56px 56px minmax(0, 1.4fr) minmax(0, 0.8fr) 120px 130px minmax(0, 1fr) 130px;
}
.arow.is-exceed { background: var(--critical-tint); }
.cs-check { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); }
.aform .cs-check label { text-transform: none; letter-spacing: 0; font-size: var(--text-xs); color: var(--ink-muted); }

.afilter input[type="text"] {
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface); font-size: var(--text-sm);
}

.aform .field select, .aform .field textarea, .aform .field input[type="date"],
.aform .field input[type="number"], .aform .field input[type="url"] {
  padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface); color: var(--ink); font-size: var(--text-md); font-family: inherit;
}
.aform .field--pair, .aform .field--triple { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2) var(--space-4); }
.aform .field--triple { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.aform .field--pair > span:not(.hint), .aform .field--triple > span:not(.hint) { display: grid; gap: var(--space-2); }
.aform .field--pair input, .aform .field--triple input { min-width: 0; }
.aform .field--pair .hint, .aform .field--triple .hint { grid-column: 1 / -1; }

.mrl-import { display: grid; gap: var(--space-2); margin-bottom: var(--space-5); padding: var(--space-4) var(--space-5);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.mrl-import .a-dim { line-height: var(--leading-normal); }

.cert-book { max-width: 640px; margin-bottom: var(--space-3); }
.cert-log { margin-top: var(--space-6); max-width: 760px; }
.cert-log-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); font-size: var(--text-sm); }
.cert-log-list li { display: flex; gap: var(--space-3); flex-wrap: wrap; align-items: baseline;
  padding-bottom: var(--space-2); border-bottom: 1px solid var(--border-soft); }


/* --- the certification and MRL screens: terse by rule ---------------------------
   The owner's rule (2026-10-01): actions are buttons with icons, the main one
   top right of the header; one line of copy; explanations in tooltips or one
   folded "How this works". Tables scroll inside their own box rather than
   pushing the page sideways. */

.page-head--act { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-5);
  flex-wrap: wrap; max-width: none; }
.page-head--act > div:first-child { min-width: 0; max-width: 62ch; }
.page-head--act p { margin-top: var(--space-2); }
.page-acts { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.page-acts form { display: contents; }

/* The counts that need chasing, as filter buttons. */
.acount-row { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-4); }
.acount { display: inline-flex; align-items: baseline; gap: var(--space-1); padding: var(--space-1) var(--space-4);
  border-radius: var(--radius-pill); border: 1px solid var(--border); background: var(--surface);
  font-size: var(--text-xs); color: var(--ink-muted); text-decoration: none; }
a.acount:hover { border-color: var(--border-strong); color: var(--ink); }
.acount--bad { color: var(--critical); border-color: var(--critical-strong-tint); background: var(--critical-tint); }
.acount--warn { color: var(--caution); border-color: var(--suggest-border); background: var(--caution-tint); }
.acount.is-on { box-shadow: inset 0 0 0 1px currentColor; }

/* One compact row of filters, buttons aligned to the inputs' baseline. */
.afilters--row { padding: var(--space-3) var(--space-4); gap: var(--space-3); max-width: 100%; }
.afilters--row .afilter select { max-width: 160px; }
.afilters--row .afilter input[type="text"] { width: 9ch; }
.afilter-acts { display: flex; gap: var(--space-2); align-items: center; align-self: flex-end; }
.afilters .afilter-acts .btn { margin-left: 0; }

.btn--icon { padding: 5px 7px; }
.btn--icon .icon { --icon-size: 13px; }
.a-actions form { display: inline-flex; }

/* Wide tables scroll in their own box; the page never does. */
.atable--certs, .atable--mrl, .atable--schemes { overflow-x: auto; }
.atable.atable--certs .arow { min-width: 900px; }
.atable.atable--mrl .arow { min-width: 880px; }
.atable.atable--schemes .arow { min-width: 980px; }

.cert-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.atable--certs .cert-name { max-width: 100%; font-size: var(--text-xs); color: var(--ink-muted); }

.ahow { margin-top: var(--space-6); font-size: var(--text-xs); color: var(--ink-muted); max-width: 80ch; }
.ahow > summary { cursor: pointer; display: inline-flex; align-items: center; gap: var(--space-2); color: var(--ink-faint);
  padding: var(--space-2) 0; }
.ahow > summary:hover { color: var(--ink-muted); }
.ahow > summary .icon { --icon-size: 12px; }
.ahow ul { margin: var(--space-2) 0 0; padding-left: var(--space-5); display: grid; gap: var(--space-1);
  line-height: var(--leading-normal); }
.anote--tight { margin-bottom: var(--space-4); }

/* The drawer sections: a heading with its icon buttons, then chips and numbers. */
.d-sec-act { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.d-sec-act > span:first-child { display: inline-flex; align-items: center; gap: var(--space-2); }
.d-acts { display: inline-flex; gap: var(--space-1); text-transform: none; letter-spacing: 0; }
.cert-row .cert-what, .mrl-row .mrl-what { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.cert-row .cert-name { font-weight: var(--weight-medium); color: var(--ink); }
.cert-meta { display: inline-flex; align-items: center; gap: var(--space-2); justify-content: flex-end; flex-wrap: wrap; }
.cert-row { grid-template-columns: 40px minmax(0, 1fr) auto 36px 26px; align-items: center; }
.cert-code { font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--ink-muted); }
.cert-stand, .cert-src, .cert-edit { justify-self: end; }
.cert-row .cert-what { flex-wrap: nowrap; }
.cert-row .cert-what > :not(.cert-name) { flex: none; }
.mrl-row { align-items: center; }

/* ---------- a vessel's panel ----------
   Opened from any vessel name (the line-up, a week's "Ships calling"). A
   heading, label/value facts, chips, and outbound buttons — nothing else on
   the panel, by the owner's rule; what a chip means is in its tooltip. */

.vlink { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--border-strong); }
.vlink:hover { color: var(--accent); border-bottom-color: var(--accent); }
.sail-vessel { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sail-about { font-size: var(--text-xs); margin: var(--space-3) 0; max-width: 72ch; }

.v-title { min-width: 0; }
.v-title h2 { display: flex; align-items: center; gap: var(--space-2); overflow-wrap: anywhere; }
.v-title h2 .icon { --icon-size: 18px; color: var(--accent); flex: none; }
.v-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.v-facts { margin-top: var(--space-5); }
.v-facts dd { overflow-wrap: anywhere; }
.v-facts > .v-wide { grid-column: 1 / -1; }
.v-facts > .v-half { grid-column: span 2; }
.v-facts dd .meta { margin-left: var(--space-1); }
.vpanel .d-sec > h3 .icon { --icon-size: 12px; vertical-align: -.1em; }
.vpanel .ship { grid-template-columns: 96px minmax(0, 1fr) 100px 64px; }
.vpanel .ship.is-past { color: var(--ink-faint); }
.v-about { margin-top: var(--space-6); font-size: var(--text-xs); color: var(--ink-muted); }
.v-about > summary { display: inline-flex; align-items: center; gap: var(--space-1); cursor: pointer;
                     font-weight: var(--weight-semi); list-style: none; }
.v-about > summary::-webkit-details-marker { display: none; }
.v-about > summary:hover { color: var(--ink); }
.v-about-list { display: grid; gap: var(--space-2); margin: var(--space-3) 0 0; }
.v-about-list > div { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: var(--space-3); }
.v-about-list dt { color: var(--ink-faint); }
.v-about-list dd { margin: 0; overflow-wrap: anywhere; }

@media (max-width: 640px) {
  .vpanel .ship { grid-template-columns: 76px minmax(0, 1fr) auto; }
  .vpanel .ship > :nth-child(3) { display: none; }
}

/* ---------- where the ships go next ----------
   The carriers' forward schedules: a timeline in the vessel panel, port chips
   on a week's ships, the sailings by market, the list beside a decision and
   the admin's sources. A port that reaches a desk market wears the suggest
   colour everywhere, so the eye finds Rotterdam in a row of names. */

.rot-src { font-size: var(--text-2xs); color: var(--ink-faint); font-weight: var(--weight-normal);
           text-transform: none; letter-spacing: 0; white-space: nowrap; }
.rot-src.is-stale { color: var(--caution); }
.rot-meta { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); margin: var(--space-1) 0 0; font-size: var(--text-xs); color: var(--ink-muted); }
.rot { list-style: none; margin: var(--space-2) 0 0; padding: 0 0 0 var(--space-4); position: relative; }
.rot::before { content: ""; position: absolute; left: 4px; top: 10px; bottom: 10px; width: 1px; background: var(--border-strong); }
.rot-stop { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(64px, auto); gap: var(--space-3);
            align-items: center; padding: var(--space-2) 0 var(--space-2) var(--space-3); font-size: var(--text-xs);
            border-bottom: 1px solid var(--border-soft); }
.rot-stop:last-child { border-bottom: 0; }
.rot-stop::before { content: ""; position: absolute; left: calc(-1 * var(--space-4) + 1px); top: 50%; width: 7px; height: 7px;
                    margin-top: -3.5px; border-radius: 50%; background: var(--surface); border: 1.5px solid var(--ink-faint); }
.rot-stop.is-anchor { color: var(--ink-muted); }
.rot-stop.is-anchor::before { background: var(--ink-faint); }
.rot-stop.is-market::before { background: var(--suggest); border-color: var(--suggest); }
.rot-stop.is-market .rot-port b { color: var(--ink); }
.rot-stop.is-ours .rot-port b { font-weight: var(--weight-medium); }
.rot-port { display: flex; align-items: center; gap: var(--space-2); min-width: 0; flex-wrap: wrap; }
.rot-port b { font-weight: var(--weight-semi); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rot-when { white-space: nowrap; }
.rot-chip { justify-self: end; white-space: nowrap; }

.ship-next { grid-column: 2 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1);
             min-width: 0; margin-top: calc(-1 * var(--space-1)); }
.ship-next > .icon { --icon-size: 11px; color: var(--ink-faint); }
/* The onward ports as words joined by "→"; a port that reaches one of our
   markets is bold in the accent. */
.pchip { display: inline-block; max-width: 16ch; font-size: var(--text-2xs); line-height: 18px; color: var(--ink-muted);
         white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
.pchip.is-ours { color: var(--ink-faint); }
.pchip.is-market { color: var(--accent); font-weight: var(--weight-bold); }
.pnext-sep { font-size: var(--text-2xs); color: var(--ink-faint); }
/* Who sells the ship, its service and voyage, the schedule's age: the
   quietest thing on the row, pushed to its end, wrapping under the ports
   where there is no room beside them. */
.ship-svc { margin-left: auto; padding-left: var(--space-2); font-size: var(--text-2xs); color: var(--ink-faint);
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.ship-svc.is-stale { color: var(--caution); }
.mkt-hit { display: inline-flex; align-items: center; gap: 2px; font-size: var(--text-2xs); font-weight: var(--weight-bold); color: var(--accent); }
.mkt-hit .icon { --icon-size: 10px; }
.firm { font-size: var(--text-2xs); color: var(--ink-faint); white-space: nowrap; }
.firm--actual { color: var(--positive); }
.firm--ts { color: var(--caution); }
.sail-none-m { color: var(--ink-muted); }

.sail-filters { align-items: flex-end; }
.sail-count { margin-left: auto; align-self: center; font-size: var(--text-xs); white-space: nowrap; }
.sail-market > h2 { flex-wrap: wrap; }
.sail-market > h2 .mono { text-transform: none; letter-spacing: 0; }
.atable--routes .arow { grid-template-columns: 110px minmax(0, 1.4fr) 100px 120px 48px 70px minmax(0, 1fr); }
.sail-lines, .sail-voy { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sail-chips { display: flex; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; }

.sail-to h3 + .sail-list, .sail-to h3 + p { margin-bottom: var(--space-4); }
.sail-list .ship { grid-template-columns: 84px minmax(0, 1fr) 120px 40px; }

.atable--sources .arow { grid-template-columns: minmax(0, 1fr) 150px 110px minmax(0, 1.2fr) minmax(0, 1.4fr); }
.atable--routes .arow { grid-template-columns: minmax(0, 0.9fr) 110px minmax(0, 2fr) 56px 72px 56px; }
.atable--routes .src-ports { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.atable--sources .arow > span:first-child small { display: block; }
.src-counts { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.src-error { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-xs); }
.src-held { margin-top: var(--space-3); font-size: var(--text-xs); display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.aform.aform--ports { max-width: 760px; }
.atable--ports .arow { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 150px; }
.atable--ports input[type="text"] { width: 100%; min-width: 0; box-sizing: border-box; }
.atable--ports .arow > span { min-width: 0; }
.aform--ports .actions { margin-top: var(--space-4); }

@media (max-width: 900px) {
  .atable--routes, .atable--sources { overflow-x: auto; }
  .atable.atable--routes .arow { min-width: 760px; }
  .atable.atable--sources .arow { min-width: 820px; }
  .sail-count { margin-left: 0; }
}
@media (max-width: 640px) {
  .rot-stop { grid-template-columns: minmax(0, 1fr) auto; }
  .rot-chip { display: none; }
  .ship-next { grid-column: 1 / -1; }
  .sail-list .ship { grid-template-columns: 76px minmax(0, 1fr) auto; }
  .sail-list .ship-where { display: none; }
  /* A phone reads a sailing as when, what and where it lands; carrier,
     voyage and firmness are in the vessel's panel. */
  .atable.atable--routes { overflow-x: visible; }
  .atable.atable--routes .arow { min-width: 0; grid-template-columns: 76px minmax(0, 1fr) 86px; gap: var(--space-3);
                                 padding: var(--space-3) var(--space-4); }
  .atable--routes .arow--head { display: none; }
  .atable--routes .arow > :nth-child(3), .atable--routes .arow > :nth-child(5),
  .atable--routes .arow > :nth-child(6), .atable--routes .arow > :nth-child(7) { display: none; }
  .atable.atable--ports .arow { min-width: 0; grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
  .atable--ports .arow--head { display: none; }
}

/* ---------- staging ----------
   A loud, single mark: the one place colour is used purely to say "this is not
   the real desk". */
.stage-badge {
  margin-left: auto; padding: 1px var(--space-2); border-radius: var(--radius-sm);
  background: var(--critical); color: var(--ink-on-accent);
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
}

/* How long until a reefer stack closes: plain until the last day, when it is
   the line that matters. Provisional dates read lighter. */
.stk { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-muted); white-space: nowrap; }
.stk.is-soon { color: var(--caution); font-weight: var(--weight-semi); }
.stk.is-past { color: var(--ink-faint); text-decoration: line-through; }
.stk.is-prov { font-style: italic; }
.sx-name.is-unnamed { font-weight: var(--weight-medium); color: var(--ink-muted); font-style: italic; }

/* ---------- Admin → Data feeds ----------
   A feed a row, with a cell a day for the fortnight; then the runs. The open
   feed is filled, like any selected row. */
.atable--feeds .arow { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) 150px 90px auto; }
.atable--feeds a.arow { color: inherit; text-decoration: none; }
.atable--feeds a.arow:hover { background: var(--surface-sunk); }
.atable--feeds .arow[aria-current="true"] { background: var(--accent-tint-strong); }
.feed-n { min-width: 0; }
.feed-n small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-n + span { overflow: hidden; text-overflow: ellipsis; }
.feed-days { display: grid; grid-auto-flow: column; grid-auto-columns: 9px; gap: 2px; justify-content: end; }
.feed-day { display: block; height: 16px; border-radius: 2px; background: var(--border-soft); }
.feed-day.is-good { background: var(--positive); }
.feed-day.is-warn { background: var(--caution); }
.feed-day.is-bad { background: var(--critical); }
.feed-day.is-quiet { background: var(--ink-faint); opacity: .45; }
.feeds-bar { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; margin: var(--space-5) 0 var(--space-3); font-size: var(--text-xs); }
.feeds-empty { margin: var(--space-4) 0; }
.feeds-more { margin-top: var(--space-3); }
.atable--runs .arow { grid-template-columns: 120px minmax(0, 1.1fr) 100px 64px minmax(0, 1.3fr) minmax(0, 1.4fr); font-size: var(--text-xs); }
.atable--runs .arow.is-child { background: var(--surface-sunk); }
.run-feed { display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; flex-wrap: wrap; }
.run-feed .icon { --icon-size: 12px; color: var(--ink-faint); align-self: center; }
.run-note { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.run-note.is-error { color: var(--critical); }
@media (max-width: 900px) {
  .atable.atable--feeds .arow, .atable.atable--runs .arow { min-width: 0; }
  .atable--feeds .arow { grid-template-columns: minmax(0, 1fr) auto; }
  .atable--feeds .arow > :nth-child(2), .atable--feeds .arow > :nth-child(4) { display: none; }
  .atable--feeds .arow > :nth-child(5) { grid-column: 1 / -1; justify-content: start; }
  .atable--runs .arow { grid-template-columns: minmax(0, 1fr) auto; }
  .atable--runs .arow > :nth-child(2) { justify-content: flex-end; }
  .atable--runs .arow > .is-empty { display: none; }
  .atable--runs .arow > :nth-child(n + 5) { grid-column: 1 / -1; }
  .atable--runs .arow--head { display: none; }
  .feed-n, .run-note { min-width: 0; }
  .feed-n small, .run-note { white-space: normal; }
}

/* ---------- a button that opens a small panel ----------
   For a rare action that needs a form (the MRL import): one button in the
   bar; the form opens under it. <details data-menu> closes on Escape and a
   click away (ui.js). */
.pop { position: relative; }
.pop > summary { list-style: none; }
.pop > summary::-webkit-details-marker { display: none; }
.pop[open] > summary { background: var(--surface-sunk); }
.pop-panel { position: absolute; z-index: var(--layer-menu); top: calc(100% + var(--space-2)); right: 0; width: min(360px, calc(100vw - 2 * var(--space-4)));
  padding: var(--space-4); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-popover); font-size: var(--text-sm); text-align: left; }
.pop-panel .a-dim { margin: var(--space-1) 0 var(--space-3); line-height: var(--leading-normal); }
.pop-h { margin: 0; font-weight: var(--weight-semi); color: var(--ink); }
.pop-file { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md); background: var(--surface); cursor: pointer; }
.pop-file:hover { background: var(--surface-sunk); }
.pop-file > .icon { --icon-size: 18px; color: var(--ink-faint); flex: none; }
.pop-file input[type="file"] { min-width: 0; flex: 1; font-size: var(--text-xs); color: var(--ink-muted); }
.pop-file input[type="file"]::file-selector-button { margin-right: var(--space-3); padding: var(--space-1) var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--surface-raised); color: var(--ink); font: inherit; font-weight: var(--weight-semi); cursor: pointer; }
.pop-acts { display: flex; justify-content: space-between; align-items: center; margin-top: var(--space-3); }

/* ---------- Help ----------
   Reading pages: a measure that reads (about 72 characters), screenshots
   framed like the cards they show, the page's contents beside it on a wide
   screen. The current page in a list is a fill, like any selection. */
.hp { max-width: 1180px; }
.hp-search { display: flex; align-items: center; gap: var(--space-3); max-width: 640px; margin: 0 0 var(--space-5);
  padding: var(--space-2) var(--space-3); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.hp-search > .icon { --icon-size: 16px; color: var(--ink-faint); flex: none; }
.hp-search input[type="search"] { flex: 1; min-width: 0; border: 0; background: transparent; font-size: var(--text-md); padding: var(--space-1) 0; }
.hp-search input[type="search"]:focus { outline: none; }
.hp-search:focus-within { border-color: var(--accent-border); box-shadow: 0 0 0 3px var(--accent-tint); }
.hp-sh { font-size: var(--text-sm); font-weight: var(--weight-semi); margin: 0 0 var(--space-2); }
.hp-clear { margin-left: var(--space-2); font-weight: var(--weight-normal); font-size: var(--text-xs); }
.hp-results { margin-bottom: var(--space-6); max-width: 760px; }
.hp-sections { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: var(--space-4); align-items: start; }
.hp-sec { padding: var(--space-4) var(--space-4) var(--space-3); }
.hp-sec-h { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--text-md); }
.hp-sec-h .icon { --icon-size: 18px; color: var(--accent); }
.hp-sec-s { margin: var(--space-1) 0 var(--space-3); }
.hp-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.hp-list a { display: grid; gap: 2px; padding: var(--space-2) var(--space-3); margin: 0 calc(-1 * var(--space-3)); border-radius: var(--radius-md);
  color: inherit; text-decoration: none; }
.hp-list a:hover { background: var(--surface-sunk); }
.hp-li-h { display: flex; align-items: baseline; gap: var(--space-2); }
.hp-li-h .icon { --icon-size: 14px; color: var(--accent); align-self: center; }
.hp-li-h b { font-weight: var(--weight-semi); color: var(--ink); }
.hp-li-s { font-size: var(--text-xs); color: var(--ink-muted); line-height: var(--leading-normal); }

.hp-crumbs { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); font-size: var(--text-xs); color: var(--ink-faint); }
.hp-crumbs a { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--ink-muted); text-decoration: none; }
.hp-crumbs a:hover { color: var(--ink); }
.hp-crumbs .icon { --icon-size: 13px; }
.hp-body { display: grid; grid-template-columns: minmax(0, 1fr) 240px; gap: var(--space-6); align-items: start; }
.hp-article { padding: var(--space-6) var(--space-7); min-width: 0; }
.hp-title { margin: 0; font-size: var(--text-xl); line-height: 1.25; }
.hp-lede { margin: var(--space-2) 0 var(--space-5); color: var(--ink-muted); font-size: var(--text-md); line-height: var(--leading-normal); max-width: 72ch; }
.hp-prose { font-size: var(--text-md); line-height: 1.65; color: var(--ink); }
.hp-prose > * { max-width: 72ch; }
.hp-prose h2 { margin: var(--space-6) 0 var(--space-2); font-size: var(--text-lg); scroll-margin-top: calc(var(--strip-h) + var(--space-3)); }
.hp-prose h3 { margin: var(--space-5) 0 var(--space-2); font-size: var(--text-md); scroll-margin-top: calc(var(--strip-h) + var(--space-3)); }
.hp-prose p { margin: 0 0 var(--space-3); }
.hp-prose ul, .hp-prose ol { margin: 0 0 var(--space-3); padding-left: var(--space-5); }
.hp-prose li { margin-bottom: var(--space-1); }
.hp-prose li > p { margin: 0; }
.hp-prose code { font-family: var(--font-mono, ui-monospace, monospace); font-size: .9em; padding: 1px 5px; border-radius: var(--radius-sm); background: var(--surface-sunk); }
.hp-prose pre { padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--surface-sunk); overflow-x: auto; font-size: var(--text-sm); }
.hp-prose pre code { padding: 0; background: none; }
.hp-prose blockquote { margin: 0 0 var(--space-3); padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--surface-sunk); color: var(--ink-muted); }
.hp-prose blockquote p:last-child { margin: 0; }
.hp-prose strong { font-weight: var(--weight-semi); }
.hp-table { max-width: 100%; overflow-x: auto; margin: 0 0 var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); }
.hp-prose .hp-table { max-width: none; }
.hp-table table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); line-height: var(--leading-normal); }
.hp-table th { text-align: left; padding: var(--space-2) var(--space-3); background: var(--surface-sunk); color: var(--ink-muted); font-weight: var(--weight-semi); font-size: var(--text-xs); }
.hp-table td { padding: var(--space-2) var(--space-3); border-top: 1px solid var(--border-soft); vertical-align: top; }
.hp-shot { margin: var(--space-4) 0 var(--space-5); max-width: none !important; }
.hp-shot img { display: block; max-width: 100%; height: auto; border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-card); }
.hp-shot a { display: inline-block; max-width: 100%; }
.hp-shot figcaption { margin-top: var(--space-2); font-size: var(--text-xs); color: var(--ink-muted); }
.hp-noshot { display: block; padding: var(--space-5); border: 1px dashed var(--border-strong); border-radius: var(--radius-md); color: var(--ink-faint); font-size: var(--text-sm); }
.hp-pager { display: flex; justify-content: space-between; gap: var(--space-4); margin-top: var(--space-7); padding-top: var(--space-4); border-top: 1px solid var(--border-soft); }
.hp-pg { display: grid; gap: 2px; padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-md); color: inherit; text-decoration: none; max-width: 48%; }
.hp-pg:hover { background: var(--surface-sunk); }
.hp-pg--next { text-align: right; margin-left: auto; }
.hp-side { position: sticky; top: calc(var(--strip-h) + var(--space-4)); display: grid; gap: var(--space-5); font-size: var(--text-sm); }
.hp-side-h { margin: 0 0 var(--space-2); font-size: var(--text-2xs); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-faint); }
.hp-toc ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; }
.hp-toc a { display: block; padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); color: var(--ink-muted); text-decoration: none; line-height: 1.35; }
.hp-toc a:hover { background: var(--surface-sunk); color: var(--ink); }
.hp-toc a[aria-current="page"] { background: var(--accent-tint-strong); color: var(--ink); font-weight: var(--weight-semi); }
.hp-toc li.is-sub a { padding-left: var(--space-4); font-size: var(--text-xs); }
@media (max-width: 1100px) {
  .hp-body { grid-template-columns: minmax(0, 1fr); }
  .hp-side { position: static; order: -1; }
  .hp-side .hp-toc:last-child { display: none; }
}
@media (max-width: 640px) {
  .hp-article { padding: var(--space-4); }
  .hp-sections { grid-template-columns: minmax(0, 1fr); }
  .hp-prose { font-size: var(--text-sm); }
}

/* A ship's panel: Monitor, a toggle whose "on" is a fill (VesselWatch). */
.v-monitor[aria-pressed="true"] { background: var(--accent-tint-strong); color: var(--ink); font-weight: 600; }
/* A dashboard's Email toggle: the same "on is a fill" (DashboardSubscription). */
.dash-follow[aria-pressed="true"] { background: var(--accent-tint-strong); color: var(--ink); }

/* ---------- inspection photos: thumbnails, the viewer, comments ---------- */
button.insp-photo { position: relative; padding: 0; width: 100%; cursor: zoom-in; }
.insp-marked { position: absolute; right: 4px; bottom: 4px; display: inline-flex; align-items: center; gap: 2px;
  padding: 1px 5px; border-radius: var(--radius-pill); background: rgba(0, 0, 0, .62); color: #fff; font-size: var(--text-2xs); font-weight: 600; }
.insp-marked .icon { --icon-size: 10px; }

/* The viewer is dark in both themes: a photo is judged on a neutral dark ground. */
.pv { --pv-bg: #111312; --pv-panel: #1b1e1c; --pv-line: #2c302d; --pv-ink: #eceeec; --pv-dim: #9aa19c;
  width: 100vw; height: 100dvh; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;
  background: var(--pv-bg); color: var(--pv-ink); }
.pv[open] { display: grid; grid-template-rows: auto minmax(0, 1fr); }
.pv::backdrop { background: rgba(0, 0, 0, .7); }
.pv [hidden] { display: none !important; }
.pv .btn--quiet { color: var(--pv-ink); background: transparent; border-color: transparent; }
.pv .btn--quiet:hover { background: rgba(255, 255, 255, .08); }
.pv .btn[aria-pressed="true"] { background: rgba(255, 255, 255, .16); color: #fff; }
.pv .a-dim { color: var(--pv-dim); }
.pv-bar { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--pv-line); background: var(--pv-panel); min-width: 0; flex-wrap: wrap; }
.pv-name { display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; flex: 1; }
.pv-name b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv-tools, .pv-acts { display: flex; align-items: center; gap: 2px; }
.pv-sep { width: 1px; height: 18px; margin: 0 var(--space-2); background: var(--pv-line); }
.pv-zoom { min-width: 52px; justify-content: center; }
.pv-colour { width: 20px; height: 20px; margin: 0 3px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; padding: 0;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .25) inset; }
.pv-colour--red { background: #e5484d; } .pv-colour--amber { background: #f5a524; } .pv-colour--white { background: #fff; }
.pv-colour[aria-pressed="true"] { border-color: var(--pv-panel); box-shadow: 0 0 0 2px #fff; }
.pv-main { display: grid; grid-template-columns: minmax(0, 1fr) 260px; min-height: 0; }
.pv-stage { position: relative; overflow: hidden; cursor: grab; touch-action: none; user-select: none; }
.pv-stage.is-panning { cursor: grabbing; }
.pv-stage.is-marking { cursor: crosshair; }
.pv-frame { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
.pv-frame img { display: block; width: 100%; height: 100%; pointer-events: none; }
.pv-ink { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.pv-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 40px; height: 40px; border-radius: 50%;
  background: rgba(0, 0, 0, .5); color: #fff; border: 1px solid rgba(255, 255, 255, .2); }
.pv-nav:hover { background: rgba(0, 0, 0, .75); }
.pv-nav--prev { left: var(--space-3); } .pv-nav--next { right: var(--space-3); }
.pv-caption { position: absolute; left: 50%; bottom: var(--space-3); transform: translateX(-50%); margin: 0; max-width: 80%;
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-md); background: rgba(0, 0, 0, .6); font-style: italic; font-size: var(--text-sm); }
.pv-text { position: absolute; z-index: 2; transform: translateY(-50%); width: 220px; height: 30px; padding: 0 var(--space-2);
  border-radius: var(--radius-sm); border: 1px solid #fff; background: rgba(0, 0, 0, .75); color: #fff; font-size: var(--text-sm); }
.pv-side { display: flex; flex-direction: column; gap: var(--space-2); min-height: 0; padding: var(--space-3);
  border-left: 1px solid var(--pv-line); background: var(--pv-panel); font-size: var(--text-xs); }
.pv-side h4 { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--text-xs); text-transform: uppercase;
  letter-spacing: .06em; color: var(--pv-dim); }
.pv-side h4 .icon { --icon-size: 12px; }
.pv-versions { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; overflow-y: auto; min-height: 0; flex: 1; align-content: start; }
.pv-version { display: grid; gap: 1px; width: 100%; text-align: left; padding: var(--space-2); border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--pv-ink); cursor: pointer; font: inherit; }
.pv-version span { color: var(--pv-dim); }
.pv-version q { color: var(--pv-ink); font-style: italic; }
.pv-version:hover { background: rgba(255, 255, 255, .06); }
.pv-version[aria-pressed="true"] { background: rgba(255, 255, 255, .14); }
.pv-version:disabled { cursor: default; color: var(--pv-dim); }
.pv-save { display: grid; gap: var(--space-2); padding-top: var(--space-3); border-top: 1px solid var(--pv-line); }
.pv-save input { height: 30px; padding: 0 var(--space-2); border-radius: var(--radius-sm); border: 1px solid var(--pv-line);
  background: var(--pv-bg); color: var(--pv-ink); font-size: var(--text-xs); }
.pv-save-acts { display: flex; justify-content: flex-end; gap: var(--space-2); }
.pv-msg { margin: 0; min-height: 1em; color: var(--pv-dim); }
@media (max-width: 760px) {
  .pv-main { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) auto; }
  .pv-side { border-left: 0; border-top: 1px solid var(--pv-line); max-height: 38dvh; }
}

.insp-comments { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--border-soft); }
.insp-comments h4 { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-2); font-size: var(--text-xs);
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted); }
.insp-comments h4 .icon { --icon-size: 12px; }
.insp-thread { list-style: none; margin: 0 0 var(--space-3); padding: 0; display: grid; gap: var(--space-3); }
.insp-thread header { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); }
.insp-thread .insp-unsay { margin-left: auto; }
.insp-thread p { margin: 2px 0 0; font-size: var(--text-sm); overflow-wrap: anywhere; }
.insp-say { display: grid; gap: var(--space-2); }
.insp-say textarea { width: 100%; resize: vertical; min-height: 56px; font: inherit; font-size: var(--text-sm); }
.insp-say .btn { justify-self: end; }
.insp-said { margin: var(--space-2) 0 0; font-size: var(--text-xs); }
.insp-said a { display: inline-flex; align-items: center; gap: var(--space-1); }
.insp-said .icon { --icon-size: 12px; }
