/* Seed Database — theme tokens
   Two themes: "furrow" (light) and "loam" (dark). Every color in this file
   is a custom property; components below reference the properties, never a
   literal hex value, so the whole app's look swaps on data-theme. */

html[data-theme="furrow"] {
  --bg: #F3EFE3;
  --header-bg: #24352A;
  --header-text: #F3EFE3;
  --header-sub: #A9B8A6;
  --tab-bg: #E6E0CF;
  --tab-text: #5B5642;
  --accent: #B6752C;
  --accent-text: #FFFFFF;
  --card-bg: #FFFFFF;
  --thumb-bg: #D7C9A3;
  --text-primary: #24352A;
  --text-secondary: #8A8570;
  --badge-bg: #EFE0C6;
  --badge-text: #8A5A22;
  --nav-bg: #FFFFFF;
  --nav-border: #E5E0D2;
  --nav-inactive: #B4AF9C;
  --toggle-track-off: #D9D2BC;
  --toggle-track-on: #24352A;
  --danger: #A33B2C;
  --danger-text: #FFFFFF;
  --warning: #BF5E28;
  --warning-text: #FFFFFF;
  --accent-green-bg: #DCE8D6;
  --accent-green-text: #2F4A38;
  --tab-border: #D2C7A5;
  --accent-green-border: #BFD8B5;
  --badge-border: #DDC79C;
  --card-border-strong: rgba(0, 0, 0, 0.35);
  --card-border: rgba(0, 0, 0, 0.16);
  --btn-border: rgba(0, 0, 0, 0.18);
  --accent-tint-bg: rgba(182, 117, 44, 0.12);
  /* Opaque variants of --accent-green-bg / --tab-bg, for floating or sticky
     UI (toasts, the sticky table header) that can't use a translucent fill —
     already solid here, so these are just aliases; loam's versions below are
     real flattened colors, since loam's own tokens are translucent rgba(). */
  --accent-green-solid: var(--accent-green-bg);
  --tab-bg-solid: var(--tab-bg);
  --table-header-bg: var(--accent-green-solid);
  --table-header-text: var(--accent-green-text);
}

html[data-theme="loam"] {
  --bg: #1C1A15;
  --header-bg: #242019;
  --header-text: #EDE8DC;
  --header-sub: #8C8874;
  --tab-bg: rgba(237, 232, 220, 0.08);
  --tab-text: #C4BEAB;
  --accent: #C99A3E;
  --accent-text: #1C1A15;
  --card-bg: #26241D;
  --thumb-bg: #3A3728;
  --text-primary: #EDE8DC;
  --text-secondary: #8C8874;
  --badge-bg: rgba(107, 143, 90, 0.22);
  --badge-text: #9CC084;
  --nav-bg: #242019;
  --nav-border: rgba(237, 232, 220, 0.08);
  --nav-inactive: #8C8874;
  --toggle-track-off: #3A3728;
  --toggle-track-on: #C99A3E;
  --danger: #D97A62;
  --danger-text: #1C1A15;
  --warning: #D98F4A;
  --warning-text: #1C1A15;
  --accent-green-bg: rgba(107, 143, 90, 0.22);
  --accent-green-text: #9CC084;
  --tab-border: rgba(237, 232, 220, 0.2);
  --accent-green-border: rgba(107, 143, 90, 0.45);
  --badge-border: rgba(107, 143, 90, 0.45);
  --card-border-strong: rgba(237, 232, 220, 0.35);
  --card-border: rgba(237, 232, 220, 0.16);
  --btn-border: rgba(237, 232, 220, 0.22);
  --accent-tint-bg: rgba(201, 154, 62, 0.16);
  /* --accent-green-bg / --tab-bg are translucent here (rgba) — fine for a
     small badge or a tab sitting flat on a card, but break anything that
     floats or stickies over other content (scrolled rows show through the
     sticky table header; page content shows through a toast). These are
     that same muted green / neutral flattened solid against --card-bg. */
  --accent-green-solid: #353C2A;
  --tab-bg-solid: #36342C;
  --table-header-bg: var(--accent-green-solid);
  --table-header-text: var(--accent-green-text);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text-primary);
  font-family: "Inter", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

body.has-bottom-nav {
  padding-bottom: 4.5rem;
}

body,
.card,
.header,
.bottom-nav,
.badge,
.toggle-track,
input,
textarea,
select,
button {
  transition: background-color 0.35s ease, color 0.35s ease, border-color 0.35s ease;
}

h1,
h2,
h3,
.display {
  font-family: "Fraunces", Georgia, serif;
  font-weight: 600;
  letter-spacing: -0.01em;
}

a {
  color: inherit;
  text-decoration: none;
}

/* A <button> that needs to read as plain inline text (same inherited color,
   no decoration) as the <a> rule above — e.g. a clickable cross parent with
   no real page of its own to link to, which opens its edit modal instead. */
.text-link-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: none;
}

[x-cloak] {
  display: none !important;
}

button,
input,
select,
textarea {
  font-family: inherit;
  font-size: inherit;
}

/* Header */

.header {
  background: var(--header-bg);
  color: var(--header-text);
  padding: 1.25rem 1.25rem 1.5rem;
}

.header .brand {
  font-family: "Fraunces", Georgia, serif;
  font-size: 1.4rem;
  font-weight: 600;
}

.header .sub {
  color: var(--header-sub);
  font-size: 0.85rem;
  margin-top: 0.15rem;
}

.header nav {
  display: none;
}

.header-logo {
  display: none;
}

@media (min-width: 900px) {
  .header {
    position: sticky;
    top: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1.25rem 2rem;
  }
  .header-brand-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
  }
  .header-logo {
    display: block;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: #f5f2ea;
    padding: 6px;
    object-fit: contain;
    box-sizing: border-box;
    border: 2px solid #000;
  }
  .header nav {
    display: flex;
    align-items: center;
    gap: 1.5rem;
  }
  .header nav a {
    color: var(--header-sub);
    font-size: 0.95rem;
  }
  .header nav a.active {
    color: var(--header-text);
    font-weight: 600;
  }
}

/* Site header / footer (public marketing pages) */

.site-header,
.site-footer {
  background: var(--header-bg);
  color: var(--header-text);
}

.site-header-inner,
.site-footer-inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 1.25rem 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.site-header .brand {
  font-family: "Fraunces", Georgia, serif;
  font-size: 1.3rem;
  font-weight: 600;
}

.site-header .sub {
  color: var(--header-sub);
  font-size: 0.85rem;
  margin-top: 0.15rem;
}

.site-footer {
  margin-top: 3rem;
}

.site-footer-inner {
  padding: 1.5rem;
  color: var(--header-sub);
  font-size: 0.85rem;
}

.site-footer-inner a {
  color: var(--header-text);
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 1.25rem;
}

.back-link svg {
  width: 16px;
  height: 16px;
}

.back-link:hover {
  color: var(--text-primary);
}

/* Layout */

