:root {
    --green-700: #2e5b3e;
    --green-600: #386b4a;
    --green-100: #eaf1ec;
    --border: #d8ded9;
    --text: #23291f;
    --muted: #6b756c;
    --bg: #f7f8f6;
    --card: #ffffff;
    --danger: #b3413a;
    --warning: #b58a1a;
    /* Height of the frozen top bar. nav.js keeps this in sync with the real
       bar (it grows when tabs wrap); the value here is only the pre-JS guess.
       Anything that pins itself below the bar offsets by this. */
    --topbar-h: 56px;
}

html { scroll-padding-top: calc(var(--topbar-h) + 0.5rem); }

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    background: var(--bg);
    color: var(--text);
    font-size: 16px;
}

a { color: var(--green-700); text-decoration: none; }
a:hover { text-decoration: underline; }

.topbar {
    background: var(--green-700);
    color: white;
    padding: 0.6rem 1rem;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    /* Frozen: stays put while the page scrolls. Sticky (not fixed) so it keeps
       its space in the flow and nothing has to be padded down to clear it. */
    position: sticky;
    top: 0;
    z-index: 200;
}

.topbar .brand {
    font-weight: 700;
    font-size: 1.15rem;
    color: white;
    white-space: nowrap;
}

.topbar-right {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 0.4rem 0.75rem;
    margin-left: auto;
    min-width: 0;
}

/* ---- enterprise switcher: which business you are looking at ---- */
.ent-switch {
    display: flex;
    gap: 0.2rem;
    background: rgba(0,0,0,0.18);
    border-radius: 8px;
    padding: 0.15rem;
}
.ent-btn {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    color: rgba(255,255,255,0.85);
    padding: 0.35rem 0.8rem;
    border-radius: 6px;
    font-size: 0.92rem;
    font-weight: 600;
    white-space: nowrap;
}
.ent-btn:hover { background: rgba(255,255,255,0.14); color: white; text-decoration: none; }
.ent-btn.active { background: var(--card); color: var(--green-700); }
.ent-icon { font-size: 1rem; line-height: 1; }

/* Drawer button: only earns its place on narrow screens. */
.tree-toggle {
    display: none;
    background: none; border: 1px solid rgba(255,255,255,0.35); color: white;
    border-radius: 6px; font-size: 1.05rem; line-height: 1;
    padding: 0.3rem 0.55rem; cursor: pointer;
}

/* ===================== app shell: tree + content ===================== */
.app-shell { display: flex; align-items: flex-start; gap: 0; }

.app-tree {
    flex: 0 0 208px;
    align-self: stretch;
    position: sticky;
    top: var(--topbar-h);
    max-height: calc(100vh - var(--topbar-h));
    overflow-y: auto;
    background: var(--card);
    border-right: 1px solid var(--border);
    padding: 0.75rem 0.5rem 1.5rem;
}
.app-main { flex: 1 1 auto; min-width: 0; }

.app-tree .tree-heading {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--muted); font-weight: 700; padding: 0.25rem 0.6rem 0.5rem;
}
.app-tree ul.tree { list-style: none; margin: 0; padding: 0; }
.app-tree ul.tree li { margin: 0; }
.tree-row { display: flex; align-items: center; gap: 0.15rem; border-radius: 6px; }
/* Pointers only: on a touchscreen there is nothing hovering, and a :hover left
   sitting on the row after a tap reads as though you are still on that page. */
@media (hover: hover) { .tree-row:hover { background: var(--green-100); } }
.app-tree .tree-link {
    flex: 1 1 auto;
    display: block;
    padding: 0.4rem 0.6rem;
    border-radius: 6px;
    color: var(--text);
    font-size: 0.92rem;
    border-left: 3px solid transparent;
}
.app-tree .tree-link:hover { text-decoration: none; }
.app-tree .tree-link.active {
    background: var(--green-100); color: var(--green-700);
    font-weight: 600; border-left-color: var(--green-700);
}
/* The parent of the section you are inside: marked, but not the active page. */
.app-tree .tree-link.section-open { color: var(--green-700); font-weight: 700; }

/* Children indent under their parent, with a rule to follow the eye down.
   The indent is deliberately generous and the rule deliberately visible: at the old
   0.85rem with a hairline border, a child sat almost level with its parent and the
   sidebar read as one flat list of twenty-odd pages instead of six sections. The
   tick before each child is what makes the rule read as a branch rather than a
   stray line. */
.app-tree ul.tree-children {
    margin: 0.15rem 0 0.5rem 1.5rem;
    border-left: 2px solid var(--border);
    padding-left: 0;
}
.app-tree ul.tree-children > li { position: relative; }
/* The short horizontal tick from the rule to the label. Sits on the li rather than
   the link so the link's own padding (and its active border) stay untouched. */
.app-tree ul.tree-children > li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 1.05em;
    width: 0.55rem;
    height: 2px;
    background: var(--border);
}
.app-tree .tree-child {
    font-size: 0.86rem;
    padding-left: 1rem;
    color: var(--muted);
}
.app-tree .tree-child:hover { color: var(--text); }
/* An active child outranks the muted colour -- it is still the page you are on. */
.app-tree .tree-child.active { color: var(--green-700); }

