/* Rooted Shores — farm operations.

   Light, warm and calm. This is read by staff of mixed technical confidence, in a barn, in
   daylight, on a phone, and on a TV across a room — every one of those cases is worse on a dark
   surface, which glares in sun and blooms at distance.

   Dark follows the OS ON A DESK ONLY. A phone is the one screen that is never at a desk: it is
   held outdoors, in sun, by somebody whose phone has been on dark since they bought it and who
   never chose it for this. So the palette below is gated on width as well as on the OS, and a
   narrow screen stays light no matter what the OS says.

   The logo's green and cyan are kept as identity but darkened, because the original tones fail
   contrast on white. */

/* The hidden attribute must win over any display we set.
 *
 * The browser's own [hidden] rule is display:none, but ANY author rule with a display beats it —
 * so `.updatebar { display: flex }` left the update notice permanently on screen, on every page,
 * announcing an update that did not exist. A bar that is always on is worse than no bar: it
 * teaches people to ignore the one time it is real. */
[hidden] { display: none !important; }


:root {
  --paper:   #F5F7F2;   /* barely-green off-white: greenhouse linen, not cream */
  --card:    #FFFFFF;
  --card-2:  #FAFBF8;
  --ink:     #211E19;
  --muted:   #6B6357;
  --faint:   #938A7C;
  --line:    #E3E2D8;
  --line-2:  #EFEEE7;

  --sprout:      #3F7A34;
  --sprout-soft: #E8F2E3;
  --water:       #1F6E86;
  --water-soft:  #E2F0F4;
  --sun:         #B4740F;
  --sun-soft:    #FBF0DC;
  --clay:        #A8412A;
  --clay-soft:   #F9E7E2;

  /* Every other row, barely. See the rule down in the tables section for why it is this faint. */
  --stripe: rgba(33,30,25,.028);

  --radius: 12px;
  --r-s: 8px;
  --shadow: 0 1px 2px rgba(33,30,25,.05), 0 6px 20px rgba(33,30,25,.06);
  --shadow-s: 0 1px 2px rgba(33,30,25,.06);

  --font: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --mono: ui-monospace, 'SF Mono', 'DejaVu Sans Mono', Menlo, Consolas, monospace;

  /* Named so the browser draws its OWN furniture to match — form controls, scrollbars, the
     caret. Without it a dark-OS phone paints light text into a select we have made white. */
  color-scheme: light;
}

@media (prefers-color-scheme: dark) and (min-width: 760px) {
  :root {
    color-scheme: dark;
    --paper: #16150F; --card: #1E1D16; --card-2: #24231B;
    --ink: #F2EFE6; --muted: #A9A192; --faint: #857D6E;
    --line: #322F25; --line-2: #2A281F;
    --sprout: #8CC97A; --sprout-soft: #22301D;
    --water: #63C2DB;  --water-soft: #162B31;
    --sun: #E0A84C;    --sun-soft: #2E2416;
    --clay: #E08363;   --clay-soft: #2F1C16;
    --stripe: rgba(255,255,255,.032);
    --shadow: 0 1px 2px rgba(0,0,0,.3), 0 6px 20px rgba(0,0,0,.35);
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--paper); color: var(--ink);
  font-family: var(--font); line-height: 1.55;
  -webkit-font-smoothing: antialiased; min-height: 100vh;
  font-variant-numeric: tabular-nums;
}
a { color: var(--water); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: 3px solid var(--water); outline-offset: 2px; border-radius: 4px; }
.muted { color: var(--muted); }

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

.topbar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 11px 22px; background: var(--card); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 30;
}
/* The name, and nothing else. There was a barn here; see the note in base.html. */
.topbar .brand { display: flex; align-items: center; gap: 9px; }
.topbar .brand .name { font-weight: 650; letter-spacing: -.01em; line-height: 1.15; }
.topbar .brand .name small {
  display: block; color: var(--faint); font-size: 10px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
}
.topbar nav { display: flex; gap: 2px; margin-left: auto; align-items: center; flex-wrap: wrap; }
.topbar nav a {
  padding: 7px 13px; border-radius: 999px; color: var(--muted);
  font-size: 14px; font-weight: 550;
}
.topbar nav a:hover { background: var(--line-2); color: var(--ink); text-decoration: none; }
.topbar nav a.active { background: var(--sprout-soft); color: var(--sprout); }
.topbar .who { color: var(--faint); font-size: 13px; padding-left: 8px; }

.wrap { max-width: 1180px; margin: 0 auto; padding: 26px 20px 72px; }

/* ---- type -------------------------------------------------------------------------------- */

h1 { font-size: 27px; font-weight: 680; letter-spacing: -.02em; margin: 0 0 4px; }
h2 { font-size: 18px; font-weight: 650; letter-spacing: -.01em; margin: 0 0 12px; }
h3 { font-size: 15px; font-weight: 620; margin: 0 0 6px; }
.lede { color: var(--muted); margin: 0 0 22px; max-width: 68ch; }

/* Letter-spaced small caps: the section markers of the barn whiteboard this replaces. */
.eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--faint); margin: 34px 0 12px; display: flex; align-items: center; gap: 10px;
}
.eyebrow::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.page-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; margin-bottom: 8px; }
.page-head .actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.breadcrumb { color: var(--faint); font-size: 13px; margin-bottom: 8px; }
.breadcrumb a { color: var(--muted); }

/* ---- cards + layout ---------------------------------------------------------------------- */

.card, .panel {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-s);
}
.panel { padding: 20px; }
.panel + .panel { margin-top: 16px; }
.grid { display: grid; gap: 14px; }
.grid.g2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid.g3 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.grid.g4 { grid-template-columns: repeat(auto-fit, minmax(175px, 1fr)); }

/* ---- stats ------------------------------------------------------------------------------- */

.stat { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px 18px; }
.stat .k { font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--faint); }
.stat .v { font-size: 30px; font-weight: 640; letter-spacing: -.02em; margin-top: 4px; line-height: 1.1; }
.stat .u { font-size: 12px; color: var(--muted); margin-top: 2px; }
.stat.good .v { color: var(--sprout); }
.stat.warn .v { color: var(--clay); }

/* ---- progress: one visual language for "how far along" ----------------------------------- */

.bar { height: 8px; border-radius: 999px; background: var(--line-2); overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--sprout); border-radius: 999px; transition: width .5s ease; }
.bar.water > span { background: var(--water); }
.bar.sun > span { background: var(--sun); }
.bar-row { display: flex; align-items: center; gap: 12px; }
.bar-row .bar { flex: 1; }
.bar-row .pct { font-size: 13px; font-weight: 640; color: var(--muted); min-width: 44px; text-align: right; }

.split { display: flex; height: 12px; border-radius: 999px; overflow: hidden; background: var(--line-2); }
.split > span { display: block; }
.legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 10px; font-size: 13px; color: var(--muted); }
.legend i { width: 9px; height: 9px; border-radius: 3px; display: inline-block; margin-right: 6px; }

/* ---- pills ------------------------------------------------------------------------------- */

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 620; padding: 3px 10px; border-radius: 999px;
  background: var(--line-2); color: var(--muted); white-space: nowrap;
}
.pill.green { background: var(--sprout-soft); color: var(--sprout); }
.pill.blue  { background: var(--water-soft);  color: var(--water); }
.pill.amber { background: var(--sun-soft);    color: var(--sun); }
.pill.red   { background: var(--clay-soft);   color: var(--clay); }

/* Goal -> project -> task lineage. Shown, not merely stored: people work better when they can see
   what their work is for, and it costs one join. */
.lineage { font-size: 12px; color: var(--faint); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.lineage a { color: var(--muted); }
.lineage .sep { color: var(--line); }

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

label { display: block; font-size: 12px; font-weight: 650; color: var(--muted); margin-bottom: 5px; }
input, select, textarea {
  width: 100%; padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--r-s);
  background: var(--card); color: var(--ink); font-family: var(--font); font-size: 14px;
}
/* Not everything with a border is a text field. The blanket width:100% above stretched every
   checkbox across its row, which pushed the name beside it onto the next line — the people
   picker on a project read as a column of empty boxes with names underneath them. */
input[type="checkbox"], input[type="radio"] {
  width: auto; padding: 0; margin: 0; vertical-align: middle;
}
textarea { min-height: 70px; resize: vertical; }