.page {
  max-width: 1280px;
  margin: 0 auto;
  padding: 1.25rem;
  width: 100%;
  flex: 1 0 auto;
}

.desktop-only {
  display: none;
}

.icon-btn-desktop-only {
  display: none !important;
}

.btn-label-desktop-only {
  display: none !important;
}

.catalog-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}

@media (min-width: 900px) {
  .desktop-only {
    display: block;
  }
}

/* Catalog layout: sidebar filters (desktop) / sheet filters (mobile) + grid */

.catalog-layout {
  display: block;
}

/* Hidden by default (mobile uses the full-screen filter sheet, nothing to
   resize there) — shown only at the 900px+ breakpoint below, which must
   come after this rule in the file for the same reason .icon-btn-desktop-only
   documents: a later same-priority rule wins ties, so "hidden by default"
   has to be the earlier one. */
.catalog-filter-resize-handle {
  display: none;
}

.catalog-filters {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.catalog-filters-panel {
  background: var(--bg);
  width: 100%;
  max-width: 420px;
  max-height: 85vh;
  overflow-y: auto;
  border-radius: 20px 20px 0 0;
  padding: 1.5rem 1.25rem;
}

/* Mobile-only Clear/Apply pair, pinned to the bottom of the scrolling filter
   sheet via position: sticky (not fixed — it should stay within the sheet's
   own rounded bottom edge, not float relative to the whole viewport) so it
   stays reachable no matter how far the category list is scrolled or how
   many sections are expanded. background must be opaque and match the
   panel's own (--bg is a solid hex in both themes, not translucent, so this
   is safe — see the sticky-table-header/toast-transparency fixes elsewhere
   for what happens when it isn't). */
.filter-sheet-actions {
  display: flex;
  gap: 0.75rem;
  position: sticky;
  bottom: 0;
  margin-top: 1.25rem;
  padding-top: 0.75rem;
  background: var(--bg);
  border-top: 1px solid var(--nav-border);
}

.filter-sheet-actions .btn {
  flex: 1;
}

@media (min-width: 900px) {
  /* Pins the whole page body (title row + the two-column layout) directly
     to the viewport below the fixed site header, instead of trying to make
     it exactly tall enough to fit within the page's natural flow. That
     "exactly tall enough" version (a height: calc(100vh - Npx) on a normal
     flow element) is what this started as, and it silently failed: <body>
     is `min-height: 100vh` (not `height`), so if the subtracted pixel count
     is even slightly off from the real nav height, <body> just grows past
     100vh to fit and the whole page falls back to ordinary scrolling — the
     bounded panes never get a chance to matter. Taking the shell out of
     flow with position: fixed sidesteps that arithmetic entirely: a fixed
     element can't contribute to <body>'s height at all, so there's nothing
     left to get wrong. max-width/margin/padding are repeated here because
     fixed positioning breaks out of .page's own box, not just its flow. */
  .catalog-page-shell {
    position: fixed;
    top: 88px;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    max-width: 1280px;
    margin: 0 auto;
    padding: 1.25rem;
  }

  .catalog-header-row {
    flex-shrink: 0;
    position: relative;
    /* .catalog-layout (the filter/card grid below) is also position:
       relative and comes later in the DOM — without an explicit z-index
       here, it wins the stacking order and paints over the quick-search
       results dropdown despite that dropdown's own z-index:60, since that
       value is only meaningful relative to siblings at the same stacking
       level. */
    z-index: 2;
  }

  /* On mobile this row is toggled by the magnifying-glass icon
     (x-show="searchOpen"); on desktop there's no icon for it at all, so it
     needs to always render regardless of that Alpine state — same
     forced-visible override .catalog-filters already uses for the same
     reason. Positioned absolutely (not a plain margin-left) so its offset
     is measured from .catalog-header-row's own edge, lining it up with the
     photo grid's left edge (filter-sidebar width + the grid's own gap, the
     same two values .catalog-layout's grid-template-columns/gap use) —
     a margin-left here would instead stack on top of the "Catalog" title's
     own width, since margins on a flex item add to its normal-flow
     position rather than being measured from the container. Including the
     CSS variable means it re-aligns live as the filter sidebar is dragged
     wider/narrower. */
  .catalog-search-row {
    display: flex !important;
    max-width: 320px;
    position: absolute;
    top: 50%;
    left: calc(var(--catalog-filter-width, 240px) + 2.5rem);
    transform: translateY(-50%);
  }

  /* At desktop width there are three items in .catalog-header-row (title,
     search, view/filter icons) instead of mobile's two — plain
     justify-content: space-between would float the search box in the
     middle of the row. Pushing just the icon group to the far end via its
     own auto margin keeps title+search sitting together at the left
     instead, without touching space-between (still needed for mobile's
     2-item title/icons layout). */
  .catalog-header-actions {
    margin-left: auto;
  }

  .catalog-layout {
    display: grid;
    grid-template-columns: var(--catalog-filter-width, 240px) 1fr;
    gap: 2.5rem;
    align-items: stretch;
    flex: 1 1 auto;
    /* Anchor for .catalog-filter-resize-handle below — it can't anchor to
       .catalog-filters itself, since that element's overflow-y: auto forces
       overflow-x to also compute as auto (a real CSS Overflow spec quirk:
       one non-visible axis forces the other away from visible too), which
       clips anything positioned outside its own box, handle included. */
    position: relative;
    /* Without this, grid/flex items default to min-height: auto, which
       keeps them at least as tall as their content and defeats the whole
       point — they'd never actually shrink to fit the remaining space,
       so overflow-y: auto below would never have anything to do. */
    min-height: 0;
  }

  .catalog-filters {
    display: block !important;
    position: static;
    inset: auto;
    background: none;
    z-index: auto;
    /* The actual scroll boundary lives here, not on .catalog-filters-panel
       (its child) — a child resolving height: 100% off a grid-stretched
       parent is one more indirection than this needs, and it wasn't
       reliably producing a bounded box in practice. .catalog-filters is
       the real grid item .catalog-layout stretches, so give it the bounded
       height directly. */
    height: 100%;
    overflow-y: auto;
  }

  .catalog-filters-panel {
    background: none;
    width: auto;
    max-height: none;
    border-radius: 0;
    padding: 0 1.75rem 0 0;
  }

  /* Keeps "Clear filters" reachable no matter how far the category list is
     scrolled within .catalog-filters' own bounded scroll (see above) —
     sticky's containing block is that nearest scrolling ancestor, so top: 0
     pins it there regardless of the extra .catalog-filters-panel/form
     nesting in between. Needs its own opaque background (matching the page's
     own, since .catalog-filters-panel itself is transparent at this
     breakpoint) so scrolled category rows don't show through underneath it. */
  .catalog-clear-filters-sticky {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--bg);
    padding-bottom: 0.5rem;
  }

  .catalog-clear-filters-sticky .back-link {
    margin-bottom: 0;
  }

  .catalog-main {
    height: 100%;
    overflow-y: auto;
    /* Breathing room between the rightmost column of content and the
       scrollbar track, so it doesn't sit flush against the card/table edge. */
    padding-right: 1rem;
  }

  /* The table's own sticky header (see the Lot table section) is normally
     relative to the page's scroll — but inside this bounded, independently
     scrolling results pane, .catalog-main itself is the nearest scrolling
     ancestor, so the sticky offset needs to be relative to *its* top edge
     (0), not the page's. */
  .catalog-main .lot-table th {
    top: 0;
  }

  /* Positioned against .catalog-layout (a grid, no overflow of its own),
     not .catalog-filters — anchoring to .catalog-filters clipped the handle
     the moment its overflow-y: auto actually engaged (e.g. a category
     expanding past the visible height), since one non-visible overflow axis
     forces the other to compute as auto too, clipping anything positioned
     outside that box. Centered in the 2.5rem gap between the two grid
     columns via the same --catalog-filter-width variable the grid itself
     uses, rather than being its own grid column (avoids restructuring the
     2-column grid into 3 just for a drag strip). filter-resize.js drives the
     actual resize by setting --catalog-filter-width on .catalog-layout. */
  .catalog-filter-resize-handle {
    display: block;
    position: absolute;
    top: 0;
    left: calc(var(--catalog-filter-width, 240px) + 1.25rem - 5px);
    bottom: 0;
    width: 10px;
    cursor: col-resize;
    z-index: 5;
    touch-action: none;
  }

  .catalog-filter-resize-handle::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 4px;
    width: 2px;
    border-radius: 2px;
    background: var(--nav-border);
  }

  .catalog-filter-resize-handle:hover::after,
  .catalog-filter-resize-handle.is-dragging::after {
    background: var(--accent);
  }

  .mobile-only {
    display: none !important;
  }

  .icon-btn-desktop-only {
    display: inline-flex !important;
  }

  .btn-label-desktop-only {
    display: inline !important;
  }
}