/* No drawer above 900px, so no scrim. Stated here rather than left to the media
   query below, so the element is inert by default and only switched on for phones. */
.tree-scrim { display: none; }

.tree-sep { height: 1px; background: var(--border); margin: 0.5rem 0.4rem; }

.tree-more { margin-top: 0.5rem; border-top: 1px solid var(--border); padding-top: 0.4rem; }
.tree-more[hidden] { display: none; }
.tree-more-btn {
    cursor: pointer; list-style: none;
    font-size: 0.78rem; color: var(--muted); font-weight: 600;
    padding: 0.3rem 0.6rem; border-radius: 6px; user-select: none;
}
.tree-more-btn::-webkit-details-marker { display: none; }
.tree-more-btn::before { content: "▸ "; }
.tree-more[open] .tree-more-btn::before { content: "▾ "; }
.tree-more-btn:hover { background: var(--green-100); color: var(--green-700); }

.pin-btn {
    flex: 0 0 auto;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.78rem;
    line-height: 1;
    padding: 0.3rem 0.35rem;
    border-radius: 6px;
    opacity: 0.22;
    filter: grayscale(1);
}
/* Hover-reveal is for pointers ONLY -- see the touch block below. On a touchscreen
   a rule like this is what makes a row need two taps: the first tap applies :hover
   and is swallowed revealing the control, the second one finally follows the link. */
@media (hover: hover) {
    .tree-row:hover .pin-btn { opacity: 0.6; }
    .pin-btn:hover { opacity: 1 !important; background: rgba(0,0,0,0.06); }
}
.pin-btn[aria-pressed="true"] { opacity: 1; filter: none; }

/* ---- reordering the pinned list ----
   The grip only appears in the pinned section: that list is yours to arrange, while
   "More pages" stays in registry order so there is always one predictable place to
   find a page you have never pinned. */
.drag-btn {
    flex: 0 0 auto;
    background: none; border: none; padding: 0.3rem 0.15rem;
    border-radius: 6px; line-height: 1; font-size: 0.85rem;
    color: var(--muted); cursor: grab;
    opacity: 0;                 /* quiet until you go looking for it */
    touch-action: none;         /* the handle owns the gesture; the tree still scrolls */
    display: none;
}
#tree-pinned .drag-btn { display: block; }
@media (hover: hover) {
    #tree-pinned .tree-row:hover .drag-btn { opacity: 0.55; }
    .drag-btn:hover { opacity: 1 !important; background: rgba(0,0,0,0.06); }
}
.drag-btn:focus-visible { opacity: 1; outline: 2px solid var(--green-600); outline-offset: 1px; }

/* ---- the same two controls, on a touchscreen ----
   There is no hovering on a phone, so `opacity: 0` here does not mean "quiet until
   you go looking for it" -- it means an INVISIBLE 15px button at the left edge of
   every row and another at the right, both of which eat the tap that was meant for
   the link (the grip calls preventDefault on pointerdown; the pin toggles a pin).
   Landing on one is why the tree felt like it needed two taps: the first tap hit a
   control nobody could see. Showing them solves it -- they become something to aim
   at or avoid on purpose -- and the row keeps its full-width tap target for the link. */
@media (hover: none) {
    .pin-btn { opacity: 0.45; }
    #tree-pinned .drag-btn { opacity: 0.4; }
}
.tree-item.dragging { opacity: 0.5; }
.tree-item.dragging .drag-btn { cursor: grabbing; }
/* Where it will land. A line rather than a moving gap, so the list does not jump
   about underneath the cursor while you are aiming. */
#tree-pinned.drop-active .tree-item.drop-before > .tree-row { box-shadow: 0 -2px 0 var(--green-600); }
#tree-pinned.drop-active .tree-item.drop-after > .tree-row { box-shadow: 0 2px 0 var(--green-600); }

.owner-switch { display: flex; align-items: center; gap: 0.4rem; }
.owner-switch-label { color: rgba(255,255,255,0.85); font-size: 0.85rem; }
.owner-switch select {
    background: rgba(255,255,255,0.14); color: white; border: 1px solid rgba(255,255,255,0.35);
    border-radius: 6px; padding: 0.3rem 0.5rem; font-size: 0.9rem; cursor: pointer;
}
.owner-switch select option { color: var(--text); }

.container {
    max-width: min(1600px, 96vw);
    margin: 0 auto;
    padding: 1.25rem 1rem 3rem;
}

.flash {
    background: var(--green-100);
    border: 1px solid var(--green-600);
    color: var(--green-700);
    padding: 0.6rem 0.9rem;
    border-radius: 6px;
    margin-bottom: 1rem;
}

.flash.error {
    background: #fbeceb;
    border-color: var(--danger);
    color: var(--danger);
}

h1 { font-size: 1.6rem; margin: 0 0 0.25rem; }
h2 { font-size: 1.25rem; }
.subtitle { color: var(--muted); margin: 0 0 1.25rem; }

.page-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 1rem;
}
/* Back/cancel buttons sit on the far left; the title zone pushes any remaining
   actions (Edit, +Add, etc.) to the right so they never mix with the back button. */
.page-header .back { order: -1; }
.page-header > h1,
.page-header > div:not(.actions-row):not(.back) { margin-right: auto; }