/* Tick-as-many-as-you-need lists: chips that wrap, not a stack of full-width rows. */
.peoplepick { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 4px 0 0; }
.peoplepick .pick {
  display: inline-flex; align-items: center; gap: 7px; margin: 0;
  font-size: 13.5px; font-weight: 500; color: var(--ink); cursor: pointer;
}
/* Names you turn on and off, one click each.
 *
 * For any question of the shape "who is on this" — a project's people, and anywhere else the same
 * question comes up. One list of everybody, each name its own little form, and the click is the
 * save: no Save button, and no second list of the people who are not on it. The face and the tick
 * are what say which way it is set, because a colour alone is not an answer on a bright day.
 *
 * Whoever holds the list decides what a press means; this only draws it. */
.peopletoggle { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 0; }
.peopletoggle form { margin: 0; }
.peopletoggle button {
  display: inline-flex; align-items: center; gap: 9px; padding: 6px 14px 6px 6px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--card);
  font-size: 14px; font-weight: 550; color: var(--muted); cursor: pointer;
}
.peopletoggle button:hover { background: var(--line-2); }
.peopletoggle .avatar { width: 30px; height: 30px; font-size: 12px; opacity: .45; }
.peopletoggle .nm { line-height: 1; }
/* The tick holds its width whether it is shown or not, so a name does not shuffle sideways as it
   goes on and off — a list that moves under the finger is a list you mis-tap. */
.peopletoggle .tick { width: 12px; color: var(--sprout); font-weight: 700; visibility: hidden; }
.peopletoggle button.on {
  background: var(--sprout-soft); border-color: var(--sprout); color: var(--ink);
}
.peopletoggle button.on .avatar { opacity: 1; }
.peopletoggle button.on .tick { visibility: visible; }
.peopletoggle button:focus-visible { outline: 2px solid var(--water); outline-offset: 2px; }

input:focus, select:focus, textarea:focus { outline: none; border-color: var(--water); box-shadow: 0 0 0 3px var(--water-soft); }
.field { margin-bottom: 12px; }
.field label { margin-bottom: 5px; }
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; }
.row > * { flex: 1; min-width: 140px; }
.row > .shrink { flex: 0 0 auto; min-width: 0; }
/* A .field carries 12px under it for stacked forms. Inside a .row the items are bottom-aligned, so
   that margin lifted the control 12px clear of the button beside it and the pair read as two rows
   that had wrapped rather than one control with its button. */
.row > .field { margin-bottom: 0; }

/* A form is a block element, so two of them in one table cell put Save on its own line and the
   × underneath it — which is what the routines page had been doing since it was written. The
   class was on the markup in five templates and had never existed in here.

   nowrap on purpose: these are two or three controls that together mean one thing, and a Save
   that wraps away from the field it saves is a Save nobody presses. The CELL decides whether the
   group as a whole moves; the group never comes apart. */
form.inline, .inline {
  display: inline-flex; align-items: center; gap: 6px; flex-wrap: nowrap;
  vertical-align: middle; margin: 0;
}
form.inline + form.inline, .inline + .inline { margin-left: 6px; }
form.inline .field, .inline .field { margin-bottom: 0; }
/* Controls keep their own size rather than being stretched by the flex row they now sit in. */
form.inline > *, .inline > * { flex: 0 0 auto; width: auto; }
form.inline select, .inline select, form.inline input, .inline input { width: auto; min-width: 0; }
form.inline input[type="text"], .inline input[type="text"] { min-width: 90px; }
/* Where several of these share a cell, they line up right and stay on one line. */
td.actions, th.actions { text-align: right; white-space: nowrap; padding-right: 0; }
td.num .inline, td.actions .inline { white-space: nowrap; }

/* Saving in the background is faster than a page load, which is the problem: without a mark it
   is indistinguishable from a button that did nothing. Half a second of dimming is enough to
   read as "that went". */
form.htmx-request { opacity: .5; transition: opacity .1s; }
form.htmx-request button { pointer-events: none; }

button, .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font-family: var(--font); font-size: 14px; font-weight: 620; padding: 9px 16px;
  border-radius: var(--r-s); border: 1px solid var(--line);
  background: var(--card); color: var(--ink); cursor: pointer; text-decoration: none;
  transition: background .15s, filter .15s;
}
button:hover, .btn:hover { background: var(--line-2); text-decoration: none; }
button.primary, .btn.primary { background: var(--sprout); border-color: var(--sprout); color: #fff; }
button.primary:hover, .btn.primary:hover { filter: brightness(1.08); background: var(--sprout); }
button.ghost, .btn.ghost { background: transparent; color: var(--muted); }
button.ghost:hover { background: var(--line-2); color: var(--ink); }
button.quiet { border-color: transparent; background: transparent; color: var(--muted); }
button.quiet:hover { background: var(--line-2); }
button.danger { color: var(--clay); border-color: var(--clay-soft); background: transparent; }
button.danger:hover { background: var(--clay-soft); }
/* A control that would do the wrong thing, drawn as one that cannot be pressed. Not `disabled`:
   these are anchors, and a grey anchor that still navigates is the thing being prevented. */
button.off, .btn.off { opacity: .45; cursor: default; pointer-events: none; }
button.small, .btn.small { padding: 5px 11px; font-size: 13px; }
.num-control { display: flex; gap: 8px; align-items: center; }
.num-control input { width: 100px; }

details.adder > summary {
  cursor: pointer; font-size: 14px; font-weight: 620; color: var(--water);
  padding: 8px 0; list-style: none;
}
details.adder > summary::-webkit-details-marker { display: none; }
details.adder > summary::before { content: "+ "; font-weight: 700; }
details.adder[open] > summary::before { content: "− "; }

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

table { width: 100%; border-collapse: collapse; font-size: 14px; }
th {
  text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--faint); padding: 0 12px 9px 0;
  border-bottom: 1px solid var(--line);
}
td { padding: 11px 12px 11px 0; border-bottom: 1px solid var(--line-2); vertical-align: top; }
tr:last-child td { border-bottom: none; }

/* Alternating rows, on every table on the office side.
 *
 * Barely there on purpose. A wide row — a task with a name, two people, an estimate, an amount —
 * is read across, and the eye needs something to hold the line; a stripe you notice is a stripe
 * that says these rows are two different kinds of thing, which they are not. If it reads as a
 * colour on a barn screen in daylight it is too strong.
 *
 * On the cells rather than the row, because a row's background sits behind its cells and loses to
 * anything a page paints on a td. The week grid is left out: its cells carry their own colours,
 * and Tuesday is not a stripe. */
tbody tr:nth-child(even) > td { background: var(--stripe); }
/* A row that is drawn for a REASON keeps its own colour: the egg report hatches the gap
   between one week and the next, and a stripe on top of it made the hatching disappear on
   every other one. Anything with a meaning of its own opts out here. */
tbody tr.gapline > td, tbody tr.struckrow > td { background: revert; }
.weekgrid tbody tr:nth-child(even) > td { background: none; }
td.num, th.num { text-align: right; padding-right: 0; }
td .state { font-size: 11px; padding: 2px 9px; border-radius: 999px; background: var(--line-2); color: var(--muted); }
.state.acked { background: var(--sprout-soft); color: var(--sprout); }
.state.failed { background: var(--clay-soft); color: var(--clay); }
.state.pending, .state.sent { background: var(--sun-soft); color: var(--sun); }

/* ---- empty states: an invitation to act, never a shrug ----------------------------------- */

.empty {
  padding: 30px 22px; text-align: center; color: var(--muted);
  border: 1px dashed var(--line); border-radius: var(--radius); background: var(--card-2);
}
.empty strong { display: block; color: var(--ink); font-weight: 620; margin-bottom: 4px; }

.note { font-size: 13px; color: var(--muted); line-height: 1.5; }
/* A note that has to say something went wrong. Same size, different colour: it is still a note,
   not an alarm. */
.note.bad { color: var(--clay); font-weight: 600; }
/* WHAT WAS TYPED, IN THE SHAPE IT WAS TYPED. A description written as three lines — where the
   crate is, which gate, who to ask — renders as one paragraph in HTML, so the shape somebody put
   into it is thrown away between the box and the page. Applied by hand rather than to `.note`
   generally: a note whose SOURCE spans several lines would then break where the template happens
   to wrap, which is a different thing entirely. */