/* Filter accordion */

.catalog-search {
  display: block;
  font-size: 0.9rem;
  margin-bottom: 1.25rem;
}

.quick-search-wrap {
  position: relative;
}

.quick-search-wrap .catalog-search {
  margin-bottom: 0;
}

.quick-search-results {
  position: absolute;
  top: calc(100% + 0.4rem);
  left: 0;
  right: 0;
  z-index: 60;
  background: var(--card-bg);
  border: 1px solid var(--nav-border);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  overflow: hidden;
  max-height: 320px;
  overflow-y: auto;
}

.actions-menu-wrap {
  position: relative;
  flex-shrink: 0;
}

.actions-menu {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 60;
  min-width: 210px;
  background: var(--card-bg);
  border: 1px solid var(--nav-border);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  padding: 0.4rem;
}

.actions-menu-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  padding: 0.65rem 0.75rem;
  border-radius: 10px;
  border: none;
  background: transparent;
  color: var(--text-primary);
  font-weight: 600;
  font-size: 0.9rem;
  text-align: left;
  cursor: pointer;
}

.actions-menu-row:hover {
  background: var(--tab-bg);
}

.actions-menu-row .icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--text-secondary);
}

.quick-search-results:empty {
  display: none;
}

.quick-search-result {
  display: block;
  padding: 0.65rem 0.9rem;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--nav-border);
}

.quick-search-result:last-child {
  border-bottom: none;
}

.quick-search-result:hover {
  background: var(--tab-bg);
}

/* A "pick this variety" result (used by the +Add lot / +Growout variety-search
   steps) is a <button>, not an <a> — reset the button defaults but keep
   everything else (padding, divider, hover) inherited from .quick-search-result
   above unchanged. */
button.quick-search-result {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--nav-border);
  font: inherit;
  cursor: pointer;
}

button.quick-search-result:last-child {
  border-bottom: none;
}

/* Used when .quick-search-results is the only content of a modal step (the
   variety-search steps below), rather than a small dropdown floating under an
   input alongside other page content — renders in normal flow instead of as
   a floating absolutely-positioned overlay. */
.quick-search-results-inline {
  position: static;
  box-shadow: none;
  border-color: var(--nav-border);
  margin-top: 0.75rem;
}

.quick-search-result-name {
  font-weight: 600;
  display: block;
}

.quick-search-result-meta {
  color: var(--text-secondary);
  font-size: 0.8rem;
}

.quick-search-empty {
  padding: 0.75rem 0.9rem;
  color: var(--text-secondary);
  font-size: 0.85rem;
}

.filter-section + .filter-section {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--nav-border);
}

.filter-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  font-family: "Fraunces", Georgia, serif;
  font-weight: 600;
  font-size: 1rem;
  color: var(--text-primary);
  cursor: pointer;
}

.chevron {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  transition: transform 0.2s ease;
}

[aria-expanded="false"] .chevron {
  transform: rotate(-90deg);
}

.filter-options {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  margin-top: 0.6rem;
}

.filter-option {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.25rem 0.5rem;
  border-radius: 8px;
  font-size: 0.9rem;
  color: var(--text-secondary);
  cursor: pointer;
}

.filter-option:hover {
  background: var(--tab-bg);
}

.filter-option input {
  accent-color: var(--accent);
  width: 15px;
  height: 15px;
  margin: 0;
  flex-shrink: 0;
}

.filter-option:has(input:checked) {
  color: var(--text-primary);
  font-weight: 600;
}

.filter-count {
  flex-shrink: 0;
  font-size: 0.8rem;
}

/* Cards / grid */

.variety-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
}

/* Same dashed-box "add" tile language as .photo-add-tile, adapted for the
   variety grid's own auto-sizing (no fixed aspect-ratio — grid's default
   align-items: stretch already matches this tile's height to whatever
   .variety-card thumb+body height the rest of the row settles on; this tile
   just centers its own content within that, however tall it ends up). */
.variety-add-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 160px;
  border: 1.5px dashed var(--nav-border);
  border-radius: 16px;
  background: none;
  color: var(--text-secondary);
  font: inherit;
  text-align: center;
  padding: 1.5rem 1rem;
  cursor: pointer;
}

.variety-add-tile:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.variety-add-tile svg {
  width: 28px;
  height: 28px;
}

.card {
  background: var(--card-bg);
  border-radius: 16px;
  overflow: hidden;
  display: block;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
  border: 1px solid var(--card-border);
}

a.card,
.settings-card {
  transition: border-color 0.15s ease, border-width 0.15s ease, box-shadow 0.15s ease;
}

a.card:hover,
.settings-card:hover {
  border-width: 2px;
  border-color: var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}

.grower-card {
  border: 1px solid var(--card-border-strong);
}

/* .card .body's own bottom padding is dropped here — when collapsed, the
   tinted .grower-card-header is the only thing in .body, so with no bottom
   padding left over its background now reaches the card's bottom edge
   too (clipped to the card's own rounded corners by .card's overflow:hidden)
   instead of leaving a blank strip of plain card background beneath it.
   The expanded detail area supplies its own bottom padding back (see
   .grower-card-details below) since it's no longer inherited from .body.
   Specificity bumped to .card.grower-card (three class selectors) rather
   than relying on source order against .card .body below — same equal-
   specificity/source-order tie CLAUDE.md already flags for .icon-btn-desktop-only,
   so this doesn't get silently re-broken by a future reorder of this file. */