.btn {
    display: inline-block;
    background: var(--green-700);
    color: white;
    border: none;
    padding: 0.55rem 1rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.95rem;
}
.btn:hover { background: var(--green-600); text-decoration: none; color: white; }
.btn.secondary { background: white; color: var(--green-700); border: 1px solid var(--green-600); }
.btn.secondary:hover { background: var(--green-100); }
.btn.danger { background: var(--danger); }
.btn.small { padding: 0.3rem 0.6rem; font-size: 0.85rem; }

summary.btn { list-style: none; user-select: none; }
summary.btn::-webkit-details-marker { display: none; }
summary.btn::marker { content: ""; }
details.log-section { margin-bottom: 1rem; }
details.log-section[open] summary.btn { margin-bottom: 1rem; }
details.log-section .card { margin-bottom: 0; border-top-left-radius: 0; border-top-right-radius: 0; }

.card {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem 1.1rem;
    margin-bottom: 1rem;
}

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.9rem;
    margin-bottom: 1.25rem;
}

.stat {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem;
    text-align: center;
}
.stat .value { font-size: 1.8rem; font-weight: 700; color: var(--green-700); }
.stat .label { color: var(--muted); font-size: 0.9rem; }

table {
    width: 100%;
    border-collapse: collapse;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 10px;
    /* NOT overflow:hidden. That rounded the corners, but it also made the table
       its own scrollport, which pins a sticky <thead> to the table instead of to
       the window -- the header would never move. Corners are rounded on the
       corner cells below instead. */
}
th, td {
    text-align: left;
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--border);
    font-size: 0.95rem;
}
th { background: var(--green-100); color: var(--green-700); font-weight: 600; }
tr:last-child td { border-bottom: none; }
.table-wrap { overflow-x: auto; }
/* A box with overflow-x:auto computes overflow-y to auto as well, making the
   wrapper a scrollport and breaking sticky headers the same way. Where the table
   actually fits, tables.js adds .no-x-scroll and the wrapper stops clipping. */
.table-wrap.no-x-scroll { overflow: visible; }

/* ===================== sticky table headers =====================
   The header row stays put while the page scrolls, until its own table has
   gone past. Scoped away from two places that solve it themselves or must not:
   `table.sched` (the load-schedule matrix has a JS floating header, and it
   scrolls in its own pane) and the drill-down modal.

   border-collapse:collapse drops a detached header's borders, so the underline
   is painted with box-shadow, which does not participate in collapsing. */
table:not(.sched) thead th {
    position: sticky;
    top: var(--topbar-h);          /* clear the frozen top bar */
    z-index: 5;                    /* over cells, under .topbar (200) and filter menus (1000) */
    box-shadow: inset 0 -1px 0 var(--border);
}
.drill-modal table thead th { position: static; box-shadow: none; }
/* Where the table is too wide to fit, the wrapper KEEPS overflow-x:auto and so
   stays a scrollport. A sticky thead then resolves against the wrapper rather
   than the window, and `top: var(--topbar-h)` pushes the header DOWN inside its
   own table -- leaving a blank strip where the header belongs and the header
   itself floating over the middle rows. Common on a phone, where almost every
   table overflows. Only stick once tables.js has released the wrapper. */
/* `table:not(.sched)` is NOT optional. The load-schedule matrix wants the opposite
   thing -- its Group/Site HEADER cells are `position: sticky; left: 0` so they hold
   while the weeks scroll sideways -- and this selector is more specific than
   `table.sched .frz`, so without the exclusion it forced those two header cells back
   to static: they scrolled away and the week headings slid over the top-left corner.
   The matrix has always been excluded from the rule above; it has to be excluded here
   too. */
.table-wrap:not(.no-x-scroll) table:not(.sched) thead th {
    position: static;
    box-shadow: inset 0 -1px 0 var(--border);
}
/* Rounded corners, previously from overflow:hidden on the table. */
table thead tr:first-child th:first-child { border-top-left-radius: 10px; }
table thead tr:first-child th:last-child  { border-top-right-radius: 10px; }
table tbody tr:last-child td:first-child  { border-bottom-left-radius: 10px; }
table tbody tr:last-child td:last-child   { border-bottom-right-radius: 10px; }

