/* =====================================================================
 * CattleBase — app.css: the single stylesheet for the SPA shell (index.html)
 * and the UI app.js builds at runtime (modals, menus, toasts, wizard, etc.).
 *
 * Layout of this file:
 *   1. Theme tokens (:root CSS variables) and base reset
 *   2. Shell: sidebar, top bar, language/farm menus, screen switching
 *   3. Shared components: panels, tags, pills, tables, buttons
 *   4. Per-screen rules: dashboard, animals (register + filters), record,
 *      service, pipeline (mating board), herds/camps, reports, setup,
 *      contacts, diary
 *   5. Responsive: a <=1100px tablet step, then the <=768px phone layout
 *      (off-canvas nav drawer, single-column grids, scrolling tables)
 *   6. Late additions: catalog lot picker + print rules
 *
 * Mobile-first note: the base rules are the desktop layout; the @media
 * blocks collapse it for narrower screens (this branch's focus). Menus
 * that must escape a clipping container use position:fixed, positioned by
 * app.js (floatMenu). Dark mode is not implemented; all colour comes from
 * the tokens below so a theme can be swapped by redefining them.
 * ===================================================================== */

/* ---------- theme tokens ----------
   Warm "kraft" paper surfaces, stockman-green frame, brass ear-tag accents.
   Semantic pairs: green = ok/primary, brass = due/warning, ox = problem,
   teal = info. -tint variants are soft backgrounds for pills and chips. */