.card.grower-card .body {
  padding-bottom: 0;
}

.grower-card-details {
  padding-bottom: 1rem;
}

/* Same full-bleed collapsed-header technique as .lot-card-header (extends
   the click hit area out to the card's own edges), plus a tinted background
   so the collapsed/header row reads as visually distinct from the dropped-
   down detail beneath it. --tab-bg is the same subtle section tint already
   used for tabs/filter rows elsewhere, reused here rather than inventing a
   new color. The card's own border-radius + overflow:hidden clips this to
   the card's rounded corners automatically — top corners always (header is
   always first), bottom corners too when collapsed (header is then also
   last, since the details div beneath is display:none from x-show).
   The bottom divider border only applies while expanded (.is-open, toggled
   via Alpine) — a collapsed card is meant to read as one solid color with
   no seam, not a color bar with a stray border floating above the card's
   own outer edge. */
.grower-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: calc(100% + 2rem);
  margin: -0.85rem -1rem 0;
  padding: 0.85rem 1rem;
  background: var(--tab-bg);
  border: none;
  text-align: left;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
}

.grower-card-header.is-open {
  border-bottom: 1px solid var(--card-border);
}

.variety-card {
  border: 1px solid var(--card-border-strong);
}

/* Catalog list view (toggle alternative to .variety-grid) — the ancestor
   .catalog-page-shell carries the .catalog-view-list class via Alpine
   (:class, not x-show) specifically so the toggle keeps working after
   #variety-list-container is replaced by an htmx filter/search swap — a
   freshly-swapped node can't be trusted to resolve an ancestor's Alpine
   scope reliably (see the active/retired lot-badge toggle for the same
   lesson), but a plain CSS selector off a stable ancestor always works.
   Both views are always rendered; only one is ever visible. The "hidden by
   default" rule must come before its .catalog-view-list override in the
   file for the same reason .icon-btn-desktop-only does (see that class). */
.variety-list-view {
  display: none;
}

.catalog-view-list .variety-grid {
  display: none;
}

.catalog-view-list .variety-list-view {
  display: block;
}

.catalog-list-group + .catalog-list-group {
  margin-top: 1.5rem;
}

/* Breeding's own Grid/List toggle — same mechanism as Catalog's above (both
   views always rendered, toggled by a class on the stable page-shell
   ancestor, "hidden by default" rule ordered before its override for the
   same reason .icon-btn-desktop-only needs that order). Scoped to
   .breeding-page-shell specifically, not the shared .planning-page-shell
   (Planning reuses that same shell class for its own unrelated layout, and
   also already has its own .lot-grid/.lot-table-wrap content that must stay
   untouched by this toggle). */
.cross-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.breeding-page-shell .lot-grid,
.breeding-page-shell .lot-table-wrap {
  display: none;
}

.breeding-view-list .cross-grid {
  display: none;
}

/* Re-states the same mobile-card/desktop-table split .lot-grid/.lot-table-wrap
   already have elsewhere, rather than trying to "unhide" them generically —
   a bare display: grid/block here at every width would fight (and, by
   specificity, beat) their own @media(min-width:900px) rules above, showing
   the desktop table on mobile too. */
.breeding-view-list.breeding-page-shell .lot-grid {
  display: grid;
}

@media (min-width: 900px) {
  .breeding-view-list.breeding-page-shell .lot-grid {
    display: none;
  }

  .breeding-view-list.breeding-page-shell .lot-table-wrap {
    display: block;
  }
}

/* Sticky per-category section header — position: sticky headers stacked in
   normal flow naturally "hand off" to the next one as each .catalog-list-group
   scrolls out, no JS needed. top: 0 is correct in both the mobile (plain
   page-level scroll, no competing sticky header) and desktop (bounded,
   independently-scrolling .catalog-main pane) cases — unlike .lot-table th,
   this class is never used outside the Catalog list, so there's no other
   context needing a different offset. --table-header-bg is already the
   guaranteed-opaque variant (aliased to the -solid tokens), which matters
   here since this floats over scrolled rows same as a sticky table header. */
.catalog-list-category-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: "Fraunces", Georgia, serif;
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  color: var(--table-header-text);
  background: var(--table-header-bg);
  padding: 0.55rem 0.85rem;
  border-radius: 10px;
  border: 1px solid var(--card-border-strong);
}

/* An outlined count, not the standard filled .badge — .badge's own tokens are
   tuned for a plain card/page background, and in loam are translucent (see
   the "translucent tokens on floating/sticky elements" lesson), so they don't
   reliably contrast against this header's own accent-green fill. currentColor
   inherits the header's already-theme-tuned text color instead, so contrast
   is guaranteed regardless of theme. */
.catalog-list-category-count {
  display: inline-block;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-size: 0.7rem;
  font-weight: 600;
  opacity: 0.85;
}

.variety-list-row {
  text-decoration: none;
  color: inherit;
}

.variety-list-crop {
  font-size: 0.85rem;
  flex-shrink: 0;
}

/* Planning's "Stage varieties" checklist — reuses .catalog-list-group/
   .catalog-list-category-header/.list-row wholesale (same grouped-list
   language as Catalog's list view) rather than inventing new chrome; only
   the checkbox-row specifics below are new. */
.planning-stage-row input[type="checkbox"] {
  accent-color: var(--accent);
  width: 15px;
  height: 15px;
  margin: 0;
  flex-shrink: 0;
}

.planning-stage-actions {
  margin-top: 1rem;
  padding: 0.85rem 0.5rem;
  background: var(--bg);
  border-top: 1px solid var(--nav-border);
}

@media (min-width: 900px) {
  /* Sticks to the bottom of the bounded, independently-scrolling
     #planning-content pane (see "Independently-scrolling layout") so
     "Stage selected" stays reachable no matter how long the checklist is.
     Left in normal flow on mobile instead of also being sticky there,
     since the fixed bottom nav already occupies that part of the screen. */
  .planning-stage-actions {
    position: sticky;
    bottom: 0;
  }
}

.hero-banner {
  position: relative;
  background-size: cover;
  background-position: center;
  border-radius: 20px;
  overflow: hidden;
  margin: 1.5rem 0 0.5rem;
  padding: 5rem 1.25rem;
  min-height: 60vh;
  display: flex;
  align-items: center;
}

.hero-banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(20, 20, 15, 0.6);
}

.hero-banner-content {
  position: relative;
  max-width: 820px;
  margin: 0 auto;
  text-align: center;
  color: #fff;
  width: 100%;
}

.hero-banner-content p {
  color: rgba(255, 255, 255, 0.85);
  /* Balances line lengths across the paragraph instead of the default
     ragged-last-line wrap — widely supported (Chrome/Edge/Firefox/Safari),
     a harmless no-op fallback to normal wrapping on anything older. */
  text-wrap: balance;
}