.lines { white-space: pre-line; }
.err { color: var(--clay); font-size: 13px; margin: 8px 0 0; }
.flash { padding: 11px 14px; border-radius: var(--r-s); font-size: 14px; margin-bottom: 16px; }
.flash.ok  { background: var(--sprout-soft); color: var(--sprout); }
.flash.bad { background: var(--clay-soft); color: var(--clay); }

/* ---- reconciliation: neutral by design --------------------------------------------------- */

.recon {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 1px;
  background: var(--line); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
.recon .cell { background: var(--card); padding: 14px 16px; }
.recon .cell.result { background: var(--card-2); }
.recon .cell.flagged { background: var(--clay-soft); }
.recon .k { font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--faint); }
.recon .v { font-size: 22px; font-weight: 640; margin-top: 3px; }
.recon .cell.flagged .v { color: var(--clay); }

/* ---- device panel (existing components, restyled) ---------------------------------------- */

.devices { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.card { overflow: hidden; }
.card-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--line); }
.card-head .title { font-weight: 640; }
.card-head .kind { color: var(--faint); font-size: 11px; text-transform: uppercase; letter-spacing: .1em; }
.card-head a.title { color: var(--ink); }
.card-head a.title:hover { color: var(--water); }

.status { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; font-size: 12px; color: var(--muted); }
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--faint); }
.dot.online { background: var(--sprout); box-shadow: 0 0 0 3px var(--sprout-soft); }
.dot.no_stream, .dot.stale_data { background: var(--sun); box-shadow: 0 0 0 3px var(--sun-soft); }
.dot.offline, .dot.never { background: var(--faint); }

.tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--line); }
.tile { background: var(--card); padding: 14px 16px; min-height: 78px; }
.tile .k { color: var(--faint); font-size: 11px; font-weight: 650; letter-spacing: .06em; }
.tile .v { font-size: 26px; font-weight: 640; margin-top: 4px; letter-spacing: -.02em; }
.tile .v .u { font-size: 14px; color: var(--muted); font-weight: 550; margin-left: 4px; }
.tile.bool .v.on { color: var(--sprout); }
.tile.bool .v.off { color: var(--faint); }

.controls { padding: 12px 16px 16px; border-top: 1px solid var(--line); display: grid; gap: 10px; background: var(--card-2); }
.control { display: flex; align-items: center; gap: 12px; }
.control .lbl { font-size: 14px; flex: 1; }
.control .pending { font-size: 11px; color: var(--sun); }

.switch { position: relative; width: 52px; height: 30px; flex: 0 0 auto; }
.switch input { opacity: 0; width: 0; height: 0; }
.slider { position: absolute; inset: 0; background: var(--line); border: 1px solid var(--line); border-radius: 999px; cursor: pointer; transition: .18s; }
.slider::before { content: ""; position: absolute; height: 22px; width: 22px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: .18s; box-shadow: var(--shadow-s); }
.switch input:checked + .slider { background: var(--sprout); border-color: var(--sprout); }
.switch input:checked + .slider::before { transform: translateX(22px); }
.switch.busy .slider { opacity: .5; }

.enroll-card { background: var(--water-soft); border: 1px dashed var(--water); border-radius: var(--radius); padding: 16px; margin-top: 14px; }
.enroll-card code { font-family: var(--mono); color: var(--water); font-size: 13px; word-break: break-all; }
.enroll-card pre { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-s); padding: 12px; overflow-x: auto; font-family: var(--mono); font-size: 12.5px; }
.code-pill { display: inline-block; font-family: var(--mono); font-size: 20px; letter-spacing: 1px; color: var(--sprout); background: var(--card); border: 1px solid var(--line); padding: 8px 14px; border-radius: var(--r-s); font-weight: 650; }

.sparkline { display: block; width: 100%; height: 40px; margin-top: 8px; }

/* ---- login ------------------------------------------------------------------------------- */

.login-wrap { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.login-card { width: 100%; max-width: 380px; text-align: center; }
.login-card img { height: 80px; margin-bottom: 6px; }
.login-card .panel { text-align: left; margin-top: 18px; }

/* ---- staff kiosk: big targets, muddy hands, one shared tablet ---------------------------- */

.kiosk { max-width: 900px; margin: 0 auto; padding: 26px 18px 60px; }
.people { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px; }
.person {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 22px 12px; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); cursor: pointer; text-decoration: none; color: var(--ink);
  box-shadow: var(--shadow-s); transition: transform .12s, box-shadow .12s;
}
.person:hover { transform: translateY(-2px); box-shadow: var(--shadow); text-decoration: none; }
.avatar {
  width: 58px; height: 58px; border-radius: 50%; display: grid; place-items: center;
  color: #fff; font-weight: 700; font-size: 21px; letter-spacing: -.02em;
}
.person .nm { font-weight: 620; font-size: 15px; text-align: center; }
.person .rl { font-size: 12px; color: var(--faint); }
.person.on { border-color: var(--sprout); box-shadow: 0 0 0 3px var(--sprout-soft); }

.pinpad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; max-width: 300px; margin: 20px auto 0; }
.pinpad button { font-size: 24px; font-weight: 620; padding: 18px 0; }
.pindots { display: flex; gap: 12px; justify-content: center; margin: 18px 0 4px; }
.pindots i { width: 14px; height: 14px; border-radius: 50%; background: var(--line); display: block; }
.pindots i.on { background: var(--sprout); }

.activity-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
.activity {
  padding: 20px 16px; border-radius: var(--radius); border: 2px solid var(--line);
  background: var(--card); font-weight: 620; font-size: 16px; cursor: pointer; text-align: left;
}
.activity:hover { background: var(--line-2); }
.activity.current { border-color: var(--sprout); background: var(--sprout-soft); color: var(--sprout); }
.activity .sub { display: block; font-size: 12px; font-weight: 550; color: var(--faint); margin-top: 3px; }

@media (max-width: 640px) {
  .wrap { padding: 18px 14px 60px; }
  h1 { font-size: 23px; }
  .topbar { padding: 10px 14px; }
  .topbar nav a { padding: 6px 10px; font-size: 13px; }

  /* The head STACKS on a phone rather than wrapping.
     It was a flex row whose buttons would not shrink, so the title column — flex:1 with no basis
     — gave up every pixel it had. On a 390px screen "Farm Cleanup" was rendered in a box fourteen
     pixels wide and read "Far". Wrapping never rescued it, because a column that can shrink to
     nothing never has to wrap. Stacked, the name gets the full line and the buttons get theirs. */
  .page-head { display: block; }
  .page-head .actions { margin: 10px 0 0; flex-wrap: wrap; }
}

/* ---- plot map: beds drawn as the strips they actually are -------------------------------- */

.plotmap { display: flex; gap: 5px; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }
.bed {
  flex: 1 1 0; min-width: 62px; min-height: 120px; text-decoration: none; color: var(--ink);
  border: 1.5px solid var(--line); border-radius: 7px; background: var(--card-2);
  padding: 9px 7px; display: flex; flex-direction: column; gap: 3px; align-items: center;
  text-align: center; transition: transform .1s, box-shadow .1s;
}
.bed:hover { transform: translateY(-2px); box-shadow: var(--shadow); text-decoration: none; }
.bed .bnum { font-size: 11px; font-weight: 700; color: var(--faint); }
.bed .bcrop { font-size: 13px; font-weight: 620; line-height: 1.2; }
.bed .bwhen { font-size: 11px; color: var(--muted); margin-top: auto; }
.bed .bempty { font-size: 12px; color: var(--faint); font-style: italic; margin-top: auto; }


/* ---- update notice ------------------------------------------------------------------------- */
.updatebar {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px; z-index: 80;
  background: var(--ink); color: var(--paper);
  padding: 11px 12px 11px 18px; border-radius: 999px; font-size: 14px;
  box-shadow: 0 8px 28px rgba(0,0,0,.28);
}
.updatebar button {
  background: var(--sprout); color: #fff; border: none; font: inherit; font-weight: 650;
  padding: 7px 15px; border-radius: 999px; cursor: pointer; width: auto;
}
.updatebar button.dismiss {
  background: transparent; color: var(--paper); opacity: .6; padding: 4px 9px; font-size: 19px;
  line-height: 1;
}
.updatebar button.dismiss:hover { opacity: 1; }
@media (max-width: 520px) { .updatebar { left: 12px; right: 12px; transform: none; border-radius: 14px; } }