:root{
    --ink:#241E18; --muted:#6E6459; --faint:#938779;
    --paper:#F1ECE2; --surface:#FBF9F4; --surface-2:#F6F2E9;
    --line:#DDD4C4; --line-soft:#E7E0D2;
    --green:#234C36; --green-deep:#183526; --green-tint:#E4ECE4;
    --brass:#B07C1B; --brass-deep:#8F6412; --brass-tint:#F0E4C6;
    --ox:#9A3B2C; --ox-tint:#F1DED8;
    --teal:#3B5F63; --teal-tint:#DDE8E7;
    --tag:#E9B84A; --tag-edge:#C9971F;
    --radius:10px; --radius-sm:7px;
    --shadow:0 1px 0 rgba(36,30,24,.04), 0 1px 3px rgba(36,30,24,.05);
    --sans:'Inter',system-ui,sans-serif; --serif:'Fraunces',Georgia,serif;
  }
  /* ---------- base reset ---------- */
  *{box-sizing:border-box}
  /* The app toggles visibility with the `hidden` attribute (?hidden bindings and the
     initial .app hide). This must win over an element's own display (grid/flex), so it is
     !important. Served from app.css (not a JS-injected <style>) so a strict CSP keeps it. */
  [hidden]{display:none!important}
  html,body{margin:0}
  body{background:var(--paper);color:var(--ink);font-family:var(--sans);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}
  .num{font-variant-numeric:tabular-nums}

  /* Fixed 230px sidebar + fluid main. At <=768px this becomes a single column and
     the sidebar turns into an off-canvas drawer (see the phone layout block). */
  /* ---------- shell ---------- */
  .app{display:grid;grid-template-columns:230px 1fr;min-height:100vh}
  .side{background:var(--green-deep);color:#DCE6DC;display:flex;flex-direction:column;position:sticky;top:0;height:100vh}
  .brand{display:flex;align-items:center;gap:10px;padding:20px 20px 16px}
  .brand .mark{width:38px;height:38px;border-radius:9px;background:#fff;display:grid;place-items:center;overflow:hidden;box-shadow:inset 0 0 0 1.5px var(--line-soft)}
  .brand .mark img{width:100%;height:100%;object-fit:contain;padding:4px;box-sizing:border-box}
  .brand b{font-family:var(--serif);font-weight:600;font-size:20px;color:#fff;letter-spacing:.2px}
  /* farm picker (sidebar) and its dropdown menu — the menu element is created by app.js */
  .farmpick{position:relative;margin:0 14px 10px;padding:9px 11px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.09);border-radius:var(--radius-sm);cursor:pointer}
  .farmpick .lbl{font-size:11px;color:#9DB4A2;letter-spacing:.3px}
  .farmpick .val{display:flex;justify-content:space-between;align-items:center;color:#fff;font-weight:600;margin-top:1px}
  .farmpick .val svg{opacity:.6;transition:transform .15s ease}
  .farmpick[aria-expanded="true"] .val svg{transform:rotate(180deg)}
  .farmmenu{position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:45;max-height:300px;overflow-y:auto;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm);box-shadow:0 10px 30px rgba(0,0,0,.28);padding:5px}
  .farmmenu[hidden]{display:none}
  .farmopt{display:block;width:100%;text-align:left;border:none;background:none;font-family:inherit;font-size:13.5px;color:var(--ink);padding:8px 10px;border-radius:var(--radius-sm);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .farmopt:hover{background:var(--surface-2)}
  .farmopt.on{background:var(--green-tint);color:#2C5A40;font-weight:600}
  .farmopt.muted{color:var(--faint);cursor:default}
  /* sidebar navigation */
  nav{padding:8px 12px;display:flex;flex-direction:column;gap:1px;overflow-y:auto}
  .navitem{display:flex;align-items:center;gap:11px;padding:9px 12px;border-radius:var(--radius-sm);color:#BFD0C2;cursor:pointer;font-weight:500;border:none;background:none;width:100%;text-align:left;font-size:13.5px;font-family:inherit}
  .navitem svg{width:17px;height:17px;flex:none;opacity:.85}
  .navitem:hover{background:rgba(255,255,255,.05);color:#EAF1EA}
  .navitem.on{background:var(--green);color:#fff}
  .navitem.on svg{opacity:1}
  .navsec{font-size:10.5px;letter-spacing:.8px;color:#7E948499;padding:14px 14px 5px;text-transform:uppercase}
  .side .foot{margin-top:auto;padding:14px 18px;border-top:1px solid rgba(255,255,255,.08);display:flex;align-items:center;gap:10px;font-size:12.5px;color:#A9BBAD}
  .side .foot .av{width:28px;height:28px;border-radius:50%;background:var(--brass);color:#3a2a08;display:grid;place-items:center;font-weight:700;font-size:12px}

  /* top bar: sticky, holds the breadcrumb, search, language menu and primary action */
  main{min-width:0;display:flex;flex-direction:column}
  .top{display:flex;align-items:center;gap:16px;padding:14px 26px;background:var(--surface);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:5}
  .crumb{font-family:var(--serif);font-size:21px;font-weight:600;letter-spacing:.2px}
  .crumb small{font-family:var(--sans);font-weight:500;font-size:12.5px;color:var(--faint);display:block;letter-spacing:.2px;margin-bottom:-1px}
  .search{margin-left:auto;display:flex;align-items:center;gap:8px;background:var(--surface-2);border:1px solid var(--line);border-radius:20px;padding:7px 14px;color:var(--muted);min-width:230px}
  .search input{border:none;background:none;outline:none;font-family:inherit;font-size:13px;color:var(--ink);width:100%}
  /* justify-content + text-align centre the label on both axes: on phones many buttons
     stretch to share a row (mating-card Pregnant / Not, Record weaning…) and a two-line
     label wraps, so a start-aligned label read as off-centre. */
  .btn{font-family:inherit;font-size:13px;font-weight:600;border-radius:var(--radius-sm);padding:8px 14px;border:1px solid var(--line);background:var(--surface);color:var(--ink);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;text-align:center;gap:7px}
  .btn:hover{border-color:var(--faint)}
  .btn.primary{background:var(--green);border-color:var(--green);color:#fff}
  .btn.primary:hover{background:var(--green-deep)}
  /* A disabled action (e.g. Add lot / Print catalog with no catalog selected) must LOOK unavailable. */
  .btn:disabled,.btn:disabled:hover{opacity:.45;cursor:not-allowed;border-color:var(--line)}
  .btn.primary:disabled:hover{background:var(--green)}
  .btn.ghost{padding:6px 11px;font-size:12.5px}
  .btn.danger{color:#9A2B2B;border-color:#E3C4BE}
  .btn.danger:hover{background:#FBECE9;border-color:#C9796E}
  .btn svg{width:15px;height:15px}
  /* language dropdown (EN/AF) in the top bar */
  .langbtn{gap:6px;padding:8px 11px;color:var(--muted)} .langbtn #langCode{font-weight:700;color:var(--ink)}
  .langbtn .chev{transition:transform .18s ease}
  .langbtn[aria-expanded="true"] .chev{transform:rotate(180deg)}
  .langwrap{position:relative}
  .langmenu{position:absolute;top:calc(100% + 6px);right:0;min-width:170px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:0 8px 26px rgba(36,30,24,.16);padding:5px;z-index:30}
  .langmenu[hidden]{display:none}
  .langopt{display:flex;align-items:center;gap:8px;width:100%;text-align:left;border:none;background:none;font-family:inherit;font-size:13.5px;color:var(--ink);padding:8px 10px;border-radius:var(--radius-sm);cursor:pointer}
  .langopt:hover{background:var(--surface-2)}
  .langopt .tick{width:15px;height:15px;flex:none;opacity:0;color:var(--green)}
  .langopt.on{font-weight:600}
  .langopt.on .tick{opacity:1}
  .langopt .code{margin-left:auto;font-size:11px;font-weight:700;color:var(--faint)}

  /* screens: only the one with .on is displayed (app.js toggles it from the router) */
  .screen{padding:26px;display:none;animation:rise .35s ease both}
  .screen.on{display:block}
  /* opacity-only: a transform here (even the identity matrix the animation
     freezes with fill-mode both) would make descendant position:fixed popups
     resolve against .screen instead of the viewport */
  @keyframes rise{from{opacity:0}to{opacity:1}}
  @media (prefers-reduced-motion:reduce){.screen,.screen.on{animation:none}}

  /* ---------- shared bits ---------- */
  /* panels and ear-tag chip: .tag is the signature brass ear-tag look used for animal ids */
  .panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
  .panel-h{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid var(--line-soft)}
  .panel-h h3{margin:0;font-family:var(--serif);font-size:16px;font-weight:600}
  .panel-h .btn{margin-left:auto}
  .tag{display:inline-flex;align-items:center;white-space:nowrap;font-variant-numeric:tabular-nums;font-weight:700;font-size:12.5px;color:#4A3A12;background:var(--tag);padding:3px 9px 3px 8px;border-radius:4px 9px 9px 4px;box-shadow:inset 0 0 0 1.5px var(--tag-edge);position:relative;letter-spacing:.3px}
  .tag::before{content:"";position:absolute;left:-4px;top:50%;transform:translateY(-50%);width:6px;height:6px;border-radius:50%;background:var(--tag);box-shadow:inset 0 0 0 1.5px var(--tag-edge)}
  /* status pills (colour = state) */
  .pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;padding:2px 9px;border-radius:20px;white-space:nowrap}
  .pill i{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.9}
  .p-green{background:var(--green-tint);color:#2C5A40} .p-brass{background:var(--brass-tint);color:var(--brass-deep)}
  .p-teal{background:var(--teal-tint);color:var(--teal)} .p-ox{background:var(--ox-tint);color:var(--ox)}
  .p-grey{background:#EAE4D8;color:#786C5C}
  .muted{color:var(--muted)}

  /* ---------- dashboard ---------- */
  .stat-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:20px}
  .stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:16px 18px;box-shadow:var(--shadow)}
  .stat .k{font-size:12.5px;color:var(--muted);display:flex;align-items:center;gap:7px}
  .stat .k svg{width:15px;height:15px;color:var(--brass)}
  .stat .v{font-family:var(--serif);font-size:34px;font-weight:600;line-height:1.05;margin-top:8px}
  .stat .d{font-size:12px;margin-top:3px}
  .up{color:#2C5A40} .down{color:var(--ox)}
  .grid2{display:grid;grid-template-columns:1.35fr 1fr;gap:16px}
  .tasklist{padding:6px 0}
  .task{display:flex;align-items:center;gap:12px;padding:11px 18px;border-bottom:1px solid var(--line-soft)}
  .task:last-child{border-bottom:none}
  .task .ic{width:32px;height:32px;border-radius:8px;display:grid;place-items:center;flex:none}
  .task .ic svg{width:16px;height:16px}
  .ic.warn{background:var(--ox-tint);color:var(--ox)} .ic.due{background:var(--brass-tint);color:var(--brass-deep)} .ic.ok{background:var(--green-tint);color:#2C5A40}
  .task .tx{flex:1;min-width:0}
  .task .tx b{font-weight:600} .task .tx .s{font-size:12px;color:var(--muted)}
  .task .ct{font-family:var(--serif);font-size:20px;font-weight:600}
  .activity{padding:4px 18px}
  .act{display:flex;gap:12px;padding:10px 0;border-bottom:1px solid var(--line-soft);font-size:13px}
  .act:last-child{border:none}
  .act .dot{width:8px;height:8px;border-radius:50%;margin-top:6px;flex:none;background:var(--brass)}
  .act .t{color:var(--faint);font-size:11.5px;margin-left:auto;white-space:nowrap;font-variant-numeric:tabular-nums}

  /* ---------- table ---------- */
  .toolbar{display:flex;align-items:center;gap:9px;margin-bottom:14px;flex-wrap:wrap}
  /* Filter pill (register type chips, contact groups, camp herds, report picker). An
     inline-flex box centred on both axes: the phone rules grow chips to a 40px tap
     target, and a plain block left the label stuck to the top of the taller pill. */
  .chip{font-size:12.5px;font-weight:500;padding:6px 12px;border-radius:20px;border:1px solid var(--line);background:var(--surface);color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;text-align:center}
  .chip.on{background:var(--green);border-color:var(--green);color:#fff}
  table{width:100%;border-collapse:collapse}
  /* wraps a wide table that shares a panel with a header, so only the table
     scrolls sideways and the header/action button stay put */
  .tbl-scroll{overflow-x:auto}
  thead th{text-align:left;font-size:11.5px;font-weight:600;letter-spacing:.4px;color:var(--faint);padding:11px 16px;border-bottom:1px solid var(--line)}
  /* Click-to-sort column headers (register). A low-contrast ↕ hints sortability; the active
     column shows a solid ▲/▼ and tints its label. ::after carries the caret so it survives the
     [data-i18n] effect overwriting the header's textContent (e.g. the Quality column). */
  thead th.sortable{cursor:pointer;user-select:none;white-space:nowrap}
  thead th.sortable::after{content:'↕';margin-left:5px;font-size:10px;opacity:.35}
  thead th.sortable:hover{color:var(--ink)}
  thead th.sort-asc,thead th.sort-desc{color:var(--ink)}
  thead th.sort-asc::after{content:'▲';opacity:.9}
  thead th.sort-desc::after{content:'▼';opacity:.9}
  thead th.sortable:focus-visible{outline:2px solid var(--green);outline-offset:-2px}
  tbody td{padding:12px 16px;border-bottom:1px solid var(--line-soft);font-size:13px}
  tbody tr{cursor:pointer}
  tbody tr:hover{background:var(--surface-2)}
  .animal-cell{display:flex;align-items:center;gap:11px}
  .animal-cell .nm{font-weight:600}
  .animal-cell .br{font-size:11.5px;color:var(--faint)}
  /* Quality grade badge — grade letter + score, next to the name, before the type */
  .grade-badge{display:inline-flex;align-items:center;gap:4px;font-family:var(--sans);font-size:11px;font-weight:700;line-height:1;padding:3px 7px;border-radius:20px;border:1px solid transparent;cursor:pointer;white-space:nowrap}
  .grade-badge b{font-size:11.5px}
  .grade-badge span{font-weight:600;opacity:.85}
  .grade-badge:hover{filter:brightness(.97)}
  .g-A{background:var(--green-tint);color:#2C5A40;border-color:#CADBC9}
  .g-B{background:var(--teal-tint);color:var(--teal);border-color:#C7D9D7}
  .g-C{background:var(--brass-tint);color:var(--brass-deep);border-color:#E7D4A6}
  .g-D{background:var(--ox-tint);color:var(--ox);border-color:#E4C6BE}
  .g-NA{background:transparent;color:var(--faint);border:1px dashed var(--line);font-size:10.5px}
  .g-NA b{font-weight:500}
  .swatch{width:14px;height:14px;border-radius:4px;border:1px solid rgba(0,0,0,.12);flex:none}
  .herd{white-space:nowrap}
  .wt{font-variant-numeric:tabular-nums;font-weight:600;white-space:nowrap}
  .wt small{color:var(--faint);font-weight:400}
  /* animal register: chips grouped left, count + actions right; wraps cleanly */
  /* two stacked zones: the type pills wrap freely across as many rows as they need,
     then the count + Filter + Export sit together on their own row, aligned right.
     No horizontal scroll, consistent whichever pill is active */
  /* Tighten the space below the action row before the table (overrides .toolbar's 14px). */
  .an-toolbar{display:flex;flex-direction:column;gap:10px;align-items:stretch;margin-bottom:6px}
  .an-toolbar .chips{display:flex;flex-wrap:wrap;gap:8px}
  .an-toolbar .tb-actions{display:flex;align-items:center;gap:9px;justify-content:flex-end}
  .an-toolbar .tb-actions .muted{white-space:nowrap}
  /* column-filter bar: a tidy 5-column grid, shown/hidden by the Filter button. Controls
     share the design's input look so the dropdowns match the rest. The three .an-filter-row
     wrappers collapse with display:contents so all their controls flow into ONE grid — giving
     a clean 5 × 3: identity+classification, lineage+origin+status, then the Sex + Pregnancy pair
     on the last row with Clear pinned to its right. (The ≤768px block overrides to 2 columns.) */
  .an-filters{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;align-items:center}
  .an-filters[hidden]{display:none}
  .an-filter-row{display:contents}
  /* each grid item (text input or the dropdown's mount span) fills its column; min-width:0 lets
     it shrink inside the track instead of forcing overflow */
  .an-filter-ctl,.an-filters .an-filter-sel{min-width:0}
  /* text inputs: same frame as the modal's .cb-inp / styled select button */
  input.an-filter-ctl{font-family:inherit;font-size:13px;color:var(--ink);background:var(--surface);border:1px solid var(--line);border-radius:6px;padding:7px 11px;outline:none}
  input.an-filter-ctl::placeholder{color:var(--faint)}
  input.an-filter-ctl:focus{border-color:var(--green)}
  /* styled dropdown (buildSelect) scoped into the filter bar — mirrors .setrow */
  .an-filters .an-filter-sel{display:block}
  .an-filters .cb-select{position:relative;width:100%}
  .an-filters .cb-select-btn{display:flex;align-items:center;gap:8px;width:100%;text-align:left;cursor:pointer;border:1px solid var(--line);background:var(--surface);border-radius:6px;padding:7px 11px;font-size:13px;font-weight:600;color:var(--ink);font-family:inherit}
  .an-filters .cb-select-btn .chev{width:13px;height:13px;flex:none;opacity:.6;margin-left:auto}
  .an-filters .cb-select-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .an-filters .cb-select-label.cb-ph{color:var(--faint);font-weight:400}
  .an-filters .cb-select-menu{position:fixed;z-index:30;display:flex;flex-direction:column;max-height:280px;min-width:160px;background:var(--surface);border:1px solid var(--line);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.18);padding:5px}
  .an-filters .cb-select-menu[hidden]{display:none}
  .an-filters .cb-select-search{border:1px solid var(--line);border-radius:6px;padding:8px 10px;font-family:inherit;font-size:13px;color:var(--ink);outline:none;margin-bottom:5px}
  .an-filters .cb-select-search:focus{border-color:var(--green)}
  .an-filters .cb-select-list{overflow-y:auto}
  .an-filters .cb-select-opt{display:block;width:100%;text-align:left;border:none;background:none;font-family:inherit;font-size:13.5px;color:var(--ink);padding:8px 10px;border-radius:6px;cursor:pointer;white-space:nowrap}
  .an-filters .cb-select-opt:hover{background:var(--surface-2)}
  .an-filters .cb-select-opt.on{background:var(--green-tint);color:#2C5A40;font-weight:600}
  /* Clear sits on the last grid row, pinned to the far-right column so the Sex + Pregnancy
     pair keeps the left of that row (overridden to column 2 in the ≤768px block). */
  .an-filter-clear{grid-column:5;justify-self:end;align-self:center}
  /* Filter toggle: active/open tint so the collapsed bar's state is visible */
  .an-toolbar .tb-actions .btn.on{background:var(--green-tint);border-color:var(--green);color:#2C5A40}
  /* Register action row: dedicated search on the left (~1/3), actions pushed right. */
  .an-actionbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
  .an-actionbar .tb-actions{margin-left:auto}                 /* count + Filter + Export stay right */
  /* search field: ~1/3 of the row (clamped), same rounded frame as the record-picker search box */
  .an-search{flex:0 0 clamp(240px,44%,480px);display:flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:10px 14px;color:var(--muted);box-shadow:var(--shadow)}
  .an-search svg{width:15px;height:15px;flex:none;opacity:.7}
  .an-search input{border:none;background:none;outline:none;font-family:inherit;font-size:14px;color:var(--ink);width:100%}
  .an-search input::placeholder{color:var(--muted)}
  @media (max-width:640px){ .an-search{flex-basis:100%} }     /* full width on phones */
  /* Register pager — "Showing X–Y of Z" · page-size dropdown · prev/next + numbered pages. */
  .an-pager{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:14px}
  .an-pager[hidden]{display:none}
  .an-pg-info{color:var(--muted);font-size:12.5px}
  .an-pg-size{display:flex;align-items:center;gap:8px;margin-left:auto;font-size:12.5px;color:var(--muted)}
  .an-pg-size .cb-select{position:relative;min-width:86px}
  .an-pg-nav{display:flex;align-items:center;gap:4px}
  .an-pg-btn{min-width:32px;height:32px;padding:0 9px;border:1px solid var(--line);background:var(--surface);color:var(--ink);border-radius:6px;font-family:inherit;font-size:13px;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
  .an-pg-btn:hover:not(:disabled){background:var(--surface-2)}
  .an-pg-btn.on{background:var(--green);border-color:var(--green);color:#fff}
  .an-pg-btn:disabled{opacity:.4;cursor:default}
  .an-pg-ellip{padding:0 2px;color:var(--muted)}
  /* styled page-size dropdown (buildSelect) scoped into the pager — mirrors the filter-bar select */
  .an-pg-size .cb-select-btn{display:flex;align-items:center;gap:8px;width:100%;text-align:left;cursor:pointer;border:1px solid var(--line);background:var(--surface);border-radius:6px;padding:7px 11px;font-size:13px;font-weight:600;color:var(--ink);font-family:inherit}
  .an-pg-size .cb-select-btn .chev{width:13px;height:13px;flex:none;opacity:.6;margin-left:auto}
  .an-pg-size .cb-select-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .an-pg-size .cb-select-menu{position:fixed;z-index:30;display:flex;flex-direction:column;max-height:280px;min-width:86px;background:var(--surface);border:1px solid var(--line);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.18);padding:5px}
  .an-pg-size .cb-select-menu[hidden]{display:none}
  .an-pg-size .cb-select-list{overflow-y:auto}
  .an-pg-size .cb-select-opt{display:block;width:100%;text-align:left;border:none;background:none;font-family:inherit;font-size:13.5px;color:var(--ink);padding:8px 10px;border-radius:6px;cursor:pointer;white-space:nowrap}
  .an-pg-size .cb-select-opt:hover{background:var(--surface-2)}
  .an-pg-size .cb-select-opt.on{background:var(--green-tint);color:#2C5A40;font-weight:600}
  /* uniform-width ID tags so the Animal column lines up regardless of id length */
  #animals .animal-cell .tag{min-width:72px;justify-content:center}

  /* ---------- record ---------- */
  .rec-pick{position:relative;display:flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:10px 14px;margin-bottom:14px;color:var(--muted);box-shadow:var(--shadow)}
  .rec-pick input{border:none;background:none;outline:none;font-family:inherit;font-size:14px;color:var(--ink);width:100%}
  .rec-pick-menu{position:absolute;top:calc(100% + 5px);left:0;right:0;z-index:40;max-height:320px;overflow-y:auto;background:var(--surface);border:1px solid var(--line);border-radius:10px;box-shadow:0 12px 30px rgba(0,0,0,.2);padding:5px}
  .rec-pick-item{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 11px;border-radius:7px;cursor:pointer;font-size:13.5px}
  .rec-pick-item:hover,.rec-pick-item.active{background:var(--surface-2)}
  .rec-pick-item .sub{color:var(--muted);font-size:12px;text-align:right;white-space:nowrap}
  .rec-pick-empty{padding:11px;color:var(--muted);font-size:13px}
  .cb-lightbox{position:fixed;inset:0;z-index:200;display:grid;place-items:center;padding:28px;background:rgba(22,17,10,.84);opacity:0;transition:opacity .18s ease}
  .cb-lightbox.on{opacity:1}
  .cb-lightbox[hidden]{display:none}
  .cb-lightbox-img{max-width:94vw;max-height:90vh;object-fit:contain;border-radius:10px;background:var(--surface);box-shadow:0 20px 60px rgba(0,0,0,.5)}
  .cb-lightbox-x{position:absolute;top:16px;right:18px;width:40px;height:40px;border-radius:50%;border:none;background:rgba(255,255,255,.15);color:#fff;font-size:24px;line-height:1;cursor:pointer;display:grid;place-items:center}
  .cb-lightbox-x:hover{background:rgba(255,255,255,.28)}
  /* Phase A: the photo's taken_on date, bottom-left of the full image, with an inline edit control
     (field+ only — hidden for readonly by the generic [data-min-role] rule below). */
  .cb-lightbox-date{position:absolute;left:20px;bottom:18px;display:flex;align-items:center;gap:10px;z-index:2}
  .cb-lightbox-date-t{background:rgba(22,17,10,.72);color:#fff;font-size:13px;font-weight:600;padding:5px 11px;border-radius:7px}
  .cb-lightbox-date-edit{background:rgba(255,255,255,.15);color:#fff;border:none;font-size:12px;font-weight:600;padding:7px 12px;border-radius:7px;cursor:pointer;min-height:40px}
  .cb-lightbox-date-edit:hover{background:rgba(255,255,255,.28)}
  /* Quality breakdown overlay */
  .cb-quality{position:fixed;inset:0;z-index:210;display:grid;place-items:center;padding:24px;background:rgba(22,17,10,.5);opacity:0;transition:opacity .18s ease}
  .cb-quality.on{opacity:1}
  .cb-quality[hidden]{display:none}
  .cb-q-card{position:relative;width:min(460px,94vw);max-height:88vh;overflow-y:auto;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:0 24px 60px rgba(36,30,24,.3);padding:22px}
  .cb-q-x{position:absolute;top:12px;right:14px;width:30px;height:30px;border-radius:50%;border:none;background:var(--surface-2);color:var(--muted);font-size:19px;line-height:1;cursor:pointer}
  .cb-q-x:hover{background:var(--line-soft);color:var(--ink)}
  .cb-q-head{display:flex;align-items:center;gap:14px;margin-bottom:18px;padding-right:28px}
  .cb-q-grade{width:52px;height:52px;flex:none;border-radius:12px;display:grid;place-items:center;border:1px solid transparent}
  .cb-q-grade b{font-family:var(--serif);font-size:26px;line-height:1}
  .cb-q-title{font-family:var(--serif);font-size:18px;font-weight:600}
  .cb-q-sub{font-size:12px;color:var(--muted);margin-top:2px}
  .cb-q-none{font-size:13px;color:var(--muted);margin:6px 0}
  /* ── "points" breakdown (design option B): one stacked meter shows how the score is built ── */
  .cb-qp-cap{font-size:11px;font-weight:700;letter-spacing:.5px;color:var(--faint);text-transform:uppercase;margin:16px 0 7px}
  /* the meter: a coloured segment per contributing trait + a hatched headroom tail; widths are points */
  .cb-qp-meter{display:flex;height:30px;border-radius:7px;overflow:hidden;border:1px solid var(--line);background:var(--surface-2)}
  .cb-qp-seg{height:100%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;color:#fff;overflow:hidden;white-space:nowrap}
  .cb-qp-gap{background:repeating-linear-gradient(45deg,var(--surface-2),var(--surface-2) 5px,#ECE4D4 5px,#ECE4D4 10px);color:var(--faint)}
  .cb-qp-scale{display:flex;justify-content:space-between;font-size:10.5px;color:var(--faint);margin-top:4px}
  .cb-qp-leg{margin-top:14px}
  .cb-qp-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-top:1px solid var(--line-soft)}
  .cb-qp-row.muted{opacity:.6}
  .cb-qp-key{width:11px;height:11px;border-radius:3px;flex:none;margin-top:3px;align-self:flex-start}
  .cb-qp-key.na{background:var(--surface-2);border:1px solid var(--faint)}
  .cb-qp-tx{min-width:0}
  .cb-qp-nm{font-weight:600;font-size:12.5px}
  .cb-qp-vl{font-size:11px;color:var(--faint)}
  .cb-qp-pt{margin-left:auto;text-align:right;font-size:11px;color:var(--muted);white-space:nowrap}
  .cb-qp-pt b{font-family:var(--serif);font-size:15px;color:var(--ink);display:block;font-weight:600}
  .cb-q-untracked{margin-top:16px;padding-top:14px;border-top:1px solid var(--line)}
  .cb-q-uthead{font-size:11px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--faint);margin-bottom:8px}
  .cb-q-tags{display:flex;flex-wrap:wrap;gap:6px}
  .cb-q-tag{font-size:11.5px;color:var(--muted);background:var(--surface-2);border:1px dashed var(--line);border-radius:14px;padding:3px 9px}
  /* Record layout: fixed-width photo/identity column + the detail panel. The second
     track is minmax(0,1fr), NOT 1fr — a bare 1fr track has min-width:auto, so when the
     panel's content (the metrics grid, long notes) is wider than the space left over, the
     track refuses to shrink and the whole grid overflows its container, clipping the
     "General" panel off the right edge. minmax(0,…) lets the panel shrink and its content
     wrap instead of pushing past the viewport. Collapses to one column at <=1100px below. */
  .rec{display:grid;grid-template-columns:300px minmax(0,1fr);gap:18px}
  /* Identity panel (Name/Type/Sex/Breed/Herd/Camp/Status). Generous 22px side gutters so
     labels and right-aligned values sit clear of the panel edges on every width; top/bottom
     is light because each .kv row carries its own 9px vertical padding. */
  .rec .cb-rec-ident{margin-top:16px;padding:10px 22px}
  .rec .photo{aspect-ratio:4/3;border-radius:var(--radius);background:linear-gradient(135deg,#7B6A54,#3E3527);position:relative;overflow:hidden;display:grid;place-items:center;color:#E9DFCB}
  .rec .photo .ph{font-family:var(--serif);opacity:.5;font-size:14px}
  .rec .photo .tag{position:absolute;left:12px;top:12px}
  .cb-gallery{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:10px}
  .cb-thumb{position:relative;width:58px;height:58px;border-radius:8px;background-size:cover;background-position:center;cursor:grab;border:2px solid transparent;box-shadow:var(--shadow);touch-action:none}
  .cb-thumb.primary{border-color:var(--green)}
  .cb-gallery .cb-thumb,.cb-gallery .cb-thumb-placeholder{transition:transform .18s ease}   /* siblings slide out of the way */
  .cb-thumb.dragging{opacity:.97;cursor:grabbing;z-index:50;box-shadow:0 14px 28px rgba(36,30,24,.42);transition:none}
  .cb-thumb-placeholder{width:58px;height:58px;border-radius:8px;border:2px dashed var(--brass);background:transparent;box-shadow:none;flex:none}
  .cb-gallery.reordering{cursor:grabbing}
  .cb-gallery.reordering .cb-thumb .del{opacity:0;pointer-events:none}
  /* while dragging, nothing but the lifted thumb reacts to the pointer — no hover/active on the "+" tile or the other photos */
  .cb-gallery.reordering .cb-add-photo,.cb-gallery.reordering .cb-thumb:not(.dragging){pointer-events:none}
  /* a drag must not sweep a text selection across the page */
  body.cb-dragging{cursor:grabbing;user-select:none;-webkit-user-select:none}
  body.cb-dragging *{user-select:none !important;-webkit-user-select:none !important}
  .cb-thumb .del{position:absolute;top:-7px;right:-7px;width:19px;height:19px;border-radius:50%;background:var(--ox);color:#fff;border:1.5px solid var(--surface);font-size:12px;line-height:1;cursor:pointer;padding:0;display:grid;place-items:center}
  .cb-add-photo{width:58px;height:58px;border-radius:8px;border:1.5px dashed var(--line);background:var(--surface-2);color:var(--faint);cursor:pointer;font-size:22px;line-height:1;display:grid;place-items:center}
  .cb-add-photo:hover{border-color:var(--green);color:var(--green)}
  /* Phase A: date caption on the record hero (bottom-left), and the "Manual ⇄ By date" gallery toggle.
     The caption is pointer-events:none so it never blocks the hero's click-to-zoom. */
  .cb-photo-date{position:absolute;left:12px;bottom:12px;background:rgba(22,17,10,.72);color:#fff;font-size:11px;font-weight:600;padding:3px 8px;border-radius:6px;letter-spacing:.2px;pointer-events:none;z-index:2}
  .cb-gallery-bar{margin-top:8px}
  .cb-gallery-bar[hidden]{display:none}
  .cb-gallery-sort{font-size:12px;font-weight:600;color:var(--faint);background:var(--surface-2);border:1px solid var(--line);border-radius:7px;padding:8px 12px;min-height:40px;cursor:pointer}
  .cb-gallery-sort:hover{border-color:var(--green);color:var(--green)}
  .cb-gallery-sort[aria-pressed="true"]{color:var(--green);border-color:var(--green);background:var(--surface)}
  /* Phase D: the Growth-information readout on the record Growth tab — a responsive label/value grid. */
  .cb-growth-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:2px 18px;margin:2px 0 6px}
  .cb-growth-cell{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:8px 2px;border-bottom:1px solid var(--line)}
  .cb-growth-cell .k{color:var(--faint);font-size:13px}
  .cb-growth-cell .v{font-weight:600}
  /* Phase D: the "Calculate" row under the wizard's Growth-information fields. */
  .cb-wiz-calc{display:flex;align-items:center;gap:10px;margin-top:12px;flex-wrap:wrap}
  .cb-calc-hint{font-size:12px}
  /* gap:16px keeps a minimum channel between the label and the value so a long value
     (e.g. a long herd name) never butts up against the label; the value is right-aligned
     and wraps on word boundaries instead of overflowing/clipping at the panel's edge. */
  .kv{display:flex;justify-content:space-between;gap:16px;padding:9px 0;border-bottom:1px solid var(--line-soft);font-size:13px}
  .kv:last-child{border:none} .kv .k{color:var(--muted);flex:none} .kv .v{font-weight:600;text-align:right;min-width:0;word-break:break-word}
  /* .seg-tabs/.seg-tab mirror .tabs/.tab exactly, but carry their own class so the
     generic pane-switching tab logic (which resets every .tab inside a .panel) can
     never clear a nested segmented selector's active state — see the grading
     animal-type selector in app.js (renderGradingShell). */
  .tabs,.seg-tabs{display:flex;flex-wrap:wrap;gap:2px;border-bottom:1px solid var(--line);padding:0 6px}
  .tab,.seg-tab{padding:11px 15px;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer;border:none;background:none;font-family:inherit;border-bottom:2px solid transparent;margin-bottom:-1px;flex:none;white-space:nowrap}
  .tab.on,.seg-tab.on{color:var(--green);border-bottom-color:var(--brass)}
  .tabpane{display:none;padding:18px} .tabpane.on{display:block}
  .ped{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;align-items:center}
  .pcard{border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--surface-2)}
  .pcard .r{font-size:11px;color:var(--faint);margin-bottom:4px} .pcard .n{font-weight:600;font-size:13px}
  .pcard.self{background:var(--green-tint);border-color:#Bcd2Bf}
  /* graphical genealogy tree (photos of sire & dam) */
  .tree{overflow-x:auto;padding:8px 0 2px}
  .tree ul{display:flex;justify-content:center;list-style:none;margin:0;padding:24px 0 0;position:relative}
  /* width:max-content + auto margins: centers the tree when it fits, and lets it
     scroll fully from the left when it's wider than the pane (plain
     justify-content:center clips the left edge unreachably in a scroll box) */
  .tree > ul{padding-top:4px;width:max-content;margin-inline:auto}
  .tree li{position:relative;padding:24px 12px 0;display:flex;flex-direction:column;align-items:center}
  /* The drop-in connectors are 18px, not the full 24px padding band, so the line
     stops ~6px above each box instead of ending flush on its top border (which
     read as the line overlapping the box). The parent→bar drop (.tree ul ul::before)
     stays 24px so the lines still meet at the horizontal bar. */
  .tree li::before,.tree li::after{content:"";position:absolute;top:0;width:50%;height:18px;border-top:2px solid var(--line)}
  .tree li::before{right:50%}
  .tree li::after{left:50%;border-left:2px solid var(--line)}
  .tree li:only-child{padding-top:6px}
  .tree li:only-child::before,.tree li:only-child::after{display:none}
  .tree li:first-child::before,.tree li:last-child::after{border:0}
  .tree li:last-child::before{border-right:2px solid var(--line);border-radius:0 7px 0 0}
  .tree li:first-child::after{border-radius:7px 0 0 0}
  .tree ul ul::before{content:"";position:absolute;top:0;left:50%;height:24px;border-left:2px solid var(--line)}
  .tnode{display:flex;flex-direction:column;align-items:center;width:132px}
  .tnode.sm{width:106px}
  .tphoto{width:100%;aspect-ratio:4/3;border-radius:9px;overflow:hidden;position:relative;display:grid;place-items:center;background:linear-gradient(135deg,#7B6A54,#3E3527);color:#E9DFCB;border:1px solid rgba(36,30,24,.12)}
  .tphoto .ph{font-family:var(--serif);font-size:12.5px;opacity:.6}
  .tphoto .tag{position:absolute;left:7px;top:7px;transform:scale(.82);transform-origin:left top}
  .tphoto.np{background:var(--surface-2);border:1px dashed var(--line);color:var(--faint)}
  .tphoto.np .ph{font-family:var(--sans);font-size:10.5px;opacity:.85}
  .tnode.focus .tphoto{outline:2px solid var(--brass);outline-offset:2px}
  .tsex{position:absolute;right:6px;top:6px;width:18px;height:18px;border-radius:50%;display:grid;place-items:center;font-size:11px;font-weight:700;color:#fff}
  .tsex.m{background:var(--teal)} .tsex.f{background:var(--ox)}
  .tlabel{margin-top:8px;text-align:center;font-size:12px;line-height:1.35;width:100%;min-width:0}
  /* Fixed label band for every node that has children (root + parents) so a
     missing name or id line can't make one node shorter than its sibling and
     drop the whole generation below it out of line with the other branch.
     Grandparents (.sm) are the last row, so their label height can't cascade. */
  .tnode:not(.sm) .tlabel{min-height:49px}
  /* Long names stay on one line (ellipsis) — they can't add a line (which would
     shift the generation below) or widen the fixed-width node. */
  .tlabel b{display:block;font-weight:600;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .tlabel span{color:var(--faint);font-size:11px}
  .trole{font-size:10px;letter-spacing:.4px;color:var(--brass-deep);font-weight:700;margin-bottom:3px}
  /* On wide screens the record is a 2-col grid, so the tabs panel stretches to
     the taller left column. Let the active tab pane fill that height and pin the
     weighing chart (with its caption) to the bottom instead of floating at the
     top of a tall, mostly-empty panel. On ≤1100px the grid is single-column, so
     the panel is content-height and this is a no-op. */
  .rec > .panel{display:flex;flex-direction:column}
  .rec > .panel > .tabpane.on{flex:1 1 auto;min-height:0}
  #t-growth.on{display:flex;flex-direction:column}
  #t-growth .growth{margin-top:auto}
  .growth{display:flex;align-items:flex-end;gap:14px;height:150px;padding:14px 6px 0}
  .bar{flex:1;background:linear-gradient(var(--green),#3A6B4E);border-radius:5px 5px 0 0;position:relative;min-height:8px}
  .bar span{position:absolute;top:-19px;left:0;right:0;text-align:center;font-size:11px;font-weight:700;font-variant-numeric:tabular-nums}
  .bar em{position:absolute;bottom:-22px;left:0;right:0;text-align:center;font-size:11px;color:var(--faint);font-style:normal}
  .metrics{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}
  .metric{border:1px solid var(--line);border-radius:8px;padding:12px 14px;background:var(--surface-2)}
  .metric .k{font-size:11.5px;color:var(--muted)} .metric .v{font-family:var(--serif);font-size:22px;font-weight:600;margin-top:2px}

  /* ---------- service animals ---------- */
  /* the service-date picker sits beside the Herd/Method fields — match their
     white --surface background and their size (the modal default is bigger) */
  .svc-head .cb-date-btn{background:var(--surface);border-radius:6px;padding:6px 10px;font-size:13px;font-weight:600;line-height:1.5}
  /* cap both long lists so each scrolls on its own instead of stretching the
     page; the bull-panel header + hint stay fixed, the cow header/footer sit outside */
  .bullpick{display:flex;flex-direction:column;max-height:min(52vh,460px)}
  /* full-width divider between the fixed heading/hint and the scrolling rows,
     matching the cow section's .svc-head border (negative margin cancels the
     panel's 8px padding so the line spans edge to edge) */
  .bullpick .bullsearch{display:flex;align-items:center;gap:7px;margin:6px 4px 2px;padding:7px 10px;background:var(--surface-2);border:1px solid var(--line);border-radius:9px;color:var(--muted)}
  .bullpick .bullsearch input{border:none;background:none;outline:none;font-family:inherit;font-size:13px;color:var(--ink);width:100%}
  .bullpick .bullscroll{overflow-y:auto;flex:1;border-top:1px solid var(--line-soft);margin:10px -8px 0;padding:6px 8px 0}
  .svc-head .cowsearch .cowsearch-inp{display:flex;align-items:center;gap:6px;border:1px solid var(--line);background:var(--surface);border-radius:6px;padding:6px 9px;color:var(--muted);width:150px}
  .svc-head .cowsearch input{border:none;background:none;outline:none;font-family:inherit;font-size:13px;color:var(--ink);width:100%;min-width:0}
  /* Herd + Method selects in the service header (buildSelect, outside a modal) */
  .svc-head .cb-select{position:relative;min-width:130px}
  .svc-head .cb-select-btn{display:flex;align-items:center;gap:8px;width:100%;text-align:left;cursor:pointer;border:1px solid var(--line);background:var(--surface);border-radius:6px;padding:6px 10px;font-size:13px;font-weight:600;color:var(--ink);font-family:inherit}
  .svc-head .cb-select-btn .chev{width:13px;height:13px;flex:none;opacity:.6}
  .svc-head .cb-select-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .svc-head .cb-select-label.cb-ph{color:var(--faint);font-weight:400}
  .svc-head .cb-select-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:30;display:flex;flex-direction:column;max-height:260px;min-width:170px;background:var(--surface);border:1px solid var(--line);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.18);padding:5px}
  .svc-head .cb-select-menu[hidden]{display:none}
  .svc-head .cb-select-search{border:1px solid var(--line);border-radius:6px;padding:8px 10px;font-family:inherit;font-size:13px;color:var(--ink);outline:none;margin-bottom:5px}
  .svc-head .cb-select-search:focus{border-color:var(--green)}
  .svc-head .cb-select-list{overflow-y:auto}
  .svc-head .cb-select-opt{display:block;width:100%;text-align:left;border:none;background:none;font-family:inherit;font-size:13.5px;color:var(--ink);padding:8px 10px;border-radius:6px;cursor:pointer;white-space:nowrap}
  .svc-head .cb-select-opt:hover{background:var(--surface-2)}
  .svc-head .cb-select-opt.on{background:var(--green-tint);color:#2C5A40;font-weight:600}
  .cb-cowlist{max-height:min(52vh,460px);overflow-y:auto}
  .svc{display:grid;grid-template-columns:280px 1fr;gap:18px}
  .bullpick{padding:8px}
  .bullrow{display:flex;align-items:center;gap:11px;padding:11px 12px;border-radius:8px;cursor:pointer;border:1.5px solid transparent}
  .bullrow:hover{background:var(--surface-2)}
  .bullrow.on{background:var(--green-tint);border-color:var(--green)}
  .bullrow .av{width:36px;height:36px;border-radius:8px;background:linear-gradient(135deg,#6B5A44,#3E3527);flex:none}
  .bullrow .nm{font-weight:600} .bullrow .mt{font-size:11.5px;color:var(--faint)}
  .bullrow .cnt{margin-left:auto;font-size:12px;color:var(--brass-deep);font-weight:700}
  .svc-head{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line-soft);background:var(--surface-2);border-radius:var(--radius) var(--radius) 0 0;flex-wrap:wrap}
  .field{display:flex;flex-direction:column;gap:3px}
  .field label{font-size:10.5px;letter-spacing:.3px;color:var(--faint)}
  .field .inp{border:1px solid var(--line);background:var(--surface);border-radius:6px;padding:6px 10px;font-size:13px;font-weight:600;min-width:120px}
  .cowrow{display:flex;align-items:center;gap:12px;padding:11px 16px;border-bottom:1px solid var(--line-soft)}
  .cowrow:last-child{border:none}
  .cowrow:hover{background:var(--surface-2)}
  /* service cow rows are role="checkbox" + tabindex=0: show a keyboard focus ring (mouse clicks get none) */
  .cowrow{cursor:pointer} .cowrow:focus{outline:none} .cowrow:focus-visible{outline:2px solid var(--green);outline-offset:-2px}
  .check{width:22px;height:22px;border-radius:6px;border:2px solid var(--line);flex:none;display:grid;place-items:center;cursor:pointer;background:var(--surface)}
  .cowrow.sel .check{background:var(--green);border-color:var(--green)}
  .cowrow.sel .check svg{opacity:1;color:#fff} .check svg{width:13px;height:13px;opacity:0}
  .cowrow.sel{background:var(--green-tint)}
  .cowrow .link{margin-left:auto;display:flex;align-items:center;gap:7px;font-size:12px;color:var(--faint)}
  .cowrow.sel .link{color:var(--green);font-weight:600}
  .cowrow .link .dot{width:9px;height:9px;border-radius:50%}
  .svc-foot{padding:14px 16px;display:flex;flex-wrap:wrap;align-items:center;gap:14px 34px;border-top:1px solid var(--line-soft)}
  .svc-foot b{font-family:var(--serif);font-size:17px}
  /* sales & catalog — stacked title-over-value fields (header meta + footer) */
  .cat-meta{display:flex;flex-wrap:wrap;gap:16px 34px;padding:14px 18px;border-bottom:1px solid var(--line-soft)}
  .cat-field{display:flex;flex-direction:column;gap:2px}
  .cat-field .k{font-size:11px;letter-spacing:.3px;color:var(--muted)}
  .cat-field .v{font-weight:600;font-size:14px}
  .svc-foot .cat-field .v{font-family:var(--serif);font-size:17px}

  /* ---------- pipeline (Option A — Refined Board, existing palette) ---------- */
  /* two stages per row (wrapping into rows), widening to three on very wide screens.
     Each panel is capped to a sensible kanban width, not half the page. */
  .board{display:grid;grid-template-columns:repeat(2,minmax(280px,420px));gap:14px;align-items:start;justify-content:start}
  @media (min-width:1500px){.board{grid-template-columns:repeat(3,minmax(280px,1fr))}}
  /* each stage caps its height; the header is a fixed region and only the cards below
     scroll (.col-body), so a busy stage doesn't stretch the page down */
  .col{background:var(--surface-2);border:1px solid var(--line-soft);border-radius:14px;min-height:190px;max-height:min(56vh,520px);display:flex;flex-direction:column;overflow:hidden}
  /* header: a coloured left accent bar (per-stage via --stage), stage name, count chip */
  .col-h{position:relative;flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:14px 15px 12px}
  .col-h::before{content:"";position:absolute;left:0;top:13px;bottom:13px;width:4px;border-radius:0 3px 3px 0;background:var(--stage,var(--brass))}
  .col-h .nm{font-family:var(--serif);font-weight:600;font-size:15px;padding-left:7px}
  .col-h .ct{margin-left:auto;font-size:12px;font-weight:700;color:var(--faint);font-variant-numeric:tabular-nums;background:var(--surface);border:1px solid var(--line);border-radius:20px;padding:2px 10px}
  .col-h .bar{display:none}                 /* legacy dot — replaced by the ::before accent bar */
  .col-sub{flex:0 0 auto;font-size:11.5px;color:var(--faint);padding:0 15px 11px 22px;margin-top:-4px}
  .col-body{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:9px;padding:2px 11px 13px}
  .mcard{position:relative;margin:0;background:var(--surface);border:1px solid var(--line);border-radius:11px;padding:12px 13px;box-shadow:var(--shadow);display:flex;flex-direction:column;gap:8px;transition:box-shadow .18s cubic-bezier(.2,0,0,1),transform .18s cubic-bezier(.2,0,0,1)}
  .mcard:hover{box-shadow:0 2px 6px rgba(36,30,24,.07),0 10px 26px rgba(36,30,24,.07);transform:translateY(-1px)}
  /* The card header reuses the class name `.top`, which collides with the global
     app-bar `.top` (position:sticky;top:0;z-index:5 + padding/background/border).
     Neutralise that inheritance so the header is a plain static row. The right
     padding leaves room for the corner correction icons. */
  /* the whole card is clickable — it opens the cow's animal record (buttons excepted) */
  .mcard.clickable{cursor:pointer}
  /* Sales catalog: a lot row opens the animal record. */
  .cb-lot-row{cursor:pointer}
  .cb-lot-row:hover .nm{text-decoration:underline;text-underline-offset:2px}
  .cb-lot-row:focus-visible{outline:2px solid var(--green);outline-offset:-2px}
  .mcard .top{display:flex;align-items:center;gap:8px;position:static;z-index:auto;padding:0 56px 0 0;background:none;border-bottom:0}
  .mcard .top .tag{margin-left:-5px}            /* standard project ear-tag, nudged to the card edge */
  .mcard .top .nm{font-weight:600;font-size:14px;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mcard.clickable:hover .nm{text-decoration:underline;text-underline-offset:2px}
  .mcard.clickable:focus-visible{outline:2px solid var(--green);outline-offset:2px}
  .mcard .meta{font-size:11.5px;color:var(--muted);display:flex;align-items:baseline;justify-content:space-between;gap:10px}
  .mcard .meta>span:first-child{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mcard .meta .d{color:var(--faint);font-variant-numeric:tabular-nums;flex:0 0 auto;white-space:nowrap}
  /* calf-pairing line: the calf's tag shown on the dam's card once it is on the ground */
  .mcard .calf-link{font-size:11.5px;color:var(--teal);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .mcard .calf-link.clickable{cursor:pointer}
  .mcard .calf-link.clickable:hover{text-decoration:underline;text-underline-offset:2px}
  .mcard .calf-link.clickable:focus-visible{outline:2px solid var(--green);outline-offset:2px;border-radius:4px}
  /* corrections (move back / remove): compact, always-visible icon buttons, top-right */
  .mcard .corner{position:absolute;top:10px;right:11px;display:flex;gap:5px}
  .mcard .corner button{width:26px;height:26px;display:grid;place-items:center;border-radius:7px;border:1px solid var(--line);background:var(--surface);color:var(--muted);cursor:pointer;padding:0}
  .mcard .corner button:hover{border-color:var(--faint);color:var(--ink)}
  .mcard .corner button.rm:hover{border-color:var(--ox);color:var(--ox);background:var(--ox-tint)}
  .mcard .corner svg{width:14px;height:14px}
  /* phase actions: the project's standard buttons, sharing the card width */
  .mcard .acts{display:flex;gap:7px;margin-top:3px}
  .mcard .acts .btn{flex:1 1 0;padding:6px 8px;font-size:12px}
  .legend{display:flex;gap:16px;flex-wrap:wrap;margin-top:16px;font-size:12px;color:var(--muted)}
  .legend span{display:flex;align-items:center;gap:6px}
  .legend i{width:9px;height:9px;border-radius:50%}

  .note{margin:22px 26px 30px;padding:13px 16px;background:var(--brass-tint);border:1px solid #E4D2A4;border-radius:var(--radius);font-size:12.5px;color:#6E551A}
  .note b{color:#5A4512}

  /* ---------- herds & camps ---------- */
  .campgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-top:16px}
  .campcard{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:16px 18px;box-shadow:var(--shadow);display:flex;flex-direction:column}
  /* fields fill the card so the Edit button always sits at the bottom, lining up
     across cards regardless of how many fields each farm has */
  .campcard .fcard-body{flex:1 1 auto}
  .campcard .ch{display:flex;align-items:center;gap:10px;margin-bottom:12px}
  .campcard .ch .nm{font-family:var(--serif);font-size:17px;font-weight:600}
  .campcard .ch .ha{margin-left:auto;font-size:12px;color:var(--faint);font-variant-numeric:tabular-nums}
  .stock{height:9px;background:var(--line-soft);border-radius:6px;overflow:hidden;margin:4px 0 6px}
  .stock i{display:block;height:100%;border-radius:6px}
  .campcard .row{display:flex;justify-content:space-between;font-size:12.5px;margin-top:8px}
  .campcard .row .l{color:var(--muted)} .campcard .row .r{font-weight:600;font-variant-numeric:tabular-nums}
  .herdchips{display:flex;gap:6px;flex-wrap:wrap;margin-top:12px}
  .hchip{font-size:11.5px;font-weight:600;padding:3px 9px;border-radius:6px;background:var(--surface-2);border:1px solid var(--line);color:var(--muted)}
  .hchip b{color:var(--ink)}
  /* camp drill-down */
  .campcard{transition:border-color .12s,box-shadow .12s}
  .campcard.sel{border-color:var(--brass-deep,#8a6d1f);box-shadow:0 0 0 2px var(--brass-deep,#8a6d1f) inset}
  .cb-camp-detail{margin-top:18px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:16px 18px}
  .cb-camp-head{align-items:flex-start;margin-bottom:8px}
  /* Delete herd / Delete camp / Close grouped at the right of the camp header (wraps on a phone). */
  .cb-camp-acts{margin-left:auto;display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}
  .cb-herd-del-slot{display:contents}
  .cb-camp-title{font-family:var(--serif);font-size:18px;font-weight:600}
  .cb-camp-herds{margin-top:14px}
  .cb-camp-herds .chip{font-family:inherit}
  .cb-camp-herds .chip b{font-weight:700}
  .cb-camp-herds .chip.on b{color:#fff}
  .cb-camp-animals{margin-top:12px}
  .cb-camp-table{width:100%;border-collapse:collapse}
  .cb-camp-table td{padding:10px 8px;border-top:1px solid var(--line-soft);font-size:13px;vertical-align:middle}
  .cb-camp-table tr:hover td{background:var(--surface-2)}

  /* ---------- reports ---------- */
  /* Separate the category cards from the "Herd composition" graph panel above them —
     both are direct children of #reports with no margins, so they otherwise touch. */
  #reports .repgrid{margin-top:18px}
  .repgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
  .repcat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:15px 17px;box-shadow:var(--shadow);cursor:pointer}
  .repcat:hover{border-color:var(--faint)}
  .repcat .rh{display:flex;align-items:center;gap:9px;margin-bottom:8px}
  .repcat .rh svg{width:17px;height:17px;color:var(--green)}
  .repcat .rh b{font-family:var(--serif);font-size:15px;font-weight:600}
  .repcat .rh .n{margin-left:auto;font-size:11.5px;font-weight:700;color:var(--brass-deep);background:var(--brass-tint);padding:1px 8px;border-radius:20px}
  .repcat .list{font-size:12px;color:var(--muted);line-height:1.7}
  /* each report name in the grid is a runnable link */
  .repcat .list .cb-replink{color:var(--green);cursor:pointer;font-weight:500}
  .repcat .list .cb-replink:hover,.repcat .list .cb-replink:focus-visible{text-decoration:underline;outline:none}
  /* ===== Reports — category cards (prototype style) =====
     Each category is one card (icon · serif title · count pill · name preview);
     clicking it opens that category's page (/reports/{category}, styled further below). */
  /* .repgrid keeps the base 3-col grid + the shared responsive breakpoints
     (2-col ≤1100px, 1-col ≤768px) — don't re-assert columns here or it would
     override them and overflow on mobile. */
  /* flex column + top alignment: a stretched <button> would otherwise centre its
     content vertically, leaving a gap above the title on shorter cards */
  #reports .repcat{font:inherit;text-align:left;width:100%;display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;transition:border-color .14s ease,box-shadow .14s ease,transform .08s ease}
  #reports .repcat:hover{border-color:var(--faint);box-shadow:0 4px 14px rgba(36,30,24,.08);transform:translateY(-2px)}
  #reports .repcat:focus-visible{outline:2px solid var(--green);outline-offset:2px}

  /* ---------- shared form controls + auth screen ----------
     Also moved out of a JS-injected <style>: a strict Content-Security-Policy blocks an
     injected <style>, which left form inputs/labels (reused by every modal) and the login
     screen unstyled. .cb-inp / .cb-lbl are the base frame for inputs and the custom select
     button, so when they were dropped the modal selects fell back to default browser buttons. */
  .cb-auth{position:fixed;inset:0;background:var(--green-deep);display:grid;place-items:center;padding:24px;z-index:100;font-family:var(--sans)}
  .cb-auth-card{background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:0 12px 44px rgba(0,0,0,.28);padding:26px 26px 22px;width:min(400px,100%);display:flex;flex-direction:column;gap:12px}
  .cb-auth-brand{display:flex;align-items:center;gap:10px;margin-bottom:2px}
  .cb-auth-brand img{height:42px;width:auto;display:block}
  .cb-auth-sub{margin:0 0 6px;color:var(--muted);font-size:13.5px}
  .cb-lbl{display:flex;flex-direction:column;gap:5px;font-size:12px;color:var(--faint);font-weight:600}
  .cb-inp{border:1px solid var(--line);background:var(--surface-2);border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;color:var(--ink)}
  .cb-inp:focus{outline:none;border-color:var(--green)}
  /* Required-field marker (formModal + the New-animal wizard): a red asterisk after the label text.
     aria-hidden in the markup — the control's aria-required="true" is what screen readers announce. */
  .cb-req{color:var(--ox);margin-left:3px;font-weight:700}
  /* A field that failed client-side validation (set by focusInvalidField, cleared on the next
     edit/pick) gets the danger border so the user sees WHICH field the message is about. */
  .cb-inp[aria-invalid="true"],.cb-inp[aria-invalid="true"]:focus{border-color:var(--ox);box-shadow:0 0 0 2px var(--ox-tint)}
  /* Read-only parameter value (a single-choice "Order By" rendered as text, not a dropdown): same
     box as .cb-inp but visibly non-interactive — muted, default cursor, no hover/focus affordance. */
  .cb-inp-static{cursor:default;color:var(--muted);display:flex;align-items:center;min-height:21px}
  /* Reports page search: a full-width box above the category grid, with a flat results list of
     matching reports (name + its group). Each hit opens its category page with that report's form expanded. */
  .rep-search{margin:18px 0 14px}
  .rep-search-inp{width:100%;box-sizing:border-box}
  .rep-search-results{margin-top:8px;display:flex;flex-direction:column;gap:6px}
  .rep-search-hit{display:flex;align-items:center;gap:12px;width:100%;text-align:left;cursor:pointer;
    border:1px solid var(--line);background:var(--surface);border-radius:8px;padding:10px 12px;font-family:inherit}
  .rep-search-hit:hover{border-color:var(--green);background:var(--surface-2)}
  .rep-search-name{font-weight:600;color:var(--ink);font-size:14px}
  .rep-search-grp{margin-left:auto;color:var(--muted);font-size:12px}
  .cb-auth-btn{margin-top:6px;background:var(--green);color:#fff;border:none;border-radius:8px;padding:11px;font-size:14px;font-weight:700;font-family:inherit;cursor:pointer}
  .cb-auth-btn:hover{background:var(--green-deep)} .cb-auth-btn:disabled{opacity:.6;cursor:default}
  .cb-auth-err{background:var(--ox-tint);color:var(--ox);border-radius:7px;padding:8px 11px;font-size:12.5px}
  /* Spacing modifiers for markup-rendered elements (tina4-js html`` templates) — a CSP-safe
     replacement for inline style="" attributes, which the strict CSP (default-src 'self') blocks. */
  .cb-auth-err.cb-mt12{margin-top:12px}                        /* wizard error line */
  .cb-auth-btn.cb-m0{margin:0}                                 /* wizard footer buttons (no top margin) */
  .cb-unit{font-size:12px;color:var(--faint)}                  /* record metric unit (kg/day, days) */
  .cb-auth-switch{text-align:center;font-size:12.5px;color:var(--muted);margin-top:4px}
  .cb-auth-switch a{color:var(--green);font-weight:600;cursor:pointer}

  /* ---------- shared modal chrome (overlay, card, custom select + date, wizard) ----------
     These were previously injected at runtime from a JS <style> element. A strict
     Content-Security-Policy (default-src 'self', no 'unsafe-inline') blocks an injected
     <style>, which dropped ALL of this and left every modal (New animal, the report
     builder, the pickers) unstyled — rendered inline instead of as a centred overlay.
     Serving them from this external stylesheet keeps them under 'self', so CSP allows them. */
  .cb-modal{position:fixed;inset:0;background:rgba(24,53,38,.55);display:grid;place-items:center;padding:24px;z-index:90}
  .cb-modal-card{background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 44px rgba(0,0,0,.3);padding:20px 22px;width:min(560px,100%);max-height:90vh;overflow:auto}
  .cb-modal-h{display:flex;align-items:center;margin:0 0 14px}
  .cb-modal-h h3{margin:0;font-family:var(--serif);font-size:18px;font-weight:600}
  .cb-modal-x{margin-left:auto;border:none;background:none;font-size:20px;color:var(--muted);cursor:pointer;line-height:1}
  .cb-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
  .cb-grid .cb-lbl.wide{grid-column:1/-1}
  /* Label text at the top, control at the bottom: when one label in a row wraps to two lines (e.g.
     "Mother's weight at weaning (kg)"), every input in that row still lines up (tester feedback round 2). */
  .cb-grid > .cb-lbl{justify-content:space-between}
  /* formModal file field: preview of the picked image (Record calf photo). */
  .cb-file-preview{display:block;max-width:180px;max-height:140px;border-radius:8px;object-fit:cover;border:1px solid var(--line)}
  .cb-wizard{width:min(620px,100%)}
  .cb-steps{display:flex;gap:8px;margin:0 0 16px}
  .cb-step{flex:1;text-align:center;font-size:12px;font-weight:600;font-family:inherit;color:var(--faint);padding:7px 6px;border-radius:8px;background:var(--surface-2);border:1px solid var(--line-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
  .cb-step:hover{border-color:var(--faint);color:var(--muted)}
  .cb-step.on{background:var(--green-tint);border-color:var(--green);color:#2C5A40}
  .cb-step.on:hover{color:#2C5A40}
  .cb-step.done{color:var(--green);border-color:var(--green-tint)}
  .cb-step.done:hover{border-color:var(--green)}
  .cb-step-pane[hidden]{display:none}
  .cb-sec{font-family:var(--serif);font-size:14px;font-weight:600;color:var(--ink);margin:16px 0 8px;padding-bottom:5px;border-bottom:1px solid var(--line-soft)}
  .cb-sec:first-child{margin-top:4px}
  .cb-sec-note{color:var(--muted);font-size:12.5px;margin:0 0 6px}
  .cb-check{flex-direction:row;align-items:center;gap:9px;font-weight:600;color:var(--ink)}
  .cb-check input{width:16px;height:16px;accent-color:var(--green)}
  .cb-wiz-foot{align-items:center}
  .cb-wiz-nav{display:flex;gap:10px;margin-left:auto}
  textarea.cb-inp{resize:vertical;min-height:62px;max-height:220px;font-family:inherit;line-height:1.45}
  textarea.cb-textarea{min-height:96px}
  .cb-drop{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;text-align:center;padding:26px 16px;border:2px dashed var(--line);border-radius:12px;background:var(--surface-2);color:var(--muted);cursor:pointer;transition:border-color .12s,background .12s}
  .cb-drop:hover,.cb-drop:focus-visible{border-color:var(--green);outline:none}
  .cb-drop.over{border-color:var(--green);background:var(--green-tint);color:#2C5A40}
  .cb-drop svg{opacity:.7}
  .cb-drop-text{display:flex;flex-direction:column;gap:2px;font-size:13px}
  .cb-drop-text b{color:var(--ink);font-size:13.5px}
  .cb-drop-count{margin:8px 2px 0;font-size:12px}
  .cb-drop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:10px;margin-top:10px}
  .cb-drop-cell{display:flex;flex-direction:column;gap:6px}
  .cb-drop-thumb{position:relative;aspect-ratio:1;border-radius:9px;overflow:hidden;border:1px solid var(--line);background:var(--surface)}
  /* Per-photo date under a staged wizard thumbnail: an inline date control whose label truncates in
     the narrow cell (the calendar popup floats as fixed, so it is never clipped by the column). */
  .cb-drop-date .cb-date-btn{min-height:36px;font-size:11px;padding:6px 8px}
  .cb-drop-date .cb-select-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .cb-drop-thumb img{width:100%;height:100%;object-fit:cover;display:block}
  .cb-drop-x{position:absolute;top:3px;right:3px;width:20px;height:20px;border:none;border-radius:50%;background:rgba(24,53,38,.72);color:#fff;font-size:14px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
  .cb-drop-x:hover{background:rgba(24,53,38,.92)}
  .cb-drop-primary{position:absolute;left:0;bottom:0;right:0;background:var(--green);color:#fff;font-size:10px;font-weight:600;text-align:center;padding:2px 0}
  .cb-inp-row{display:flex;gap:6px} .cb-inp-row .cb-inp{flex:1}
  /* show/hide toggle beside a masked password input (formModal type:'password'); min-width:0 on the
     input lets the pair fit a 375px modal without overflowing */
  .cb-inp-row{min-width:0} .cb-inp-row .cb-inp{min-width:0;width:100%}
  .cb-pw-toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;width:40px;padding:0;color:var(--muted)}
  .cb-pw-toggle[aria-pressed="true"]{color:var(--green)}
  .cb-pw-toggle svg{flex:none;width:18px;height:18px}   /* keep the eye from being squeezed by inherited svg sizing */
  .cb-suggest{border:1px solid var(--line);background:var(--surface-2);border-radius:8px;padding:0 11px;font-size:12px;font-weight:600;color:var(--brass-deep);cursor:pointer;white-space:nowrap}
  .cb-modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:18px}
  /* confirmDialog: a small in-app confirm, above any open form modal (appended last; same z-index). */
  .cb-confirm .cb-modal-card{width:min(420px,100%)}
  .cb-confirm-msg{margin:0;font-size:14px;line-height:1.5;color:var(--ink)}
  .cb-confirm .cb-confirm-danger{background:var(--ox)}   /* destructive OK (remove / delete) */
  .cb-btn-ghost{background:var(--surface);border:1px solid var(--line);color:var(--ink);border-radius:8px;padding:10px 16px;font-weight:600;font-family:inherit;cursor:pointer}
  .cb-select{position:relative}
  .cb-select-btn{display:flex;align-items:center;gap:8px;width:100%;text-align:left;cursor:pointer}
  .cb-select-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .cb-select-btn .chev{width:14px;height:14px;flex:none;opacity:.6}
  .cb-select-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:20;display:flex;flex-direction:column;max-height:260px;background:var(--surface);border:1px solid var(--line);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.18);padding:5px}
  .cb-select-menu[hidden]{display:none}
  .cb-select-search{border:1px solid var(--line);border-radius:6px;padding:8px 10px;font-family:inherit;font-size:13px;color:var(--ink);outline:none;margin-bottom:5px}
  .cb-select-search:focus{border-color:var(--green)}
  .cb-select-list{overflow-y:auto}
  .cb-select-opt{display:block;width:100%;text-align:left;border:none;background:none;font-family:inherit;font-size:13.5px;color:var(--ink);padding:8px 10px;border-radius:6px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .cb-select-opt:hover{background:var(--surface-2)}
  .cb-select-opt.on{background:var(--green-tint);color:#2C5A40;font-weight:600}
  .cb-select-empty{padding:8px 10px;font-size:13px}
  .cb-select-label.cb-ph{color:var(--faint);font-weight:400}
  .cb-date-btn .cal{width:15px;height:15px;flex:none;opacity:.6}
  .cb-date-menu{position:absolute;top:calc(100% + 4px);left:0;z-index:20;width:280px;max-width:calc(100vw - 40px);background:var(--surface);border:1px solid var(--line);border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,.18);padding:12px}
  .cb-date-menu[hidden]{display:none}
  .cb-date-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}
  .cb-date-title{flex:1;text-align:center;font-weight:600;font-family:var(--serif);font-size:15px}
  /* The title doubles as a button in the day/month views (click to step up to the
     month/year picker). It inherits .cb-date-title's serif look; this only resets
     the <button> chrome and adds a hover affordance so it reads as clickable. */
  .cb-date-title-btn{border:none;background:none;color:var(--ink);padding:3px 8px;border-radius:7px;cursor:pointer}
  .cb-date-title-btn:hover{background:var(--surface-2);color:var(--green)}
  .cb-date-nav{width:30px;height:30px;border:1px solid var(--line);background:var(--surface);border-radius:7px;font-size:16px;line-height:1;color:var(--ink);cursor:pointer}
  .cb-date-nav:hover{border-color:var(--faint)}
  /* Month/year quick-pick grids: a 3×4 layout reusing the day-cell colour tokens so
     the three panels read as one picker. '.other' dims the two edge years that fall
     just outside the shown decade. */
  .cb-date-mgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
  .cb-date-mcell{height:46px;display:flex;align-items:center;justify-content:center;border:none;background:none;font-family:inherit;font-size:14px;color:var(--ink);border-radius:7px;cursor:pointer}
  .cb-date-mcell:hover{background:var(--surface-2)}
  .cb-date-mcell.other{color:var(--faint)}
  .cb-date-mcell.today{box-shadow:inset 0 0 0 1px var(--line)}
  .cb-date-mcell.on{background:var(--green);color:#fff;font-weight:600}
  .cb-date-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
  .cb-date-wd{text-align:center;font-size:11px;color:var(--faint);font-weight:600;padding:4px 0}
  .cb-date-cell{height:34px;display:flex;align-items:center;justify-content:center;border:none;background:none;font-family:inherit;font-size:13px;color:var(--ink);border-radius:7px;cursor:pointer}
  .cb-date-cell.empty{visibility:hidden;cursor:default}
  .cb-date-cell:not(.empty):hover{background:var(--surface-2)}
  .cb-date-cell.today{box-shadow:inset 0 0 0 1px var(--line)}
  .cb-date-cell.on{background:var(--green);color:#fff;font-weight:600}
  .cb-date-foot{display:flex;justify-content:space-between;margin-top:8px;padding-top:8px;border-top:1px solid var(--line-soft)}
  .cb-date-link{border:none;background:none;color:var(--green);font-weight:600;font-size:13px;font-family:inherit;cursor:pointer}
  @media (max-width:768px){
    .cb-modal{padding:14px}
    .cb-modal-card{padding:18px 16px}
    .cb-grid{grid-template-columns:1fr}
    .cb-modal-actions{flex-direction:column-reverse}
    .cb-modal-actions>*{width:100%}
  }

  /* ---------- desktop: fit modals to the viewport using horizontal space ----------
     On phones a modal is a tall single column that scrolls (see the max-width:768px
     block above), and that is correct there. On desktop a scrolling modal wastes the
     wide screen — especially the New-animal wizard's step 1, whose ~24 fields in a
     2-column grid run well past 90vh. Here we spend the horizontal room instead of
     scrolling: .cb-grid flows into as many ~230px columns as the card is wide (via
     auto-fit, which only creates as many tracks as there are fields, so a small
     breed/herd/contact form stays 2-up and never shows empty columns), and the
     New-animal wizard card is widened so its long form lays out ~4 columns deep and
     lands inside the viewport. The .cb-modal-card max-height:90vh + overflow:auto from
     the base rule is intentionally kept as a last-resort fallback: on a genuinely short
     screen where even this layout cannot fit, the content stays reachable rather than
     being clipped off-screen. In normal desktop use nothing scrolls. */
  @media (min-width:769px){
    /* Generic modals: a bit wider than the 560px base so a field-heavy form (e.g. Edit
       animal, ~18 fields) flows into ~3 columns and lands inside the viewport instead of
       scrolling. auto-fit only creates as many tracks as there are fields, so a small
       breed/herd/contact form collapses to 2-up (or 1 row) and never shows empty columns. */
    .cb-modal-card{width:min(760px,100%)}
    .cb-grid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
    /* Field-heavy modals — the New-animal wizard (24-field step 1) and the Edit-animal form
       (.cb-modal-wide, ~20 fields), the two tallest forms in the app — get a wide card and
       tighter (~6-up) columns. At ~4 field-rows deep the whole form fits the viewport height
       on any normal desktop without scrolling (the fields here are short — numbers and narrow
       selects — so ~165px columns stay perfectly legible). */
    .cb-wizard,.cb-modal-wide{width:min(1120px,100%)}
    .cb-wizard .cb-grid,.cb-modal-wide .cb-grid{grid-template-columns:repeat(auto-fit,minmax(165px,1fr))}
  }

  /* ===== Reports — category page (/reports/{category}) =====
     Replaces the old report-builder modal. A category page lists EVERY report in the category on
     the page itself (no inner scroll box); each report is a row that expands in place into its
     parameter form (accordion, one open at a time — see renderReportsView in app.js). Mobile-first:
     the base rules are the phone layout; wider screens only add room. */
  .rep-page{margin-top:2px}
  .rep-page-h{display:flex;align-items:center;gap:10px;margin-bottom:6px}
  .rep-page-h h2{font-family:var(--serif);font-size:19px;font-weight:600;margin:0;line-height:1.25;min-width:0}
  .rep-page-h .rb-tag{width:34px;height:34px;border-radius:9px;display:grid;place-items:center;flex:none}
  .rep-page-h .n{margin-left:auto;flex:none;font-size:11.5px;font-weight:700;color:var(--brass-deep);background:var(--brass-tint);padding:2px 9px;border-radius:20px}
  .rep-page-hint{margin:0 0 14px;font-size:13px}
  /* Back to the category grid: 44px square so it is a comfortable touch target. */
  .rb-back{border:1px solid var(--line);background:var(--surface);color:var(--ink);border-radius:9px;width:44px;height:44px;display:grid;place-items:center;cursor:pointer;flex:none}
  .rb-back:hover{border-color:var(--faint)}
  .rb-back:focus-visible{outline:2px solid var(--green);outline-offset:2px}
  .rep-items{display:flex;flex-direction:column;gap:8px}
  /* One button per report: choosing it opens the parameter modal (openReportParamsModal). */
  .rep-item{display:flex;align-items:center;gap:10px;width:100%;min-height:50px;padding:12px 14px;background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--green);border-radius:0 var(--radius) var(--radius) 0;box-shadow:var(--shadow);font:inherit;font-size:14.5px;font-weight:600;color:var(--ink);text-align:left;cursor:pointer;transition:border-color .12s ease,background .12s ease}
  .rep-item>span:first-child{flex:1;min-width:0}
  .rep-item .rb-chev{color:var(--faint);display:flex;flex:none;transition:color .14s ease}
  .rep-item:hover{background:var(--green-tint);border-color:var(--green)}
  .rep-item:hover .rb-chev{color:var(--green)}
  .rep-item:focus-visible{outline:2px solid var(--green);outline-offset:2px}
  /* The parameter modal sits BELOW the preview sheet (.cb-report-preview, z 80) so Preview shows on
     top of it, and closing the preview returns to the chosen parameters. Wider than the default
     card on desktop so the form keeps its multi-column grid. */
  .cb-rep-modal{z-index:75}
  @media (min-width:769px){.cb-rep-modal .cb-modal-card{width:min(760px,100%)}}
  /* Report form actions stay in the flow under the form (NOT the sticky modal footer). On a phone the
     three buttons share one row at full touch height; from tablet up they sit right-aligned. */
  .rep-actions{display:flex;gap:8px;margin-top:16px}
  .rep-actions>*{flex:1 1 0;min-height:44px}
  @media (min-width:769px){
    .rep-page-h h2{font-size:21px}
    /* Tablet up: the category's reports sit in two columns (row by row, catalogue order kept). */
    .rep-items{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}
    .rep-actions{justify-content:flex-end;gap:10px}
    .rep-actions>*{flex:0 0 auto;min-width:110px}
  }
  .gchart{display:flex;align-items:flex-end;gap:9px;height:190px;padding:20px 8px 14px}
  .gcol{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;height:100%;justify-content:flex-end}
  .gbars{display:flex;gap:3px;align-items:flex-end;height:100%;width:100%;justify-content:center}
  .gbars i{width:11px;border-radius:3px 3px 0 0} .gbars .exp{background:var(--brass)} .gbars .act{background:var(--green)}
  .gcol em{font-size:11px;color:var(--faint);font-style:normal}
  /* single-series category chart (Herd composition): group the columns together
     with a tight gap instead of spreading them across the full width */
  .gchart-cat{justify-content:center;gap:18px}
  .gchart-cat .gcol{flex:none;width:auto;min-width:52px}

  /* ---------- studbook ---------- */
  .sbstat{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:20px}

  /* ---------- setup ---------- */
  .setrow{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:12px 0;border-bottom:1px solid var(--line-soft)}
  .setrow:last-child{border:none}
  .setrow .info{flex:1;min-width:0} .setrow .info .k{font-weight:600} .setrow .info .s{font-size:12px;color:var(--muted)}
  .setrow .inp{border:1px solid var(--line);background:var(--surface-2);border-radius:6px;padding:7px 11px;font-size:13px;font-weight:600;font-family:inherit;flex:none;width:160px;text-align:right}
  .setrow input.set-num{outline:none}
  .setrow input.set-num:focus{border-color:var(--green)}
  /* clean text-field look: no native spinner arrows */
  .setrow input.set-num::-webkit-outer-spin-button,.setrow input.set-num::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
  .setrow input.set-num{-moz-appearance:textfield;appearance:textfield}
  /* unit (kg) sits INSIDE the bordered box, as in the original "320 kg" */
  .setrow .set-inp-wrap{display:flex;align-items:center;justify-content:flex-end;gap:5px;flex:none;width:160px;border:1px solid var(--line);background:var(--surface-2);border-radius:6px;padding:7px 11px}
  .setrow .set-inp-wrap:focus-within{border-color:var(--green)}
  .setrow .set-inp-wrap .inp{border:none;background:none;padding:0;width:auto;max-width:92px;text-align:right}
  .setrow .set-unit{font-size:13px;font-weight:600;color:var(--muted)}
  /* default-breed select (buildSelect) in a setup row */
  .setrow .cb-select{position:relative;flex:none;width:160px}
  .setrow .cb-select-btn{display:flex;align-items:center;gap:8px;width:100%;text-align:left;cursor:pointer;border:1px solid var(--line);background:var(--surface-2);border-radius:6px;padding:7px 11px;font-size:13px;font-weight:600;color:var(--ink);font-family:inherit}
  .setrow .cb-select-btn .chev{width:13px;height:13px;flex:none;opacity:.6}
  .setrow .cb-select-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .setrow .cb-select-label.cb-ph{color:var(--faint);font-weight:400}
  .setrow .cb-select-menu{position:absolute;top:calc(100% + 4px);right:0;left:auto;min-width:160px;z-index:30;display:flex;flex-direction:column;max-height:280px;background:var(--surface);border:1px solid var(--line);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.18);padding:5px}
  /* only the Operational breed dropdown needs extra width (long "Holstein-Friesland (HOL)" labels) */
  #s-op .cb-select-menu{min-width:280px}
  .setrow .cb-select-menu[hidden]{display:none}
  .setrow .cb-select-search{border:1px solid var(--line);border-radius:6px;padding:8px 10px;font-family:inherit;font-size:13px;color:var(--ink);outline:none;margin-bottom:5px}
  .setrow .cb-select-search:focus{border-color:var(--green)}
  .setrow .cb-select-list{overflow-y:auto}
  .setrow .cb-select-opt{display:block;width:100%;text-align:left;border:none;background:none;font-family:inherit;font-size:13.5px;color:var(--ink);padding:8px 10px;border-radius:6px;cursor:pointer;white-space:nowrap}
  .setrow .cb-select-opt:hover{background:var(--surface-2)}
  .setrow .cb-select-opt.on{background:var(--green-tint);color:#2C5A40;font-weight:600}
  /* Animal values tab — editable "R <amount>" per type */
  #s-val .val-wrap{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);background:var(--surface-2);border-radius:6px;padding:7px 11px;width:150px}
  #s-val .val-wrap:focus-within{border-color:var(--green)}
  #s-val .val-cur{color:var(--muted);font-weight:600;font-size:13px}
  #s-val .val-wrap input.val-inp{border:none;background:none;padding:0;width:100%;text-align:right;font-weight:600;font-size:13px;font-family:inherit;color:var(--ink);outline:none}
  #s-val input.val-inp::-webkit-outer-spin-button,#s-val input.val-inp::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
  #s-val input.val-inp{-moz-appearance:textfield;appearance:textfield}
  /* The value column (header + "R <amount>" boxes) sits against the panel's right
     edge, like the inputs on the Operational tab, instead of floating mid-row. */
  #s-val th:last-child,#s-val td:last-child{text-align:right}
  /* Setup row actions (Breeds, Ease of birth, Team): the pencil / trash .dact icons
     from rowIconActionsCell() in app.js, side by side at the right of the row. */
  .row-icon-acts{display:flex;gap:4px;justify-content:flex-end}
  /* Grading type selector (Bulls / Cows / Oxen / Calves). app.js renders the strip
     with no side margin; dropping the shared .seg-tabs side padding and matching
     each tab's padding to the table cells (16px, 12px on phones below) puts the
     first label on the same line as the "Trait" column and the underline flush
     with the table edge. */
  #s-grade .seg-tabs{padding:0}
  #s-grade .seg-tab{padding-left:16px;padding-right:16px}
  /* Grading tab — numeric inputs matched to the rest of Setup.
     The trait table (renderGradingTable) emits bare number inputs (.inp.num.g-*),
     which otherwise render full-width and unstyled next to the compact bordered
     boxes every other Setup tab uses. Scope the shared Setup field look to the
     grading cells so weight/low/high read as one family with the page, and keep
     each box a fixed width so it sits inline instead of stretching the column. */
  #s-grade .inp{border:1px solid var(--line);background:var(--surface-2);border-radius:6px;padding:7px 11px;font-size:13px;font-weight:600;font-family:inherit;width:120px;text-align:right;outline:none}
  #s-grade .inp:focus{border-color:var(--green)}
  /* strip the native number spinners — the rest of Setup uses clean text-style fields */
  #s-grade .inp::-webkit-outer-spin-button,#s-grade .inp::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
  #s-grade .inp{-moz-appearance:textfield;appearance:textfield}
  .toggle{width:40px;height:23px;border-radius:20px;background:var(--green);position:relative;cursor:pointer}
  .toggle::after{content:"";position:absolute;top:2px;right:2px;width:19px;height:19px;border-radius:50%;background:#fff}
  .toggle.off{background:var(--line)} .toggle.off::after{right:auto;left:2px}

  /* ---------- contacts ---------- */
  /* A–Z index — a compact segmented strip that stays tidy at a full 26 letters.
     Each letter is a uniform circular cell in a subtle container: available
     letters are tappable (hover tint), the active letter fills green, and
     letters with no contacts are dimmed + inert so the row reads at a glance. */
  /* Its own row beneath the group chips, left-aligned and sized to its content. */
  .alpha{display:flex;flex-wrap:wrap;gap:2px;width:fit-content;max-width:100%;margin:0 0 14px;padding:3px;background:var(--surface-2);border:1px solid var(--line-soft);border-radius:var(--radius)}
  .alpha b{min-width:20px;height:20px;display:grid;place-items:center;font-size:10.5px;font-weight:600;color:var(--muted);border-radius:50%;cursor:pointer;user-select:none;transition:background .12s ease,color .12s ease}
  .alpha b:hover{background:var(--green-tint);color:var(--green)}
  .alpha b.on{background:var(--green);color:#fff}
  .alpha b:focus-visible{outline:2px solid var(--green);outline-offset:1px}
  .alpha b.off{color:var(--faint);opacity:.4;cursor:default;pointer-events:none;font-weight:500}
  .avatar{width:32px;height:32px;border-radius:50%;background:var(--teal-tint);color:var(--teal);display:grid;place-items:center;font-weight:700;font-size:12px;flex:none}

  /* ---------- diary ---------- */
  .diary-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:16px}
  .dday{padding:14px 18px;border-bottom:1px solid var(--line-soft)}
  .dday:last-child{border:none}
  .dday .dh{font-size:12px;color:var(--faint);font-weight:600;margin-bottom:8px;font-variant-numeric:tabular-nums}
  .dentry{display:flex;align-items:center;gap:11px;padding:7px 0;position:relative}
  .dentry .tm{font-size:12px;color:var(--faint);width:52px;flex:none;font-variant-numeric:tabular-nums}
  .dentry .bd{width:3px;height:26px;border-radius:3px;flex:none}
  /* per-row edit/delete — hidden until the row is hovered/focused, keyboard-reachable */
  .dactions{margin-left:auto;display:flex;gap:4px;opacity:0;transition:opacity .12s}
  .dentry:hover .dactions,.dentry:focus-within .dactions{opacity:1}
  .dact{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:1px solid var(--line-soft);border-radius:7px;background:var(--surface);color:var(--faint);cursor:pointer}
  .dact svg{width:15px;height:15px}
  .dact:hover{color:var(--ink);border-color:var(--line)}
  .dact-del:hover{color:#b23b3b;border-color:#e0b4b4}

  /* contacts — one card per contact, every field shown on the card itself */
  .cb-contact-list{display:flex;flex-direction:column;gap:12px}
  .cb-contact-card{background:var(--surface);border:1px solid var(--line-soft);border-radius:12px;padding:14px 16px;box-shadow:0 1px 2px rgba(36,30,24,.04)}
  .cb-card-head{display:flex;align-items:center;gap:12px}
  .cb-card-who{min-width:0;flex:1}
  .cb-card-name{font-weight:600;color:var(--ink)}
  .cb-card-sub{font-size:13px;color:var(--faint)}
  .cb-card-head .pill{flex:none}
  .cb-card-head .dactions{opacity:1;flex:none}
  /* chevron toggle — mobile only (shown in the media query below); inert on desktop */
  .cb-card-toggle{display:none;align-items:center;justify-content:center;width:34px;height:34px;flex:none;padding:0;border:1px solid var(--line-soft);border-radius:8px;background:var(--surface);color:var(--faint);cursor:pointer}
  .cb-card-toggle svg{width:18px;height:18px;transition:transform .18s ease}
  .cb-contact-card.open .cb-card-toggle svg{transform:rotate(180deg)}
  .cb-detail-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:10px 24px;margin-top:12px;padding-top:12px;border-top:1px solid var(--line-soft)}
  .cb-detail-grid:empty{display:none}
  .cb-detail-cell{display:flex;flex-direction:column;gap:2px;min-width:0}
  .cb-detail-k{font-size:11px;font-weight:600;color:var(--faint);text-transform:uppercase;letter-spacing:.04em}
  .cb-detail-v{font-size:14px;color:var(--ink);word-break:break-word;white-space:pre-wrap}

  .stickies{display:flex;flex-direction:column;gap:16px}
  .stickies-h{font-size:12px;font-weight:600;color:var(--faint);text-transform:uppercase;letter-spacing:.04em}
  .sticky{position:relative;background:#F3E7A0;border:1px solid #E4D473;border-radius:3px;padding:15px 16px 12px;font-size:13px;box-shadow:2px 4px 9px rgba(36,30,24,.16);transform:rotate(-.5deg);transition:transform .12s,box-shadow .12s;cursor:pointer}
  .sticky:nth-child(odd){transform:rotate(.6deg)}
  .sticky:hover{transform:rotate(0) translateY(-2px);box-shadow:3px 7px 15px rgba(36,30,24,.22)}
  /* folded-corner "pin" accent at the top of each note */
  .sticky::before{content:"";position:absolute;top:-7px;left:50%;transform:translateX(-50%);width:46px;height:14px;background:rgba(36,30,24,.14);border-radius:2px}
  .sticky-c1{background:#EBD9E4;border-color:#D9BFCE}
  .sticky-c2{background:#CFE6D6;border-color:#AFD3BC}
  .sticky .st{font-weight:700;margin-bottom:4px;padding-right:18px}
  .sticky .sbody{white-space:pre-wrap;word-break:break-word;color:#3a3326}
  .sticky .mt{font-size:11px;color:#7a6f4a;margin-top:9px}
  .sticky-x{position:absolute;top:6px;right:8px;width:20px;height:20px;line-height:18px;text-align:center;border:none;background:none;color:#8a7d55;font-size:17px;cursor:pointer;border-radius:5px;opacity:0;transition:opacity .12s}
  .sticky:hover .sticky-x,.sticky:focus-within .sticky-x{opacity:1}
  .sticky-x:hover{background:rgba(36,30,24,.1);color:#b23b3b}
  /* dashboard sticky-notes strip — pinned diary notes shown on startup */
  .dash-notes{margin-top:18px}
  .dash-stickies{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:16px 18px;padding:10px 4px 4px}

  /* ---------- reports: "Run a report" panel ---------- */
  .cb-report-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 10px}
  .cb-report-export{margin-left:auto;padding:5px 12px;font-size:12px}
  /* cap the results height so a long report scrolls inside its own box instead of
     pushing the rest of the page down; header stays visible while scrolling */
  .cb-report-table{overflow:auto;max-height:min(58vh,460px);border:1px solid var(--line-soft);border-radius:8px}
  .cb-report-table thead th{position:sticky;top:0;background:var(--surface);z-index:1}
  /* keep each cell (ids, names, headers) on one line — the box scrolls if needed */
  .cb-report-table th,.cb-report-table td{white-space:nowrap}

  /* Tablet step (<=1100px): 4-up stat rows become 2-up, two-column layouts stack,
     and the 3-up grids drop to 2. */
  @media (max-width:1100px){.stat-row,.sbstat{grid-template-columns:repeat(2,1fr)}.grid2,.rec,.svc,.diary-grid{grid-template-columns:1fr}.board,.campgrid,.repgrid{grid-template-columns:1fr 1fr}}

  /* ---------- mobile drawer + phone layout ---------- */
  /* hidden on desktop; shown at phone width */
  .navtoggle{display:none;align-items:center;justify-content:center;flex:none;width:38px;height:38px;padding:0;border:1px solid var(--line);background:var(--surface);border-radius:var(--radius-sm);color:var(--ink);cursor:pointer}
  .navtoggle svg{width:20px;height:20px}
  .nav-scrim{position:fixed;inset:0;background:rgba(24,20,14,.44);z-index:35;opacity:0;visibility:hidden;transition:opacity .25s ease,visibility .25s ease}

  @media (max-width:768px){
    .app{grid-template-columns:1fr}
    /* sidebar becomes an off-canvas drawer */
    /* No box-shadow while closed: the drawer sits just off the left edge and a 40px blur still
       bled back onto the screen as a grey strip at 375px. The shadow only exists when open; it
       fades with the slide (box-shadow is in the transition) so it never pops. */
    .side{position:fixed;left:0;top:0;z-index:40;width:264px;max-width:84vw;transform:translateX(-100%);transition:transform .25s ease,box-shadow .25s ease;box-shadow:none}
    .app.nav-open .side{transform:none;box-shadow:0 0 40px rgba(0,0,0,.35)}
    .app.nav-open .nav-scrim{opacity:1;visibility:visible}
    .navtoggle{display:inline-flex}

    /* top bar reflow */
    .top{padding:10px 14px;gap:10px}
    .crumb{font-size:17px}
    .crumb small{font-size:11px}
    .search{display:none}
    /* search is hidden on phones, so push the language dropdown + button as a group
       to the right — the dropdown sits directly left of the + button */
    .top .langwrap{margin-left:auto}
    .top .btn.primary span{display:none}
    .top .btn.primary{padding:8px 11px}

    /* screens breathe less */
    .screen{padding:16px 14px}
    .stat-row{grid-template-columns:repeat(2,1fr);gap:12px}
    .board,.campgrid,.repgrid,.sbstat,.metrics{grid-template-columns:1fr}

    /* wide data tables scroll inside their panel instead of stretching the page */
    .panel{overflow-x:auto}
    table{min-width:560px}
    /* the small Setup reference tables (Breeds, Animal values, Service codes) are
       narrow — let them fit the screen and wrap instead of clipping */
    #setup table{min-width:0}
    /* report results fill the box when narrow, and grow (scrolling inside
       .cb-report-table) only when the values are actually wider than the box */
    .cb-report-table table{min-width:100%;width:auto}
    thead th,tbody td{padding:10px 12px}
    .toolbar{gap:7px}
    /* report picker: even, full-width chips instead of a ragged wrap */
    .cb-report-run .toolbar{display:grid;grid-template-columns:1fr;gap:8px}
    .cb-report-run .toolbar .chip{text-align:center;padding:10px 12px}
    /* animal register: count on the left, Filter + Export grouped together on the
       right as a button pair (space-between strands Filter awkwardly in the middle) */
    .an-toolbar .tb-actions{justify-content:flex-start;gap:8px}
    .an-toolbar .tb-actions .num{margin-right:auto}
    .an-toolbar .tb-actions .btn{flex:none}
    /* all 12 filter controls flow into ONE 2-column grid → a clean 6 rows × 2
       columns; the row wrappers collapse with display:contents so there's no
       lone/half-empty row, then Clear drops to its own spot below */
    .an-filters{display:grid;grid-template-columns:1fr 1fr;gap:7px;align-items:center}
    .an-filter-row{display:contents}
    .an-filters .an-filter-ctl,.an-filters .an-filter-sel{min-width:0;max-width:none}
    /* Clear: its own spot under the grid, bottom-right, same height as the buttons
       (two-class selector to beat .btn.ghost's smaller padding) */
    .an-filters .an-filter-clear{grid-column:2;justify-self:end;padding:8px 14px;font-size:13px}

    /* touch has no hover — keep the per-row edit/delete and the sticky × visible,
       and bump the icon buttons to a thumb-friendly size */
    .dactions,.sticky-x{opacity:1}
    .dact{width:38px;height:38px}
    .sticky-x{width:30px;height:30px;line-height:28px;font-size:20px}

    /* contacts: each card's detail is a drop-down — collapsed by default, the
       chevron reveals it. On desktop (outside this query) it stays open. */
    .cb-card-head{cursor:pointer}
    .cb-card-toggle{display:inline-flex}
    .cb-contact-card .cb-detail-grid{display:none}
    .cb-contact-card.open .cb-detail-grid{display:grid}
  }

  /* ---------- phone fit pass (QA at 375px) ----------
     CSS-only fixes for the problems QA found walking every screen at phone width.
     Everything here lives inside max-width media queries, so the desktop layout
     (wider than 768px) is byte-for-byte unchanged. The rules use the theme tokens
     only, and no inline styles are added in markup. */
  @media (max-width:768px){
    /* Top-bar subtitle (e.g. "154 active animals · Kameeldrift: 44 · …") wrapped to
       three lines and pushed the title down. Keep it to one line with an ellipsis;
       min-width:0 lets the flex item shrink so the ellipsis can kick in. */
    .crumb{min-width:0;flex:1 1 auto}
    .crumb small{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

    /* (1) New-animal wizard step tabs: four "N · Label" chips in one row left about
       70px each, so labels were clipped ("1 · Gene…"). A 2 × 2 grid gives each chip
       half the card width, so the full label fits, and the tap target grows to 40px. */
    .cb-steps{display:grid;grid-template-columns:1fr 1fr;gap:6px}
    .cb-step{min-height:40px;padding:8px 6px}

    /* (2) Sticky action bar for every modal (wizard Next/Back/Save, formModal Save).
       .cb-modal-card is the scroll container (max-height + overflow:auto), so a sticky
       footer pins to the bottom of the visible card while the fields scroll under it.
       The negative margins pull the bar edge-to-edge over the card's own padding, and
       the bar keeps its normal place in the flow, so the last field always scrolls into
       view above it (it never covers a field). Bottom padding respects the phone's home
       indicator through the safe-area inset. */
    .cb-modal-card{padding-bottom:0}
    .cb-modal-actions{position:sticky;bottom:0;z-index:3;margin:18px -16px 0;
      padding:12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
      background:var(--surface);border-top:1px solid var(--line-soft);
      /* one compact row instead of the stacked column: the bar is always on screen,
         so it must stay short. Each button shares the width equally. */
      flex-direction:row;gap:8px}
    .cb-modal-actions>*{width:auto;flex:1 1 0;min-height:44px}
    /* wizard footer: Cancel | [Back][Next]. The nav group takes two shares so its
       two buttons are as wide as Cancel; a [hidden] Back simply drops out. */
    .cb-wiz-foot .cb-wiz-nav{flex:2 1 0;margin-left:0;gap:8px}
    .cb-wiz-nav>*{flex:1 1 0;min-height:44px}

    /* (2) Service screen: the "Record services" button sat under the full cow list.
       Pin its footer to the bottom of the viewport. The panel normally scrolls sideways
       on phones (.panel overflow-x:auto), which would make the panel the sticky
       container and stop the pin, so this one panel opts back to visible overflow
       (its cow rows already fit the width). */
    #service .panel{overflow:visible}
    #service .svc-foot{position:sticky;bottom:0;z-index:3;background:var(--surface);
      border-radius:0 0 var(--radius) var(--radius);box-shadow:0 -6px 14px rgba(36,30,24,.06);
      padding-bottom:calc(14px + env(safe-area-inset-bottom, 0px))}
    /* keep the pinned bar short: a one-line summary ("3 cows selected for Rooi Baas")
       with tighter gaps, then a full-width button below it */
    #service .svc-foot{gap:4px 6px;padding-top:10px;font-size:13px}
    #service .svc-foot b{font-size:15px}
    #service .svc-foot .btn.primary{min-height:44px;flex:1 1 100%;justify-content:center}
    /* the herd / method pickers in the service header were 30px tall — too small to tap */
    .svc-head .cb-select-btn{min-height:40px}

    /* (4) Mating cycle: an empty stage rendered as a 190px blank card. Collapse it to
       its header plus a short "No animals" line. :has() tests the live .col-body that
       renderPipeline() fills, so the rule follows the data with no JS change. */
    .col:has(.col-body:empty){min-height:0}
    .col:has(.col-body:empty) .col-body{display:none}
    .col:has(.col-body:empty) .col-empty{display:block}
    /* card action + correction buttons: grow to a thumb-sized target (were 26–29px) */
    .mcard .acts .btn{min-height:40px}
    .mcard .corner button{width:36px;height:36px}
    .mcard .top{padding-right:84px}            /* room for the two larger corner icons */
    .mcard .calf-link{padding:8px 0;margin:-4px 0}  /* taller tap band, same visual spacing */

    /* (5) Other small tap targets found on the walk-through */
    .campcard .btn,.toolbar .chip{min-height:40px}     /* farm Edit, catalog chips */
    .cb-lot-x{min-width:40px;min-height:40px}           /* catalog lot remove × */
    /* the A–Z index is laid out as a 13 × 2 grid in the phone fit pass 2 block below */
    #setup .btn{min-height:40px}                         /* Grading "Reset to defaults" etc. */
    /* the small Setup tables (Breeds, Team) must fit the screen without a sideways
       scroll: tighter cell padding, and long unbroken values (an email address) may
       break anywhere instead of forcing the column wider than the phone. Their row
       Edit / Remove are now compact .dact icons (rowIconButton in app.js), so the
       old rules that stacked and grew the text buttons are gone. */
    #setup thead th,#setup tbody td{padding:8px 6px}
    /* Team (Name, Email, Role, actions): four columns squeezed the email to ~44px.
       Each member becomes a small card instead — line 1 name + role + the edit /
       remove icons, line 2 the email (may break anywhere). Header hidden: the values
       are self-explanatory. Column order is fixed by renderTeam() in app.js. */
    #s-team table,#s-team tbody{display:block;width:100%}
    #s-team thead{display:none}
    #s-team tbody tr{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;padding:10px 6px;border-bottom:1px solid var(--line-soft)}
    #s-team tbody td{display:block;padding:0;border:none}
    #s-team tbody td:nth-child(1){flex:1 1 0;min-width:0;font-weight:600}
    #s-team tbody td:nth-child(2){order:3;flex:1 1 100%;overflow-wrap:anywhere;font-size:12.5px;color:var(--muted)}
    #s-team tbody td:nth-child(4){order:2;flex:none}
  }
  /* "No animals" line for an empty mating stage — only shown by the phone rule above */
  .col-empty{display:none;padding:0 15px 13px 22px;font-size:12.5px;color:var(--faint)}

  /* (3) Animal register on phones (<=640px): a real table that scrolls sideways
     inside its panel (.panel{overflow-x:auto} from the max-width:768px block).
     The earlier stacked-card layout squeezed the values and left the sort-header
     strip out of line with the data under it (QA review, phone fit pass 2), so
     every column — Colour included — keeps its own header cell again. No cell
     wraps, so a tag, name, breed or "235,7 kg" always reads on one line; the
     min-width is wide enough for all 8 columns at that size, and only the table
     scrolls, never the page. Desktop and tablet (above 640px) are unchanged. */
  @media (max-width:640px){
    #animals table{min-width:860px}
    #animals thead th,#animals tbody td{white-space:nowrap}
    /* the empty-state row is one long sentence: let it wrap in the visible width
       instead of running off along the 860px table */
    #animals tbody td[colspan]{white-space:normal}
    #animals .grade-badge{padding:6px 9px;min-height:32px} /* bigger tap target for the grade popup */

    /* Animal record → Genealogy tree. Fixed 132/106px nodes need ~520px for the four
       grandparents, so the tree scrolled sideways. On phones every generation shares
       the full width instead: each ul spans its parent, each li takes an equal slice
       (1 → 2 → 4), and the node fills its slice. The connector lines are drawn from
       50%-wide pseudo-elements on each li, so they still meet at the slice centres. */
    .tree{overflow-x:visible}
    .tree > ul,.tree ul{width:100%}
    .tree li{flex:1 1 0;min-width:0;padding:20px 3px 0}
    .tnode,.tnode.sm{width:100%;max-width:132px}
    .tlabel{font-size:11px}
    .tnode.sm .tlabel{font-size:10px}
    .tnode.sm .tlabel span{font-size:9.5px}
    /* the ear-tag badge and sex dot shrink with the narrow grandparent photo */
    .tnode.sm .tphoto .tag{transform:scale(.6);left:4px;top:4px}
    .tnode.sm .tsex{width:14px;height:14px;font-size:9px;right:4px;top:4px}
  }

  /* ---------- phone fit pass 2 (QA review at 375px) ----------
     Second walk-through of every screen at phone width. Everything is inside a
     max-width media query, so desktop is unchanged; rules that also apply on
     desktop (button / chip centring, Animal values alignment, Grading tabs) live
     with their base styles. Some rules need !important only because app.js writes
     an inline margin on that element — each one says so. */
  @media (max-width:768px){
    /* (5) Camp drill-down list: the camp table inherits the phone table{min-width:560px},
       and this panel did not scroll, so the table widened the whole PAGE to ~593px.
       Every dialog opened over it (Add camp, Add herd, Delete camp) was then centred on
       that wider page and ran off the right edge. Scrolling the list inside its own box
       keeps the page at the screen width, which fixes all of those dialogs too. */
    .cb-camp-animals{overflow-x:auto;-webkit-overflow-scrolling:touch}
    .cb-camp-table td{white-space:nowrap}

    /* (9, 10) Sales & catalog and Logix submissions panel headers: the title and two or
       three buttons shared one row, so every label broke over two or three lines. The
       title takes its own line, and the buttons wrap below it, each label on one line.
       app.js / index.html give these buttons an inline margin-left (auto or 8px) for the
       desktop row, hence the !important to reset it here. */
    #sales .panel-h,#studbook .panel-h{flex-wrap:wrap;gap:8px}
    #sales .panel-h h3,#studbook .panel-h h3{flex:1 1 100%}
    #sales .panel-h .btn,#studbook .panel-h .btn{flex:1 1 auto;white-space:nowrap;min-height:40px;margin-left:0 !important}
    /* catalog lots: keep "Beefmaster · 24 y 11 m" and "R 18 000" on one line — the
       table already scrolls inside its panel */
    #sales tbody td{white-space:nowrap}
    /* Logix submissions: same — "Birth notification", "Logix ref" and dates stay on one
       line and the table scrolls inside its .tbl-scroll box */
    #studbook thead th,#studbook tbody td{white-space:nowrap}
    #studbook tbody td[colspan]{white-space:normal}
    /* Logix: with no submissions the 560px table only holds the empty-state sentence,
       which ran off the edge. Drop the minimum width and the header row so the
       sentence wraps inside the screen; data rows bring the scroll table back. */
    #studbook table:has(td[colspan]){min-width:0}
    #studbook table:has(td[colspan]) thead{display:none}

    /* (11) Contacts: "New contact" wraps under the group chips; push it to the right
       edge of the toolbar. app.js sets an inline margin-left:10px for desktop. */
    #contacts .cb-contact-add{margin-left:auto !important}

    /* (12) Contacts A–Z index: a 13 × 2 grid (A–M, N–Z) that fills the panel width,
       instead of ~10 fixed 32px circles that left a gap on the right and a short
       last row. Cells are ~25px wide × 34px tall, so the shape is a rounded square. */
    .alpha{display:grid;grid-template-columns:repeat(13,minmax(0,1fr));width:100%}
    .alpha b{min-width:0;height:34px;font-size:12px;border-radius:8px}

    /* (13) Report parameter modal: the phone modal rule drops the card's bottom
       padding (it expects the sticky .cb-modal-actions bar, which pads itself), but
       this modal uses its own .rep-actions row, so its buttons sat on the card edge.
       Pad the row (plus the home-indicator safe area), put Cancel | Preview side by
       side, and give "Generate report" (the submit button, last) a full-width row so
       its label never breaks. */
    .cb-rep-modal .rep-actions{display:grid;grid-template-columns:1fr 1fr;gap:8px;
      padding-bottom:calc(16px + env(safe-area-inset-bottom, 0px))}
    .cb-rep-modal .rep-actions>[type="submit"]{grid-column:1 / -1}

    /* (14) Setup category tabs: nine underline tabs wrapped into three ragged rows.
       On phones they become an even 3 × 3 grid of buttons, the open one filled
       green, so every category is visible and easy to tap. Scoped to the Setup
       .tabs strip only (the Grading selector uses .seg-tabs and is untouched). */
    #setup .tabs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:10px}
    #setup .tab{display:flex;align-items:center;justify-content:center;text-align:center;
      white-space:normal;line-height:1.25;min-height:44px;padding:6px;margin:0;
      border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--surface)}
    #setup .tab.on{background:var(--green);border-color:var(--green);color:#fff}
    /* (15, 17) Setup code tables (Breeds, Ease of birth) with the pencil / trash icons:
       Code + Name + Description + two 38px icons came to ~335px, wider than the
       309px left inside the 18px pane padding, so the trash icon spilled past the
       panel edge. A slimmer pane padding and 34px icons (still an easy tap) make the
       row fit; long description words may break rather than widen the table. */
    #setup .tabpane{padding:14px 10px}
    #setup .dact{width:34px;height:34px}
    #setup tbody td.muted{overflow-wrap:anywhere}

    /* (17) Setup → Ease of birth: keep the Modified date on one line, and when no code
       has a description, hide the empty Description column (header + cells) so it
       stops taking width. :has() checks the live rows, so the column returns as soon
       as one code has a description. Row actions are compact icons (rowIconButton). */
    #s-ease td.num{white-space:nowrap}
    #s-ease table:not(:has(tbody td:nth-child(3):not(:empty))) :is(th,td):nth-child(3){display:none}

    /* (18) Setup → Grading: the five-column trait table ran off the screen (High cut
       off, trait names over three lines). It scrolls sideways inside #grade-body like
       the register, with one-line cells. Cell and tab padding are both 12px, so the
       Bulls / Cows tabs stay lined up with the "Trait" column. These beat the
       #setup table{min-width:0} and #setup th/td 8px 6px phone rules above (same
       specificity, later in the file). */
    #grade-body{overflow-x:auto;-webkit-overflow-scrolling:touch}
    #s-grade table{min-width:560px}
    #s-grade thead th,#s-grade tbody td{padding:10px 12px;white-space:nowrap}
    #s-grade .seg-tab{padding-left:12px;padding-right:12px}
  }

/* Catalog lot remove (×) — a compact ghost button in the actions column, and a
   print rule so the actions column never appears on a printed catalog. */
.cb-lot-x{border:0;background:transparent;color:var(--muted,#8a8f98);cursor:pointer;
  font-size:18px;line-height:1;padding:4px 8px;border-radius:8px}
.cb-lot-x:hover{background:rgba(220,70,70,.12);color:#c0392b}
.cb-lot-picker .cb-lotp-filters{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin:4px 0 12px}
.cb-lot-picker .cb-lotp-search{margin-bottom:10px}
.cb-lot-picker .cb-lotp-list{max-height:46vh;overflow:auto;border:1px solid var(--line,#e5e7eb);border-radius:12px}
.cb-lot-picker .cb-lotp-row{display:flex;align-items:center;gap:10px;padding:9px 12px;border-bottom:1px solid var(--line,#eef0f2)}
.cb-lot-picker .cb-lotp-row:last-child{border-bottom:0}
.cb-lot-picker .cb-lotp-row .nm{color:var(--muted,#8a8f98)}
.cb-lot-picker .cb-lotp-row .btn{margin-left:auto}
.cb-lot-picker .cb-lotp-empty{padding:18px 14px;text-align:center}
@media (max-width:640px){.cb-lot-picker .cb-lotp-filters{grid-template-columns:1fr}}
/* ---------- report preview / print overlay ---------- */
/* Full-screen overlay (sibling of .app in <body>) that previews one report as a page-like
   "sheet" sized to A4 landscape, so what the user sees on screen matches the printout. */
.cb-report-preview{position:fixed;inset:0;z-index:80;background:rgba(24,20,14,.55);
  display:flex;flex-direction:column;align-items:center;gap:16px;overflow:auto;padding:22px}
.cb-rp-toolbar{position:sticky;top:0;z-index:1;align-self:stretch;display:flex;align-items:center;gap:10px;
  padding:10px 14px;background:var(--surface);border:1px solid var(--line-soft);border-radius:10px}
.cb-rp-bartitle{font-weight:600}
.cb-rp-spacer{flex:1}
/* A4 landscape is 297mm ≈ 1123px at 96dpi — size the sheet to that so columns lay out as they print */
.cb-rp-sheet{width:min(1123px,96vw);background:var(--surface);color:var(--ink);
  border:1px solid var(--line-soft);border-radius:8px;box-shadow:0 10px 40px rgba(0,0,0,.25);
  /* 18 px sides ≈ the PDF's 5 mm side margin: narrow so wide tables shrink their text less. */
  padding:28px 18px;margin-bottom:22px}
/* Number columns (rpNumericCol) sit on the right edge so digits line up, as in the PDF. */
.cb-rp-sheet th.cb-rp-num,.cb-rp-sheet td.cb-rp-num{text-align:right}
.cb-rp-title{margin:0 0 4px;font-size:20px}
.cb-rp-meta{margin:0 0 16px;color:var(--muted);font-size:13px}
/* Branded report header (legacy CattleBase layout, current design tokens): mark + wordmark on the
   left, farm name + Contact Information / Registration Details columns on the right. */
.cb-rp-head{display:flex;gap:22px;align-items:flex-start;margin-bottom:12px}
.cb-rp-brand{display:flex;align-items:center;gap:10px;flex-shrink:0}
.cb-rp-mark{width:46px;height:46px;border-radius:9px;background:#fff;display:grid;place-items:center;overflow:hidden;box-shadow:inset 0 0 0 1.5px var(--line-soft)}
.cb-rp-mark img{width:100%;height:100%;object-fit:contain;padding:5px;box-sizing:border-box}
.cb-rp-brand b{font-family:var(--serif,Georgia,serif);font-weight:600;font-size:17px;color:var(--green);letter-spacing:.2px}
.cb-rp-headmain{flex:1;min-width:0}
.cb-rp-farmname{margin:0;font-family:var(--serif,Georgia,serif);font-size:26px;line-height:1.1;color:var(--ink)}
.cb-rp-entity{color:var(--muted);font-size:13px;margin-top:3px}
.cb-rp-cols{display:flex;gap:48px;margin-top:12px;flex-wrap:wrap}
.cb-rp-col{min-width:190px}
.cb-rp-colh{font-weight:700;font-size:13px;margin-bottom:5px;color:var(--ink)}
.cb-rp-line{display:flex;gap:8px;font-size:12.5px;line-height:1.55}
.cb-rp-l{color:var(--muted);min-width:76px}
.cb-rp-v{color:var(--ink)}
/* Rule under the header + the TOTAL footer line. */
.cb-rp-rule{border-top:2px solid var(--ink);margin:4px 0 16px}
.cb-rp-total{display:flex;align-items:baseline;gap:18px;margin-top:12px;border-top:2px solid var(--line);
  padding-top:9px;font-weight:700;font-size:14px;color:var(--ink)}
/* Zebra striping like the legacy preview (and the register), using the warm surface tint. */
.cb-rp-sheet tbody tr:nth-child(even){background:var(--surface-2)}
/* Two-level report header (legacy parity — Growth report for bulls: "Bull Data" / "Calf Data" each
   spanning its columns), centred with a rule between the two groups. */
.cb-report-table tr.cb-rp-hgroup th,.cb-rp-sheet thead tr.cb-rp-hgroup th{text-align:center;border-bottom:1px solid var(--line)}
.cb-report-table tr.cb-rp-hgroup th+th,.cb-rp-sheet thead tr.cb-rp-hgroup th+th{border-left:2px solid var(--line)}
/* Animal information with picture (legacy parity, batch D) — one card per animal. Mobile-first: the
   field lists, notes and photo stack; from 720px they sit side by side (fields | fields | notes+photo)
   like the legacy card. Each card stays on one printed page where it fits. */
.cb-ac-list{display:flex;flex-direction:column;gap:16px}
.cb-ac-card{border:1px solid var(--line);border-radius:10px;padding:14px;background:var(--surface);break-inside:avoid;page-break-inside:avoid}
.cb-ac-grid{display:grid;grid-template-columns:1fr;gap:14px}
.cb-ac-kv{display:grid;grid-template-columns:minmax(120px,auto) 1fr;gap:4px 10px;margin:0;font-size:13px}
.cb-ac-kv dt{font-weight:600;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:.03em;align-self:center}
.cb-ac-kv dd{margin:0;border:1px solid var(--line-soft);border-radius:4px;padding:3px 8px;min-height:24px}
.cb-ac-side{display:flex;flex-direction:column;gap:10px}
.cb-ac-notes p{margin:4px 0 0;border:1px solid var(--line-soft);border-radius:4px;padding:6px 8px;min-height:48px;font-size:13px;white-space:pre-wrap}
.cb-ac-photo{border:1px solid var(--line);border-radius:6px;min-height:150px;display:grid;place-items:center;overflow:hidden;background:var(--surface-2)}
.cb-ac-photo img{display:block;width:100%;height:auto;object-fit:cover}
.cb-ac-calves{margin-top:12px}
@media (min-width:720px){.cb-ac-grid{grid-template-columns:1fr 1fr minmax(180px,240px)}}
@media print{.cb-ac-grid{grid-template-columns:1fr 1fr 200px}.cb-ac-photo{-webkit-print-color-adjust:exact;print-color-adjust:exact}}
/* Grouped reports (legacy parity — e.g. Animals per herd): a "Herd : <name>" section row before each
   group and a "TOTAL <n>" row after it, spanning the table. Both override the zebra stripe. */
.cb-report-table tr.cb-rp-group td,.cb-rp-sheet tbody tr.cb-rp-group td{font-weight:700;background:var(--surface);border-top:2px solid var(--line);padding-top:10px}
.cb-report-table tr.cb-rp-grouptotal td,.cb-rp-sheet tbody tr.cb-rp-grouptotal td{font-weight:700;background:var(--surface);border-top:1px solid var(--line)}
/* Totals row (tfoot) — summed money/weight columns, bold with a strong top rule so it reads as the
   report footer, not another data row. The first cell holds the TOTAL label + row count. */
.cb-rp-sheet tfoot td{font-weight:700;border-top:2px solid var(--line);background:var(--surface)}
.cb-rp-sheet tfoot td.cb-rp-ft-label{white-space:nowrap}
/* Graphs-group reports render as an SVG bar chart (see buildReportChart). CSS vars resolve here
   because they are applied via the stylesheet, not inline attributes (which the pane CSP strips). */
.cb-rp-chartwrap{margin-top:6px}
.cb-rp-chart{display:block;width:100%;height:auto}
.cb-rp-chart .cb-rp-grid{stroke:var(--line-soft);stroke-width:1}
.cb-rp-chart .cb-rp-base{stroke:var(--line);stroke-width:1.5}
.cb-rp-chart text.cb-rp-ax{fill:var(--muted);font-size:11px}
.cb-rp-chart text.cb-rp-val{fill:var(--ink);font-size:11px;font-weight:600}
.cb-rp-legend{display:flex;gap:18px;flex-wrap:wrap;justify-content:center;margin-top:10px;font-size:12.5px;color:var(--ink)}
.cb-rp-lgitem{display:inline-flex;align-items:center;gap:6px}
.cb-rp-lgitem svg{display:block}
/* Never squashed, never wrapped (features/report-pdf-export.md): every cell stays on ONE line and each
   column takes its natural width (table-layout:auto). A table narrower than the sheet fills it
   (width:100%); a wider one is scaled down as a whole by fitReportTables() in app.js so every column
   fits the landscape sheet width — the same rule the PDF uses. Capture forms (.cb-cap) opt out below. */
.cb-rp-sheet table{width:100%;border-collapse:collapse;table-layout:auto}
/* --rp-scale (set per table by fitReportTables, default 1) shrinks the text AND padding together, so a
   wide table re-lays out smaller instead of being zoomed — its measured width stays exact. */
.cb-rp-sheet th,.cb-rp-sheet td{border:1px solid var(--line-soft);text-align:left;vertical-align:top;white-space:nowrap;
  padding:calc(6px * var(--rp-scale,1)) calc(9px * var(--rp-scale,1));font-size:calc(13px * var(--rp-scale,1))}
/* Capture forms are paper to write on: fixed column widths, and long headers may stack (as legacy). */
.cb-rp-sheet table.cb-cap{table-layout:fixed}
.cb-rp-sheet table.cb-cap th{white-space:normal;overflow-wrap:anywhere}
.cb-rp-sheet thead th{background:var(--surface-2,var(--surface));font-weight:600}
@media (max-width:640px){.cb-rp-sheet{padding:18px 14px}.cb-rp-sheet th,.cb-rp-sheet td{
  font-size:calc(12px * var(--rp-scale,1));padding:calc(5px * var(--rp-scale,1)) calc(6px * var(--rp-scale,1))}}

/* Printable data-capture forms (blank ruled field sheets). The table inherits .cb-rp-sheet table
   (fixed layout + bordered cells); here the body rows get extra height for handwriting, no zebra
   striping (it is a form to write on, not a data table), and a sign-off line under the grid. */
.cb-cap td,.cb-cap th{height:30px;vertical-align:middle}
.cb-cap tbody td{height:32px}
.cb-cap td.cap-filled{font-weight:600;white-space:nowrap}         /* pre-filled -ID No / No. */
.cb-cap tbody tr:nth-child(even){background:transparent}          /* a blank form must not be zebra-striped */
/* Fixed widths for the short/numeric columns (set via <col class="cap-c-<key>"> — CSP blocks inline
   style). Columns with no rule here share the remaining width under table-layout:fixed. */
.cb-cap col.cap-c-no{width:46px}
.cb-cap col.cap-c-yy{width:52px}
.cb-cap col.cap-c-id_no{width:92px}
.cb-cap col.cap-c-sex{width:60px}
.cb-cap col.cap-c-year{width:64px}
.cb-cap col.cap-c-photo_no{width:82px}
.cb-cap col.cap-c-ear_no{width:70px}
.cb-cap col.cap-c-date_birth{width:112px}
.cb-cap col.cap-c-birth_weight{width:78px}
.cb-cap col.cap-c-weaning_weight{width:88px}
.cb-cap col.cap-c-mother_birth_weight{width:96px}
.cb-cap col.cap-c-mother_weaning_weight{width:104px}
.cb-cap-foot{display:flex;flex-wrap:wrap;gap:30px;margin-top:26px;font-size:13px;color:var(--ink)}
.cb-cap-footitem{display:inline-flex;align-items:flex-end;gap:8px}
.cb-cap-footline{display:inline-block;border-bottom:1px solid var(--line);height:15px;min-width:210px}
.cb-cap-footline.sm{min-width:150px}

@media print{
  .cb-noprint{display:none !important}
  /* Force landscape so wide reports fit across the page; narrow 5 mm side margins (as the PDF) give
     wide tables more width so their text is shrunk less. */
  @page{size:A4 landscape;margin:12mm 5mm}
  /* When a preview is open, print ONLY its sheet — drop the whole SPA chrome and the backdrop. */
  body:has(.cb-report-preview) > *:not(.cb-report-preview){display:none !important}
  html,body{background:#fff !important}
  .cb-report-preview{position:static !important;inset:auto !important;display:block !important;
    padding:0 !important;background:#fff !important;overflow:visible !important}
  .cb-rp-sheet{width:100% !important;max-width:none !important;margin:0 !important;padding:0 !important;
    border:0 !important;border-radius:0 !important;box-shadow:none !important}
  /* Same no-wrap rule as on screen: cells stay on one line at their natural width and fitReportTables()
     (re-run on beforeprint for the printed page width) scales a wide table down to the page width.
     SEPARATE (not collapsed) borders: every cell paints its OWN right+bottom border and the header its
     top border, so NO border is dropped where a physical page splits — the grid stays fully closed on
     every page (top and bottom), instead of looking cut open at the page boundary. border-spacing:0
     keeps every line single (no doubling). This is what makes a multi-page report cut cleanly. */
  .cb-rp-sheet table{width:100% !important;table-layout:auto;border-collapse:separate;border-spacing:0;
    border-left:1px solid #bbb}
  .cb-rp-sheet table.cb-cap{table-layout:fixed}
  .cb-rp-sheet th,.cb-rp-sheet td{border-right:1px solid #bbb;border-bottom:1px solid #bbb;
    font-size:calc(9pt * var(--rp-scale,1));padding:calc(4px * var(--rp-scale,1)) calc(6px * var(--rp-scale,1));
    white-space:nowrap;break-inside:avoid;page-break-inside:avoid}   /* one line; a cell never splits a page */
  .cb-rp-sheet table.cb-cap th{white-space:normal}
  .cb-rp-sheet thead th{border-top:1px solid #bbb}     /* the table's top edge — repeats on EVERY page */
  .cb-rp-sheet thead{display:table-header-group}       /* repeat the column header on every printed page */
  .cb-rp-sheet tfoot{display:table-footer-group}       /* repeat the totals row at the foot of every page */
  .cb-rp-sheet tfoot td{-webkit-print-color-adjust:exact;print-color-adjust:exact;border-top:2px solid #777 !important;font-weight:700}
  /* Never split a row across a page; keep the title + meta WITH their table (no stranded heading at a
     page foot); keep the branded header block whole. Both the modern (break-*) and legacy
     (page-break-*) spellings are set so every print engine honours them. */
  .cb-rp-sheet tr{break-inside:avoid;page-break-inside:avoid}
  .cb-rp-title,.cb-rp-meta{break-inside:avoid;page-break-inside:avoid;break-after:avoid;page-break-after:avoid}
  .cb-rp-head{break-inside:avoid;page-break-inside:avoid}
  .cb-rp-sheet tbody tr:nth-child(even){-webkit-print-color-adjust:exact;print-color-adjust:exact;background:var(--surface-2) !important}
  .cb-rp-mark{-webkit-print-color-adjust:exact;print-color-adjust:exact}
  /* Force the chart bars/fills to print, and keep the whole chart on one page. */
  .cb-rp-chartwrap{-webkit-print-color-adjust:exact;print-color-adjust:exact;page-break-inside:avoid;break-inside:avoid}
  .cb-rp-chart{max-height:150mm}
  /* Capture forms print as a plain ruled grid: keep the row height for handwriting and turn OFF the
     shared zebra striping (a blank form must not be shaded). Header still repeats via table-header-group. */
  .cb-cap tbody td{height:9mm}
  .cb-cap tbody tr:nth-child(even){background:#fff !important}
  .cb-cap-foot{break-inside:avoid;page-break-inside:avoid}   /* sign-off line never splits across a page */
}

/* ===== ROLE GATING (UI mirror of the server's role checks) =====
   app.js writes the signed-in user's role onto <html data-role="owner|manager|field|readonly">
   (an effect on the `me` signal), and every write control carries data-min-role="field" or
   "manager" matching the MINIMUM role its API route enforces (forbid_readonly -> "field",
   has_min_role(payload, "manager") -> "manager"). Pure CSS means the rule also covers controls a
   screen re-renders later, and flips the moment `me` changes, with no per-screen re-gating code.
   Before /api/me resolves (no data-role yet) gated controls stay hidden, so a read-only user never
   sees a write button flash. This is presentation only: the API's 403 stays the real enforcement. */
html:not([data-role="field"]):not([data-role="manager"]):not([data-role="owner"]) [data-min-role="field"],
html:not([data-role="manager"]):not([data-role="owner"]) [data-min-role="manager"]{display:none !important}

/* Sidebar footer "Sign out": a real <button> so it is reachable by keyboard and has an accessible
   name (aria-label, translated via data-i18n-aria). Styled to sit quietly in the dark sidebar. */
.side .foot .foot-signout{margin-left:auto;background:transparent;border:1px solid rgba(255,255,255,.18);color:#EAF1EA;border-radius:7px;padding:5px 7px;cursor:pointer;display:grid;place-items:center;flex-shrink:0}
.side .foot .foot-signout:hover{background:rgba(255,255,255,.08)}
.side .foot .foot-signout:focus-visible{outline:2px solid var(--brass);outline-offset:2px}
.side .foot .foot-signout svg{width:16px;height:16px}