.hero-banner-content h2 {
  color: #fff;
}

/* Cards keep their normal (non-white) text regardless of the banner's white copy */
.hero-banner-content .card {
  color: var(--text-primary);
  text-align: left;
}

.login-card {
  max-width: 400px;
  margin: 2rem auto 0;
  padding: 1.75rem;
}

.org-card-carousel {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.org-card-track {
  flex: 1;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  text-align: center;
}

.org-card-row {
  display: inline-flex;
  gap: 1rem;
  padding: 0.25rem;
}

.org-card-row .card {
  scroll-snap-align: start;
  min-width: 180px;
  flex-shrink: 0;
}

.carousel-arrow {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: none;
  background: rgba(255, 255, 255, 0.9);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.carousel-arrow svg {
  width: 18px;
  height: 18px;
}

.card .thumb {
  position: relative;
  background: var(--thumb-bg);
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 0.8rem;
}

.card .thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.thumb-badge {
  position: absolute;
  bottom: 0.5rem;
  right: 0.5rem;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
}

.card .body {
  padding: 0.85rem 1rem 1rem;
}

.card .name {
  font-weight: 600;
  font-size: 1.05rem;
}

.card .meta {
  color: var(--text-secondary);
  font-size: 0.8rem;
  margin-top: 0.15rem;
}

.badge {
  display: inline-block;
  background: var(--badge-bg);
  color: var(--badge-text);
  border: 1px solid var(--badge-border);
  border-radius: 999px;
  padding: 0.2rem 0.6rem;
  font-size: 0.75rem;
  font-weight: 600;
  margin-top: 0.5rem;
}

.empty-state {
  color: var(--text-secondary);
  text-align: center;
  padding: 3rem 1rem;
}

/* Variety photo gallery */

.photo-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.6rem;
  margin-bottom: 1.5rem;
}

/* Fixed 2-column only on phone-sized screens, where the "add tile stretches
   when it's alone on its own row" trick below is calibrated to a known
   column count. Tablet and up get the same flexible column count as
   desktop so thumbnails don't balloon in size. */
@media (min-width: 600px) {
  .photo-grid {
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  }
}

.photo-sortable {
  display: contents;
}

.photo-thumb {
  position: relative;
  aspect-ratio: 1;
  border-radius: 12px;
  overflow: hidden;
  background: var(--thumb-bg);
  cursor: grab;
  /* manipulation (not none) — disables double-tap-zoom without blocking the
     browser's own scroll handling, which SortableJS's delay/delayOnTouchOnly
     config (base.html) needs to still work for a quick swipe-through. */
  touch-action: manipulation;
}

.photo-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  cursor: pointer;
  /* Without these, iOS's own long-press "Save Image" callout and the
     browser's native drag-image-out behavior both compete with SortableJS
     for the same touch-and-hold gesture and usually win, since neither is
     suppressed by default on an <img> — this is why drag-reorder worked on
     desktop (mouse) but not touch. */
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  user-select: none;
}

.photo-thumb-hero {
  box-shadow: 0 0 0 2px var(--accent);
}

/* The "which cross photos should come along to the new variety" picker on
   the promote modal — deliberately its own small grid, not a reuse of
   .photo-thumb (that one's styled/behaved around drag-reorder + a delete
   button, neither of which apply here). */
.photo-pick-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
  margin: 0.25rem 0 0.5rem;
}

.photo-pick-item {
  position: relative;
  aspect-ratio: 1;
  border-radius: 10px;
  overflow: hidden;
  background: var(--thumb-bg);
  cursor: pointer;
}

.photo-pick-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.photo-pick-item input {
  position: absolute;
  top: 0.35rem;
  left: 0.35rem;
  width: 1.1rem;
  height: 1.1rem;
  margin: 0;
}

.photo-pick-item:has(input:checked) {
  box-shadow: 0 0 0 2px var(--accent);
}

/* SortableJS's chosenClass (base.html) — applied once the press-and-hold
   delay elapses and the photo is actually armed to drag, not on first touch.
   Rules after .photo-thumb-hero so this wins if the hero photo itself is the
   one being picked up. */
.photo-thumb-armed {
  opacity: 0.55;
  box-shadow: 0 0 0 3px var(--accent);
  cursor: grabbing;
}

.photo-hero-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.photo-hero-badge svg {
  width: 12px;
  height: 12px;
}