/* ---- board: it updates itself, and says so when it cannot ----------------------------------- */
.boardstale {
  position: fixed; right: 16px; top: 16px; z-index: 90;
  background: var(--clay); color: #fff; padding: 8px 14px; border-radius: 999px;
  font-size: 14px; font-weight: 650;
}


/* ---- pinning a project to the barn board ---------------------------------------------------
   A star in the corner rather than a button in the row: the button wrapped the pill line onto two
   lines on every card, and the state it shows is binary, so a control the size of a word was doing
   the job of a control the size of a glyph. */
.pcard { position: relative; }

/* ---- the three lists on /projects ----------------------------------------------------------
   The page is about live work. What is waiting to be reviewed sits above it because it is a short
   queue somebody has to clear; what has been reviewed sits behind a summary, because the reward
   for finishing a project should not be another card to scroll past for ever. */
.secthead { display: flex; align-items: baseline; gap: 9px; margin: 26px 0 4px; }
.secthead .ct {
  font-size: 12px; font-weight: 700; color: var(--paper); background: var(--clay);
  border-radius: 999px; padding: 1px 8px; letter-spacing: .02em;
}
details.finished { margin-top: 30px; border-top: 1px solid var(--line); padding-top: 12px; }
details.finished > summary {
  cursor: pointer; font-size: 14px; font-weight: 620; color: var(--muted);
  padding: 6px 0; list-style: none;
}
details.finished > summary::-webkit-details-marker { display: none; }
details.finished > summary::before { content: "\25b8  "; }
details.finished[open] > summary::before { content: "\25be  "; }
/* Reviewed cards are quiet but still legible — greying them right out would defeat the one thing
   the drawer is for, which is going back to look something up. */
.pcard.done { opacity: .72; }
.pcard.done:hover, .pcard.done:focus-within { opacity: 1; }

.pinstar { position: absolute; top: 10px; right: 12px; margin: 0; }
.pinstar button {
  background: none; border: none; padding: 2px 4px; width: auto;
  font-size: 22px; line-height: 1; cursor: pointer; color: var(--line-2);
  transition: color .12s;
}
.pinstar button:hover { color: var(--faint); }
.pinstar button.on { color: #E0A33C; }

/* ---- the settings menu -------------------------------------------------------------------
   A <details> rather than a scripted dropdown: it opens with a click and closes with Escape on
   its own, keyboard included, and it still works if the JavaScript on a page has thrown. */
.topbar nav details.menu { position: relative; display: inline-block; }
.topbar nav details.menu > summary {
  list-style: none; cursor: pointer; padding: 6px 10px; border-radius: 7px;
  color: var(--muted); font-size: 14px; white-space: nowrap;
}
.topbar nav details.menu > summary::-webkit-details-marker { display: none; }
.topbar nav details.menu > summary::after { content: " ▾"; opacity: .55; }
.topbar nav details.menu > summary:hover { color: var(--ink); background: var(--card-2); }
.topbar nav details.menu > summary.active { color: var(--sprout); font-weight: 650; }
.topbar nav .menupop {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40; min-width: 170px;
  display: flex; flex-direction: column; padding: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 10px 26px rgba(0,0,0,.16);
}
.topbar nav .menupop a { padding: 8px 10px; border-radius: 6px; }

/* ---- Work: a word and a caret ---------------------------------------------------------------
   The label is a link to the crew board and the caret opens the rest, so the page behind the word
   is one click and the planning screens are two — on a phone as well, where there is no hover to
   open anything with. The caret needs a target of its own, hence the padding on the summary and
   the anchor stopping short of it. */
.topbar nav details.work { margin-right: 2px; }
.topbar nav details.work > summary { padding-right: 6px; }
.topbar nav details.work > summary > a {
  color: inherit; text-decoration: none; padding: 6px 2px 6px 0; margin: -6px 0;
}
.topbar nav details.work > summary::after { content: " ▾"; padding: 0 4px; }
.topbar nav details.work .menupop { right: auto; left: 0; }

/* ---- handing work out ------------------------------------------------------------------------ */

/* The bar above the task table. Deliberately plain: it is a control, not a feature. */
.handout { display: grid; gap: 10px; }
.handout .row { align-items: baseline; }
.handout .row > * { flex: 0 0 auto; min-width: 0; }
.handout .row .note { flex: 1 1 260px; }
.handout .peoplepick { gap: 8px; }
.handout .peoplepick .chip {
  display: inline-flex; align-items: center; gap: 7px; margin: 0; cursor: pointer;
  padding: 7px 12px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--card); font-size: 13.5px; font-weight: 550;
}
.handout .peoplepick .chip:has(input:checked) {
  background: var(--sprout-soft); border-color: var(--sprout); color: var(--sprout);
}
.handout .peoplepick .chip input { width: auto; margin: 0; }
.tasktable td.pickbox { text-align: center; vertical-align: top; padding-top: 14px; }
.tasktable td.pickbox input { width: auto; }

/* The task table's own column widths live in project.html, beside the markup they count. */

/* ---- the week --------------------------------------------------------------------------------- */

.weekwrap { overflow-x: auto; }
.weekgrid { width: 100%; border-collapse: collapse; min-width: 900px; }
.weekgrid th, .weekgrid td {
  border: 1px solid var(--line); padding: 8px 9px; vertical-align: top; text-align: left;
}
.weekgrid thead th { background: var(--card-2); font-size: 12.5px; }
.weekgrid thead th .dn { display: block; font-weight: 700; }
.weekgrid thead th .dd { display: block; color: var(--muted); font-weight: 500; }
.weekgrid th.who { width: 140px; font-size: 14px; font-weight: 650; background: var(--card-2); }
.weekgrid td { width: 12.2%; }
/* A day nobody is scheduled is a day that cannot take work, and it should look like one. */
.weekgrid td.off { background: repeating-linear-gradient(135deg,
  transparent 0 6px, rgba(0,0,0,.025) 6px 12px); }
.weekgrid .is-today { box-shadow: inset 0 3px 0 var(--sprout); }
.weekgrid .hrs { display: block; font-size: 11.5px; color: var(--muted); margin-bottom: 6px; }
/* Scoped to .daylist rather than to .weekgrid: the day board draws the same chip, and `.on` on
   its own is a class four other things on this site already use. */
.daylist .on {
  display: flex; align-items: center; gap: 5px; margin-bottom: 5px;
  background: var(--sprout-soft); border-radius: 8px; padding: 4px 6px;
  font-size: 12.5px; font-weight: 600; line-height: 1.25;
}
.daylist .on .em { font-size: 14px; }
.daylist .on form { margin-left: auto; }
.daylist .on form + form { margin-left: 0; }
.daylist .on .x {
  background: none; border: 0; padding: 0 3px; color: var(--muted); font-size: 14px;
  cursor: pointer; min-height: 0;
}
.daylist .on .x.rest { color: var(--sprout); }
.weekgrid select.mini, .daycol select.mini { width: 100%; font-size: 12px; padding: 5px 6px; }

/* ---- what a chip is doing --------------------------------------------------------------------
   Two marks, and they have to survive being read at arm's length across seven columns.

   LIVE is an outline: a ring in the sprout green over a white fill, so the chip lifts off the
   others rather than merely changing shade. Somebody is stood in a field doing this right now.
   DONE is struck through and drained — the same treatment the crew's own screen gives a finished
   row, so the two screens say the same thing the same way. Finished work stays on the grid: a
   list that empties as it is worked reads as though the work was never there. */
/* The name is wrapped in a span only so the strike-through lands on it and not on the buttons
   beside it. It is deliberately given NO width behaviour: as a flex item free to shrink it wraps
   a character per line in a column a seventh of the screen wide, which is what it did for one
   round of this. */
.daylist .on.live { box-shadow: 0 0 0 2px var(--sprout); background: #fff; }
.daylist .on .nowdot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--sprout); flex: 0 0 auto;
  box-shadow: 0 0 0 3px var(--sprout-soft);
}
.daylist .on .nowtag, .daylist .on .donetag {
  font-size: 10.5px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  flex: 0 0 auto;
}
.daylist .on .nowtag { color: var(--sprout); }
.daylist .on .donetag { color: var(--faint); }
.daylist .on.done { background: var(--card-2); color: var(--faint); box-shadow: none; }
.daylist .on.done .what { text-decoration: line-through; }
.daylist .on.done .em { filter: grayscale(.75); opacity: .55; }
.daylist .on.done .grip { opacity: .4; }