.badge {
    display: inline-block;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
    background: var(--green-100);
    color: var(--green-700);
}
.badge.warning { background: #fbf1dc; color: var(--warning); }
.badge.danger { background: #fbeceb; color: var(--danger); }
.badge.muted { background: #eee; color: var(--muted); }

/* --- Table filter + collapse (tables.js) --- */
th.filterable-th { position: relative; padding-right: 1.6rem; white-space: nowrap; }
.col-filter-btn {
    position: absolute; top: 50%; right: 0.35rem; transform: translateY(-50%);
    border: none; background: transparent; color: var(--green-700);
    cursor: pointer; font-size: 0.8rem; line-height: 1; padding: 0.15rem 0.2rem;
    border-radius: 4px; opacity: 0.55;
}
.col-filter-btn:hover { opacity: 1; background: rgba(0,0,0,0.06); }
th.filter-active .col-filter-btn { opacity: 1; color: var(--warning); }
th.filter-active { background: #fbf1dc; }

.col-filter-menu {
    position: absolute; z-index: 1000; background: var(--card);
    border: 1px solid var(--border); border-radius: 8px;
    box-shadow: 0 6px 20px rgba(0,0,0,0.15); padding: 0.5rem; width: 230px;
    font-size: 0.9rem;
}
.col-filter-menu .cf-sortrow { display: flex; gap: 0.4rem; margin-bottom: 0.4rem; }
.col-filter-menu .cf-sort {
    flex: 1; border: 1px solid var(--border); background: var(--green-100);
    color: var(--green-700); border-radius: 6px; padding: 0.3rem; cursor: pointer; font-size: 0.85rem;
}
.col-filter-menu .cf-sort:hover { background: #dfe9e1; }
.col-filter-menu .cf-search {
    width: 100%; padding: 0.35rem 0.5rem; border: 1px solid var(--border);
    border-radius: 6px; margin-bottom: 0.4rem; font-size: 0.9rem;
}
.col-filter-menu .cf-list {
    max-height: 220px; overflow-y: auto; border: 1px solid var(--border);
    border-radius: 6px; padding: 0.3rem; margin-bottom: 0.4rem;
}
.col-filter-menu .cf-list label {
    display: flex; align-items: center; gap: 0.4rem; padding: 0.15rem 0.2rem;
    font-weight: normal; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.col-filter-menu .cf-list label:hover { background: var(--green-100); border-radius: 4px; }
.col-filter-menu .cf-list label.cf-all { font-weight: 600; border-bottom: 1px solid var(--border); margin-bottom: 0.2rem; padding-bottom: 0.25rem; }
.col-filter-menu .cf-list input[type=checkbox] { width: auto; margin: 0; }
.col-filter-menu .cf-actions { display: flex; gap: 0.4rem; }
.col-filter-menu .cf-apply, .col-filter-menu .cf-clear {
    flex: 1; border-radius: 6px; padding: 0.35rem; cursor: pointer; font-size: 0.85rem; border: 1px solid var(--border);
}
.col-filter-menu .cf-apply { background: var(--green-700); color: #fff; border-color: var(--green-700); }
.col-filter-menu .cf-apply:hover { background: var(--green-600); }
.col-filter-menu .cf-clear { background: #fff; color: var(--text); }
.col-filter-menu .cf-clear:hover { background: var(--green-100); }

.tbl-bar { display: flex; margin-bottom: 0.35rem; }
.tbl-toggle {
    border: 1px solid var(--border); background: var(--card); color: var(--muted);
    border-radius: 6px; padding: 0.2rem 0.6rem; cursor: pointer; font-size: 0.82rem;
}
.tbl-toggle:hover { background: var(--green-100); color: var(--green-700); }
.tbl-toggle span { font-weight: 600; }

form .field { margin-bottom: 1rem; }
form label {
    display: block;
    font-weight: 600;
    margin-bottom: 0.3rem;
    font-size: 0.92rem;
}
/* Everything you type into, by exclusion rather than by listing types. The old
   rule named text/number/date only, so password fields were left completely
   unstyled -- 13px browser default with no padding or border, right next to a
   15px styled username box on the same form. Excluding the non-text types keeps
   any future input kind styled by default instead of silently missed. */
form input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
form select, form textarea {
    width: 100%;
    padding: 0.55rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.95rem;
    background: white;
    color: var(--text);
    font-family: inherit;
}
form textarea { min-height: 70px; resize: vertical; }

/* ---- "what sets Cut 1?" chooser on the load generator ----
   Three ways to anchor the schedule, each showing the week it would actually land
   on as you change it. The date is the point of the control, so it gets its own
   column rather than being buried in helper text. */
.anchor-modes { display: grid; gap: 0.35rem; margin: 0.2rem 0 0.9rem; }
.anchor-mode {
    display: grid;
    grid-template-columns: auto minmax(11rem, auto) minmax(9rem, auto) 1fr;
    align-items: center; gap: 0.5rem;
    padding: 0.45rem 0.6rem; border: 1px solid var(--border);
    border-radius: 8px; cursor: pointer; background: var(--card);
}
.anchor-mode:hover { border-color: var(--green-600); }
.anchor-mode:has(input[type=radio]:checked) {
    border-color: var(--green-600); background: var(--green-100);
}
.anchor-mode input[type=radio] { margin: 0; }
.anchor-name { font-weight: 600; font-size: 0.92rem; display: flex; align-items: center; gap: 0.4rem; }
.anchor-name em { font-weight: 400; color: var(--muted); font-style: normal; font-size: 0.85rem; }
/* The form rule stretches inputs to 100%; these sit inline inside a label. */
.anchor-mode .anchor-num { width: auto; max-width: 8.5rem; padding: 0.25rem 0.4rem; font-size: 0.88rem; }
.anchor-when { font-weight: 700; color: var(--green-700); font-size: 0.92rem; white-space: nowrap; }
.anchor-note { color: var(--muted); font-size: 0.82rem; }
.anchor-preview { margin: 0 0 0.9rem; }
.anchor-preview table { font-size: 0.88rem; }
@media (max-width: 640px) {
    /* Stack: the radio and label on one line, then the date, then the note. */
    .anchor-mode { grid-template-columns: auto 1fr; }
    .anchor-when, .anchor-note { grid-column: 2; }
}
.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 0 1rem;
}
.form-actions { display: flex; gap: 0.6rem; margin-top: 1.25rem; }
.barn-row { margin-bottom: 0.75rem; }
.deduction-row { margin-bottom: 0.75rem; }

.empty {
    color: var(--muted);
    font-style: italic;
    padding: 1rem 0;
}

.section-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 1.5rem;
    margin-bottom: 0.5rem;
}

.detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 0.75rem 1.5rem;
    margin-bottom: 1rem;
}
.detail-grid .k { color: var(--muted); font-size: 0.82rem; }
.detail-grid .v { font-size: 1rem; font-weight: 600; }

.actions-row { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* ===================== Load Schedule ===================== */
/* Sync/connection status banner */
.sync-status {
    border-radius: 6px;
    padding: 0.6rem 0.85rem;
    font-size: 0.9rem;
    margin-bottom: 1.25rem;
    border: 1px solid var(--border);
    background: var(--card);
}
.sync-status.ok { background: var(--green-100); border-color: var(--green-600); color: var(--green-700); }
.sync-status.warn { background: #fbf1dc; border-color: var(--warning); color: var(--warning); }

/* Booked-load detail (expandable inner table on the Actual page) */
.detail-row > td { background: rgba(127,127,127,0.05); }
.detail-toggle {
    cursor: pointer;
    padding: 0.5rem 0.85rem;
    font-size: 0.85rem;
    color: var(--green-700);
    font-weight: 600;
}
.barn-select { font-size: 0.8rem; padding: 0.15rem 0.3rem; max-width: 150px; }
/* Farm load-out appointment: date over time, so the column stays narrow. These two are
   the boxes on the row that get SET every week (the rest are set once), so they are
   sized to be hit and read rather than shrunk to the 0.8rem of the dropdowns beside
   them -- and the column is given room so they never squeeze to a sliver. */
.loadout { min-width: 11rem; }
.loadout .lo-input {
    display: block;
    width: 100%;
    max-width: 10rem;
    margin-bottom: 0.25rem;
    font-size: 0.85rem;
    padding: 0.25rem 0.3rem;
}
/* Whether a truck's head have come off on-hand yet. */
.shipped-cell .btn { white-space: nowrap; }
.left-yes { color: var(--green-700); font-weight: 600; }
.barn-saved { font-size: 0.75rem; color: var(--green-700); margin-left: 0.3rem; opacity: 0; transition: opacity 0.2s; white-space: nowrap; }
.barn-saved.show { opacity: 1; }
.legacy-hint { font-size: 0.72rem; color: var(--muted); margin-top: 0.2rem; white-space: nowrap; }
.legacy-hint .lh-ok { color: var(--green-700); font-weight: 600; }
.legacy-hint .lh-warn { color: var(--warning); font-weight: 600; }
.inner-table { margin: 0 0 0.5rem; }
.inner-table th { background: transparent; font-size: 0.78rem; color: var(--muted); }
.inner-table td, .inner-table th { padding: 0.35rem 0.6rem; }
/* An inner table's header NEVER sticks. The sticky rule above is written for a
   page-level table sitting in a released .table-wrap; an inner table is nested in
   some other box (the matrix's .bk-panel-body, a <details>), and those boxes clip,
   so `top: var(--topbar-h)` resolves against the box and pushes the header 55px DOWN
   -- landing it on top of the first row, which is transparent-backgrounded here, so
   the heading and the first truck print over each other. Static, always.
   `table.inner-table` (not a bare `.inner-table`) to match the specificity of the
   sticky rule above -- one class short of it and this loses, silently. */
table.inner-table thead th { position: static; box-shadow: inset 0 -1px 0 var(--border); }

.help-steps { margin: 0 0 1rem; padding-left: 1.2rem; color: var(--text); font-size: 0.92rem; line-height: 1.6; }
.help-steps code { background: var(--green-100); padding: 0.05rem 0.3rem; border-radius: 4px; font-size: 0.88em; }
.code-block {
    background: #2b322a;
    color: #eaf1ec;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    font-size: 0.85rem;
    overflow-x: auto;
    margin: 0;
    white-space: pre-wrap;
    word-break: break-all;
}

@media (max-width: 900px) {
    /* The tree becomes a drawer: off-canvas until the hamburger opens it, so a
       phone gets the full width for content instead of losing 208px of it. */
    .tree-toggle { display: block; }
    .app-tree {
        position: fixed;
        top: var(--topbar-h);
        left: 0;
        bottom: 0;
        width: 240px;
        max-height: none;
        z-index: 190;
        transform: translateX(-100%);
        transition: transform 0.18s ease-out;
        box-shadow: 2px 0 12px rgba(0,0,0,0.12);
    }
    .app-tree.open { transform: translateX(0); }
    /* The page beside an open drawer: dimmed, and tappable to close. It stops below
       the topbar so the hamburger stays live -- tapping it is the other way out. */
    .tree-scrim {
        display: block;
        position: fixed;
        top: var(--topbar-h); right: 0; bottom: 0; left: 0;
        z-index: 189;               /* under the drawer (190), over the page */
        background: rgba(0, 0, 0, 0.28);
        /* iOS will not fire a click for a plain div unless it looks clickable. */
        cursor: pointer;
    }
    .tree-scrim[hidden] { display: none; }
    /* Roomier rows in the drawer. A 33px row is a comfortable mouse target and a
       cramped thumb one, and the miss lands on the row above or below -- or on the
       grip beside it. Closer to the 44px a finger actually needs. */
    .app-tree .tree-link { padding: 0.6rem 0.6rem; }
    .app-main { width: 100%; }
    .ent-btn { padding: 0.35rem 0.6rem; }
    .ent-btn .ent-label { display: none; }
}
@media (max-width: 640px) {
    /* ---- tables on a phone ----
       `table { width: 100% }` is right on a desktop and wrong at 390px: the table is
       squeezed to the viewport and every column shrinks to fit, so one long cell --
       a weigh-in note, a load comment -- wraps into a vertical ribbon. Measured on the
       group page: a 312-character note in a 105px column rendered a TWO-ROW table 742px
       tall with the header scrolled off the top.

       Letting the table size to its own content instead, with min-width:100% so narrow
       tables still fill the card, restores sensible column widths. `.table-wrap` is
       already overflow-x:auto, so a table that no longer fits scrolls sideways inside
       its own box rather than stretching the page -- the pattern this app already uses
       for wide tables. Sticky headers are unaffected: the rule at the top of this file
       already turns them static inside a scrolling wrapper.

       Cells stop wrapping mid-column for the same reason, except where the text is
       genuinely long -- `.cell-wrap` opts a column back into wrapping and caps it, so
       a paragraph-length note is readable without being a column of single letters. */
    .table-wrap table { width: auto; min-width: 100%; }
    .table-wrap td, .table-wrap th { white-space: nowrap; }
    .table-wrap td.cell-wrap {
        white-space: normal;
        min-width: 14rem;
        max-width: 20rem;
    }

    .topbar { gap: 0.4rem 0.6rem; }
    .topbar .brand { font-size: 1rem; }
    .topbar-right { margin-left: auto; }
    .owner-switch-label { display: none; }
    h1 { font-size: 1.35rem; }
    .btn { width: 100%; text-align: center; }
    .form-actions { flex-direction: column; }
    .form-actions .btn { width: 100%; }
}

/* ===================== operations multi-select (top bar) =====================
   Sits beside the owner switcher: that one asks whose share of the money, this
   one asks which ground it was earned on. A <select multiple> cannot show
   checkboxes and is miserable on a phone, so it is a button plus a panel. */
.ops-switch { position: relative; display: flex; align-items: center; gap: 0.35rem; }
.ops-toggle {
    display: flex; align-items: center; gap: 0.4rem;
    background: rgba(255,255,255,0.12); color: #fff;
    border: 1px solid rgba(255,255,255,0.25); border-radius: 6px;
    padding: 0.3rem 0.55rem; font: inherit; font-size: 0.9rem; cursor: pointer;
}
.ops-toggle:hover { background: rgba(255,255,255,0.2); }
.ops-summary { font-weight: 600; }
.ops-menu {
    position: absolute; top: calc(100% + 0.35rem); right: 0; z-index: 300;
    background: var(--card); color: var(--text);
    border: 1px solid var(--border); border-radius: 8px;
    box-shadow: 0 6px 20px rgba(0,0,0,0.18);
    padding: 0.5rem; min-width: 210px; font-size: 0.9rem;
}
.ops-menu[hidden] { display: none; }
.ops-menu label {
    display: flex; align-items: center; gap: 0.5rem;
    padding: 0.3rem 0.35rem; border-radius: 5px; cursor: pointer; white-space: nowrap;
}
.ops-menu label:hover { background: var(--green-100); }
.ops-menu-actions {
    display: flex; justify-content: space-between; gap: 0.5rem;
    margin-top: 0.45rem; padding-top: 0.45rem; border-top: 1px solid var(--border);
}
/* Narrow screens: the bar wraps, so pin the panel to the left edge instead of
   letting it hang off the right of the viewport. */
@media (max-width: 600px) { .ops-menu { right: auto; left: 0; } }


/* ===================== cost-line notes =====================
   Every cost line can carry a note saying where the figure came from -- the
   survey year and rate behind an imputed machinery charge, which invoice a
   share was taken from, why an amount was split. It is the audit trail, and it
   was previously stored but never shown. Collapsed by default so the table
   still reads as a table; the whole note wraps when opened. */
details.line-note { margin-top: .2rem; }
details.line-note > summary {
    cursor: pointer;
    font-size: .75rem;
    color: var(--muted, #777);
    list-style: none;
    display: inline-block;
    border-bottom: 1px dotted var(--muted, #777);
}
details.line-note > summary::-webkit-details-marker { display: none; }
details.line-note > summary:hover { color: var(--green-700, #2f6b34); }
details.line-note[open] > summary { margin-bottom: .25rem; }
details.line-note {
    font-size: .8rem;
    color: var(--muted, #777);
    line-height: 1.35;
    white-space: normal;
    max-width: 34rem;
}
@media print { details.line-note { display: none; } }


/* ---------------------------------------------------------------------------
   Growth panel + chart. Shared by the group page (templates/groups/_growth_card.html)
   and the group tree's click-through panel, so it lives here rather than in either
   template -- both draw the same SVG from static/js/growth-chart.js.
   --------------------------------------------------------------------------- */
.gc-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.gc-head h2 { margin: 0; font-size: 1.15rem; }
.gc-basis { font-size: 0.78rem; font-weight: 600; padding: 0.15rem 0.6rem; border-radius: 999px;
            background: var(--green-100); color: var(--green-700); }
.gc-basis-curve { background: #f1f2f0; color: var(--muted); }
.gc-lead { margin: 0.6rem 0 0.8rem; font-size: 1.02rem; }
.gc-card { border-left: 4px solid var(--green-600); }

.gc-chart svg { width: 100%; height: auto; display: block; background: var(--card);
                border: 1px solid var(--border); border-radius: 8px; }
.gc-key { display: flex; flex-wrap: wrap; gap: 0.35rem 1.1rem; align-items: center;
          font-size: 0.78rem; color: var(--muted); margin: 0.4rem 0 0.9rem; }
.gc-key .gk { display: inline-block; width: 0.85rem; height: 0.55rem; vertical-align: 0;
              margin-right: 0.3rem; border-radius: 2px; }
.gk-curve { background: var(--green-700); height: 0.2rem; }
.gk-pace { background: #b06a00; height: 0.2rem; }
.gk-obs { background: #1f6fb2; border-radius: 50%; width: 0.6rem; height: 0.6rem; }
.gk-band { background: var(--green-100); border: 1px solid var(--green-600); }
.gk-cliff { background: #c62828; height: 0.2rem; }

.gc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
           gap: 1rem; margin: 0.25rem 0 0.75rem; }
.gc-k { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.gc-v { font-size: 1.35rem; font-weight: 600; }
.gc-u { font-size: 0.85rem; font-weight: 400; color: var(--muted); }
.gc-sub { font-size: 0.82rem; color: var(--muted); }
.gc-warn { color: #c62828; }
.gc-ahead { color: var(--green-700); }
.gc-behind { color: #b06a00; }
.gc-note { font-size: 0.8rem; color: var(--muted); margin: 0.4rem 0 0; }

/* Chart internals -- these classes are set by growth-chart.js on SVG elements. */
.gc-grid-line { stroke: var(--border); stroke-width: 1; }
.gc-axis-y { font-size: 10px; fill: var(--muted); text-anchor: end; }
.gc-axis-x, .gc-axis-t { font-size: 10px; fill: var(--muted); text-anchor: middle; }
.gc-window { fill: var(--green-100); }
.gc-cliff { stroke: #c62828; stroke-width: 1.2; stroke-dasharray: 5 4; opacity: 0.75; }
.gc-curve { stroke: var(--green-700); stroke-width: 2.2; }
/* Pace is dashed on purpose: it is the feed odometer's opinion of WHEN, drawn on the
   weight axis for comparison -- not a second estimate of what the pigs weigh. */
.gc-pace { stroke: #b06a00; stroke-width: 1.8; stroke-dasharray: 5 3; opacity: 0.9; }
.gc-pace-dot { fill: #b06a00; opacity: 0.9; }
.gc-now { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; }
.gc-now-label { font-size: 9.5px; fill: var(--muted); text-anchor: middle; }
.gc-obs { fill: #1f6fb2; stroke: #fff; stroke-width: 1.2; }
.gc-obs-place { fill: var(--muted); }
.gc-est { fill: var(--green-700); stroke: #fff; stroke-width: 2; }

/* Who logged a mortality row, when it came from a barn phone rather than this desk.
   A quiet inline tag rather than a column: the overwhelming majority of rows are
   office-entered and carry no name, and a column of dashes is worse than nothing. */
.by-tag {
    display: inline-block;
    margin-left: 0.35rem;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background: var(--accent-soft, #eef4ee);
    color: var(--muted, #555);
    font-size: 0.72rem;
    white-space: nowrap;
}

/* ===================== tree search box =====================
   Sits above the tree and finds pages, tasks and records (see hogapp/appsearch.py).
   The dropdown is absolutely positioned over the tree rather than pushing it down:
   a list that shoves the nav around while you type is disorienting, and the tree is
   the thing you are trying to skip past anyway. */
.tree-search { position: relative; padding: 0 0.6rem 0.5rem; }
.tree-search input {
    width: 100%; box-sizing: border-box;
    /* 16px: below that, iOS Safari zooms the page when the field takes focus. */
    font-size: 16px; padding: 0.42rem 0.6rem;
    border: 1px solid var(--border); border-radius: 6px;
    background: var(--card); color: var(--text);
}
.tree-search input:focus { outline: 2px solid var(--green-600, #2d5a3d); outline-offset: -1px; }

.tree-search-results {
    /* FIXED, not absolute, and positioned by search.js. The tree aside is
       `overflow: auto`, so an absolutely-positioned child wider than the 208px
       sidebar is clipped by it -- which crushed every result into two-word-per-line
       ribbons. Fixed takes the dropdown out of that scroll box entirely. */
    position: fixed; z-index: 400;
    margin: 0; padding: 0.25rem; list-style: none;
    background: var(--card); border: 1px solid var(--border); border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.18);
    max-height: 60vh; overflow-y: auto;
}
.tree-search-results[hidden] { display: none; }
.ts-item { list-style: none; }
.ts-link {
    /* Explicit placement: left the two lines of text, right the kind badge. Left to
       auto-placement the badge takes the first cell and shoves the title into the
       second column, which is what crushed every title onto two lines. */
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    gap: 0 0.6rem;
    padding: 0.42rem 0.55rem; border-radius: 5px; text-decoration: none; color: var(--text);
}
.ts-item.is-on .ts-link { background: var(--green-100, #e8f5e9); }
.ts-title {
    grid-column: 1; grid-row: 1;
    font-weight: 600; font-size: 0.88rem; line-height: 1.25;
    /* One line each: a dropdown whose rows change height as you arrow through it is
       hard to track, and the full text is on the page you land on. */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ts-sub {
    grid-column: 1; grid-row: 2;
    font-size: 0.74rem; color: var(--muted); line-height: 1.2;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ts-kind {
    grid-column: 2; grid-row: 1 / span 2; align-self: center;
    font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.04em;
    padding: 0.08rem 0.4rem; border-radius: 999px;
    background: var(--green-100, #eef4ee); color: var(--muted);
    white-space: nowrap;
}
.ts-kind-task { background: #fbf0d8; color: #7a5307; }
.ts-kind-group { background: #e4f0e7; color: #1d5130; }
.ts-none { padding: 0.5rem; font-size: 0.85rem; color: var(--muted); font-style: italic; }

/* --- Accounts page (admin/users.html) ------------------------------------- */
/* A password that is about to be read down a phone line. Big, spaced, and in a
   monospace face, because the person on the other end is writing it on a feed bag:
   "clover" and "clever" have to be told apart at a glance. */
.temp-pass-card { border-left: 4px solid var(--green-600); }
.temp-pass { margin: 0.25rem 0 0.75rem; }
.temp-pass code {
    font-family: "Cascadia Mono", Consolas, "SF Mono", monospace;
    font-size: 1.35rem; letter-spacing: 0.03em;
    background: var(--green-100); color: var(--text);
    padding: 0.35rem 0.7rem; border-radius: 6px; display: inline-block;
    word-break: break-all;
}
/* A switched-off account stays in the list -- greyed rather than hidden, because
   "why can't Tony sign in" is answered by seeing the row, not by its absence. */
tr.inactive-row td { opacity: 0.55; }
/* Three small buttons per row; they wrap rather than stretching the table on a phone. */
.row-actions { white-space: nowrap; }
.row-actions form { display: inline; }
.row-actions .btn { margin-right: 0.25rem; }
@media (max-width: 700px) {
    .row-actions { white-space: normal; }
    .row-actions .btn { margin-bottom: 0.25rem; }
}
/* Checkbox and its words on one line, aligned on the box rather than the text box. */
label.check-line { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; }
label.check-line input { width: auto; margin: 0; }

/* ---- the Accounts page's permission grids ----
   Two of them: the barn list (a plain multi-column set of tick boxes) and the area
   list, where each row is "may see" + "may change" + a line saying what the area
   actually is. The blurb matters more than it looks: the decision being made is
   which of the farm's books somebody else reads, and "Finance" on its own does not
   tell you that it includes the tax returns. */
.area-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.35rem 1rem;
}
.area-group { margin-bottom: 0.9rem; }
.area-group h4 {
    margin: 0.6rem 0 0.35rem;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    border-bottom: 1px solid var(--border);
    padding-bottom: 0.2rem;
}
.area-row {
    display: grid;
    grid-template-columns: minmax(12rem, 18rem) 9rem 1fr;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.3rem 0;
    border-bottom: 1px dotted var(--border);
}
.area-row .area-edit-box { font-weight: 400; color: var(--muted); }
.area-row .area-blurb { margin: 0; }
@media (max-width: 760px) {
    /* One column per row on a phone: the three-column grid squeezes the blurb into
       a vertical ribbon, which is the same failure the tables hit at 390px. */
    .area-row { grid-template-columns: 1fr; gap: 0.15rem; }
    .area-row .area-edit-box { padding-left: 1.6rem; }
    .area-row .area-blurb { padding-left: 1.6rem; }
}

/* A view-only account's banner. Warning-coloured rather than red: nothing has gone
   wrong, they simply cannot change this page. Sits at the top of the content, where
   the flash messages go, because that is where this app says things. */
.flash.read-only-note {
    background: #fdf6e3;
    border-left: 4px solid var(--warning);
    color: var(--text);
}
.flash.read-only-note[hidden] { display: none; }
/* A disabled control should look disabled. Browsers grey the text but leave a
   <select> looking otherwise clickable, which is most of what made the load
   schedule's dropdowns misleading in the first place. */
.app-main :disabled {
    cursor: not-allowed;
    opacity: 0.55;
}
/* The read-only banner, when a blocked write makes it speak up mid-page. A short
   flash rather than anything permanent: it is answering "why did nothing happen",
   and it has to be noticed the second time as readily as the first. */
.flash.read-only-note.pulse { animation: ro-pulse 1.2s ease-out 2; }
@keyframes ro-pulse {
    0%   { background: #fdf6e3; }
    25%  { background: #f7e5b0; }
    100% { background: #fdf6e3; }
}
@media (prefers-reduced-motion: reduce) {
    .flash.read-only-note.pulse { animation: none; background: #f7e5b0; }
}