.photo-delete {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: none;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.photo-delete svg {
  width: 14px;
  height: 14px;
}

.photo-add-tile {
  align-self: stretch;
  aspect-ratio: 1;
  border-radius: 12px;
  border: 1.5px dashed var(--nav-border);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  color: var(--text-secondary);
  font-size: 0.75rem;
  cursor: pointer;
}

.photo-add-tile-alone {
  grid-column: 1 / -1;
  align-self: start;
  aspect-ratio: auto;
  padding: 0.65rem 0;
  flex-direction: row;
  gap: 0.4rem;
}

.photo-add-tile svg {
  width: 20px;
  height: 20px;
}

@media (min-width: 600px) {
  .photo-add-tile-alone {
    grid-column: auto;
    align-self: stretch;
    aspect-ratio: 1;
    padding: 0;
    flex-direction: column;
    gap: 0.3rem;
  }

  .photo-add-tile svg {
    width: 22px;
    height: 22px;
  }
}

.sortable-ghost {
  opacity: 0.4;
}

.sortable-drag {
  cursor: grabbing;
}

/* Photo lightbox */

.lightbox-backdrop {
  align-items: center;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.96);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.lightbox-image {
  max-width: 100%;
  max-height: 85vh;
  border-radius: 12px;
  object-fit: contain;
}

.lightbox-close,
.lightbox-nav {
  position: absolute;
  border: none;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.lightbox-close {
  top: 1rem;
  right: 1rem;
  width: 40px;
  height: 40px;
}

.lightbox-close svg {
  width: 20px;
  height: 20px;
}

.lightbox-nav {
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
}

.lightbox-nav svg {
  width: 22px;
  height: 22px;
}

.lightbox-prev {
  left: 1rem;
}

.lightbox-next {
  right: 1rem;
}

/* Toasts */

.toast-container {
  position: fixed;
  bottom: 5.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: calc(100% - 2rem);
  max-width: 360px;
  align-items: center;
}

@media (min-width: 900px) {
  .toast-container {
    bottom: 1.5rem;
    left: auto;
    right: 1.5rem;
    transform: none;
    align-items: flex-end;
  }
}

.toast {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 0.75rem 1rem;
  border-radius: 12px;
  background: var(--card-bg);
  color: var(--text-primary);
  border: 1px solid var(--nav-border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  font-size: 0.9rem;
}

.toast-success {
  background: var(--accent-green-solid);
  color: var(--accent-green-text);
  border-color: var(--accent-green-border);
}

.toast-error {
  background: var(--danger);
  color: var(--danger-text);
  border-color: var(--danger);
}

.toast-warning {
  background: var(--warning);
  color: var(--warning-text);
  border-color: var(--warning);
}

.toast-info {
  background: var(--tab-bg-solid);
  color: var(--tab-text);
  border-color: var(--tab-border);
}

.toast button {
  display: flex;
  flex-shrink: 0;
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  opacity: 0.7;
}

.toast button:hover {
  opacity: 1;
}

.toast button svg {
  width: 14px;
  height: 14px;
}

/* Buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  border: 1px solid var(--btn-border);
  border-radius: 999px;
  padding: 0.65rem 1.25rem;
  font-size: 0.9rem;
  font-weight: 500;
  letter-spacing: 0;
  cursor: pointer;
  background: var(--accent);
  color: var(--accent-text);
  transition: filter 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease,
    background-color 0.35s ease, color 0.35s ease, border-color 0.35s ease;
}

.btn:hover:not(:disabled) {
  filter: brightness(0.92);
  transform: translateY(-1px);
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
}

.btn.secondary {
  background: var(--tab-bg);
  color: var(--tab-text);
}

.btn.danger {
  background: var(--danger);
  color: var(--danger-text);
}

.btn.green {
  background: var(--accent-green-bg);
  color: var(--accent-green-text);
  width: 34px;
  height: 34px;
  padding: 0;
}

.btn.warning {
  background: var(--warning);
  color: var(--warning-text);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  flex-shrink: 0;
  border: 1px solid var(--tab-border);
  border-radius: 999px;
  background: var(--tab-bg);
  color: var(--tab-text);
  cursor: pointer;
  transition: filter 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease,
    background-color 0.35s ease, color 0.35s ease, border-color 0.35s ease;
}

.icon-btn:hover:not(:disabled) {
  filter: brightness(0.92);
  transform: translateY(-1px);
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
}

.icon-btn svg {
  width: 20px;
  height: 20px;
}

.icon-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}

.icon-btn.small {
  width: 30px;
  height: 30px;
}

.icon-btn.small svg {
  width: 15px;
  height: 15px;
}

.icon-btn-info {
  width: 22px;
  height: 22px;
}

.icon-btn-info svg {
  width: 13px;
  height: 13px;
}

.icon-btn-box {
  border-radius: 10px;
}

/* A plain "go to" arrow — no border/background at rest, unlike .icon-btn,
   so it reads as a stylized inline navigation glyph rather than another
   button sitting next to real action buttons (e.g. "Promote to variety").
   Used for the variety<->cross cross-links. */
.nav-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  color: var(--accent);
  flex-shrink: 0;
  transition: transform 0.15s ease, background-color 0.15s ease;
}

.nav-arrow svg {
  width: 18px;
  height: 18px;
}

.nav-arrow:hover {
  background: var(--tab-bg);
  transform: translateX(3px);
}

.danger-zone {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--nav-border);
}

/* Lot cards */

.lot-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  /* Extend the clickable hit area out to the card's edges, covering the
     surrounding .card .body padding, so the whole collapsed card responds
     to a click/tap — not just the text or chevron. */
  width: calc(100% + 2rem);
  margin: -0.85rem -1rem 0;
  padding: 0.85rem 1rem 0;
  background: none;
  border: none;
  text-align: left;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
}

.badge-retired {
  background: var(--danger);
  color: var(--danger-text);
  border-color: var(--card-border);
  margin-top: 0;
}

.badge-active {
  background: var(--accent-green-bg);
  color: var(--accent-green-text);
  border-color: var(--card-border);
  margin-top: 0;
}

.badge-toggle {
  cursor: pointer;
  transition: filter 0.15s ease, opacity 0.15s ease;
}

.badge-toggle:hover {
  filter: brightness(0.94);
}

.badge-toggle.is-off {
  opacity: 0.5;
}

/* Active/retired lot visibility on the variety detail page — driven by a class
   toggled on the ancestor (Alpine, reliable since that element is never itself
   swapped) combined with plain static data-attributes on each lot card (not
   Alpine, so hiding doesn't depend on Alpine re-initializing a freshly
   htmx-swapped card's own x-show, which proved unreliable). */

.hide-active-lots [data-lot-status="active"] {
  display: none;
}

.hide-retired-lots [data-lot-status="retired"] {
  display: none;
}

[data-has-active][data-has-retired] {
  display: none;
}

.hide-active-lots.hide-retired-lots [data-has-active="true"][data-has-retired="true"],
.hide-active-lots [data-has-active="true"][data-has-retired="false"],
.hide-retired-lots [data-has-active="false"][data-has-retired="true"] {
  display: block;
}

/* Product cards — same collapsible card shell as lot cards, plus a leading
   print icon button, so the two sit at equal height side by side. */

.product-card-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: calc(100% + 2rem);
  margin: -0.85rem -1rem 0;
  padding: 0.85rem 1rem 0;
  background: none;
  border: none;
  text-align: left;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
}

.product-card-header-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.lot-section {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--nav-border);
}

.list-row {
  display: block;
  font-size: 0.9rem;
  cursor: pointer;
  padding: 0.65rem 0.75rem;
}

.note-body {
  min-width: 0;
}

.note-body p {
  margin: 0;
}

.list-row:hover {
  background: var(--tab-bg);
}

.list-row + .list-row {
  border-top: 1px solid var(--nav-border);
}

.taxonomy-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.65rem 0;
}

.taxonomy-row + .taxonomy-row {
  border-top: 1px solid var(--nav-border);
}

.taxonomy-row-indent {
  padding-left: 1rem;
}

.taxonomy-row-inactive {
  opacity: 0.55;
}

.taxonomy-row-clickable {
  cursor: pointer;
  border-radius: 8px;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  margin: 0 -0.5rem;
}

.taxonomy-row-clickable:hover {
  background: var(--tab-bg);
}

.taxonomy-category + .taxonomy-category {
  border-top: 1px solid var(--nav-border);
}

.taxonomy-crops {
  padding-bottom: 0.5rem;
}

.filter-subsection + .filter-subsection {
  margin-top: 0.5rem;
}

.filter-subheader {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: none;
  padding: 0.3rem 0.5rem;
  margin-top: 0.4rem;
  font-family: "Fraunces", Georgia, serif;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
}

.filter-category-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.05rem 0.5rem 0.05rem 0.1rem;
}

.filter-category-row .filter-subheader {
  margin-top: 0;
  padding: 0.15rem 0;
}

.lot-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.6rem;
}

@media (min-width: 900px) {
  .lot-grid {
    display: none;
  }
}

/* Lot table — styled to match the Catalog list view's language: the header
   is its own bordered, rounded box (see .lot-table th below), and the table
   body itself carries no border/background/radius of its own at all, just
   plain row-divider lines (.lot-table td's border-bottom) sitting on the
   ordinary page background — same look as .list-row's dividers. Swaps in
   for .lot-grid at the same 900px breakpoint the rest of this page's layout
   already uses. This used to be a bordered, rounded card with an
   overflow: clip / corner-masking dance to keep the sticky header's
   rounded corners aligned with the table's own — none of that is needed
   anymore now that the table has no border/radius/overflow of its own to
   misalign against; see .lot-table th's own comment for what replaced it. */