/* The routines table below the grid, marked the same way. */
tr.struckrow .what { text-decoration: line-through; }
tr.struckrow td, tr.struckrow .what { color: var(--faint); }

/* The routines fold is a LIST OF NAMES, not a table (Adam, 1 Sep). Same mark for a finished round
   as everywhere else on this page, applied to a line rather than a row. */
.plainlist { list-style: none; margin: 0; padding: 0; }
.plainlist li { padding: 9px 2px; border-bottom: 1px solid var(--line); font-size: 1.02rem; }
.plainlist li:last-child { border-bottom: 0; }
.plainlist li.struckrow { color: var(--faint); text-decoration: line-through; }

/* ---- week or day ------------------------------------------------------------------------------ */

.planhead { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 22px 0 8px; }
.planhead .stepper { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.planhead .stepper strong { min-width: 13ch; text-align: center; }
.planhead .viewtoggle { margin-left: auto; display: flex; border: 1px solid var(--line);
  border-radius: 9px; overflow: hidden; background: var(--card); }
.planhead .seg {
  padding: 6px 14px; font-size: 13px; font-weight: 650; color: var(--muted);
  text-decoration: none;
}
.planhead .seg.is-on { background: var(--sprout); color: #fff; }
.planhead .seg:not(.is-on):hover { background: var(--card-2); }

/* One day, one column per person. Columns hold a floor width so that squeezing a ninth person in
   cannot turn the day board back into the thing it was built to replace.

   THEY WRAP — they used to run off the side instead. The wrapper scrolls sideways, and sideways
   scroll inside a page that scrolls down is a thing nobody finds: with eighteen names the board
   showed seven and gave no sign whatever that the other eleven existed. A planner that hides
   people is worse than one that is tall. Now the row wraps onto the next line and everybody is on
   the page; the floor width is untouched, so no column is ever squeezed to fit. The people who
   are actually rostered are sorted first, so a second line is the people who are not. */
.dayboard { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }
.daycol {
  flex: 1 1 220px; min-width: 210px; border: 1px solid var(--line); border-radius: 10px;
  padding: 9px 10px; background: var(--card);
}
/* Narrower, because the board now carries a column for EVERY person whether they are in or not —
   work is handed out by dragging it onto somebody, and a person with no column is a person you
   cannot hand anything to. They are drop targets rather than plans, so they take the space of one
   and leave the width to the people who are actually on today. */
.daycol.off { background: repeating-linear-gradient(135deg,
  transparent 0 6px, rgba(0,0,0,.025) 6px 12px);
  flex: 0 1 160px; min-width: 150px; }
.daycol .dayhead { margin-bottom: 8px; }
.daycol .dayhead strong { display: block; font-size: 14.5px; }
.daycol .hrs { font-size: 11.5px; color: var(--muted); }
.daycol .daylist { min-height: 10px; }
.daycol .daylist .on { margin-bottom: 6px; font-size: 13px; padding: 6px 8px; }
/* The rule between the people who are on today and the people who are not. Vertical, because
   the board is columns, and labelled, because an unexplained line is just a gap. */
.dayrule {
  flex: 0 0 auto; align-self: stretch; width: 1px; background: var(--line);
  position: relative; margin: 0 12px;
}
.dayrule span {
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%) rotate(180deg);
  writing-mode: vertical-rl; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); background: var(--bg, #fff); padding: 6px 1px;
}
/* One dialog serves every column: the button fills in whose it is. */
.assignbox { border: 1px solid var(--line); border-radius: 12px; padding: 0; max-width: 640px;
  width: calc(100% - 32px); }
.assignbox::backdrop { background: rgba(20,18,15,.42); }
.assignbox .abhead { padding: 14px 16px 10px; border-bottom: 1px solid var(--line); }
.assignbox .abhead h3 { margin: 0; font-size: 16px; }
.assignbox .abbody { padding: 8px 16px; max-height: 52vh; overflow: auto; }
.assignbox .abfoot { padding: 12px 16px; border-top: 1px solid var(--line); display: flex;
  justify-content: space-between; align-items: center; gap: 10px; }
.assignbox .abproj { font-size: 11.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin: 12px 0 4px; }
.assignbox label.abtask { display: flex; gap: 9px; align-items: baseline; padding: 5px 2px;
  border-bottom: 1px solid var(--line); cursor: pointer;
  /* Shift-clicking a range would otherwise drag a blue selection across the list. */
  -webkit-user-select: none; user-select: none; }
.assignbox label.abtask:last-child { border-bottom: 0; }
.assignbox label.abtask .who { margin-left: auto; font-size: 11.5px; color: var(--muted); }
.spareday { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.spareday .spare { font-size: 12.5px; }

/* ---- what is behind a chip ---------------------------------------------------------------------
   The name is a button because it opens something. It must not look like one: a chip full of
   buttons reads as a toolbar, and this is a label you can press. */
.daylist .on .what.open {
  background: none; border: 0; padding: 0; margin: 0; min-height: 0; font: inherit; color: inherit;
  text-align: left; cursor: pointer;
}
.daylist .on .what.open:hover { text-decoration: underline; }
.daylist .on.done .what.open { text-decoration: line-through; }

/* How much of a project has this person's name on it. "no tasks" is the whole point of it being
   here, so it is the one that is coloured. */
.daylist .on .tally {
  font-size: 10.5px; font-weight: 700; color: var(--muted); flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
.daylist .on .tally.none {
  color: #8A6A1F; background: var(--sun-soft); border-radius: 5px; padding: 0 4px;
  text-transform: uppercase; letter-spacing: .02em;
}

.chippanel {
  width: min(720px, 94vw); max-height: 88vh; border: 1px solid var(--line); border-radius: 14px;
  padding: 20px 22px; background: var(--card); color: var(--ink); overflow: auto;
}
.chippanel::backdrop { background: rgba(24,28,20,.42); }
.chiptitle { margin: 0 0 2px; font-size: 20px; }
.chipsub { margin: 0 0 14px; color: var(--muted); font-size: 13.5px; }
.chipdetail { margin: 0 0 14px; }
.chipwarn {
  margin: 0 0 14px; padding: 10px 12px; border-radius: 10px; background: var(--sun-soft);
  font-size: 13.5px;
}
/* ---- the wording, edited where it is read ----------------------------------------------------
   The name and the description ARE the boxes. They are meant to look like the heading and the
   paragraph they replaced until you put a cursor in one — an editor that announces itself with
   framed inputs at the top of a panel turns "read this job" into "fill this form in". */
.chipwords { margin: 0 0 4px; }
.chipwords .wordshead { display: flex; align-items: baseline; gap: 8px; }
.chipwords input, .chipwords textarea {
  font: inherit; color: inherit; background: transparent; border: 1px solid transparent;
  border-radius: 8px; padding: 4px 6px; width: 100%;
}
.chipwords input:hover, .chipwords textarea:hover { border-color: var(--line); }
.chipwords input:focus, .chipwords textarea:focus {
  border-color: var(--sprout); background: var(--card); outline: none;
}
.chipwords .chipemoji { width: 2.4em; flex: 0 0 auto; text-align: center; font-size: 20px; }
.chipwords .chiptitlebox { font-size: 20px; font-weight: 650; }
.chipwords .chipdetailbox { resize: vertical; line-height: 1.5; }
/* Save is for a keyboard and for a browser with no script. Quiet, because leaving a field has
   already saved. */
.chipwords .wordsrow { margin-top: 2px; }
.chipwords .wordsaid, .chipwho .wordsaid { font-size: 12.5px; color: var(--muted);
  font-weight: 500; letter-spacing: 0; text-transform: none; margin-left: 6px; }
.chipwords .chipaskbox { font-size: 14px; color: var(--muted); margin-top: 2px; }
.chipwords .chipaskbox:focus { color: var(--ink); }
/* What came back: the answer to the question, and any photographs. Drawn only when there is
   something to draw — an empty "Photo: 0 in" row is a line about nothing. */
.chipback { margin: 0 0 16px; padding: 10px 12px; border-radius: 10px; background: var(--card-2); }
.chipback .chipanswer { margin: 0; font-size: 14px; }
.chipback .shots { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.chipback .shots img { width: 88px; height: 88px; object-fit: cover; border-radius: 8px; }
.chipagain { margin: 0 0 16px; }

.chipform { margin: 0 0 18px; }
.chiprow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.chiprow.end { justify-content: flex-end; border-top: 1px solid var(--line); padding-top: 14px; }
.chiptasks { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.chiptasks td { border-top: 1px solid var(--line); padding: 7px 8px; vertical-align: top; }
.chiptasks td.pick { width: 26px; }
.chiptasks td.who, .chiptasks td.when { width: 92px; color: var(--muted); font-size: 12.5px; }
.chiptasks tr.isdone .what { text-decoration: line-through; color: var(--faint); }
.chiptasks .tag { font-weight: 650; }
.chiptasks .tag.is-mine { color: var(--sprout); }
.chiptasks .tag.none { color: var(--faint); font-weight: 500; }
/* A name that came from the project rather than from the job. Lighter, and it wears a star, so a
   glance down the column can still tell a decision from a consequence. */
.chiptasks .tag.soft { color: var(--muted); font-weight: 500; }
.chiptasks .tag.soft.is-mine { color: var(--sprout); }
.chiptasks .note { color: var(--muted); font-size: 12px; margin-top: 2px; }

/* Names as pressable chips rather than a multi-select: a list box that needs ctrl-click to add a
   second name is a control people use wrong once and then avoid. */
.pickpeople { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.pickpeople .person {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px; padding: 5px 12px; font-size: 13px;
}
.pickpeople .person input { margin: 0; }
.pickpeople .person.is-on { border-color: var(--sprout); background: var(--sprout-soft); }
.chipfeed .upd { border-top: 1px solid var(--line); padding: 9px 0; font-size: 13.5px; }
.chipfeed .meta { color: var(--muted); font-size: 12px; margin-bottom: 3px; }
.chipfeed .shots { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.chipfeed .shots img { width: 84px; height: 84px; object-fit: cover; border-radius: 8px; }
.chipfacts { display: grid; gap: 10px; margin-bottom: 16px; font-size: 13.5px; }
.chipfacts .k {
  display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 700;
}

/* The Connecteam sync being down, said on the roster page. Warm rather than red: nothing is
   broken or lost, the numbers below have simply stopped moving, and an alarm colour for "this is
   a snapshot" is the kind of thing people learn to scroll past. */
.ctdown {
  border: 1px solid #E4C48A; background: #FDF6E7; border-radius: 12px;
  padding: 20px 22px; margin: 0 0 22px;
}
.ctdown h2 { margin: 0 0 8px; font-size: 18px; line-height: 1.35; }
.ctdown p { margin: 0 0 10px; max-width: 68ch; }
.ctdown .when { color: #7A6A4A; font-size: 14px; }
.ctdown ol { margin: 14px 0 0; padding-left: 22px; display: grid; gap: 11px; }
.ctdown li b { display: block; }
.ctdown li span { display: block; color: #5E5647; font-size: 14.5px; line-height: 1.55; max-width: 66ch; }

/* A save that landed. The whole <main> is redrawn on every action, so without a mark the only
   evidence a tick took is that a number somewhere changed — which is why saving twice by accident
   was so easy. The row it happened to lights briefly and settles. Colour rather than movement:
   these lists are forty rows long and a row that slides pushes the next one under the cursor. */
@keyframes just-saved-fade {
  0%   { background: var(--sprout-soft); }
  70%  { background: var(--sprout-soft); }
  100% { background: transparent; }
}
.just-saved, .just-saved > td {
  animation: just-saved-fade 1.4s ease-out 1;
}
@media (prefers-reduced-motion: reduce) {
  .just-saved, .just-saved > td { animation-duration: 0.01s; background: transparent; }
}

/* Who is packing today, on the pack pages.
   It sits in the page head where the lede was, so it reads as a fact about the day rather than as
   a setting buried in a form — it is the name that goes on every one of forty labels. */
.packer { display: flex; align-items: center; gap: 8px; margin: 6px 0 0; flex-wrap: wrap; }
.packer label { font-size: 13px; color: var(--muted); font-weight: 600; }
.packer select { font-size: 14px; padding: 6px 10px; min-width: 170px; }
.packer .on { font-size: 12.5px; color: var(--faint); }

/* ---- "see their app" ------------------------------------------------------------------------
   An eye beside a person's name, quiet until it is wanted: it is a way of checking something, not
   an action anybody takes as part of planning a day, so it sits at the weight of a caption and
   comes up to full strength under the pointer. */
.aseye { display: inline-flex; vertical-align: middle; margin-left: 6px; }
.aseye .eye {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; border: 0; border-radius: 7px;
  background: transparent; color: var(--faint); cursor: pointer;
}
.aseye .eye svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; }
.aseye .eye:hover, .aseye .eye:focus-visible { color: var(--sprout); background: var(--card-2); }

/* ---- flagged as stuck --------------------------------------------------------------------------
   Somebody in a field pressed "I am stuck". Amber, above the board: it is not an error and not a
   success, it is a thing waiting on a decision. */
.stalledlist { border-color: var(--sun); }
.stalledrow {
  display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap;
  padding: 10px 0; border-top: 1px solid var(--line);
}
.stalledlist .stalledrow:first-of-type { border-top: 0; }
.stalledrow > div:first-child { flex: 1; min-width: 220px; }
.stalledrow .note { display: block; }
.stalledrow .actions { display: flex; gap: 8px; align-items: center; }


/* ---- The rewrite button --------------------------------------------------------------------

   A small mark in the top corner of a text field: press it and what is in the box comes back
   said better. Adam's account only, so most people never see one (see core/sharpen).

   It sits INSIDE the field rather than beside it, because these pages have fields in table cells
   where there is no beside. Faint until the field is hovered or focused: there is one on nearly
   every box on the project page, and a column of bright marks down the page would read as though
   the app were asking for something. */
/* The wrapper stands where the field stood. A field that was a flex child — the project name
   across the top of the page is one — must not shrink to its default size because something was
   put around it, so the wrapper takes the growing on and the field fills it. */
.sharpwrap { position: relative; display: block; flex: 1 1 auto; min-width: 0; }
.sharpwrap > input, .sharpwrap > textarea { width: 100%; box-sizing: border-box; }

.sharpbtn {
  position: absolute; top: 4px; right: 4px;
  width: 22px; height: 22px; padding: 0; line-height: 1;
  border: 0; border-radius: 6px; background: transparent;
  color: var(--water); opacity: .3; cursor: pointer;
  font-size: 14px; transition: opacity .12s ease, background .12s ease;
}
/* A single-line input has no top corner worth having — the mark would sit above the text it is
   about. Centred on the line instead, which is where the eye already is. */
.sharpwrap.is-line .sharpbtn { top: 50%; transform: translateY(-50%); }
.sharpwrap:hover .sharpbtn, .sharpwrap:focus-within .sharpbtn { opacity: .75; }
.sharpbtn:hover, .sharpbtn:focus-visible { opacity: 1; background: var(--water-soft); }
.sharpbtn[disabled] { opacity: .5; cursor: progress; }

/* The per-job chevron on the phone list, and the empty heading it would otherwise need. There is
   no room shortage at a desk, so nothing is folded there and the control has nothing to do. */
.tasktable td.rowmore, .tasktable th.moreh { display: none; }

/* Room for the mark, so a long line does not run underneath it. */
.sharpwrap > input, .sharpwrap > textarea { padding-right: 30px; }

/* An unsaved rewrite is a visible state. Without the mark, a field that has just been rewritten
   looks exactly like a field that was always that way, and the Save underneath it looks optional. */
.sharpwrap.is-new > input, .sharpwrap.is-new > textarea {
  border-color: var(--water); background: var(--water-soft);
}

.sharpbar {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  margin: 4px 0 2px; font-size: 13px; color: var(--muted);
}
.sharpbar .sharpnote { flex: 1; min-width: 140px; }
.sharpbar.is-bad .sharpnote { color: var(--clay); }
.sharpbar button {
  padding: 3px 10px; font-size: 12px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
}
.sharpbar button.keep { border-color: var(--water); background: var(--water); color: #fff; }



/* ================================================================================================
   THE OFFICE, ON A PHONE
   ================================================================================================

   Second attempt. The first one broke two things and it is worth writing down why, because both
   were the same mistake — a rule written wider than the thing it was aimed at.

     * `input, select, textarea { padding: 12px 13px }`. A SHORTHAND, and thirty lines up this file
       reserves the right-hand end of every sharpen field for the rewrite button:
       `.sharpwrap > input { padding-right: 30px }`. The shorthand wiped that reservation, so the
       text ran under the button.
     * `.row button { width: 100% }`. A DESCENDANT selector. `.sharpbtn` is a button, it lives
       inside a `.row` on the project page, and it is `position: absolute` — so `width: 100%`
       stretched it across the whole field. Every tap anywhere in that box hit the rewrite button
       and called the model. That is the "it triggers the LLM instead of letting me edit".

   So: no shorthands over anything another rule has already reserved, and `>` not a space wherever
   a rule says `width`.

   THE REWRITE BUTTON ON A PHONE IS OUT OF THE FIELD, NOT GONE.
   It was taken off phones entirely, and taking it off was too much: it is the most useful control
   on these pages and a phone is where the farm writes most of its words. What made it a trap was
   never the button, it was WHERE it was — a 22px target floating inside the box it edits, so an
   aim-anywhere tap into the field hit the model instead of the caret.

   So it comes out of the field and sits under it, labelled, at a size a finger can hit on
   purpose and cannot hit by accident. The field gets its full width back either way. At a desk it
   is unchanged: a mouse has a hover state and does not miss. */

/* ---- updates: what the crew said, on one page ------------------------------------------------
   Everything here is under .updatelist. Nothing above is touched — a stylesheet that reaches out
   of its own section is how a card on one page moved a button on another. */

.updatelist { padding: 4px 6px; }
.updatelist .upd { padding: 13px 8px; border-bottom: 1px solid var(--line-2); }
.updatelist .upd:last-child { border-bottom: none; }
.updatelist .updwho { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 14px; }
.updatelist .updwho .dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.updatelist .updabout { margin: 3px 0 0 17px; font-size: 13px; }
.updatelist .updabout a { color: var(--water); font-weight: 550; }
/* The sentence itself, at reading size and keeping the line breaks somebody typed — these arrive
   as lists ("1. Dump the dirt into...") and a paragraph flattens them into nonsense. */
.updatelist .updbody {
  margin: 6px 0 0 17px; font-size: 15px; line-height: 1.5; color: var(--ink);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.updatelist .updshot { display: inline-block; margin: 9px 0 0 17px; }
.updatelist .updshot img {
  max-width: 220px; max-height: 170px; border-radius: 10px; border: 1px solid var(--line);
  display: block;
}

@media (max-width: 760px) {
  .updatelist { padding: 0; }
  .updatelist .upd { padding: 12px 2px; }
  .updatelist .updabout, .updatelist .updbody, .updatelist .updshot { margin-left: 0; }
  .updatelist .updshot img { max-width: 100%; }
}


/* The phone's drawer does not exist at a desk. Stated out here so the DEFAULT is hidden: if this
   file is served stale from a cache while base.html is fresh, a farm gets no stray Menu button. */
.navdrawer { display: none; }

@media (max-width: 760px) {

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

  /* 16px is not a preference. Safari on iOS zooms the page in whenever a focused field is under
     it, and does not zoom back — which is most of what "unusable" meant here.
     Padding in LONGHAND, and never `padding-right`: that end belongs to whatever the desktop
     rules put there. */
  input, select, textarea {
    font-size: 16px; padding-top: 11px; padding-bottom: 11px; padding-left: 13px;
    border-radius: 10px;
  }
  /* A tick box takes size, not padding — 12px of padding makes it a grey slab with the name
     floating beside it. The one on every task row is how a job gets picked and it was 13px. */
  input[type="checkbox"], input[type="radio"] {
    width: 22px; height: 22px; padding: 0; border-radius: 6px;
  }
  textarea { min-height: 84px; }

  /* The rewrite button, below the field rather than inside it — see the note at the top. Nothing
     is reserving the right-hand end any more, so the field takes it back. */
  /* `stretch`, NOT `flex-start`: flex-start makes a column's children shrink to their content,
     which took every text field on every phone down to the width of the words already in it.
     The field stretches; the button under it is the one thing that does not. */
  .sharpwrap { display: flex; flex-direction: column; align-items: stretch; }
  .sharpwrap > .sharpbtn { align-self: flex-start; }
  .sharpwrap > input, .sharpwrap > textarea { padding-right: 13px; }
  .sharpwrap > .sharpbtn {
    position: static; transform: none; display: inline-flex; align-items: center;
    width: auto; height: auto; min-height: 34px; padding: 0 12px; margin-top: 6px;
    border: 1px solid var(--line); border-radius: 9px; background: var(--card-2);
    color: var(--water); opacity: 1; font-size: 13px; font-weight: 620; line-height: 1;
  }
  /* Said in words. The mark alone is discoverable by hovering, and a phone cannot hover. */
  .sharpwrap > .sharpbtn::after { content: " Rewrite"; margin-left: 6px; font-weight: 620; }
  .sharpwrap > .sharpbtn[disabled] { opacity: .5; }
  /* The bar of Keep/Undo buttons is a real row of controls on a phone, not 12px of small print. */
  .sharpbar { font-size: 14px; }
  .sharpbar button { padding: 8px 14px; font-size: 14px; min-height: 40px; }

  /* 44px, because a finger is not a mouse. `>` throughout: these say `width`, and a descendant
     match is what put a full-width button on top of a text field last time. */
  button, .btn { font-size: 15px; min-height: 44px; border-radius: 10px; }
  button, .btn { padding-top: 11px; padding-bottom: 11px; padding-left: 18px; padding-right: 18px; }
  .row > button, .row > .btn, .row > .btnrow > button, .row > .btnrow > .btn { width: 100%; }
  .row > .btnrow { width: 100%; }
  /* Never these, whatever else is said: they are positioned inside something else. */
  .sharpbtn, .tasktable .grip { width: auto; min-height: 0; }

  /* One field per line. `min-width: 0` must go with the basis or the old 140px floor still wins
     and the two-column squeeze comes straight back. */
  .row { gap: 10px; }
  .row > * { flex: 1 1 100%; min-width: 0; }
  .row > .shrink { flex: 0 0 auto; width: auto; }

  .wrap { padding: 14px 12px 80px; }
  .panel { padding: 14px 12px; border-radius: 12px; }

  /* ---- the bar, and the drawer under it ----------------------------------------------------- */

  /* One row that stays one row. The bar is sticky, so its height comes off every page on the
     site; twelve words wrapping to four lines cost about a fifth of the screen everywhere. */
  .topbar { padding: 7px 12px; gap: 10px; flex-wrap: nowrap; }
  .topbar .brand { min-width: 0; }
  .topbar .brand .name {
    font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .topbar .brand .name small { display: none; }  /* a caption, not a door */
  .topbar > nav { display: none; }               /* replaced by the drawer */

  /* `static`, deliberately: it makes the panel below resolve against `.topbar`, which is sticky
     and therefore a positioned box. That is how the panel knows where the bottom of the bar is
     without anybody hard-coding a height that a padding change would then quietly break. */
  .navdrawer { display: block; margin-left: auto; position: static; }

  .navdrawer > summary {
    display: inline-flex; align-items: center; gap: 10px; cursor: pointer;
    min-height: 44px; padding: 0 15px; border-radius: 10px;
    border: 1px solid var(--line); background: var(--card);
    font-size: 15px; font-weight: 620; color: var(--ink);
    list-style: none;               /* Firefox */
  }
  .navdrawer > summary::-webkit-details-marker { display: none; }   /* Safari */
  .navdrawer[open] > summary { background: var(--sprout-soft); border-color: var(--sprout); }

  /* Three bars: the middle one IS the element, the other two hang off it. Drawn this way rather
     than with borders, which is what made the last one look like a squashed box. */
  .navdrawer .bars {
    display: block; position: relative;
    width: 18px; height: 2px; background: currentColor; border-radius: 2px;
  }
  .navdrawer .bars::before, .navdrawer .bars::after {
    content: ""; position: absolute; left: 0;
    width: 18px; height: 2px; background: currentColor; border-radius: 2px;
  }
  .navdrawer .bars::before { top: -6px; }
  .navdrawer .bars::after  { top: 6px; }

  .navdrawer .navpop {
    position: absolute; top: 100%; left: 8px; right: 8px; z-index: 60;
    margin-top: 6px;
    max-height: calc(100vh - 84px); overflow-y: auto; -webkit-overflow-scrolling: touch;
    display: flex; flex-direction: column; padding: 8px;
    background: var(--card); border: 1px solid var(--line); border-radius: 14px;
    box-shadow: 0 14px 40px rgba(0,0,0,.22);
  }
  .navdrawer .navpop a {
    padding: 13px 12px; border-radius: 9px; font-size: 16px; font-weight: 550;
    color: var(--ink); text-decoration: none; min-height: 0;
  }
  .navdrawer .navpop a.active {
    background: var(--sprout-soft); color: var(--sprout); font-weight: 680;
  }
  .navdrawer .navpop .navgroup {
    padding: 13px 12px 5px; font-size: 11px; font-weight: 700; letter-spacing: .09em;
    text-transform: uppercase; color: var(--faint);
  }
  .navdrawer .navpop .navgroup:first-child { padding-top: 4px; }

  /* ---- the week planner, as cards ----------------------------------------------------------- */

  /* People down the side, days across the top; a phone has room for one of the two, and the
     answer is the person — a week is planned by thinking about Kurtis, not about Tuesday.
     The chips and the + are untouched: this moves boxes, it does not touch the dragging. */
  .weekwrap { overflow-x: visible; }
  .weekgrid { min-width: 0; display: block; }
  .weekgrid thead { display: none; }
  .weekgrid tbody, .weekgrid tfoot { display: block; }
  .weekgrid tr {
    display: block; margin-bottom: 14px; background: var(--card);
    border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  }
  .weekgrid th.who {
    display: block; width: auto; border: none; background: var(--card-2);
    padding: 11px 13px; font-size: 16px; font-weight: 700;
    text-transform: none; letter-spacing: 0; color: var(--ink);
  }
  .weekgrid td {
    display: block; width: auto; border: none;
    border-top: 1px solid var(--line-2); padding: 11px 13px;
  }
  .weekgrid td::before {
    content: attr(data-day); display: block;
    font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
    color: var(--faint); margin-bottom: 7px;
  }
  /* A day they are not working cannot take work — the planner refuses it and says so — so on a
     phone it is seven-twelfths of a scroll spent on hatched grey. The desk keeps them. */
  .weekgrid td.off { display: none; }
  .weekgrid .is-today { box-shadow: inset 3px 0 0 var(--sprout); }
  .weekgrid .hrs { font-size: 13px; }
  .daylist .on { font-size: 14px; padding: 9px 10px; gap: 8px; margin-bottom: 7px; }
  .daylist .on .em { font-size: 17px; }

  /* ---- the task list: ONE JOB, ONE LINE ------------------------------------------------------ */

  /* Every cell as its own block was the dog's breakfast. Nine cells with a heading over each made
     one job about 550px tall — ten jobs was two thousand pixels of scrolling to read ten short
     names, and the name, the only thing being looked for, was one line in nine.

     A job is now the name, who has it, and where it is up to. Two lines. The estimate, the time
     logged, the money and the description are still every one of them here, behind the chevron at
     the end of the first line, which is where somebody goes when they are working on THAT job
     rather than reading the list. It IS a grid this time: two lines with things sitting side by
     side on them is what a grid is for, and stacked blocks cannot do it at all. */
  .tasktable, .tasktable tbody { display: block; }
  .tasktable thead { display: none; }
  /* FIVE columns, and the last-but-one is a share rather than `auto`. With four `auto` columns
     the status box — "Blocked", in a 16px select — set the width of the last column for the whole
     row, and the name above it got whatever was left: 131px of a 390px screen, so every job read
     "Photo of the". A named track cannot be widened from underneath by the cell below it. */
  .tasktable tr {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr) minmax(0, 1fr) auto;
    align-items: center; gap: 4px 8px;
    border: 1px solid var(--line); border-radius: 12px;
    padding: 10px 12px; margin-bottom: 10px; background: var(--card);
  }
  .tasktable td { display: block; border: none; padding: 0; text-align: left; }

  /* Line one: the handle, the tick, the name across the rest of it, and the chevron. */
  .tasktable td.drag    { grid-area: 1 / 1; }
  .tasktable td.pickbox { grid-area: 1 / 2; }
  .tasktable td.tname   { grid-area: 1 / 3 / 2 / 5; min-width: 0; }
  .tasktable td.rowmore { grid-area: 1 / 5; display: block; }
  /* Line two: who has it, and where it is up to. Both dropdowns, side by side. */
  .tasktable td[data-label="Who"]    { grid-area: 2 / 1 / 3 / 4; min-width: 0; }
  .tasktable td[data-label="Status"] { grid-area: 2 / 4 / 3 / 6; min-width: 0; }

  .tasktable .grip { min-height: 34px; min-width: 26px; padding: 0; font-size: 17px; }
  .tasktable td.tname input.flat {
    font-size: 16px; font-weight: 650; padding-top: 6px; padding-bottom: 6px;
  }
  /* The status box holds one word and must not take a third of the line to do it. */
  .tasktable td[data-label="Status"] select { padding-left: 8px; padding-right: 4px; }
  .tasktable td[data-label="Who"] select,
  .tasktable td[data-label="Status"] select,
  .tasktable td.tname input.flat,
  .tasktable td.tname .sharpwrap { width: 100%; }
  /* No headings on line two: a name and a status say what they are. */
  .tasktable td[data-label="Who"]::before,
  .tasktable td[data-label="Status"]::before { content: none; }

  .morebtn {
    min-height: 34px; min-width: 34px; padding: 0; font-size: 15px;
    border: 1px solid var(--line); border-radius: 9px;
    background: var(--card-2); color: var(--muted); line-height: 1;
  }
  tr.open > td.rowmore .morebtn { background: var(--sprout-soft); border-color: var(--sprout);
                                  color: var(--sprout); transform: rotate(180deg); }

  /* Folded away until the chevron is pressed. */
  .tasktable tr:not(.open) .taskdetail,
  .tasktable tr:not(.open) > td[data-label="Est"],
  .tasktable tr:not(.open) > td[data-label="Logged"],
  .tasktable tr:not(.open) > td[data-label="Budget"],
  .tasktable tr:not(.open) > td.tdel { display: none; }
  /* Nothing on the compact line calls a model. The rewrite mark comes back with the rest. */
  .tasktable tr:not(.open) .sharpwrap > .sharpbtn { display: none; }
  .tasktable tr:not(.open) .sharpwrap > input { padding-right: 13px; }

  .tasktable tr.open > td[data-label="Est"]    { grid-area: 3 / 1 / 4 / 4; }
  .tasktable tr.open > td[data-label="Logged"] { grid-area: 3 / 4 / 4 / 6; }
  .tasktable tr.open > td[data-label="Budget"] { grid-area: 4 / 1 / 5 / 4; }
  .tasktable tr.open > td.tdel                 { grid-area: 4 / 4 / 5 / 6; text-align: right; }
  .tasktable tr.open > td[data-label] {
    margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-2);
  }
  /* Open, the name cell grows to hold the description and its rewrite button, and the handle, the
     tick and the chevron were centred against all of it — three controls floating halfway down a
     card. They belong on the name's line, which is the top. */
  .tasktable tr.open > td.drag,
  .tasktable tr.open > td.pickbox,
  .tasktable tr.open > td.rowmore { align-self: start; margin-top: 6px; }
  .tasktable tr.open > td[data-label]::before {
    content: attr(data-label); display: block;
    font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
    color: var(--faint); margin-bottom: 4px;
  }
  .tasktable tr.open > td[data-label="Who"]::before,
  .tasktable tr.open > td[data-label="Status"]::before { content: none; }
  .tasktable td.num { text-align: left; }

  /* ---- every other table -------------------------------------------------------------------- */

  /* The pages not singled out above keep their tables and scroll them inside their own box,
     rather than moving the whole page sideways. A floor, not a fix. */
  .panel > table:not(.weekgrid):not(.tasktable),
  .wrap > table:not(.weekgrid):not(.tasktable) {
    display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap;
  }
}

/* ---- rostered and not here ---------------------------------------------------------------------
   Clay rather than green: this is the one panel on the page reporting that the plan and the farm
   have come apart. Quiet enough not to shout on a morning when somebody is simply late. */
.noshow { border-color: #E3B8AE; background: var(--clay-soft, #F9E7E2); margin-bottom: 16px; }
.noshow .noshowrow { padding: 7px 2px; }
.noshow .noshowrow + .noshowrow { border-top: 1px solid rgba(0,0,0,.06); }
.noshow .noshowrow strong { font-size: 15px; }
.noshow .noshowrow .note { display: block; font-size: 13px; color: var(--muted); }