.lot-table-wrap {
  overflow: visible;
}

.lot-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}

/* Styled to match the Catalog list view's own category header
   (.catalog-list-category-header) — its own fully-bordered, 4-corner-rounded
   box that floats (position: sticky) above the plain, borderless rows below,
   rather than a flat table header sharing one rounded box with the table
   body. Since the header now carries its own complete border/radius
   directly (not clipped via a parent's overflow), the corner-mask ::before
   hack this used to need is gone too — that only existed to patch a
   mismatch between the *table's* rounded-corner clip region (tied to its
   scrolled position) and the header's fixed sticky position; with no
   table-level border/radius/overflow left to misalign against, there's
   nothing left for it to fix. */
.lot-table th {
  text-align: left;
  font-family: "Fraunces", Georgia, serif;
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--table-header-text);
  background: var(--table-header-bg);
  padding: 0.65rem 1rem;
  border-top: 1px solid var(--card-border-strong);
  border-bottom: 1px solid var(--card-border-strong);
  white-space: nowrap;
  position: sticky;
  top: 88px;
  z-index: 10;
}

.lot-table th:first-child {
  border-left: 1px solid var(--card-border-strong);
  border-top-left-radius: 10px;
  border-bottom-left-radius: 10px;
}

.lot-table th:last-child {
  border-right: 1px solid var(--card-border-strong);
  border-top-right-radius: 10px;
  border-bottom-right-radius: 10px;
}

.lot-table td {
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--nav-border);
  font-size: 0.9rem;
  vertical-align: middle;
}

.lot-table th.lot-table-status,
.lot-table td.lot-table-status {
  text-align: center;
}

.lot-table tbody tr {
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.lot-table tbody tr:hover {
  background: var(--tab-bg);
}

.lot-table tbody tr:hover .lot-table-code {
  color: var(--accent);
}

.lot-table tbody tr:last-child td {
  border-bottom: none;
}

.lot-table-code {
  font-family: "Fraunces", Georgia, serif;
  color: var(--text-primary);
}

.lot-table-crop {
  color: var(--text-secondary);
  font-size: 0.8rem;
}

.lot-row {
  display: flex;
  flex-direction: column;
  color: inherit;
  background: var(--card-bg);
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
  border: 1px solid var(--card-border-strong);
  overflow: hidden;
}

.lot-row:hover {
  border-width: 2px;
  border-color: var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}

.lot-row-header {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  background: var(--accent-tint-bg);
  padding: 0.55rem 1rem;
  border-bottom: 1px solid var(--card-border-strong);
}

.lot-row-title {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.lot-row-variety {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
  color: var(--text-primary);
}

.lot-row-code-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex-shrink: 0;
  gap: 0.2rem;
}

.lot-row-code {
  font-family: "Fraunces", Georgia, serif;
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--accent);
}

.lot-row-crop {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.lot-row-body {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1rem;
}

.lot-row-info {
  min-width: 0;
  flex: 1;
}

@media (max-width: 899px) {
  .lot-row-year-line {
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
    min-width: 0;
  }

  .lot-row-grower {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1;
  }

  .lot-row-stats-line {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
  }

  .lot-row-stats-line .lot-row-sep {
    display: none;
  }
}

.lot-row svg {
  width: 18px;
  height: 18px;
  color: var(--text-secondary);
  transition: transform 0.15s ease;
}

.lot-row:hover svg {
  transform: translateX(2px);
  color: var(--accent);
}

/* Settings — each section is a clickable card opening a modal (see
   accounts/settings.html), rather than the sections themselves being shown
   inline. Keeps this page from growing unbounded as more settings sections
   get added over time. */

.settings-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

@media (min-width: 700px) {
  .settings-cards {
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  }
}

.settings-card {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.settings-footer {
  max-width: 420px;
  text-align: center;
}

@media (min-width: 700px) {
  .settings-footer {
    text-align: left;
  }
}

/* Variety detail: Lots / Products side-by-side on wide screens */

.variety-columns {
  display: block;
}

@media (min-width: 900px) {
  .variety-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2.5rem;
    align-items: start;
  }
}

/* Variety detail header: search bar spans the top on mobile, moves inline
   with the edit button on wide screens. */

.variety-header {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "search"
    "info";
  gap: 0.75rem 1rem;
  align-items: start;
  margin-bottom: 1rem;
}

.variety-header-info {
  grid-area: info;
}

.variety-header-search {
  grid-area: search;
  width: 100%;
}

@media (min-width: 600px) {
  .variety-header {
    grid-template-columns: 1fr auto;
    grid-template-areas: "info search";
  }

  .variety-header-search {
    width: clamp(140px, 30vw, 220px);
  }
}

/* Notes + usage button: stacked on mobile, side by side on wide screens. */

.variety-notes-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

@media (min-width: 600px) {
  .variety-notes-row {
    flex-direction: row;
    align-items: flex-start;
    gap: 1rem;
  }
}

/* Planning header: title row, then action buttons on their own line on
   mobile — side by side on wide screens, same breakpoint the table/card
   swap on this page already uses. */

.planning-header {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

@media (min-width: 900px) {
  .planning-header {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }

  /* Same fixed-shell approach as .catalog-page-shell (see that block for
     the full reasoning): pins the header/filter row/suggestions in place
     below the nav and makes #planning-content the one scrolling pane, with
     its own table header locked at the top of *that* pane instead of the
     page's scroll. */
  .planning-page-shell {
    position: fixed;
    top: 88px;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    max-width: 1280px;
    margin: 0 auto;
    padding: 1.25rem;
  }

  .planning-header,
  .planning-filters-row {
    flex-shrink: 0;
  }

  #planning-content {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding-right: 1rem;
  }

  #planning-content .lot-table th {
    top: 0;
  }
}

@media (max-width: 899px) {
  #variety-products {
    margin-top: 2rem;
  }
}

.settings-header-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin-bottom: 0.25rem;
}

.taxonomy-name-group {
  min-width: 0;
  flex: 1 1 auto;
}

.taxonomy-actions {
  flex-shrink: 0;
}

.truncate {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.badge-count-desktop {
  display: none;
}

.badge-count-mobile {
  display: inline;
}

@media (min-width: 900px) {
  .badge-count-desktop {
    display: inline;
  }

  .badge-count-mobile {
    display: none;
  }
}

@media (max-width: 399px) {
  .taxonomy-actions {
    gap: 0.1rem !important;
  }

  .icon-btn.small {
    width: 26px;
    height: 26px;
  }
}

.stock-seed-usage {
  margin-top: 1.25rem;
  padding: 0.85rem;
  border: 1px solid var(--nav-border);
  border-radius: 12px;
}

.usage-summary {
  padding: 0.85rem;
  border: 1px solid var(--nav-border);
  border-radius: 12px;
}

.stock-seed-usage-header {
  background: var(--accent-green-bg);
  color: var(--accent-green-text);
  margin: -0.85rem -0.85rem 0.6rem;
  padding: 0.5rem 0.85rem;
  border-radius: 12px 12px 0 0;
}

/* Forms */

form .field {
  margin-bottom: 1rem;
}

form label,
.field label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
  color: var(--text-secondary);
}

form input,
form textarea,
form select,
.datepicker-display,
.catalog-search {
  width: 100%;
  padding: 0.6rem 0.75rem;
  border-radius: 10px;
  border: 1px solid var(--nav-border);
  background: var(--card-bg);
  color: var(--text-primary);
  font-size: 1rem;
  font-family: inherit;
}

form input:focus,
form textarea:focus,
form select:focus,
.datepicker-display:focus-within,
.catalog-search:focus {
  outline: none;
  border-color: var(--accent);
}

form select:disabled,
form input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Password show/hide toggle (static/js/password-toggle.js) */

.password-field-wrap {
  position: relative;
}

.password-field-wrap input {
  padding-right: 2.5rem;
}

.password-toggle-btn {
  position: absolute;
  top: 50%;
  right: 0.6rem;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  color: var(--text-secondary);
  cursor: pointer;
}

.password-toggle-btn svg {
  width: 18px;
  height: 18px;
}

.password-toggle-btn:hover {
  color: var(--text-primary);
}

form input[type="checkbox"] {
  width: 16px;
  height: 16px;
  padding: 0;
  border-radius: 4px;
  accent-color: var(--accent);
  flex-shrink: 0;
}

form .errorlist {
  color: var(--danger);
  font-weight: 600;
  font-size: 0.8rem;
  list-style: none;
  padding: 0;
  margin: 0.25rem 0 0.75rem;
}

.no-spinner {
  -moz-appearance: textfield;
}

.no-spinner::-webkit-outer-spin-button,
.no-spinner::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Date picker */

.datepicker-wrapper {
  position: relative;
}

.datepicker-display {
  text-align: left;
  cursor: pointer;
}

.datepicker-display.is-placeholder {
  color: var(--text-secondary);
}

.datepicker-popover {
  position: fixed;
  z-index: 100;
  width: 280px;
  background: var(--card-bg);
  border: 1px solid var(--nav-border);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  padding: 0.9rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.datepicker-popover.position-below {
  transform: translateY(-8px);
}

.datepicker-popover.position-above {
  transform: translateY(8px);
}

.datepicker-popover.open {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.datepicker-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.7rem;
}

.datepicker-label {
  font-family: "Fraunces", Georgia, serif;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text-primary);
}

.datepicker-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 999px;
  background: none;
  color: var(--text-primary);
  cursor: pointer;
}

.datepicker-nav svg {
  width: 16px;
  height: 16px;
}

.datepicker-nav:hover:not(:disabled) {
  background: var(--tab-bg);
}

.datepicker-nav:disabled {
  color: var(--nav-inactive);
  cursor: not-allowed;
}

.datepicker-weekdays,
.datepicker-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

.datepicker-weekdays {
  color: var(--text-secondary);
  font-size: 0.7rem;
  font-weight: 600;
  text-align: center;
  margin-bottom: 0.3rem;
}

.datepicker-grid {
  gap: 2px;
}

.datepicker-day {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 999px;
  background: none;
  color: var(--text-primary);
  font-size: 0.85rem;
  font-family: inherit;
  cursor: pointer;
}

.datepicker-day.filler {
  cursor: default;
}

.datepicker-day:hover:not(:disabled):not(.filler) {
  background: var(--tab-bg);
}

.datepicker-day.today {
  box-shadow: inset 0 0 0 1px var(--accent);
}

.datepicker-day.selected {
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}

.datepicker-day.disabled {
  color: var(--nav-inactive);
  cursor: not-allowed;
}

/* Bottom nav (mobile) */

.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--nav-bg);
  border-top: 1px solid var(--nav-border);
  display: flex;
  z-index: 40;
}

.bottom-nav a,
.bottom-nav button {
  flex: 1;
  text-align: center;
  padding: 0.6rem 0 0.75rem;
  color: var(--nav-inactive);
  font-size: 0.7rem;
  font-weight: 600;
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
}

.bottom-nav a.active,
.bottom-nav button.active {
  color: var(--accent);
}

.bottom-nav .icon {
  width: 22px;
  height: 22px;
  display: block;
  margin: 0 auto 0.2rem;
}

@media (min-width: 900px) {
  .bottom-nav {
    display: none;
  }
  body {
    padding-bottom: 0;
  }
}

/* "More" bottom sheet (mobile only — desktop has room for plain nav links) */

.more-sheet {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.more-sheet-panel {
  background: var(--bg);
  width: 100%;
  max-width: 420px;
  border-radius: 20px 20px 0 0;
  padding: 1.25rem;
  padding-bottom: calc(1.25rem + env(safe-area-inset-bottom));
}

.more-sheet-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.75rem 0.25rem;
  color: var(--text-primary);
  font-weight: 600;
}

.more-sheet-row + .more-sheet-row {
  border-top: 1px solid var(--nav-border);
}

.more-sheet-row .icon {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  color: var(--text-secondary);
}

/* Logout is a <button> (inside a POST <form>, not a plain link), so it needs
   the native-button reset — same idea as button.quick-search-result. */
button.more-sheet-row {
  width: 100%;
  background: none;
  border: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

@media (min-width: 900px) {
  .more-sheet {
    display: none !important;
  }
}

/* Modal / sheet */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 50;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

@media (min-width: 900px) {
  .modal-backdrop {
    align-items: center;
  }
}

.modal-sheet {
  background: var(--bg);
  width: 100%;
  max-width: 520px;
  border-radius: 20px 20px 0 0;
  padding: 1.5rem;
  max-height: 90vh;
  overflow-y: auto;
}

@media (min-width: 900px) {
  .modal-sheet {
    border-radius: 20px;
  }
}

.modal-sheet-wide {
  max-width: 640px;
}

.chart-toggle {
  display: inline-flex;
  gap: 0.3rem;
  background: var(--tab-bg);
  border-radius: 999px;
  padding: 0.25rem;
}

.chart-toggle-btn {
  border: none;
  border-radius: 999px;
  padding: 0.35rem 0.9rem;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--tab-text);
  background: transparent;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.chart-toggle-btn.active {
  background: var(--accent);
  color: var(--accent-text);
}

/* Toggle switch */

.toggle {
  position: relative;
  display: inline-block;
  width: 46px;
  height: 26px;
}

.toggle-track {
  position: absolute;
  inset: 0;
  background: var(--toggle-track-off);
  border-radius: 999px;
  cursor: pointer;
}

.toggle-track.on {
  background: var(--toggle-track-on);
}

.toggle-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.2s ease;
}

.toggle-track.on .toggle-thumb {
  transform: translateX(20px);
}

.toggle-track.disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* Utility */

.text-secondary {
  color: var(--text-secondary);
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.space-between {
  justify-content: space-between;
}
