/* =====================================================================
   Revolutionary Farm Management — Marketplace theme
   Clean agricultural style: cream backgrounds, deep green accents,
   earth tones, readable tables, simple cards.
   ===================================================================== */

:root {
    /* Palette tuned to the RFM logo: olive-green lettering/fields,
       barn rust-red, wheat gold, cream arch. */
    --green-900: #39441f;
    --green-700: #55622e;
    --green-600: #68763a;
    --green-100: #eaecdb;
    --cream: #faf8ee;
    --cream-dark: #f2edda;
    --earth-700: #8e3d1e;   /* barn rust — accent */
    --gold-600: #c8952c;    /* wheat gold */
    --gold-100: #f6ecd0;
    --ink: #24281f;
    --ink-soft: #5c6355;
    --line: #ddd8c8;
    --danger: #a33c2f;
    --danger-bg: #f7e4e1;
    --info: #2f5d7c;
    --info-bg: #e2edf4;
    --radius: 10px;
    --shadow: 0 1px 3px rgba(36, 40, 31, 0.08), 0 4px 14px rgba(36, 40, 31, 0.05);

    /* Height of the header row. Used to pad a floating hero clear of the chrome —
       see "Header → hero transition" below. Re-measure if the header gains a row. */
    --header-h: 74px;
}

* { box-sizing: border-box; }

html { font-size: 16px; }

body {
    margin: 0;
    font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
    background: var(--cream);
    color: var(--ink);
    line-height: 1.55;
}

h1 { font-size: 1.6rem; margin: 0 0 .5rem; color: var(--green-900); }
h1:focus { outline: none; }
h2 { font-size: 1.25rem; margin: 1.5rem 0 .5rem; color: var(--green-900); }
h3 { font-size: 1.05rem; margin: 1rem 0 .4rem; color: var(--green-900); }

a { color: var(--green-700); }
a:hover { color: var(--green-600); }

.muted { color: var(--ink-soft); font-size: .95em; }

/* ---------- Shell ---------- */
/* overflow-x is required by the .bleed utility below: the viewport-width trick can
   otherwise expose a horizontal scrollbar equal to the vertical scrollbar's width.

   CLIP, not hidden, and the distinction is not cosmetic. `overflow-x: hidden` forces the other
   axis to compute to `auto`, which makes this box a scroll container — and a position:sticky
   descendant then sticks to THIS box's scrollport, which never scrolls, so it never sticks at
   all. That silently disabled the sticky rails on the retailer offer form and the farmer
   product detail page. `overflow: clip` clips without creating a scroll container, so the
   viewport stays the scrollport and sticky works. The `hidden` line is kept first as the
   fallback for a browser that does not know `clip`. */
.page-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
    overflow-x: clip;
}

/* Break an element out of .site-main's 1200px column to the full viewport width, while
   .band-inner / .hero-inner realign the content back onto the same column. */
.bleed {
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    border-radius: 0;
}

/* =====================================================================
   Header → hero transition

   Two treatments, chosen automatically by whether the page opens with a
   full-bleed hero:

   A. No hero (Contact, request forms, account pages, farmer/retailer inner
      pages): solid header closed with a gold rule, so the chrome ends on a
      deliberate edge instead of an arbitrary one.

   B. Opens with a full-bleed hero (Home, For Suppliers, How It Works): the
      chrome floats on the photograph and there is no seam at all.

   Selection uses :has(). Browsers without it fall back to treatment A
   everywhere, which is correct-looking rather than broken.
   ===================================================================== */
.site-header {
    background: var(--green-900);
    color: #fff;
    border-bottom: 2px solid var(--gold-600);   /* treatment A */
}

.page-shell:has(.site-main > .hero.bleed:first-child) { position: relative; }

.page-shell:has(.site-main > .hero.bleed:first-child) .site-header {
    position: absolute;
    inset: 0 0 auto;
    z-index: 5;
    border-bottom: none;
    /* Padding-bottom exists purely to give the gradient somewhere to fade out
       below the nav, so the floating chrome has no hard bottom edge. The box is
       absolutely positioned, so this costs no layout space. */
    padding-bottom: 2.5rem;
    /* NOTE: strongest at the TOP and still ~.74 where the nav sits (~74% down).
       An earlier draft faded to .15 at the bottom, which put the nav links at
       2.95:1 over the home hero's bright sky — measured, not guessed. Keep the
       alpha at the nav's depth above ~.70 for any hero photograph. */
    background: linear-gradient(to bottom,
        rgba(26, 32, 15, .88) 0%,
        rgba(26, 32, 15, .80) 55%,
        rgba(26, 32, 15, .74) 74%,
        rgba(26, 32, 15, .34) 90%,
        rgba(26, 32, 15, 0) 100%);
}

.page-shell:has(.site-main > .hero.bleed:first-child) .site-main { padding-top: 0; }

/* The hero carries the space the header would have occupied. */
.page-shell:has(.site-main > .hero.bleed:first-child) .site-main > .hero.bleed:first-child {
    padding-top: calc(var(--header-h) + 3.25rem);
}

.header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .75rem 1.25rem;
    max-width: 1200px;
    margin: 0 auto;
    width: 100%;
}

.brand { display: flex; align-items: center; gap: .6rem; text-decoration: none; color: #fff; }
.brand-mark { font-size: 1.6rem; }

/* Full-color logo sits on a light chip so it reads on the dark header. */
.brand-logo-chip {
    display: inline-flex;
    align-items: center;
    background: #fff;
    border-radius: 8px;
    padding: 3px 7px;
}

.brand-logo-chip img { height: 36px; display: block; }

.auth-logo { display: block; max-height: 64px; max-width: 240px; margin: 0 auto 1rem; }
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-name { font-weight: 600; font-size: 1.02rem; }
.brand-tag { font-size: .78rem; color: #dfe3cd; letter-spacing: .06em; text-transform: uppercase; }

.header-user { display: flex; align-items: center; gap: .75rem; }
.header-user .btn { white-space: nowrap; }
.user-name { font-size: .9rem; color: #e3e6d3; }
.logout-form { margin: 0; }

/* The nav sits in the brand row rather than in a band of its own: one row of
   chrome reads lighter over a photograph, and the primary action lives beside
   Sign In where people look for it. */
.site-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .1rem;
    flex: 1 1 auto;
    margin-left: 1rem;
}

.site-nav a {
    color: #e6efe9;
    text-decoration: none;
    padding: .6rem .7rem;
    font-size: .93rem;
    border-bottom: 3px solid transparent;
    white-space: nowrap;
}

.site-nav a:hover { background: rgba(255, 255, 255, .08); color: #fff; }
.site-nav a.active { border-bottom-color: var(--gold-600); color: #fff; font-weight: 600; }
.site-nav a.nav-admin-link { color: var(--gold-100); font-weight: 600; }

/* Pushes the primary action away from the browse links so it reads as an action
   rather than a fifth destination. */
.nav-cta {
    background: var(--gold-600);
    color: #2b2205;
    font-weight: 700;
    padding: .5rem .95rem;
    border-radius: 8px;
    border: none;
    text-decoration: none;
    white-space: nowrap;
    font-size: .92rem;
}

.nav-cta:hover { background: #dda93c; color: #2b2205; }

/* ---------- Hamburger menu (narrow screens) ---------- */
.nav-menu { display: none; }

.nav-menu-trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 8px;
    color: #fff;
    cursor: pointer;
    list-style: none;
}

.nav-menu-trigger::-webkit-details-marker { display: none; }
.nav-menu-trigger:hover { background: rgba(255, 255, 255, .12); }
.nav-menu-trigger:focus-visible { outline: 2px solid var(--gold-600); outline-offset: 2px; }
.nav-menu-trigger svg { width: 1.5rem; height: 1.5rem; }

.nav-menu-bar {
    transform-box: fill-box;
    transform-origin: center;
    transition: transform .2s ease, opacity .15s ease;
}

.nav-menu[open] .nav-menu-bar-top { transform: translateY(5px) rotate(45deg); }
.nav-menu[open] .nav-menu-bar-mid { opacity: 0; }
.nav-menu[open] .nav-menu-bar-bot { transform: translateY(-5px) rotate(-45deg); }

.nav-menu-backdrop {
    position: fixed;
    inset: var(--header-h) 0 0 0;
    z-index: 30;
    background: rgba(26, 32, 15, .35);
}

.nav-menu-panel {
    position: fixed;
    top: var(--header-h);
    right: 0;
    bottom: 0;
    z-index: 40;
    width: min(20rem, 86vw);
    display: flex;
    flex-direction: column;
    padding: .35rem .85rem .85rem;
    background: var(--green-900);
    border-left: 2px solid var(--gold-600);
    box-shadow: -12px 0 24px rgba(26, 32, 15, .3);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.nav-menu-panel,
.nav-menu-backdrop { transition: opacity .25s ease, transform .25s ease; }

.nav-menu::details-content { transition: content-visibility .25s allow-discrete; }

.nav-menu:not([open]) .nav-menu-panel { transform: translateX(100%); }
.nav-menu:not([open]) .nav-menu-backdrop { opacity: 0; pointer-events: none; }

@starting-style {
    .nav-menu[open] .nav-menu-panel { transform: translateX(100%); }
    .nav-menu[open] .nav-menu-backdrop { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .nav-menu-bar,
    .nav-menu-panel,
    .nav-menu-backdrop,
    .nav-menu::details-content { transition: none; }
}

.nav-menu-panel a {
    color: #e6efe9;
    text-decoration: none;
    font-size: 1rem;
    padding: .8rem .75rem;
    border-left: 3px solid transparent;
}


.nav-menu-panel a + a { border-top: 1px solid rgba(255, 255, 255, .08); }

.nav-menu-panel a:hover { background: rgba(255, 255, 255, .08); color: #fff; }
.nav-menu-panel a.active { border-left-color: var(--gold-600); color: #fff; font-weight: 600; }
.nav-menu-panel a.nav-admin-link { color: var(--gold-100); font-weight: 600; }

/* Only needed once Sign In has left the header row (560px block). */
.nav-menu-panel a.nav-menu-signin { display: none; font-weight: 600; }

.site-main {
    flex: 1;
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 1.5rem 1.25rem 3rem;
}

/* Footer carries the same field texture as the hero at low contrast, so the page
   closes on brand rather than trailing off into a grey strip. */
.site-footer {
    background-color: var(--green-900);
    background-image:
        repeating-linear-gradient(97deg, rgba(255, 255, 255, .035) 0 2px, transparent 2px 32px),
        linear-gradient(160deg, #313b1b 0%, #242c13 100%);
    color: #cfd6bd;
    font-size: .9rem;
    margin-top: 3rem;
}

.footer-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 2.25rem 1.25rem 1.75rem;
    display: grid;
    grid-template-columns: minmax(240px, 2fr) repeat(3, minmax(140px, 1fr));
    gap: 1.75rem 2rem;
}

.footer-brand { min-width: 0; }
.footer-name { font-weight: 700; color: #fff; font-size: 1.02rem; display: block; margin-bottom: .5rem; }
.footer-blurb { margin: 0; max-width: 44ch; color: #b9c2a4; font-size: .88rem; }

.footer-links { display: flex; flex-direction: column; gap: .45rem; }

.footer-links h2 {
    font-size: .76rem;
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--gold-100);
    margin: 0 0 .25rem;
}

.footer-links a { color: #dfe5d0; text-decoration: none; font-size: .9rem; }
.footer-links a:hover { color: #fff; text-decoration: underline; text-underline-offset: 3px; }

.footer-legal {
    border-top: 1px solid rgba(255, 255, 255, .12);
}

.footer-legal p {
    max-width: 1200px;
    margin: 0 auto;
    padding: .9rem 1.25rem 1.1rem;
    font-size: .82rem;
    color: #a8b294;
}

/* ---------- Cards ---------- */
.card {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.1rem 1.25rem;
    margin-bottom: 1rem;
}

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1rem;
    margin: 1rem 0;
}

.stat-card {
    background: #fff;
    border: 1px solid var(--line);
    border-left: 5px solid var(--green-600);
    border-radius: var(--radius);
    padding: .9rem 1.1rem;
    box-shadow: var(--shadow);
}

.stat-card .stat-number { font-size: 1.9rem; font-weight: 700; color: var(--green-900); }
.stat-card .stat-label { color: var(--ink-soft); font-size: .9rem; }
.stat-card.stat-attention { border-left-color: var(--gold-600); }
.stat-card a { text-decoration: none; }

/* ---------- Product/offer cards (image-first, classic ecommerce layout) ---------- */
.offer-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

.offer-card {
    position: relative;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: box-shadow .15s ease;
}

.offer-card:hover {
    box-shadow: 0 2px 6px rgba(36, 40, 31, .12), 0 8px 22px rgba(36, 40, 31, .1);
}

/* Category flag over the image, like marketplace badge chips. */
.offer-flag {
    position: absolute;
    top: .6rem;
    left: .6rem;
    z-index: 1;
    background: var(--green-100);
    color: var(--green-700);
    font-size: .75rem;
    font-weight: 700;
    padding: .18rem .55rem;
    border-radius: 4px;
}

/* A 4:3 frame filled edge to edge, not a padded box with the art floating inside it.
   The category placeholders are 4:3 scenes drawn to fill, so they land exactly; a real
   product photo of another shape is cover-cropped, which is the trade the mockup makes.
   aspect-ratio rather than a fixed height so the frame tracks the column width and every
   card in a row stays the same shape. */
.offer-image {
    display: block;
    aspect-ratio: 4 / 3;
    background: var(--cream);
    border-bottom: 1px solid #efede4;
    overflow: hidden;
}

.offer-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.offer-body {
    padding: .8rem 1rem .4rem;
    display: flex;
    flex-direction: column;
    gap: .3rem;
    flex: 1;
}

.offer-title { margin: 0; font-size: 1rem; line-height: 1.3; }
.offer-title a { color: var(--ink); text-decoration: none; }
.offer-title a:hover { color: var(--green-700); text-decoration: underline; }

/* Reserved for two lines so the price sits at the same height on every card in a row.
   A one-line and a two-line description otherwise stagger the whole grid, and price is the
   thing people scan across. Longer descriptions are truncated in OfferCard, so two is the
   most that can ever appear. */
.offer-desc { margin: 0; font-size: .85rem; line-height: 1.4; min-height: 2.4em; }

/* Price with superscript cents, ecommerce style. */
.offer-price-row {
    display: flex;
    align-items: baseline;
    gap: .1rem;
    margin-top: .35rem;
    color: var(--ink);
}

.price-currency { font-size: .95rem; font-weight: 700; align-self: flex-start; margin-top: .3rem; }
.price-dollars { font-size: 1.75rem; font-weight: 800; line-height: 1; }
.price-cents { font-size: .95rem; font-weight: 700; align-self: flex-start; margin-top: .3rem; }
.price-unit { font-size: .85rem; color: var(--ink-soft); margin-left: .35rem; }

.offer-fact { font-size: .85rem; color: var(--ink-soft); }
.offer-fact strong { color: var(--ink); font-weight: 600; }

.offer-price { font-size: 1.35rem; font-weight: 700; color: var(--green-700); }
.offer-meta { color: var(--ink-soft); font-size: .9rem; }

.offer-card .offer-actions {
    display: flex;
    gap: .5rem;
    align-items: center;
    padding: .65rem 1rem .95rem;
}

/* Superseded by .qty-stepper, which owns the input's box now. Kept scoped to a bare
   number input so any card still using one is not left unstyled. */
.offer-card .offer-actions > input[type="number"] {
    width: 4.6rem;
    padding: .45rem .5rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    font-size: .95rem;
}

/* Solid rather than outlined (mockup, 2026-08-26). It is the primary action on the card and
   the only one, so there is nothing for an outline to defer to. */
.btn-cart {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    background: var(--green-700);
    color: #fff;
    border: 1.5px solid var(--green-700);
    border-radius: 8px;
    padding: .5rem .5rem;
    font-size: .88rem;
    font-weight: 700;
    cursor: pointer;
    /* Two lines here is the difference between a tidy row of cards and a ragged one, and the
       label is short enough that wrapping never buys anything. */
    white-space: nowrap;
    transition: background .15s ease, border-color .15s ease;
}

.btn-cart svg { width: 14px; height: 14px; flex: 0 0 auto; }

.btn-cart:hover { background: var(--green-600); border-color: var(--green-600); color: #fff; }

/* ---------- Offer detail (image + facts side by side) ---------- */
.offer-detail-card { display: flex; gap: 1.5rem; flex-wrap: wrap; }

.offer-detail-image {
    flex: 0 1 340px;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    border: 1px solid #efede4;
    border-radius: var(--radius);
    padding: 1rem;
    background: #fff;
    align-self: flex-start;
}

.offer-detail-image img { max-width: 100%; max-height: 320px; object-fit: contain; }

.offer-detail-info { flex: 1 1 320px; min-width: 0; }

/* ---------- Tables ---------- */
/* position:relative is load-bearing, not decoration. Without it an absolutely
   positioned descendant — every .sr-only label on an in-row control — resolves
   against the page instead of this box, escapes the scroller, and lands at the
   table's full width. The document's scroll width then follows it: the admin
   order review page was 790px wide on a 420px phone, with the whole layout
   pushed into the left half of the screen. */
.table-wrap {
    position: relative;
    overflow-x: auto;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

table.data-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .93rem; }

.data-table th {
    text-align: left;
    background: var(--cream-dark);
    color: var(--green-900);
    padding: .6rem .75rem;
    border-bottom: 2px solid var(--line);
    white-space: nowrap;
}

.data-table td { padding: .55rem .75rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: #fbfaf5; }
.data-table .num { text-align: right; white-space: nowrap; }

/* A table inside a .panel-card runs edge to edge, but the card's OTHER rows — the .panel-head
   above it and any footnote below — are inset 1rem. Cell padding is .75rem, so every column of
   every table in the app sat 4px to the left of the title over it and the note under it. Three
   visible pixels, and once seen on one card it is seen on all of them (Jackie, 2026-08-27).
   Only the outer edges are touched; the gaps between columns keep the tighter .75rem. */
.panel-card .data-table th:first-child,
.panel-card .data-table td:first-child { padding-left: 1rem; }

.panel-card .data-table th:last-child,
.panel-card .data-table td:last-child { padding-right: 1rem; }

/* ---------- Badges ---------- */
.badge {
    display: inline-block;
    padding: .2rem .55rem;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 600;
    /* 1.3, not the body's 1.55. A chip is a label, not a paragraph: at body leading the text
       floats high in the pill with a gap under it, which is visible even on one line and
       obvious on two. Stated here so every badge in the app shares it. */
    line-height: 1.3;
    text-align: center;
    white-space: nowrap;
}

.badge-success { background: var(--green-100); color: var(--green-700); }
.badge-warning { background: var(--gold-100); color: #7c6118; }
.badge-danger { background: var(--danger-bg); color: var(--danger); }
.badge-info { background: var(--info-bg); color: var(--info); }
.badge-neutral { background: #eceada; color: var(--ink-soft); }

/* ---------- Buttons ---------- */
.btn {
    display: inline-block;
    border: 1px solid transparent;
    border-radius: 8px;
    padding: .5rem 1rem;
    font-size: .93rem;
    line-height: 1.25;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    transition: background .15s ease;
}

.btn-primary { background: var(--green-700); color: #fff; }
.btn-primary:hover { background: var(--green-600); color: #fff; }

.btn-secondary { background: var(--cream-dark); color: var(--green-900); border-color: var(--line); }
.btn-secondary:hover { background: #e9e3d2; }

.btn-danger { background: #fff; color: var(--danger); border-color: var(--danger); }
.btn-danger:hover { background: var(--danger-bg); }

.btn-outline { background: transparent; color: #fff; border-color: rgba(255, 255, 255, .55); }
.btn-outline:hover { background: rgba(255, 255, 255, .12); color: #fff; }

.btn-sm { padding: .3rem .7rem; font-size: .85rem; }
.btn-lg { padding: .7rem 1.5rem; font-size: 1.02rem; }

/* Text-weight action for the secondary choice beside a primary button — keeps the
   hierarchy unmistakable where two buttons would read as a decision. */
.btn-quiet {
    background: transparent;
    border-color: transparent;
    color: var(--green-700);
    padding-left: .25rem;
    padding-right: .25rem;
    text-decoration: underline;
    text-underline-offset: 3px;
}

.btn-quiet:hover { background: transparent; color: var(--green-600); }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .55; cursor: not-allowed; }

/* ---------- Forms (modernized inputs, applied globally) ---------- */
.form-field { margin-bottom: .95rem; display: flex; flex-direction: column; gap: .3rem; }
.form-field label { font-weight: 600; font-size: .87rem; color: var(--green-900); }

input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]),
select,
textarea {
    padding: .6rem .8rem;
    border: 1.5px solid #d9d4c3;
    border-radius: 10px;
    font-size: .95rem;
    font-family: inherit;
    background: #fff;
    color: var(--ink);
    transition: border-color .15s ease, box-shadow .15s ease;
}

@media (max-width: 860px) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]),
    select,
    textarea { font-size: 1rem; }
}

input:not([type="checkbox"]):not([type="radio"]):hover,
select:hover,
textarea:hover {
    border-color: #bcb69e;
}

input:not([type="checkbox"]):not([type="radio"]):focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--green-600);
    box-shadow: 0 0 0 3.5px rgba(56, 113, 76, .14);
}

::placeholder { color: #a8ab9c; }

/* Custom chevron on selects */
select {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%235c6355' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .8rem center;
    padding-right: 2.4rem;
}

input[type="checkbox"],
input[type="radio"] {
    accent-color: var(--green-700);
    width: 1.1rem;
    height: 1.1rem;
    flex: 0 0 auto;
}

.form-row { display: flex; gap: .75rem; flex-wrap: wrap; }
.form-row .form-field { flex: 1 1 180px; }
.form-row .form-field-sm { flex: 0 1 110px; }

.checkbox-field label { display: flex; align-items: center; gap: .45rem; font-weight: 500; }

.validation-message { color: var(--danger); font-size: .85rem; }

.form-section {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.1rem 1.25rem;
    margin-bottom: 1rem;
}

.form-section > h2:first-child { margin-top: 0; }

/* ---------- Alerts ---------- */
.alert { padding: .7rem 1rem; border-radius: 8px; margin-bottom: 1rem; font-size: .93rem; }
.alert-danger { background: var(--danger-bg); color: var(--danger); border: 1px solid #e5c4bf; }
.alert-success { background: var(--green-100); color: var(--green-700); border: 1px solid #c4dccb; }
.alert-info { background: var(--info-bg); color: var(--info); border: 1px solid #c4d8e5; }

/* Added 2026-08-26 for the presence notice. Gold rather than red because nothing is broken —
   somebody else is simply mid-edit, and the reader has a choice to make rather than a fault to
   fix. Red here would be crying wolf on the screen where the real failures also show. */
.alert-warning { background: var(--gold-100); color: #7c6118; border: 1px solid #e2d0a4; }

/* ---------- Auth / narrow cards ---------- */
.auth-card {
    max-width: 460px;
    margin: 2.5rem auto;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.75rem;
}

/* ---------- Page toolbar (filters/search) ---------- */
.toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: .75rem;
    margin: .75rem 0 1rem;
}

.toolbar .form-field { margin-bottom: 0; min-width: 170px; }
.toolbar .spacer { flex: 1; }

/* =====================================================================
   Hero — photograph-ready.

   Three stacked layers so the hero looks designed with no photo present and
   simply gets better when one arrives:
     ::before  the image layer. Takes --hero-image first and falls back to a
               CSS-drawn field texture. Multiple background-images mean a
               missing or 404'd photo degrades silently to the texture.
     ::after   the scrim. This is what guarantees legible white text over an
               arbitrary photograph — never remove it when adding an image.
     content   .hero-inner, realigned to the 1200px column.

   To use a photograph, set --hero-image on the page's modifier class. Nothing
   else needs to change. See docs/UI-PHOTOGRAPHY.md.
   ===================================================================== */
.hero {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    color: #fff;
    background-color: var(--green-900);
    border-radius: var(--radius);
    padding: 3.25rem 2rem;
    margin-bottom: 1.75rem;

    /* Furrowed field rows under a low warm sun — the no-photo default. */
    --hero-fallback:
        radial-gradient(1100px 380px at 78% 12%, rgba(200, 149, 44, .34), transparent 62%),
        repeating-linear-gradient(97deg, rgba(255, 255, 255, .05) 0 2px, transparent 2px 34px),
        linear-gradient(135deg, #2b3417 0%, #3f4b22 46%, #55622e 100%);
}

.hero::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -2;
    background-image: var(--hero-image, none), var(--hero-fallback);
    background-size: cover, cover;
    background-position: center 62%, center;
    background-repeat: no-repeat;
}

/* Scrim: strongest behind the text, clearing toward the right so a photograph
   still reads. Tuned so white body copy holds >7:1 contrast at the text edge. */
.hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        linear-gradient(100deg, rgba(26, 32, 15, .93) 0%, rgba(26, 32, 15, .82) 42%, rgba(26, 32, 15, .42) 100%),
        linear-gradient(to top, rgba(26, 32, 15, .55), transparent 45%);
}

.hero-inner { max-width: 1200px; margin: 0 auto; padding: 0 1.25rem; width: 100%; }

.hero-eyebrow {
    text-transform: uppercase;
    letter-spacing: .1em;
    font-size: .78rem;
    font-weight: 700;
    color: var(--gold-100);
    margin: 0 0 .5rem;
}

.hero h1 { color: #fff; font-size: 3.05rem; line-height: 1.12; margin-bottom: .85rem; max-width: 16ch; }
.hero p { max-width: 54ch; font-size: 1.09rem; color: #eef2e4; }
.hero .hero-lead { margin: 0; }
.hero .hero-actions { margin-top: 1.5rem; display: flex; gap: .75rem; flex-wrap: wrap; }
.hero .btn-primary { background: var(--gold-600); color: #2b2205; border-color: transparent; font-weight: 700; }
.hero .btn-primary:hover { background: #dda93c; color: #2b2205; }
.hero .btn-secondary { background: rgba(255, 255, 255, .14); color: #fff; border-color: rgba(255, 255, 255, .45); }
.hero .btn-secondary:hover { background: rgba(255, 255, 255, .22); color: #fff; }

/* Qualified with .hero, not left bare. `.hero p` above scores (0,1,1) and beat a bare
   `.hero-note` at (0,1,0) despite coming later, so the note rendered at the hero body size and
   colour — visually identical to the lead paragraph above it, which is the one thing it must
   not be. This is the specificity trap documented further down the file; it had quietly been
   flattening the notes on Home and For Suppliers too. */
.hero .hero-note { margin: 1rem 0 0; font-size: .92rem; color: #d7dfc6; max-width: none; }
.hero-note a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.hero-note a:hover { color: var(--gold-100); }

/* Taller on the home page — this is the first thing anyone sees. */
.hero-home {
    padding: 4.5rem 2rem 4rem;
    --hero-image: url("../img/hero-fields.jpg");
}

/* Scrim tuned to hero-fields.jpg specifically, rather than using the conservative
   default above. Measured against the actual pixels behind the copy block (x 13–46%,
   the image cover-cropped to a ~1440x560 hero): white text holds 10.95:1 against the
   brightest 1% of pixels there — comfortably AAA.
   
   These values are lighter than the ones the previous photograph needed. That frame
   sat at 7.3:1 with a .86/.74/.22 scrim; this one is darker behind the copy, so the
   scrim was opened up to .78/.64/.16 to let more of the landscape through and still
   gained margin. Measured at .70/.56/.10 it is 9.79:1, so there is room to go further
   if the photo ever wants to read brighter — but re-measure before assuming that of a
   different frame, especially one with sky behind the text rather than field. */
.hero-home::after {
    background:
        linear-gradient(100deg, rgba(26, 32, 15, .78) 0%, rgba(26, 32, 15, .64) 44%, rgba(26, 32, 15, .16) 100%),
        linear-gradient(to top, rgba(26, 32, 15, .5), transparent 42%);
}

.hero-suppliers {
    padding: 3.25rem 2rem 3rem;
    --hero-image: url("../img/hero-supplier.jpg");
    --hero-fallback:
        radial-gradient(900px 340px at 82% 16%, rgba(142, 61, 30, .38), transparent 62%),
        repeating-linear-gradient(97deg, rgba(255, 255, 255, .045) 0 2px, transparent 2px 38px),
        linear-gradient(135deg, #2b3417 0%, #44381f 55%, #5b4526 100%);
}

/* Deliberately a cooler slate scrim rather than the olive used elsewhere: the supplier
   photograph is overcast and blue-grey, and keeping that coolness is what distinguishes
   this page from the home page's golden-hour landscape. Measured at a 1440px viewport —
   7.7:1 for white text against the brightest 1% behind the copy. */
.hero-suppliers::after {
    background:
        linear-gradient(100deg, rgba(22, 28, 34, .86) 0%, rgba(22, 28, 34, .72) 44%, rgba(22, 28, 34, .26) 100%),
        linear-gradient(to top, rgba(22, 28, 34, .5), transparent 42%);
}

/* Page hero — the standard banner for a secondary public page (How It Works,
   Contact). Shorter than the home hero, with no eyebrow and no action row: a
   title, a gold rule, and at most one line of orientation. The photograph is set
   per page as an inline --hero-image (see the pages), because these two use
   different frames and neither should repeat the home page's.

   Scrim runs heavier on the left than .hero-home's. The copy block here is
   narrow and there is no action row competing for the space, so there is nothing
   to gain by trading contrast for landscape on that side; the right clears to
   .12 so the grain bins still read.

   NOTE the scrim below is switched off above 900px — see .hero-page::after. These
   numbers are the <=900px fallback, where the photo covers the band again: both
   frames cover-cropped to 1440x266, white title 9.2:1 over hero-grain-bins.jpg and
   10.0:1 over hero-grain-sunset.jpg, sub-line 9.9:1. Above 900px the title sits on
   flat green-900 instead, which is 10.4:1 whatever the photograph.

   The floating header is NOT protected by this scrim above 900px — it sits directly
   over the photo zone — and does not need to be: its own gradient is ~.83 alpha at
   the nav's depth, which measures 11.0-13.8:1 across 920-1440px on both frames.
   That gradient is the thing to re-measure for a brighter photograph, not this. */
.hero-page { padding: 3rem 2rem 3.25rem; }

/* Shorter band for a page whose job starts immediately below it. The photograph
   and the scrim are untouched — only the padding — so every contrast figure
   recorded above still holds. */
.hero-compact { padding: 2rem 2rem 2.1rem; }

/* The photograph occupies a zone on the right rather than covering the whole band,
   which is the one meaningful departure from .hero-home. These frames are roughly
   2.3:1 and the band is nearly 5:1, so covering the full width crops the grain bins
   off at the roof. A right-hand zone is close to the picture's own proportions, so
   it crops barely at all — and it puts the title on flat green-900 instead of on a
   scrim over a sunset, which is both cleaner and a better contrast story.

   Same technique as .cta-band-photo: pin right, fade the LEFT edge with a mask.
   Fixed-percentage gradients cannot track where the copy ends, because that moves
   with the viewport; a reserved zone can. */
.hero-page::before,
.hero-photo-right::before {
    inset: 0 0 0 auto;
    width: min(58%, 46rem);
    background-position: center 45%, center;
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 34%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 34%);
}

/* No scrim: the copy sits on green-900 (white measures 10.4:1) and the mask above
   already keeps the photograph clear of it. Restored below 900px, where the band
   goes back to a full-width photo — see the media query further down. */
.hero-page::after,
.hero-photo-right::after { display: none; }

.hero-page h1 { font-size: 2.5rem; margin-bottom: 0; max-width: 18ch; }

/* The rule belongs to the title rather than being its own element, so a page
   with no sub-line (Contact) gets it with no extra markup. */
.hero-page h1::after {
    content: "";
    display: block;
    width: 88px;
    border-top: 3px solid var(--gold-600);
    margin-top: .85rem;
}

.hero-page .hero-lead { margin: 1.1rem 0 0; max-width: 34ch; font-size: 1.02rem; }

/* Compact hero variant for signed-in dashboards — same layering, lower height,
   lighter texture so it frames the page without competing with the data on it. */
.hero-compact {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 1.75rem 2rem;
    --hero-fallback:
        radial-gradient(700px 240px at 88% 10%, rgba(200, 149, 44, .26), transparent 65%),
        repeating-linear-gradient(97deg, rgba(255, 255, 255, .04) 0 2px, transparent 2px 30px),
        linear-gradient(135deg, #323c1a 0%, #4a5628 100%);
}

.hero-compact h1 { font-size: 1.55rem; margin-bottom: .25rem; max-width: none; }
.hero-compact p { margin: 0; font-size: .95rem; }
.hero-compact .hero-actions { margin-top: 0; }

/* When a hero is full-bleed, .hero-inner owns the horizontal padding — otherwise the
   two stack and the headline sits further in than the content below it. Higher
   specificity than the size modifiers above so it survives them and the media query. */
.hero.bleed { padding-left: 0; padding-right: 0; }

/* =====================================================================
   Sectioning bands — alternating background weight down the public pages
   so the page reads as composed sections rather than a stack of cards.
   ===================================================================== */
.band { margin: 2.75rem 0; }
.band-inner { max-width: 1200px; margin: 0 auto; padding: 0 1.25rem; width: 100%; }
.site-main > .band:not(.bleed) > .band-inner { padding-inline: 0; }

.band-tint {
    background: var(--cream-dark);
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    padding: 2.5rem 0 2.75rem;
    margin: 3rem 0;
}

.section-title { font-size: 1.5rem; margin: 0 0 1.25rem; }

.band-cta {
    display: flex;
    gap: 1rem;
    align-items: center;
    flex-wrap: wrap;
    margin-top: 1.75rem;
}

/* ---------- Value cards ---------- */
.value-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 1.15rem;
}

.value-card {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 14px;
    padding: 1.5rem 1.45rem;
    box-shadow: var(--shadow);
    transition: transform .18s ease, box-shadow .18s ease;
}

.value-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 26px rgba(36, 40, 31, .14);
}

.value-card h3 { margin-top: 0; }
.value-card p { margin-bottom: 0; }

@media (min-width: 561px) and (max-width: 860px) {
    .value-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .value-grid > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* Filled circle rather than a tinted square: these are the page's only decorative
   marks, so they carry more weight than the steps' numerals deliberately. White on
   the lightest gradient stop measures 4.95:1 — comfortably past the 3:1 bar for
   non-text graphics. */
.value-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.4rem;
    height: 3.4rem;
    border-radius: 50%;
    background: linear-gradient(140deg, var(--green-600), var(--green-900));
    color: #fff;
    box-shadow: 0 3px 10px rgba(57, 68, 31, .28);
    margin-bottom: .95rem;
}

.value-icon svg { width: 28px; height: 28px; }

/* ---------- Supplier band (deliberately quiet) ---------- */
/* Mirrored on purpose. This sits directly beneath the closing CTA band, which runs
   icon-left / text / action-right; repeating that silhouette made the two read as the
   same block twice. Reversing the visual order — action left, icon right — separates
   them at a glance. DOM order is unchanged, so it is still read and tabbed as
   icon, pitch, then action. The accent rule moves to the right so it frames the icon
   side rather than crowding the button. */
.supplier-band {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
    background: var(--green-100);
    border: 1px solid var(--line);
    border-right: 4px solid var(--green-600);
    border-radius: var(--radius);
    padding: 1.35rem 1.5rem;
    margin: 2.5rem 0 1rem;
}

.supplier-band h2 { margin: 0 0 .25rem; font-size: 1.15rem; }
.supplier-band p { margin: 0; max-width: 60ch; }
.supplier-band-text { flex: 1 1 320px; }

.site-main > .supplier-band:last-child { margin-bottom: 0; }

/* Section heading with a trailing "see all" link. */
.section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 1.75rem;
}

.section-head h2 { margin: 0; }
.section-head a { font-size: .9rem; font-weight: 600; text-decoration: none; }
.section-head a:hover { text-decoration: underline; }

/* Compact clickable offer cards (dashboard "Latest Offers"). */
.offer-card-link { text-decoration: none; color: inherit; }
.offer-card-link .offer-title { font-weight: 600; color: var(--ink); }
.offer-card-link:hover .offer-title { color: var(--green-700); }
.offer-card-link .offer-body { padding-bottom: .9rem; }
.offer-image-sm { height: 140px; }

/* Steps are a SEQUENCE, not a set of parallel benefits — the value cards above are
   the parallel set. A dashed connector runs behind the row and shows through the
   gutters between the (opaque) cards, so the eye reads 1 → 2 → 3 → 4 instead of four
   equally-weighted boxes. The badge is filled to sit on that line like a station stop.

   The connector's vertical position is derived from the card padding and badge size
   rather than hard-coded, so changing either keeps it aligned. */
.step-grid {
    --step-pad: 1.25rem;
    --step-badge: 2.7rem;
    position: relative;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
    margin: 1rem 0;
}

.step-grid::before {
    content: "";
    position: absolute;
    left: 6%;
    right: 6%;
    top: calc(var(--step-pad) + var(--step-badge) / 2 - 1px);
    height: 2px;
    z-index: 0;
    background: repeating-linear-gradient(to right, var(--green-600) 0 7px, transparent 7px 13px);
    opacity: .55;
}

.step-card {
    position: relative;
    z-index: 1;                 /* keeps the connector in the gutters, not across the card */
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 14px;
    padding: var(--step-pad) 1.2rem 1.35rem;
    box-shadow: var(--shadow);
}

.step-card .step-num {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--step-badge); height: var(--step-badge); border-radius: 50%;
    background: var(--green-700); color: #fff;
    font-weight: 700; font-size: 1.1rem;
    margin-bottom: .7rem;
}

/* ---------- Detail lists ---------- */
.detail-list { display: grid; grid-template-columns: max-content 1fr; gap: .3rem 1.25rem; margin: .5rem 0 1rem; }
.detail-list dt { font-weight: 600; color: var(--green-900); }
.detail-list dd { margin: 0; }

/* ---------- Header cart indicator ---------- */
.cart-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    color: #fff;
    padding: .3rem .35rem;
    border-radius: 8px;
    text-decoration: none;
}

.cart-link:hover { background: rgba(255, 255, 255, .12); color: #fff; }

.cart-badge {
    position: absolute;
    top: -4px;
    right: -6px;
    background: var(--gold-600);
    color: #fff;
    font-size: .7rem;
    font-weight: 700;
    min-width: 1.15rem;
    height: 1.15rem;
    padding: 0 .25rem;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

/* ---------- Cart & checkout ---------- */
.cart-layout {
    display: flex;
    gap: 1.25rem;
    align-items: flex-start;
    flex-wrap: wrap;
    margin-top: 1rem;
}

.cart-items { flex: 1 1 480px; min-width: 0; display: flex; flex-direction: column; gap: .75rem; }

/* The rail is sticky on both the cart and the checkout, and on checkout it did not appear to
   work. Nothing was broken above it: the ancestors are identical on the two pages, .page-shell
   already uses overflow-x: clip precisely so it is not a scroll container, and .cart-layout sets
   align-items: flex-start.

   The cause is arithmetic. Sticky can only travel (container height - element height), and the
   container here is .cart-layout, whose height is its tallest child. On the cart page that is
   the item table, so the short rail has hundreds of pixels to travel. On the checkout page the
   rail itself is the tallest child — it lists every cart item, then the balance, then three
   paragraphs and two buttons, against a fixed-height form beside it — so the container is
   exactly as tall as the rail and the travel is zero. A rail taller than the viewport cannot pin
   with a top offset either, so its buttons scroll away regardless.

   Both are fixed by refusing to let the rail exceed the viewport: it now scrolls internally past
   that point, which caps .cart-layout's height by the left column again and gives sticky
   something to travel. */
.cart-summary {
    flex: 0 1 320px;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.1rem 1.25rem;
    position: sticky;
    top: 1rem;
    max-height: calc(100vh - 2rem);
    overflow-y: auto;
}

/* Below this the rail is stacked under the items rather than beside them, and a capped height
   would turn the whole summary into a small scrolling box in the middle of the page. */
@media (max-width: 860px) {
    .cart-summary {
        max-height: none;
        overflow-y: visible;
    }
}

.cart-summary h2 { margin-top: 0; }

.summary-row {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    font-size: .93rem;
    padding: .3rem 0;
}

.summary-total {
    border-top: 2px solid var(--line);
    margin-top: .5rem;
    padding-top: .65rem;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--green-900);
}

.cart-table td { vertical-align: middle; }
.cart-product { display: flex; align-items: center; gap: .75rem; }
.cart-product .cart-thumb { flex: 0 0 auto; }

.cart-thumb {
    width: 58px;
    height: 58px;
    object-fit: contain;
    border: 1px solid #efede4;
    border-radius: 8px;
    background: #fff;
    padding: 3px;
}

.cart-qty { width: 5rem; }

.cart-remove {
    background: none;
    border: none;
    color: var(--ink-soft);
    font-size: 1rem;
    cursor: pointer;
    padding: .3rem .5rem;
    border-radius: 6px;
}

.cart-remove:hover { color: var(--danger); background: var(--danger-bg); }

@media (max-width: 720px) {
    .cart-table,
    .cart-table tbody,
    .cart-table tr,
    .cart-table td { display: block; width: auto; }

    .cart-table thead { display: none; }

    .cart-table tr {
        position: relative;
        padding: 1rem 2.75rem 1rem 1rem;
        border-bottom: 1px solid var(--line);
    }

    .cart-table tr:last-child { border-bottom: none; }
    .cart-table td { padding: 0; border: none; text-align: left; }

    .cart-table td[data-label] {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
        padding: .3rem 0;
    }

    .cart-table td[data-label]::before {
        content: attr(data-label);
        font-size: .85rem;
        font-weight: 600;
        color: var(--ink-soft);
    }

    .cart-table .cart-product { margin-bottom: .5rem; }

    .cart-table .cart-remove-cell { position: absolute; top: .6rem; right: .5rem; }
    .cart-table .cart-remove { width: 40px; height: 40px; }
}

/* Selectable option cards (fulfillment choice at checkout) */
.choice-cards { display: flex; gap: .75rem; flex-wrap: wrap; margin-bottom: 1rem; }

.choice-card {
    flex: 1 1 220px;
    display: flex;
    gap: .7rem;
    align-items: flex-start;
    border: 1.5px solid var(--line);
    border-radius: 10px;
    padding: .85rem .95rem;
    cursor: pointer;
    transition: border-color .15s ease, box-shadow .15s ease;
    background: #fff;
}

.choice-card:hover { border-color: var(--green-600); }

.choice-card:has(input:checked) {
    border-color: var(--green-700);
    box-shadow: 0 0 0 3px rgba(56, 113, 76, .14);
}

.choice-card input { margin-top: .2rem; }
.choice-body { display: flex; flex-direction: column; gap: .15rem; }
.choice-title { font-weight: 700; color: var(--green-900); }
.choice-desc { font-size: .85rem; color: var(--ink-soft); }

/* ---------- Utility ---------- */
.actions-row { display: flex; gap: .5rem; flex-wrap: wrap; margin: 1rem 0; }
.text-right { text-align: right; }
.total-line { font-size: 1.1rem; font-weight: 700; color: var(--green-900); }
.empty-state {
    text-align: center; color: var(--ink-soft);
    padding: 2.5rem 1rem; background: #fff;
    border: 1px dashed var(--line); border-radius: var(--radius);
}

/* ---------- Responsive ---------- */

@media (max-width: 1200px) {
    .brand-text { display: none; }
    .site-nav { margin-left: .75rem; }
}

@media (max-width: 960px) {
    :root { --header-h: 64px; }

    .header-inner { position: relative; gap: .5rem; padding: .6rem 1.25rem; }
    .brand-text { display: flex; }
    .brand-name { white-space: nowrap; }
    .site-nav { display: none; }
    .header-user { margin-left: auto; gap: .45rem; }
    .nav-cta { padding: .45rem .8rem; font-size: .86rem; }
    .nav-menu { display: block; }
    .site-header:has(.nav-menu[open]) { position: relative; z-index: 70; }
    .page-shell:has(.site-main > .hero.bleed:first-child) .site-header:has(.nav-menu[open]) { z-index: 70; }
    html:has(.nav-menu[open]) { overflow: hidden; }
}

@media (max-width: 860px) {
    .brand-text { display: none; }
}

@media (max-width: 720px) {
    .header-inner { padding-inline: 1rem; }
}

@media (max-width: 380px) {
    .header-inner { gap: .4rem; padding: .6rem .75rem; }
    .site-header .brand-logo-chip img { height: 30px; }
    .nav-cta-verb { display: none; }
}

@media (max-width: 980px) {
    .footer-inner { grid-template-columns: 1fr 1fr; }
    .hero h1 { font-size: 2.15rem; }
}

@media (max-width: 720px) {

    .site-main { padding: 1rem 1rem 2.5rem; }
    .detail-list { grid-template-columns: 1fr; }
    .detail-list dt { margin-top: .4rem; }

    /* CRITICAL — do not simplify this away.
       The desktop scrim is a left-to-right gradient: dark under the narrow text column,
       clearing to the right so the photograph shows. On a phone the copy spans the FULL
       width and runs straight into the cleared region. Measured at 390px with the desktop
       gradient, right-edge text lands at 2.2:1 — a hard WCAG failure — even though the
       same text is 9:1 on desktop. Below tablet width the scrim therefore becomes
       near-uniform, which measures 9:1 or better across the whole line.
       Re-check this if the hero copy width or the gradient stops ever change. */
    /* .hero-page is deliberately absent: it turns its scrim off entirely on desktop
       and the 900px block further down restores it, which is later in source than
       this one and would win anyway. Listing it here twice only invites someone to
       "fix" one copy and not the other. */
    .hero::after,
    .hero-home::after,
    .hero-suppliers::after {
        background:
            linear-gradient(180deg, rgba(26, 32, 15, .84) 0%, rgba(26, 32, 15, .78) 100%);
    }

    .hero-suppliers::after {
        background:
            linear-gradient(180deg, rgba(22, 28, 34, .82) 0%, rgba(22, 28, 34, .76) 100%);
    }

    .hero { padding: 2.5rem 1.25rem; }
    .hero-home { padding: 3rem 1.25rem 2.75rem; }
    .hero-inner, .band-inner { padding: 0 1rem; }
    .hero h1 { font-size: 1.85rem; max-width: none; }
    .hero p { font-size: 1rem; }
    .hero .hero-actions .btn-lg { width: 100%; text-align: center; }

    .band { margin: 2rem 0; }
    .band-tint { margin: 2.25rem 0; padding: 2rem 0; }
    .section-title { font-size: 1.3rem; }

    /* Cards stack into a single column, so a horizontal connector would point at
       nothing. The numbered badges carry the sequence on their own. */
    .step-grid::before { display: none; }

    /* Stacked, the mirror has nothing to mirror — fall back to natural order so the
       pitch is read before the button. */
    .supplier-band { flex-direction: column; align-items: flex-start; }
    .supplier-band-text { flex: none; }

    .footer-inner { grid-template-columns: 1fr; gap: 1.5rem; padding: 1.75rem 1rem 1.5rem; }
    .footer-legal p { padding: .9rem 1rem 1.1rem; }
    .site-footer { margin-top: 1.5rem; }
}

/* Photographs are decorative here; users who ask for reduced data or reduced motion
   still get the full design from the CSS-drawn fallback. */
@media (prefers-reduced-data: reduce) {
    .hero::before { background-image: var(--hero-fallback); }
}

/* The card lift is ornament, not information — drop it for anyone who has asked the
   system for less movement. The shadow change stays, so hover still gives feedback. */
@media (prefers-reduced-motion: reduce) {
    .value-card { transition: box-shadow .18s ease; }
    .value-card:hover { transform: none; }
}

/* =====================================================================
   Administration shell — deliberately distinct from the public site:
   dark forest-green chrome, traditional left navigation, gold accents,
   neutral (non-cream) content background for readable data tables.
   ===================================================================== */

:root {
    --admin-chrome: #1a2011;      /* sidebar — near-black olive, tuned to the logo green */
    --admin-chrome-2: #242c18;    /* topbar / hover */
    --admin-line: #38412a;
    --admin-text: #d5dac4;
    --admin-text-dim: #99a284;
    --admin-content-bg: #f2f2ec;  /* neutral gray-olive, cooler than the cream front-end */
}

.admin-shell {
    display: flex;
    min-height: 100vh;
    background: var(--admin-content-bg);
}

/* ---------- Sidebar ---------- */
.admin-sidebar {
    width: 230px;
    flex-shrink: 0;
    background: var(--admin-chrome);
    color: var(--admin-text);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
}

.admin-brand {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: 1rem .9rem;
    text-decoration: none;
    color: #fff;
    border-bottom: 1px solid var(--admin-line);
}

.admin-logo-chip {
    display: inline-flex;
    align-items: center;
    background: #fff;
    border-radius: 8px;
    padding: 3px 6px;
}

.admin-logo-chip img { height: 32px; display: block; }

.admin-brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.admin-brand-name { font-weight: 600; font-size: .95rem; }
.admin-brand-tag {
    font-size: .72rem;
    color: var(--gold-600);
    letter-spacing: .1em;
    text-transform: uppercase;
    font-weight: 700;
}

.admin-nav { display: flex; flex-direction: column; padding: .6rem 0; flex: 1; }

.admin-nav-section {
    padding: .9rem .95rem .25rem;
    font-size: .72rem;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--admin-text-dim);
    font-weight: 700;
}

.admin-nav a {
    color: var(--admin-text);
    text-decoration: none;
    padding: .5rem .95rem;
    font-size: .92rem;
    border-left: 3px solid transparent;
}

.admin-nav a:hover { background: var(--admin-chrome-2); color: #fff; }

.admin-nav a.active {
    background: var(--admin-chrome-2);
    border-left-color: var(--gold-600);
    color: #fff;
    font-weight: 600;
}

.admin-sidebar-footer {
    padding: .85rem .95rem;
    border-top: 1px solid var(--admin-line);
    font-size: .85rem;
}

.admin-sidebar-footer a { color: var(--admin-text-dim); text-decoration: none; }
.admin-sidebar-footer a:hover { color: #fff; }

/* ---------- Main column ---------- */
.admin-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }

.admin-topbar {
    background: var(--admin-chrome-2);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .55rem 1.25rem;
    border-bottom: 3px solid var(--gold-600);
}

.admin-topbar-title {
    font-size: .85rem;
    letter-spacing: .09em;
    text-transform: uppercase;
    font-weight: 700;
    color: var(--gold-100);
}

/* No max-width (Jackie, 2026-08-26). The console is a working surface — master tracking has ten
   columns and the release queue eleven — and capping it at 1400px left a wide monitor showing
   empty page beside a table that was scrolling sideways. */
.admin-content {
    flex: 1;
    padding: 1.5rem 1.5rem 3rem;
    width: 100%;
    min-width: 0;
}

/* Admin content keeps the shared card/table styles; only the page chrome differs. */

/* ---------- Responsive: the sidebar gives way to the hamburger menu ---------- */
/* The menu itself is in the ADMIN CONSOLE section further down. */
@media (max-width: 860px) {
    .admin-sidebar { display: none; }
    .admin-content { padding: 1rem .85rem 2.5rem; }
}

/* Blazor error UI */
#blazor-error-ui {
    background: var(--gold-100);
    color: var(--ink);
    bottom: 0; left: 0; right: 0;
    box-shadow: 0 -1px 4px rgba(0,0,0,.2);
    display: none;
    padding: .6rem 1.25rem .7rem;
    position: fixed;
    z-index: 1000;
}

#blazor-error-ui .dismiss { cursor: pointer; float: right; }

/* ============================================================
   SECTION & CONTENT VARIANTS
   Reusable pieces, not home-page one-offs. Anything here should be
   usable on any public page without reaching back into this file.
   ============================================================ */

/* ---------- Icon circle ----------
   The round icon token used in value cards, step rails and CTA bands.
   Size and colour are variants so a section can pick what suits it. */
.icon-circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: var(--green-700);
    color: #fff;
}

.icon-circle svg { width: 22px; height: 22px; }

.icon-circle-lg { width: 58px; height: 58px; }
.icon-circle-lg svg { width: 27px; height: 27px; }

.icon-circle-gold { background: var(--gold-600); color: #2b2205; }

/* For use on dark bands, where a solid green circle disappears. */
.icon-circle-ghost { background: rgba(255, 255, 255, .12); color: #fff; }

/* ---------- Card head ----------
   Icon beside a heading rather than stacked above it. Keeps three cards
   the same height when one title wraps to two lines and the others don't. */
.card-head {
    display: flex;
    align-items: center;
    gap: .85rem;
    margin-bottom: .8rem;
}

.card-head h3 { margin: 0; }

/* ---------- Dark band ----------
   A full-bleed dark section for breaking up a long cream page. Pair with
   .bleed. For a photographic backdrop add .band-photo and set the image
   inline as url(../img/...) — RELATIVE TO THIS STYLESHEET, not to the page,
   because that is where the var is consumed. The overlay
   is heavy on purpose so body text stays readable over any photo. */
.band-dark {
    background-color: var(--green-900);
    color: #dde5cd;
    padding: 3.25rem 0;
    margin: 0;
}

/* Anchored to the BOTTOM of the frame on purpose: with a landscape photo that puts
   the crop rows along the lower edge into the band and leaves the bright sky out of
   it, which is both the look we want and the reason the text stays readable.

   Overlay measured against hero-fields.jpg, cover-cropped to a ~1440x520 band:
   white headings 11.9:1, body text (#c6d1b1) 7.4:1 — AAA on both. It was .90/.94,
   which left only 0.8% of the photo showing and read as flat colour; this is 1.6%,
   which is still subtle but visible. .74/.84 shows 2.1% but drops body text to
   6.7:1, below AAA — hence the stopping point. Re-measure for a lighter photo. */
.band-dark.band-photo {
    background-image:
        linear-gradient(rgba(29, 37, 17, .80), rgba(29, 37, 17, .88)),
        var(--band-photo, none);
    background-size: cover;
    background-position: center bottom;
}

.band-dark .section-title,
.band-dark h2,
.band-dark h3 { color: #fff; }

.band-dark p { color: #c6d1b1; }
.band-dark .muted { color: #b6c39d; }
.band-dark a { color: var(--gold-100); }
.band-dark a:hover { color: #fff; }

.band-dark .btn-secondary {
    background: transparent;
    color: #fff;
    border-color: rgba(255, 255, 255, .45);
}

.band-dark .btn-secondary:hover { background: rgba(255, 255, 255, .12); color: #fff; }

/* ---------- Step rail ----------
   Numbered steps joined by a dotted connector. The connector is drawn per
   item and suppressed on the last one, so the count can change without
   touching CSS; it also disappears once the grid wraps, where a line
   pointing off the end of a row would be nonsense. */
.step-rail {
    display: grid;
    grid-template-columns: repeat(var(--step-count, 4), minmax(0, 1fr));
    gap: 1.75rem;
}

.step-rail-item { position: relative; }

.step-rail-item::after {
    content: "";
    position: absolute;
    top: 14px;
    left: 2.55rem;
    right: -1.15rem;
    border-top: 2px dotted var(--rail-line, rgba(200, 149, 44, .6));
}

.step-rail-item:last-child::after { display: none; }

.step-rail-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 29px;
    height: 29px;
    border-radius: 50%;
    background: var(--gold-600);
    color: #2b2205;
    font-weight: 700;
    font-size: .95rem;
}

.step-rail-icon {
    margin: 1.35rem 0 .65rem;
    color: #e8eedd;
}

.step-rail-icon svg { width: 30px; height: 30px; }

.step-rail-item h3 { font-size: 1rem; margin: 0 0 .35rem; }
.step-rail-item p { margin: 0; font-size: .93rem; }

/* ---------- Two-up grid ----------
   Narrower than .value-grid and centred, for content that reads badly at
   three across — testimonials, comparisons, a pair of options. */
.grid-2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem;
    max-width: 960px;
    margin: 0 auto;
}

/* ---------- Testimonial ----------
   Built on .value-card. The quote mark is decorative and sits in the
   card's left gutter rather than in the text flow, so long and short
   quotes both start on the same line. */
.testimonial-card {
    position: relative;
    margin: 0;
    padding-left: 3.6rem;
    border-top-color: var(--line);
}

.testimonial-card::before {
    content: "\201C";
    position: absolute;
    top: 1rem;
    left: 1.15rem;
    font: 700 3.4rem/1 Georgia, "Times New Roman", serif;
    color: var(--gold-600);
    opacity: .5;
}

.testimonial-card blockquote {
    margin: 0 0 1rem;
    font-size: 1rem;
    line-height: 1.6;
}

.testimonial-card figcaption {
    font-size: .92rem;
    line-height: 1.45;
    color: var(--ink-soft);
}

.testimonial-card figcaption::before {
    content: "";
    display: block;
    width: 30px;
    border-top: 2px solid var(--line);
    margin-bottom: .65rem;
}

.testimonial-name {
    display: block;
    font-weight: 700;
    color: var(--ink);
}

/* ---------- CTA band ----------
   Closing call to action: icon, message, actions. The outer element is only the
   backdrop — it is usually full-bleed, so .cta-band-inner below pulls the content
   back onto the 1200px column and owns the layout. Add .cta-band-photo and set
   --band-photo for a picture bleeding off the right edge, faded into the
   background so text over it stays legible at any width. */
.cta-band {
    background: var(--cream-dark);
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    padding: 0;
}

.cta-band-inner {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 1.75rem;
    max-width: 1200px;
    margin: 0 auto;
    padding: 2.25rem 1.25rem;
    width: 100%;
}

/* The photograph occupies a defined zone on the right rather than being faded in
   across the whole band. An earlier version put a left-to-right gradient over the
   entire width, but where the content actually ends moves with the viewport (61% of
   a 1920 window, 76% of a 1200 one), so no fixed gradient stop could track it — the
   actions kept landing on the picture and "Already approved? Sign in" rendered as
   dark text on a bright sunset.

   Anchoring the image to a right-hand zone and fading its LEFT EDGE with a mask is
   predictable at every width: the content column is reserved the rest, and the two
   never overlap. */
.cta-band-photo {
    position: relative;
    overflow: hidden;
}

.cta-band-photo::before {
    content: "";
    position: absolute;
    inset: 0 0 0 auto;
    width: min(40%, 32rem);
    background-image: var(--band-photo, none);
    background-size: cover;
    background-position: center;
    /* Fades in across its own left third, so the seam with the cream is soft. */
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 38%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 38%);
    pointer-events: none;
}

/* Content sits above the photo zone and stops short of it. */
.cta-band-photo .cta-band-inner {
    position: relative;
    z-index: 1;
}

@media (min-width: 900px) {
    .cta-band-photo .cta-band-inner {
        padding-right: min(36%, 28rem);
    }
}

/* Below 900px the band stacks; a picture behind stacked text is unreadable at any
   opacity, so it is dropped entirely rather than dimmed. */
@media (max-width: 899px) {
    .cta-band-photo::before { display: none; }
}

.cta-band h2 { margin: 0 0 .35rem; font-size: 1.3rem; }
.cta-band p { margin: 0; max-width: 46ch; }

.cta-band-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .85rem;
}

.cta-band-actions .btn { white-space: nowrap; }
@media (max-width: 720px) {
    .cta-band-inner { padding-inline: 1rem; }
}

/* ---------- Footer brand ---------- */
.footer-brand-row {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-bottom: .6rem;
}

.footer-logo-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 8px;
    background: #fff;
    padding: 3px;
}

.footer-logo-chip img { max-width: 100%; max-height: 100%; }

.footer-social {
    display: flex;
    gap: .55rem;
    margin-top: 1.1rem;
}

.footer-social a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .10);
    color: #dfe6d2;
}

.footer-social a:hover { background: rgba(255, 255, 255, .2); color: #fff; }
.footer-social svg { width: 16px; height: 16px; }

/* ---------- Partner links ----------
   Managed under Admin > Website > Partners; the whole section is absent from the home page
   while nothing is published. See Home.razor. */
.partner-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem 2rem;
}

.partner-list a { font-weight: 600; }

/* A logo stands in for the link text where the partner has one. Bounded here rather than
   trusted: the artwork is whatever an admin uploaded, and one full-resolution logo would
   otherwise set the height of the entire section. */
.partner-list img {
    display: block;
    max-height: 2.75rem;
    max-width: 11rem;
    width: auto;
    object-fit: contain;
}

/* Keeps a row's action buttons on one line in admin tables. */
.nowrap { white-space: nowrap; }

/* ---------- Responsive: section variants ---------- */
@media (max-width: 900px) {
    .step-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem 2rem; }
    /* A connector that points at nothing is worse than no connector. */
    .step-rail-item::after { display: none; }

    /* Once the rail wraps, the desktop arrangement (number, then icon on its own line,
       then title, then copy) spends about 180px on decoration across four steps. Pull
       the number, icon and title onto one line and let the copy sit beneath — same
       information, roughly half the height, and it scans as a numbered list. The
       connector that the stacked layout lined up with is already gone above. */
    .step-rail-item {
        display: grid;
        grid-template-columns: auto auto 1fr;
        align-items: center;
        column-gap: .7rem;
        row-gap: .3rem;
    }

    .step-rail-num { grid-column: 1; grid-row: 1; }

    .step-rail-icon { grid-column: 2; grid-row: 1; margin: 0; }
    .step-rail-icon svg { width: 23px; height: 23px; }

    .step-rail-item h3 { grid-column: 3; grid-row: 1; margin: 0; }
    .step-rail-item p { grid-column: 1 / -1; grid-row: 2; }

    .cta-band-inner {
        grid-template-columns: auto 1fr;
        row-gap: 1.25rem;
    }
    .cta-band-inner .cta-band-actions { grid-column: 1 / -1; }
}

@media (max-width: 640px) {
    /* The compact step layout is set in the 900px block; here it only goes to one column. */
    .step-rail { grid-template-columns: 1fr; gap: 1.25rem; }
    .grid-2 { grid-template-columns: 1fr; }
    .band-dark { padding: 2.5rem 0; }

    .cta-band-inner { grid-template-columns: 1fr; }
    .cta-band-inner .icon-circle { display: none; }
    .testimonial-card { padding-left: 3rem; }
}

/* ---------- Centred section heading ----------
   Alternative to the default left-aligned .section-title, with a short gold
   rule beneath. Suits pages that read as a pitch rather than as a workspace. */
.section-title-centered {
    text-align: center;
    font-size: 1.6rem;
    margin: 0 0 1.75rem;
}

.section-title-centered::after {
    content: "";
    display: block;
    width: 46px;
    border-top: 3px solid var(--gold-600);
    margin: .7rem auto 0;
}

/* ---------- Centred value card ----------
   Icon above a centred title. Reads as a benefit rather than a feature, so
   it suits marketing pages; the left-aligned default suits denser ones. */
.value-card-centered {
    text-align: center;
    padding-top: 2.4rem;
}

.value-card-centered .icon-circle {
    margin: -3.1rem auto .9rem;
    box-shadow: 0 0 0 6px var(--cream);
}

/* On a tinted band the icon's halo has to match the band, not the page. */
.band-tint .value-card-centered .icon-circle { box-shadow: 0 0 0 6px var(--cream-dark); }

.value-card-centered h3 { margin: 0 0 .5rem; }

/* ---------- Assurance note ----------
   One quiet reassuring line under a section — the objection a reader is
   already forming, answered before they leave. */
.assurance-note {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    margin: 1.5rem 0 0;
    font-size: .93rem;
    font-weight: 600;
    color: var(--ink-soft);
}

.assurance-note svg { width: 17px; height: 17px; color: var(--green-600); flex: 0 0 auto; }

/* ---------- Raster icon badge ----------
   The contact and How-It-Works pages use RFM's supplied icon set rather than the
   inline stroke icons elsewhere in this file: they are the client-approved
   artwork and include their own green disc, so there is no .icon-circle wrapper.
   Sized in CSS rather than in markup so a badge can be re-scaled in one place;
   the files are 128px square, i.e. 2x at the largest size used here. */
.icon-badge {
    flex: 0 0 auto;
    display: block;
    width: 46px;
    height: 46px;
}

.icon-badge-lg { width: 56px; height: 56px; }

/* ---------- Feature card ----------
   A .card with the breathing room a marketing page wants. The default padding is
   tuned for dense admin tables and reads cramped around a two-paragraph pitch. */
.card-feature { padding: 1.8rem 1.9rem; margin-bottom: 1.5rem; }

/* A card this wide runs to well over 100 characters a line on a large monitor,
   which is past the point where the eye loses its place returning to the left. */
.card-feature p { max-width: 68ch; }

/* .card-head is sized for an h3; these page sections lead with an h2. */
.card-head h2 { margin: 0; font-size: 1.4rem; }

/* ---------- Illustrated card ----------
   Copy on the left, artwork on the right. The artwork is decorative — it repeats
   nothing the copy does not already say — so it carries an empty alt and is
   dropped entirely below tablet width rather than being shrunk, where it would
   squeeze the copy into a column without adding anything. */
.card-illustrated {
    display: flex;
    align-items: center;
    gap: 1.75rem;
}

.card-illustrated-body { flex: 1 1 400px; min-width: 0; }
.card-illustrated-body > :last-child { margin-bottom: 0; }

.card-illustrated-figure { flex: 0 0 auto; width: clamp(165px, 21vw, 245px); }
.card-illustrated-figure img { width: 100%; height: auto; display: block; }

/* For a card with one short paragraph, where the full-size artwork would set the
   card's height and leave the copy floating in the middle of it. */
.card-illustrated-figure-sm { width: clamp(130px, 15vw, 175px); }

/* ---------- Highlighted card ----------
   Still an aside: the supplier signpost at the foot of a farmer-facing page.
   Tinted and rule-marked rather than white, so it does not read as a second
   equal-weight card competing with the ones above it. */
.card-highlight {
    background: var(--cream-dark);
    border-left: 4px solid var(--gold-600);
}

/* .btn-secondary's fill is var(--cream-dark) — the SAME token as this card, so the
   button measures 1.00:1 against its own background and only its 1.2:1 border is
   left drawing it. The control stops reading as a control. Same collision exists on
   .supplier-band (var(--green-100), 1.02:1), hence both selectors here.

   White fill alone would not fix it (1.11:1), so the boundary is carried by the
   border: green-600 on cream-dark is 4.07:1, past the 3:1 that WCAG 1.4.11 asks of
   a UI-component edge. Hover changes the FILL, not the border, so the feedback is
   visible too — it was 1.09:1 before. */
.card-highlight .btn-secondary,
.supplier-band .btn-secondary {
    background: #fff;
    border-color: var(--green-600);
    color: var(--green-900);
}

.card-highlight .btn-secondary:hover,
.supplier-band .btn-secondary:hover { background: var(--green-100); color: var(--green-900); }

/* ---------- Card overlapping the hero ----------
   Lifts the first card up into the banner above it. The hero's own margin-bottom
   is already in the stack, so the visible overlap is the sum of the two; tune it
   here rather than on the hero, which is shared. */
.card-overlap {
    position: relative;
    z-index: 1;
    margin-top: -4.1rem;
}

/* ---------- Contact list ----------
   Icon and label on the left, value on the right, ruled between rows. A <dl>
   would be the tidier semantic, but a two-column dl cannot carry a decorative
   icon inside the dt without that icon joining the term when read aloud, so
   this is a plain list and the label/value pairing is carried by layout. */
.contact-list {
    list-style: none;
    margin: 1.6rem 0 1.4rem;
    padding: 0;
}

.contact-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 1rem;
    padding: 1rem .25rem;
    border-bottom: 1px solid var(--line);
}

.contact-label { font-weight: 700; color: var(--green-900); }
.contact-value { text-align: right; color: var(--ink); }
.contact-value a { text-underline-offset: 3px; }

/* ---------- Page watermark ----------
   A quiet illustrated close for a short page, so a page carrying one card does
   not end in an expanse of cream. Purely decorative and non-interactive. The
   artwork is already matted onto the page's cream, so no opacity is applied —
   dimming it further just turns the field rows to mush. Set --watermark-photo
   inline as url(../img/...): the var is consumed HERE, in /css/app.css, so the
   path resolves against this stylesheet and not against the page. */
.page-watermark {
    height: clamp(210px, 29vw, 420px);
    margin-top: 3rem;
    background-image: var(--watermark-photo, none);
    background-size: cover;
    /* Not "bottom": the band is far wider than the artwork's 2.2:1, so cover crops
       hard vertically and anchoring at the bottom keeps only the crop rows. 30%
       holds the grain bins in frame, which is the half worth showing. */
    background-position: center 30%;
    background-repeat: no-repeat;
    pointer-events: none;
}

/* The watermark is meant to run into the footer. .site-main's bottom padding would
   otherwise leave a 3rem strip of bare cream between the two, which reads as a gap
   rather than as a close. Scoped with :has() so it only applies to a page that
   actually ends this way; browsers without :has() get the strip, not a broken page. */
.site-main:has(> .page-watermark:last-child) { padding-bottom: 0; }

/* ...and the footer's own top margin, which is the other half of that 3rem gap. */
.site-main:has(> .page-watermark:last-child) + .site-footer { margin-top: 0; }

/* Below this the copy is wide enough to reach into the photo zone, so the zone is
   abandoned: the picture goes back to covering the band and a uniform scrim comes
   back over it. Uniform, not directional — see the note on the 720px block for why
   a left-to-right scrim fails once the copy spans the full width. */
@media (max-width: 900px) {
    .hero-page::before,
    .hero-photo-right::before {
        inset: 0;
        width: auto;
        -webkit-mask-image: none;
        mask-image: none;
    }

    .hero-page::after,
    .hero-photo-right::after {
        display: block;
        background: linear-gradient(180deg, rgba(26, 32, 15, .84) 0%, rgba(26, 32, 15, .78) 100%);
    }
}

@media (max-width: 860px) {
    .card-illustrated-figure { display: none; }
    .card-illustrated { gap: 0; }
}

@media (max-width: 720px) {
    .card-feature { padding: 1.25rem 1.15rem; }
    .hero-page h1 { font-size: 1.95rem; }
    .hero-page .hero-lead { max-width: none; }

    /* Stands down here rather than at 560. The 720px block sets `.hero { padding }`
       as a SHORTHAND, which quietly drops this hero's bottom padding from 3.25rem to
       2.5rem; with the lift still applied the card's top edge ends up 2.4px clear of
       the hero copy instead of 14.4px, and butts against the gold rule under the
       Contact title. Below 720 the card is wide enough that the lift buys nothing
       anyway. */
    .card-overlap { margin-top: 0; }
}

@media (max-width: 560px) {
    /* .card-overlap is already zeroed in the 720px block above. */

    /* Stacked, the value belongs under its label rather than pushed to a right
       edge that is only a few characters away. */
    .contact-row {
        grid-template-columns: auto 1fr;
        row-gap: .15rem;
        padding: .85rem .1rem;
    }

    .contact-value { grid-column: 2; text-align: left; }

    /* Long e-mail addresses are the one value here that cannot wrap on a word. */
    .contact-value a { word-break: break-word; }
}

/* ============================================================
   FARMER ACCOUNT AREA
   Sidebar + content, sitting INSIDE MainLayout rather than being
   its own layout — the top nav, cart and footer stay the ones
   every other farmer page uses. Added 2026-08-25.
   ============================================================ */

/* 232px, not 208px, because Account Activity stacks its reference rail under the nav in
   this same column and the rail's prose needs the extra width. Shared by all four account
   pages so the sidebar does not visibly jump when you move between them. */
.account-shell {
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    gap: 1.75rem;
    align-items: start;
}

/* minmax(0, 1fr) above is load-bearing, not decoration: a grid track defaults to
   min-content, so a wide .table-wrap inside .account-main would push the whole grid
   wider than the page instead of scrolling inside its own box. */
.account-main { min-width: 0; }

.account-nav {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: .5rem;
    position: sticky;
    top: 1rem;
}

.account-nav a {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .58rem .7rem;
    border-radius: 8px;
    text-decoration: none;
    color: var(--ink);
    font-size: .93rem;
    font-weight: 500;
    line-height: 1.25;
}

.account-nav a:hover { background: var(--cream-dark); color: var(--green-900); }

.account-nav a.active {
    background: var(--green-700);
    color: #fff;
    font-weight: 600;
}

.account-nav svg { width: 18px; height: 18px; flex: 0 0 auto; }

/* ---------- Page head ----------
   Title block on the left, one piece of context or one action on the right. */
.page-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.35rem;
}

.page-head h1 { margin: 0 0 .2rem; font-size: 1.75rem; }
.page-head p { margin: 0; }

/* The farm name. Deliberately not a control — see FarmerAccountNav.razor. */
.page-head-context {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: .5rem .85rem;
    font-weight: 600;
    color: var(--green-900);
    font-size: .93rem;
}

/* ---------- Metric cards ---------- */
.metric-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.metric-card {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.1rem 1.15rem;
    display: flex;
    flex-direction: column;
    gap: .3rem;
}

/* A left edge rather than a ring. A full gold border round the card reads as an error state;
   a rule down one side reads as "look here first", which is what it means. */
.metric-card-attention { border-left: 3px solid var(--gold-600); }

.metric-top { display: flex; align-items: center; gap: .6rem; }

.metric-label { font-size: .87rem; font-weight: 600; color: var(--ink-soft); }

.metric-value {
    font-size: 1.65rem;
    font-weight: 700;
    color: var(--green-900);
    line-height: 1.15;
    /* Figures in a row of cards should line up on the decimal rather than drifting as
       digits change width. Falls back silently on faces without the feature. */
    font-variant-numeric: tabular-nums;
}

.metric-caption { font-size: .82rem; color: var(--ink-soft); }

.metric-link {
    margin-top: auto;
    padding-top: .7rem;
    border-top: 1px solid var(--line);
    font-size: .85rem;
    font-weight: 600;
    text-decoration: none;
}

.metric-link:hover { text-decoration: underline; text-underline-offset: 3px; }

.icon-circle-sm { width: 34px; height: 34px; }
.icon-circle-sm svg { width: 17px; height: 17px; }

/* ---------- Field pairs in a read-only panel ---------- */
.panel-title { margin: 0 0 1rem; font-size: 1.15rem; }

.field-label {
    margin: 0 0 .1rem;
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.field-value { margin: 0 0 1rem; }
.field-value:last-child { margin-bottom: 0; }

.grid-2-tight { gap: 1.5rem; max-width: none; margin: 0; }

/* ---------- Panel watermark ----------
   The Account Overview illustration. A background rather than an <img> so it can never
   take part in layout and push the address column around; pinned right and sized to the
   panel's own height. --panel-watermark is consumed HERE, in /css/app.css, so the url is
   written relative to this stylesheet — see the note on .page-watermark. */
.panel-watermark {
    position: relative;
    overflow: hidden;
}

.panel-watermark::after {
    content: "";
    position: absolute;
    inset: 0 0 0 auto;
    width: min(36%, 21rem);
    opacity: .7;
    background-image: var(--panel-watermark, none);
    background-size: cover;
    background-position: center bottom;
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 45%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 45%);
    pointer-events: none;
    z-index: 0;
}

/* Content above the artwork. Without this the watermark paints over the address. */
.panel-watermark > * { position: relative; z-index: 1; }

@media (max-width: 720px) {
    /* Stacked, the artwork would sit directly behind the address rather than beside it. */
    .panel-watermark::after { display: none; }
}

/* ---------- CTA strip ----------
   Icon, message, action. Same silhouette as .cta-band but card-sized and inside the
   content column. */
/* margin-top is a longhand on purpose: .card and .card-feature both set margin-bottom only,
   so nothing here uses the margin shorthand and nothing gets clobbered. The strip closes a
   page and needs air above it — without this it butts straight against the product grid or
   the table it follows. */
.cta-strip {
    display: flex;
    align-items: center;
    gap: 1.1rem;
    flex-wrap: wrap;
    margin-top: 2rem;
}

.cta-strip-text { flex: 1 1 300px; min-width: 0; }
.cta-strip-text strong { color: var(--green-900); }
.cta-strip-text p { margin: .15rem 0 0; }

/* ---------- Filter bar ----------
   A plain GET form. .toolbar exists but is built for interactive admin screens; this one
   has to align a submit button against inputs that carry labels. */
.filter-bar {
    display: flex;
    align-items: flex-end;
    gap: .75rem;
    flex-wrap: wrap;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: .9rem 1rem;
    margin-bottom: 1rem;
}

.filter-bar .form-field { margin-bottom: 0; min-width: 150px; }

/* A date input's intrinsic width is larger than any min-width you set on its wrapper,
   and a text input still defaults to roughly a 20-character box. Without these the
   fields refuse to shrink and the bar wraps to a second row while sitting half empty. */
.filter-bar input,
.filter-bar select { min-width: 0; width: 100%; }
.filter-bar .form-field-sm { min-width: 140px; flex: 0 0 auto; }
.filter-bar .spacer { flex: 1 1 auto; }
.filter-bar-actions { display: flex; gap: .4rem; align-items: center; }

.table-note { margin: .75rem 0 1.5rem; font-size: .87rem; }

/* ---------- Money ----------
   Direction, not decoration: red is what you owe and green is what came off the balance.
   Both are darkened well past the usual signalling colours so they still read as text —
   measured on white, amount-owed is 6.4:1 and amount-paid 5.1:1. Colour is never the only
   cue: the sign carries the same information. */
.amount-owed { color: #9a3324; font-variant-numeric: tabular-nums; }
.amount-paid { color: #2f6b3a; font-variant-numeric: tabular-nums; }
.data-table .num { font-variant-numeric: tabular-nums; }

/* ---------- Activity layout ----------
   Transactions plus an explanatory rail. The rail is reference material, so it follows
   the table in the DOM and is placed beside it here — a screen reader and a phone both
   get the transactions first. */
/* ---------- Account shell with a reference rail ----------
   The rail used to sit in its own column to the RIGHT of the transactions table, inside
   .account-main. That cost the table about 245px it could not spare: at 1200px of page,
   less 208px of sidebar, less the rail, a seven-column money table was down to ~690px and
   the Balance column fell off the end into a horizontal scrollbar.

   Widening the page past 1200px was the obvious alternative and is worse — the header and
   footer are capped at 1200, so the content would visibly overhang its own chrome.

   Instead the rail moves under the nav in the LEFT column, where the low-value blocks sit
   together and the table gets the whole content column (~900px, enough for every column).
   It is placed there by grid, not by DOM order: it stays last in the markup, so when this
   collapses to one column the reading order is nav, transactions, reference material —
   never reference material ahead of the numbers someone came for. */
@media (min-width: 861px) {
    .account-shell-rail { grid-template-rows: auto 1fr; }

    .account-shell-rail > .account-nav { grid-column: 1; grid-row: 1; }
    .account-shell-rail > .info-rail { grid-column: 1; grid-row: 2; }

    /* Spans both rows so the table is never boxed in by the rail's height. */
    .account-shell-rail > .account-main { grid-column: 2; grid-row: 1 / -1; }
}

/* Only Description should wrap. Left to itself the browser breaks "INV-2026-0018" and
   "$18,450.00" across lines to buy width for prose, which turns a four-row table into a
   twenty-line one and makes the figures hard to compare down the column. */
.activity-main .data-table th,
.activity-main .data-table td { padding: .6rem .55rem; }

.activity-main .data-table td:nth-child(1),
.activity-main .data-table td:nth-child(2),
.activity-main .data-table td:nth-child(4),
.activity-main .data-table td:nth-child(5),
.activity-main .data-table td:nth-child(6),
.activity-main .data-table td:nth-child(7) { white-space: nowrap; }

/* Four controls instead of Orders' three, so they share the row by flexing rather than by
   each holding a floor width. */
.activity-main .filter-bar .form-field { min-width: 0; flex: 1 1 120px; }
.activity-main .filter-bar .form-field-sm { min-width: 0; flex: 0 1 138px; }

.info-rail { display: flex; flex-direction: column; gap: 1rem; }
.info-rail .card { margin-bottom: 0; }

.rail-title { margin: 0 0 .75rem; font-size: 1rem; }

.rail-list { margin: 0; display: block; }
.rail-list dt { font-weight: 700; color: var(--green-900); font-size: .88rem; margin-top: .8rem; }
.rail-list dt:first-child { margin-top: 0; }
.rail-list dd { margin: 0; font-size: .84rem; color: var(--ink-soft); line-height: 1.45; }

@media (max-width: 860px) {
    .account-shell { grid-template-columns: minmax(0, 1fr); gap: 1rem; }

    /* Wraps rather than scrolling. An earlier version scrolled sideways with the scrollbar
       hidden. Four items measure about 480px, so on every phone it
       overflowed by a little and hid Profile behind no scrollbar, no fade and no other clue
       that anything was there. Two rows of two costs 40px and hides nothing. */
    .account-nav {
        flex-direction: row;
        flex-wrap: wrap;
        position: static;
    }
    .account-nav a { white-space: nowrap; flex: 1 1 auto; justify-content: center; }

    /* Stacked, the two rail cards sit side by side rather than as two more full-width
       blocks between the table and the footer. */
    .info-rail { flex-direction: row; flex-wrap: wrap; }
    .info-rail .card { flex: 1 1 260px; }
}

@media (max-width: 560px) {
    .page-head h1 { font-size: 1.4rem; }
    .page-head-context { font-size: .85rem; padding: .4rem .65rem; }
    .metric-grid { grid-template-columns: 1fr 1fr; gap: .7rem; }
    /* Nothing here may clip: these are the only figures on the page. */
    .metric-card { overflow-wrap: anywhere; }
    .metric-card { padding: .85rem .9rem; }
    .metric-value { font-size: 1.3rem; }
    /* Two cards across leaves no room for a per-card link; the sidebar reaches the same
       places and the label is what the card is actually for. */
    .metric-link { display: none; }
    .filter-bar { padding: .8rem .85rem; }
    .filter-bar .form-field,
    .filter-bar .form-field-sm { min-width: 0; flex: 1 1 100%; }
    .filter-bar-actions { flex: 1 1 100%; }
    .filter-bar-actions .btn { flex: 1 1 auto; text-align: center; }
    .cta-strip .icon-circle { display: none; }
    .cta-strip .btn { width: 100%; text-align: center; }
}

@media (max-width: 410px) {
    /* Two cards across leaves each about 111px of content box. A six-figure balance at
       1.3rem with tabular figures does not fit that and has no wrap opportunity, so below
       this the grid goes to one column rather than letting a number run out of its card.
       Placed after the 560px block on purpose — same specificity, later wins. */
    .metric-grid { grid-template-columns: 1fr; }
}


/* ============================================================
   MARKETPLACE CATALOG
   Rebuilt 2026-08-26 to Jackie's mockup. The card itself is
   styled under "Product/offer cards" further up; this block is
   the toolbar, the stepper and the pieces that are new.
   ============================================================ */

/* Visually hidden but still announced. The catalog toolbar's controls are labelled by their
   placeholder and their shape on screen; a screen reader gets these instead of three unnamed
   form fields. Not display:none — that would remove them from the accessibility tree too. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Built on .filter-bar, which already handles the card, the wrapping and the spacer. */
.catalog-bar { align-items: center; }
.catalog-bar .form-field { min-width: 170px; }
.catalog-bar .search-field { flex: 1 1 280px; min-width: 200px; position: relative; }

/* That :not() chain is not decoration. The global input rule above is written as
   input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=file]),
   and each :not() carries the specificity of an attribute selector — so it scores (0,6,1) and
   quietly beat a sane ".catalog-bar .search-field input" at (0,2,1). The padding never applied
   and the placeholder sat underneath the magnifier. Matching the chain gets to (0,7,1), which
   wins on merit rather than with !important. If the global rule's chain changes, change this. */
.catalog-bar .search-field
    input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]) {
    padding-left: 2.4rem;
}

.search-icon {
    position: absolute;
    left: .8rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--ink-soft);
    pointer-events: none;
    display: inline-flex;
}

.search-icon svg { width: 17px; height: 17px; }

.catalog-count { margin: 0; white-space: nowrap; }

/* ---------- Quantity stepper ----------
   The number input keeps its own spinners suppressed and the buttons drive it, so typing a
   quantity still works. min/max stay on the input because the buttons are only the
   convenient path — a typed value is clamped again in OfferCard before it is sent. */
.qty-stepper {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--line);
    border-radius: 8px;
    overflow: hidden;
    background: #fff;
    flex: 0 0 auto;
}

.qty-step {
    border: none;
    background: transparent;
    color: var(--green-900);
    font-size: 1.05rem;
    line-height: 1;
    width: 1.7rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.qty-step:hover:not([disabled]) { background: var(--cream-dark); }
.qty-step[disabled] { opacity: .35; cursor: not-allowed; }

/* The same :not() chain as the search field, and for the same reason — the global input rule
   scores (0,5,1) and beat a plain ".qty-stepper input[type=number]" at (0,2,1). Every shared
   property lost: the input kept the global 10px radius, 1.5px border, white fill and .8rem of
   side padding, so it rendered as a rounded pill floating inside the stepper with barely any
   room left for two digits. This scores (0,6,1). */
.qty-stepper
    input[type="number"]:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]) {
    width: 2.3rem;
    border: none;
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
    border-radius: 0;
    text-align: center;
    padding: .4rem .2rem;
    font-variant-numeric: tabular-nums;
    background: transparent;
    box-shadow: none;
}

.qty-stepper input[type="number"]:focus { outline: 2px solid var(--green-600); outline-offset: -2px; }

/* Chromium and Firefox draw spin buttons that fight the − and + for the same few pixels. */
.qty-stepper input[type="number"]::-webkit-outer-spin-button,
.qty-stepper input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-stepper input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* ---------- Offer facts with icons ---------- */
.offer-fact {
    display: flex;
    align-items: flex-start;
    gap: .45rem;
    margin: 0;
    font-size: .85rem;
    color: var(--ink-soft);
    line-height: 1.35;
}

.offer-fact-icon { color: var(--green-600); flex: 0 0 auto; display: inline-flex; padding-top: .05rem; }

/* Same treatment on the marketplace card. See .product-fact-alert. */
.offer-fact-alert { color: #7a5b12; }
.offer-fact-alert strong { color: #7a5b12; }
.offer-fact-alert .offer-fact-icon { color: var(--gold-600); }
.offer-fact-icon svg { width: 14px; height: 14px; }

/* ---------- Header cart link ----------
   A labelled pill rather than an icon with a floating badge. */
.cart-link { gap: .45rem; }
.cart-label { font-size: .9rem; font-weight: 600; white-space: nowrap; }

@media (max-width: 720px) {
    /* The icon carries it on a phone, where the header is already two rows and the label
       costs about 120px that Sign In and the primary action need more. */
    .cart-label { display: none; }
}

/* ---------- Footer account column ---------- */
.footer-account-email { font-size: .9rem; color: #dfe5d0; word-break: break-word; }
.site-footer .logout-form { margin-top: .35rem; }

/* ---------- Admin: category images ---------- */
.category-thumb {
    width: 64px;
    height: 48px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid var(--line);
    display: block;
    background: var(--cream);
}

.category-preview {
    width: 100%;
    max-width: 190px;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: 8px;
    border: 1px solid var(--line);
    background: var(--cream);
}

.form-field-preview { flex: 0 1 210px; }

@media (max-width: 720px) {
    .catalog-bar .form-field,
    .catalog-bar .search-field { flex: 1 1 100%; min-width: 0; }
    .catalog-bar .btn { flex: 1 1 auto; }

    /* Hidden from the eye, not from the accessibility tree. On a phone the count is one more
       line between the filters and the products they filter — but it carries role="status",
       and with a non-empty result set it is the only confirmation that a filter did anything.
       display:none would take it out of the tree and silence that announcement entirely. */
    .catalog-count {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
    }
}


/* ============================================================
   SIGNED-IN DASHBOARD
   Built on the pieces that already exist — .hero for the banner,
   .metric-grid for the summary row, .card/.data-table for the
   panels, .offer-grid for the offer cards. Only the three things
   with no existing equivalent are new: the banner's balance
   block, the alert list and the action tile. All three are
   written to be reusable rather than dashboard-specific.
   ============================================================ */

/* ---------- Welcome banner ----------
   .hero with a photo zone on the right (see .hero-photo-right, shared with the public page
   heroes) but inset in the content column rather than full-bleed, so it reads as the first
   card on the page instead of a second site header. */
.hero-banner { padding: 2rem 2.25rem; margin-bottom: 1.5rem; }

/* A narrower, quieter photo zone than the public heroes get. Those are the first thing a
   visitor sees and the picture is the point; this one sits behind an actions column, so it is
   cut back and dropped to half strength — enough to read as landscape, not enough to put white
   button text over a bright sunset. */
.hero-banner::before { width: min(40%, 25rem); opacity: .5; }

.hero-banner-inner {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
}

.hero-banner-intro { flex: 1 1 340px; min-width: 0; }

@media (min-width: 901px) {
    /* Reserved space for the photo, and DELIBERATELY narrower than the zone itself.
       .hero-photo-right masks the zone's left third to transparent, so the picture does not
       actually start showing until about two-thirds of the way across it — reserving the full
       width would leave a wide empty strip of green between the buttons and anything visible.

       Measured on the rendered banner at 1440px with the text and button fills removed, so
       only the backdrop is sampled: all three buttons sit on flat green-900 and white text on
       them holds 10.2:1. The brightest 1% anywhere in the banner is L=0.29, which would be
       3.1:1 — but no text reaches that far. Re-measure if either number here changes. */
    .hero-banner-inner { padding-right: min(30%, 19rem); }
}
.hero-banner h1 { font-size: 1.85rem; margin: 0 0 .3rem; max-width: none; }
.hero-banner .hero-lead { margin: 0; font-size: .98rem; }
.hero-banner .hero-actions { margin: 0; flex: 0 0 auto; }

.hero-balance {
    display: flex;
    align-items: flex-end;
    gap: 1.25rem;
    flex-wrap: wrap;
    margin-top: 1.4rem;
}

/* Scoped to .hero-banner, not left bare. `.hero p` sets font-size and max-width at (0,1,1),
   which beats a bare `.hero-balance-value` at (0,1,0) — the balance rendered at body size
   until these were qualified. Two classes score (0,2,0) and win. */
.hero-banner .hero-balance-label {
    margin: 0 0 .15rem;
    font-size: .82rem;
    max-width: none;
    color: #cdd6bb;
}

.hero-banner .hero-balance-value {
    margin: 0;
    font-size: 1.9rem;
    font-weight: 800;
    max-width: none;
    color: #fff;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

/* ---------- Alert list ----------
   Actionable items, each a tinted icon beside a sentence and a link. A list rather than a row
   of divs: it is a list, the count matters, and a screen reader gets that for free. */
.alert-grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 1.1rem;
}

.alert-item { display: flex; align-items: flex-start; gap: .75rem; }
.alert-message { margin: 0 0 .3rem; font-size: .92rem; line-height: 1.4; }

.alert-action {
    font-size: .88rem;
    font-weight: 700;
    text-decoration: none;
}

.alert-action:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Tints, not the solid green disc — four solid green circles in a row read as navigation
   rather than as notices, and the whole point of this block is that it looks different from
   the Quick Actions immediately below it. */
.icon-circle-warning { background: var(--gold-100); color: #7c6118; }
.icon-circle-info { background: var(--info-bg); color: var(--info); }
.icon-circle-neutral { background: var(--green-100); color: var(--green-700); }

.alert-empty { display: flex; align-items: center; gap: .85rem; }
.alert-empty strong { color: var(--green-900); }
.alert-empty p { margin: .1rem 0 0; }
.alert-empty .icon-circle { background: var(--green-100); color: var(--green-700); }

/* ---------- Action tiles ---------- */
.action-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
    gap: 1rem;
}

.action-card {
    display: flex;
    align-items: center;
    gap: .8rem;
    padding: .85rem 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: #fff;
    text-decoration: none;
    color: var(--ink);
    transition: border-color .15s ease, box-shadow .15s ease;
}

.action-card:hover {
    border-color: var(--green-600);
    box-shadow: var(--shadow);
    color: var(--ink);
}

.action-card-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.action-card-title { font-weight: 700; color: var(--green-900); font-size: .95rem; }
.action-card-detail { font-size: .82rem; color: var(--ink-soft); }

.action-card-chevron { color: var(--ink-soft); flex: 0 0 auto; display: inline-flex; }
.action-card-chevron svg { width: 16px; height: 16px; }
.action-card:hover .action-card-chevron { color: var(--green-600); }

/* ---------- Panels holding a table ----------
   .table-wrap draws its own card. Inside a .card that is a border around a border, so this
   strips the chrome and leaves the horizontal scrolling, which is the part that matters. */
.table-wrap-plain {
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
}

.table-wrap-plain .data-table th { background: transparent; }

/* A .table-wrap keeps its own frame inside a .panel-card, but not its rounded corners: the card
   has already drawn those, and a second set a pixel inside them reads as a box floating in a box
   — most visibly at the top-left, where the header band pulls away from the card's edge
   (Jackie, 2026-09-17). Square corners let the two frames land on each other as one.
   .panel-card sets overflow:hidden, so the card still clips the table to its own radius where
   the table is the last thing in it. */
.panel-card .table-wrap { border-radius: 0; }

/* ---------- A section with no card around it ----------
   For a heading plus a grid that supplies its own cards — wrapping the offer grid in another
   card would put a card inside a card. */
.band-plain { margin-top: 2rem; }
.band-plain .section-head { margin-bottom: 1rem; }

/* ---------- Compact offer card ----------
   The dashboard variant of .offer-card: no stepper, no Add to Order, one link out to the
   Marketplace where an order actually gets built. */
.offer-view-link {
    display: block;
    padding: .7rem 1rem .85rem;
    border-top: 1px solid #efede4;
    text-align: center;
    font-size: .9rem;
    font-weight: 700;
    text-decoration: none;
    color: var(--green-700);
}

.offer-view-link:hover { background: var(--cream); color: var(--green-600); }

/* Urgency flags. Gold for a closing date, green for newly posted — both opaque, so the
   photograph behind them never affects the contrast. */
.offer-flag-urgent { background: var(--gold-100); color: #7c6118; }
.offer-flag-new { background: var(--green-100); color: var(--green-700); }

@media (max-width: 980px) {
    /* Two tables side by side stop being readable before the shell itself collapses. */
    .dashboard-pair { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 1040px) {
    /* Four across, then two, then one. Left to auto-fit these land on three columns and
       orphan the fourth card on a row of its own, which reads as an afterthought rather than
       as one of a set. An explicit pair keeps the group square. */
    .metric-grid,
    .action-grid,
    .alert-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
    .metric-grid,
    .action-grid,
    .alert-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 720px) {
    .hero-banner { padding: 1.5rem 1.25rem; }
    .hero-banner h1 { font-size: 1.5rem; }
    .hero-banner .hero-actions { flex: 1 1 100%; }
    .hero-banner .hero-actions .btn { flex: 1 1 auto; text-align: center; }
    .hero-balance { margin-top: 1.1rem; }
    .hero-balance-value { font-size: 1.5rem; }
}

/* ---------------------------------------------------------------------
   SUPPLIER RELEASE — the Customer ID breakdown under each product row

   Lives inside a full-width <td> beneath its product, rather than as extra
   columns: the number of farms on a product varies from one to dozens, and a
   table that grows sideways with demand is unreadable on a tablet in a truck.
   --------------------------------------------------------------------- */

.data-table .breakdown {
    padding: .35rem 0 .5rem .75rem;
    border-left: 3px solid var(--green-100);
    /* Stuck to the left edge of the scroll port. The product table is wide enough to scroll
       sideways on a phone, and the breakdown is the part a supplier actually picks from — it
       must not be the part that slides out of view. Verified by rendering at 390px. */
    position: sticky;
    left: 0;
    max-width: min(100%, calc(100vw - 3.5rem));
}

.breakdown-label {
    display: block;
    font-size: .78rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: .35rem;
}

.breakdown-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: .4rem .5rem;
}

.breakdown-list li {
    /* Wraps so a full-width child — the delivery block on a direct-to-farm order — starts a new
       line instead of being squeezed in beside the quantity. Without this, flex-shrink simply
       crushes it and the farm's address reads as noise inside the chip. */
    flex-wrap: wrap;
    display: flex;
    align-items: baseline;
    gap: .4rem;
    padding: .25rem .6rem;
    background: var(--cream-dark);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-size: .88rem;
    font-variant-numeric: tabular-nums;
}

.breakdown-list li strong { color: var(--green-900); }

/* The multiplication sign matters: "100241 60" reads as one number. */
.breakdown-qty { font-weight: 600; }


/* Repeated on every chip rather than relying on the heading above the list — see the comment in
   ReleaseLines.razor. Sized down and cased up so it reads as a label and does not compete with
   the number it is labelling. */
.breakdown-id-label {
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* The row a breakdown sits in is a continuation of the row above it, not a row
   of its own — no hover highlight, no heavy rule between the two. */
.data-table tr:has(> td > .breakdown) td { border-bottom: 1px solid var(--line); }
.data-table tr:has(> td > .breakdown):hover td { background: transparent; }

/* ---------------------------------------------------------------------
   PACKAGE EDITOR + MONEY INPUT

   Package size stopped being one free-text box on 2026-08-26. Four controls
   now sit on one row — pack count x amount, unit, container — with a live
   preview of the label they will produce underneath.
   --------------------------------------------------------------------- */

.package-editor { margin-top: .35rem; }

.package-grid {
    display: grid;
    grid-template-columns: 6.5rem auto minmax(6rem, 1fr) minmax(9rem, 1.4fr) minmax(9rem, 1.4fr);
    gap: .5rem .7rem;
    align-items: end;
}

/* The fields are already inside a grid track, so their own bottom margin would
   push the row out of alignment with the multiplication sign. */
.package-grid .form-field { margin-bottom: 0; }

.package-times {
    padding-bottom: .55rem;
    color: var(--ink-soft);
    font-size: 1.05rem;
    text-align: center;
}

.package-preview {
    margin: .5rem 0 0;
    padding: .5rem .7rem;
    background: var(--green-100);
    border-radius: var(--radius);
    font-size: .92rem;
}

.package-preview strong { color: var(--green-900); }

@media (max-width: 760px) {
    .package-grid { grid-template-columns: 1fr 1fr; }
    /* Nothing to sit between once the fields wrap. */
    .package-times { display: none; }
}

@media (max-width: 420px) {
    .package-grid { grid-template-columns: 1fr; }
}

/* A currency symbol inside the field, so a price looks like a price while it is
   being typed. The input keeps type="number" — the symbol is decoration the
   browser never has to parse. */
.input-money { position: relative; display: block; }

.input-money-symbol {
    position: absolute;
    left: .7rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--ink-soft);
    pointer-events: none;
}

/* Matches the global input rule's :not() chain to reach (0,7,1) rather than
   reaching for !important — see the note on .catalog-bar above. */
.input-money
    input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]) {
    padding-left: 1.7rem;
    margin-bottom: 0;
}

/* =====================================================================
   RETAILER WORKSPACE — account menu, offer editor, offers list
   Built to Jackie's two mockups, 2026-08-26.
   ===================================================================== */

/* ---------- Account menu (header, all roles) ---------- */
/* A <details> element, so it opens, closes and announces itself with no
   JavaScript and no interactive circuit. js/rfm.js adds outside-click and
   Escape on top. */

.account-menu { position: relative; }

.account-menu-trigger {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .3rem .5rem .3rem .3rem;
    border-radius: 999px;
    cursor: pointer;
    list-style: none;
    white-space: nowrap;
}

/* Both are needed: the standard property and WebKit's older pseudo-element. */
.account-menu-trigger::-webkit-details-marker { display: none; }

.account-menu-trigger:hover { background: rgba(255, 255, 255, .12); }
.account-menu-trigger:focus-visible { outline: 2px solid var(--gold-600); outline-offset: 2px; }

.account-avatar {
    width: 2rem;
    height: 2rem;
    border-radius: 999px;
    background: var(--green-600);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .02em;
}

.account-menu-label { font-size: .92rem; font-weight: 600; }
.account-menu-chevron { width: 1rem; height: 1rem; opacity: .7; }
.account-menu[open] .account-menu-chevron { transform: rotate(180deg); }

.account-menu-panel {
    position: absolute;
    right: 0;
    top: calc(100% + .45rem);
    z-index: 40;
    min-width: 15rem;
    padding: .4rem;
    background: #fff;
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.account-menu-head {
    display: flex;
    flex-direction: column;
    padding: .55rem .6rem .6rem;
    border-bottom: 1px solid var(--line);
    margin-bottom: .3rem;
}

.account-menu-head-name { font-weight: 700; color: var(--green-900); }
.account-menu-head-email { font-size: .82rem; color: var(--ink-soft); word-break: break-word; }

.account-menu-links { display: flex; flex-direction: column; }

.account-menu-links a {
    padding: .45rem .6rem;
    border-radius: 6px;
    color: var(--ink);
    text-decoration: none;
    font-size: .93rem;
}

.account-menu-links a:hover { background: var(--cream-dark); color: var(--green-700); }

.account-menu-signout {
    margin: .3rem 0 0;
    padding: .35rem .25rem 0;
    border-top: 1px solid var(--line);
}

/* The dark admin topbar needs the panel to stay light and the trigger to sit
   on its own chrome. */
.admin-topbar .account-menu-trigger:hover { background: rgba(255, 255, 255, .1); }

/* ---------- Breadcrumb + wide page head ---------- */

.breadcrumb {
    display: flex;
    gap: .4rem;
    align-items: center;
    font-size: .88rem;
    color: var(--ink-soft);
    margin-bottom: .35rem;
}

.breadcrumb a { color: var(--ink-soft); }
.breadcrumb a:hover { color: var(--green-700); }
.breadcrumb [aria-current="page"] { color: var(--ink); }

.page-head-wide { align-items: flex-start; gap: 1rem; }

/* The title block SHRINKS; the actions do not. Without this the description paragraph sets the
   block's width from its own content, and a long one pushes the primary action onto a second
   row — which is what a full-width .admin-content made visible on the category editor. */
.page-head-wide > :first-child { flex: 1 1 22rem; min-width: 0; }
.page-head-wide > :first-child p { max-width: 74ch; }

.head-note {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: .4rem 0 0;
    color: var(--gold-600);
    font-size: .9rem;
    font-weight: 600;
}

.head-note-icon svg { width: 1.15rem; height: 1.15rem; display: block; }

/* Required-field marker. A red dot of meaning, with the real information
   carried by the field's own validation rather than by the colour. */
.req { color: var(--danger); font-weight: 700; }

/* ---------- Stepper ---------- */
/* A progress indicator, not pagination — every section is on the page and the
   steps light as each one completes. */

.stepper {
    list-style: none;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0;
    margin: 1.25rem 0 1.5rem;
    padding: 0;
}

.stepper-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .45rem;
    position: relative;
    text-align: center;
}

/* The connector is drawn per item and suppressed on the last, so the step count
   can change without touching CSS — same trick as .step-rail. */
.stepper-item::before {
    content: "";
    position: absolute;
    top: 1rem;
    left: 50%;
    right: -50%;
    height: 2px;
    background: var(--line);
}

.stepper-item:last-child::before { display: none; }
.stepper-item-done::before { background: var(--green-600); }

.stepper-dot {
    position: relative;
    z-index: 1;
    width: 2rem;
    height: 2rem;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border: 2px solid var(--line);
    color: var(--ink-soft);
    font-weight: 700;
    font-size: .9rem;
}

.stepper-dot svg { width: 1rem; height: 1rem; }

.stepper-item-done .stepper-dot {
    background: var(--green-700);
    border-color: var(--green-700);
    color: #fff;
}

.stepper-label { font-size: .86rem; color: var(--ink-soft); }
.stepper-item-done .stepper-label { color: var(--green-900); font-weight: 600; }

/* ---------- Two-column offer editor ---------- */

/* The one place a width cap survived .admin-content losing its own (2026-08-26). A table wants
   every pixel of a wide monitor; a form does not — a two-field category editor stretched across
   2560px is unreadable, and the fields would be a foot apart from their labels' own column. The
   cap is on the FORM layout only, so tables, the dashboard and master tracking still run full
   width. Remove this line rather than reinstating the one on .admin-content if it is unwanted. */
.offer-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 22rem;
    gap: 1.25rem;
    align-items: start;
    max-width: 1400px;
}

.offer-main { min-width: 0; }

.offer-rail { position: sticky; top: 1rem; }

.form-card { padding: 1.25rem 1.35rem 1.35rem; }

/* The same reset .panel-pad carries. Without it a trailing <p> or .alert adds its own margin on
   top of the card's bottom padding, so every form card in the console sits 20px from its title
   to the top edge and 37px from its last line to the bottom one. */
.form-card > :last-child { margin-bottom: 0; }

.numbered-title {
    display: flex;
    align-items: center;
    gap: .6rem;
    margin: 0 0 1rem;
    font-size: 1.1rem;
}

.numbered-badge {
    width: 1.7rem;
    height: 1.7rem;
    flex: 0 0 auto;
    border-radius: 999px;
    background: var(--green-700);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .88rem;
    font-weight: 700;
}

.numbered-badge-quiet { background: var(--cream-dark); color: var(--green-900); }
.numbered-badge-quiet svg { width: 1rem; height: 1rem; }

.chosen-product { display: flex; flex-direction: column; gap: .15rem; }

/* ---------- Product source choice ---------- */

.choice-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: .75rem;
    margin-bottom: 1rem;
}

.choice-card {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: .7rem;
    padding: .85rem 1rem .85rem .9rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: #fff;
    cursor: pointer;
}

.choice-card:hover { border-color: var(--green-600); }

.choice-card-on {
    border-color: var(--green-700);
    box-shadow: 0 0 0 1px var(--green-700) inset;
    background: #fcfdf9;
}

/* The radio stays a real radio — it carries the name, the group semantics and
   the arrow-key behaviour. It is only moved out of sight. */
.choice-input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    margin: 0;
}

.choice-mark {
    flex: 0 0 auto;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: .15rem;
    border-radius: 999px;
    border: 2px solid var(--line);
    background: #fff;
}

.choice-card-on .choice-mark {
    border-color: var(--green-700);
    background: var(--green-700);
    box-shadow: inset 0 0 0 3px #fff;
}

.choice-card:focus-within { outline: 2px solid var(--gold-600); outline-offset: 2px; }

.choice-icon { flex: 0 0 auto; color: var(--green-700); }
.choice-icon svg { width: 1.4rem; height: 1.4rem; display: block; }

.choice-text { display: flex; flex-direction: column; gap: .1rem; }
.choice-text strong { color: var(--green-900); }
.choice-text .muted { font-size: .87rem; }

/* ---------- Description + image, side by side ---------- */

.form-split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: .75rem 1rem;
}

/* ---------- Image drop zone ---------- */
/* The file input covers the card at zero opacity: a file drop only lands on a
   file input, so the input has to BE the target area rather than sit beside it. */

.dropzone {
    position: relative;
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .8rem;
    border: 1.5px dashed var(--line);
    border-radius: var(--radius);
    background: var(--cream);
}

.dropzone:hover, .dropzone:focus-within { border-color: var(--green-600); background: #fcfdf9; }
.dropzone:focus-within { outline: 2px solid var(--gold-600); outline-offset: 2px; }
.dropzone-busy { opacity: .65; }

.dropzone-preview {
    flex: 0 0 auto;
    width: 5rem;
    height: 5rem;
    border-radius: 8px;
    overflow: hidden;
    background: #fff;
    border: 1px solid var(--line);
}

.dropzone-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }

.dropzone-body { display: flex; flex-direction: column; gap: .15rem; font-size: .9rem; }
.dropzone-icon { color: var(--green-700); }
.dropzone-icon svg { width: 1.5rem; height: 1.5rem; display: block; }
.dropzone-lead strong { color: var(--green-900); }

.dropzone-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

/* ---------- Package preview + hints ---------- */

.package-preview-wide {
    display: flex;
    align-items: center;
    gap: .5rem;
    background: var(--gold-100);
}

.package-preview-icon { color: var(--gold-600); flex: 0 0 auto; }
.package-preview-icon svg { width: 1.1rem; height: 1.1rem; display: block; }

.hint-row {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem 2rem;
    justify-content: space-between;
    margin-top: .6rem;
    font-size: .87rem;
}

.lock-note {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-left: .4rem;
    font-weight: 400;
    font-size: .82rem;
    color: var(--ink-soft);
}

.lock-note svg { width: .9rem; height: .9rem; }

/* ---------- Summary rail ---------- */

.summary-card { padding: 1.1rem 1.15rem 1.2rem; }

.summary-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .9rem;
}

.summary-head .panel-title { margin: 0; }

.summary-product {
    display: flex;
    gap: .7rem;
    align-items: flex-start;
    padding-bottom: .9rem;
    border-bottom: 1px solid var(--line);
}

.summary-product > div { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }

.summary-thumb {
    flex: 0 0 auto;
    width: 4rem;
    height: 4rem;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid var(--line);
    background: #fff;
}

.summary-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.summary-kicker {
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--info);
}

.summary-name { color: var(--green-900); font-size: 1.02rem; }

.summary-rows { margin: .9rem 0 0; }
.summary-rows > div {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: .4rem 0;
    border-bottom: 1px solid var(--line);
    font-size: .9rem;
}
.summary-rows dt { color: var(--ink-soft); }
.summary-rows dd { margin: 0; text-align: right; font-weight: 600; color: var(--ink); }

.dot {
    display: inline-block;
    width: .5rem;
    height: .5rem;
    border-radius: 999px;
    margin-right: .3rem;
    background: var(--ink-soft);
}

.dot-success { background: var(--green-600); }
.dot-warning { background: var(--gold-600); }
.dot-danger { background: var(--danger); }
.dot-info { background: var(--info); }

.summary-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    margin: .9rem 0 0;
    padding: .6rem .75rem;
    background: var(--gold-100);
    border-radius: var(--radius);
    font-size: .92rem;
}

.summary-total strong { font-size: 1.15rem; color: var(--green-900); }

.summary-checklist {
    list-style: none;
    margin: .9rem 0 0;
    padding: .8rem .75rem;
    background: var(--cream-dark);
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    gap: .45rem;
    font-size: .89rem;
}

.summary-checklist li { display: flex; align-items: center; gap: .5rem; }
.summary-check-icon { flex: 0 0 auto; display: inline-flex; }
.summary-check-icon svg { width: 1.05rem; height: 1.05rem; display: block; }
.summary-check-done .summary-check-icon { color: var(--green-600); }
.summary-check-todo .summary-check-icon { color: var(--gold-600); }
.summary-check-todo { color: var(--ink-soft); }

.summary-actions {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    margin-top: 1rem;
}

.summary-actions .btn { width: 100%; justify-content: center; }
.summary-foot { margin: .6rem 0 0; text-align: center; font-size: .82rem; }

/* ---------- Offers list ---------- */

.btn-lead { display: inline-flex; align-items: center; gap: .4rem; }

/* Air above a button that follows prose or a list of cards. The supplier-confirmations panel
   had "Open the release queue" sitting flush against the supplier row above it, so the action
   read as part of that row rather than as the panel's own next step. */
.btn-spaced-top { margin-top: .9rem; }

.stat-tiles {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: .85rem;
    margin: 1rem 0 1.25rem;
}

.stat-tile {
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: 1rem 1.1rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.stat-tile > div { display: flex; flex-direction: column; min-width: 0; }
.stat-tile-label { font-size: .88rem; color: var(--ink-soft); }
.stat-tile-value { font-size: 1.7rem; font-weight: 700; color: var(--green-900); line-height: 1.15; }
.stat-tile-caption { font-size: .82rem; color: var(--ink-soft); }

.icon-circle-success { background: var(--green-100); color: var(--green-700); }
.icon-circle-danger { background: var(--danger-bg); color: var(--danger); }

.panel-card { padding: 0; overflow: hidden; }

/* ---------- Tabs ---------- */

.tabs {
    display: flex;
    gap: .25rem;
    padding: 0 1rem;
    box-shadow: inset 0 -1px 0 var(--line);
    overflow-x: auto;
    overflow-y: hidden;
}

.tab {
    appearance: none;
    background: none;
    border: none;
    border-bottom: 3px solid transparent;
    padding: .85rem .7rem;
    font: inherit;
    font-size: .93rem;
    color: var(--ink-soft);
    cursor: pointer;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
}

.tab:hover { color: var(--green-700); }
.tab:focus-visible { outline: 2px solid var(--green-700); outline-offset: -2px; border-radius: 4px; }
.tab-on { color: var(--green-900); font-weight: 700; border-bottom-color: var(--green-700); }

.tab-count {
    font-size: .8rem;
    font-weight: 600;
    color: var(--ink-soft);
    background: var(--cream-dark);
    border-radius: 999px;
    padding: .05rem .4rem;
}

.tab-on .tab-count { background: var(--green-100); color: var(--green-900); }

/* The filter bar and table sit inside the panel card, so they need their own
   padding back — .panel-card zeroes it so the tabs can reach the edges. */
.panel-card .filter-bar,
.panel-card .empty-state,
.panel-card .alert { margin: 1rem; }

.reset-btn { align-self: flex-end; }

/* The filter bar inside the panel card is a plain row, not a second card —
   .filter-bar's own frame read as a box inside a box. */
.panel-card .filter-bar {
    border: none;
    background: transparent;
    padding: 0;
    box-shadow: none;
}

/* .search-field's icon is absolutely positioned, so it needs a positioned
   ancestor. The existing rules are scoped to .catalog-bar, so dropped into the
   offers filter bar the magnifier flew to the corner of the page — the same
   trap .offer-flag fell into on the offers table. Scoped to the field itself
   here so it works wherever the field is used. */
.search-field { position: relative; flex: 1 1 16rem; min-width: 12rem; }

.search-field .search-icon {
    position: absolute;
    left: .65rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--ink-soft);
    pointer-events: none;
}

.search-field .search-icon svg { width: 1.05rem; height: 1.05rem; display: block; }

/* Matches the global input rule's :not() chain to reach (0,7,1) rather than
   reaching for !important — the same technique as .catalog-bar and .input-money. */
.search-field
    input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]) {
    padding-left: 2.3rem;
}

/* ---------- Password show/hide (Shared/PasswordReveal.razor) ---------- */
.password-field { position: relative; display: block; }


.password-field
    input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]) {
    width: 100%;
    padding-right: 2.9rem;
}

.password-field input::-ms-reveal { display: none; }

.password-toggle {
    position: absolute;
    top: 50%;
    right: .3rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.3rem;
    height: 2.3rem;
    padding: 0;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--ink-soft);
    cursor: pointer;
}

.password-toggle:hover { color: var(--green-700); background: var(--cream-dark); }
.password-toggle:focus-visible { outline: 2px solid var(--gold-600); outline-offset: 1px; }
.password-toggle svg {
    position: absolute;
    width: 1.25rem;
    height: 1.25rem;
    transition: opacity .18s ease, transform .18s ease;
}

.password-toggle .password-toggle-hide { opacity: 0; transform: scale(.6) rotate(-20deg); }
.password-field.is-visible .password-toggle .password-toggle-show { opacity: 0; transform: scale(.6) rotate(20deg); }
.password-field.is-visible .password-toggle .password-toggle-hide { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    .password-toggle svg { transition: none; }
}

.alert-dismissible { display: flex; align-items: flex-start; gap: .75rem; justify-content: space-between; }

.alert-close {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    background: none;
    border: none;
    border-radius: 6px;
    font-size: 1.2rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
    color: inherit;
    opacity: .7;
}

.alert-close:hover { opacity: 1; }

/* ---------- Offer rows ---------- */

.offers-table td { vertical-align: middle; }

.product-cell { display: flex; align-items: center; gap: .7rem; }
.product-cell > div { display: flex; flex-direction: column; min-width: 0; }

.product-thumb {
    flex: 0 0 auto;
    width: 3rem;
    height: 3rem;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid var(--line);
    background: #fff;
}

.product-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.product-link { font-weight: 600; }
.product-cell .muted { font-size: .82rem; }

.offers-table td > .muted { display: block; font-size: .82rem; }

.stock-cell { display: flex; align-items: flex-start; gap: .5rem; }
.stock-cell > div { display: flex; flex-direction: column; }
.stock-icon { flex: 0 0 auto; display: inline-flex; margin-top: .1rem; }
.stock-icon svg { width: 1.05rem; height: 1.05rem; display: block; }
.stock-ok { color: var(--green-600); }
.stock-warn { color: var(--gold-600); }
.stock-note { font-size: .82rem; font-weight: 600; }
.stock-note-warn { color: var(--gold-600); }

/* ---------- Row actions ---------- */

.row-actions { display: flex; align-items: center; gap: .4rem; }

.row-menu { position: relative; }
.row-menu-narrow { display: none; }

@media (max-width: 860px) {
    .row-actions.row-actions-wide { display: none; }
    .row-menu-narrow { display: block; }
}

.row-menu-trigger {
    list-style: none;
    cursor: pointer;
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    color: var(--ink-soft);
    line-height: 1;
}

.row-menu-trigger::-webkit-details-marker { display: none; }
.row-menu-trigger:hover { border-color: var(--green-600); color: var(--green-700); }
.row-menu-trigger:focus-visible { outline: 2px solid var(--gold-600); outline-offset: 2px; }

/* Fixed, not absolute. The panel lives inside .table-wrap, which scrolls
   horizontally — and a scroll container clips on BOTH axes, so an absolutely
   positioned menu was sliced off at the bottom of the table. js/rfm.js places
   it against the trigger on open and closes it on scroll. With JavaScript off
   it falls back to sitting under the trigger, still inside the clip, which is
   why the menu is never the only way to reach an action. */
.row-menu-panel {
    position: fixed;
    z-index: 60;
    min-width: 14rem;
    padding: .3rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    text-align: left;
}

.row-menu-panel button,
.row-menu-panel a {
    appearance: none;
    background: none;
    border: none;
    font: inherit;
    font-size: .9rem;
    text-align: left;
    padding: .45rem .55rem;
    border-radius: 6px;
    cursor: pointer;
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
}

.row-menu-panel button:hover,
.row-menu-panel a:hover { background: var(--cream-dark); color: var(--green-700); }

.row-menu-panel button:disabled { opacity: .5; cursor: default; }
.row-menu-panel button:disabled:hover { background: none; color: var(--ink); }

.row-menu-panel hr { border: none; border-top: 1px solid var(--line); margin: .3rem 0; }
/* Qualified: .row-menu-panel button already scores (0,1,1), so a bare
   .row-menu-danger at (0,1,0) lost and the destructive item rendered in the
   same ink as the rest. */
.row-menu-panel .row-menu-danger,
.row-menu-panel .btn-danger { color: var(--danger); }

.row-menu-panel .row-menu-danger:hover,
.row-menu-panel .btn-danger:hover { background: var(--danger-bg); color: var(--danger); }

/* ---------- Table footer ---------- */

.table-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem 1rem;
    padding: .85rem 1rem;
    border-top: 1px solid var(--line);
}

.table-foot p { margin: 0; font-size: .87rem; }
.table-foot-controls { display: flex; align-items: center; gap: 1rem; }

.table-foot-controls .form-field {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 0;
}

.table-foot-controls label { margin: 0; font-size: .87rem; white-space: nowrap; }
.table-foot-controls select { width: auto; margin: 0; }

.pager { display: flex; align-items: center; gap: .4rem; }

.pager-btn {
    appearance: none;
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: #fff;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    color: var(--ink);
}

.pager-btn:hover:not(:disabled) { border-color: var(--green-600); color: var(--green-700); }
.pager-btn:disabled { opacity: .4; cursor: default; }
.pager-current { font-size: .87rem; color: var(--ink-soft); min-width: 4rem; text-align: center; }

/* A date range that wraps mid-range reads as two dates, and a status that wraps
   reads as two statuses. */
.nowrap-text { white-space: nowrap; }

/* A list inside an alert, without the browser's default indent and spacing. */
.plain-list { list-style: none; margin: .4rem 0; padding: 0; }
.plain-list li { padding: .1rem 0; }

/* The label may wrap; the value should not. */
.summary-rows dt { padding-right: .5rem; }

/* ---------- Supplier listing preview ---------- */

.listing-preview { max-width: 22rem; margin-bottom: 1.25rem; }

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
    /* The rail stops being a rail and becomes the last block on the page —
       which is where a summary belongs once it can no longer sit alongside. */
    .offer-layout { grid-template-columns: minmax(0, 1fr); }
    .offer-rail { position: static; }
    .stat-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
    /* Labels under the dots would collide long before this; the numbers keep
       the sense of progress and the current step is announced either way. */
    .stepper-label { display: none; }
    .stepper { margin-bottom: 1rem; }
    .table-foot { justify-content: center; }
}

@media (max-width: 560px) {
    .stat-tiles { grid-template-columns: minmax(0, 1fr); }
    /* The business name is the first thing to go: the avatar and the chevron
       carry the control, and the name is repeated inside the panel. */
    .account-menu-label { display: none; }
    .hint-row { flex-direction: column; }
}


/* =====================================================================
   ADMIN CONSOLE — shell chrome, master tracking, product editor,
   order review. Built to Jackie's three mockups, 2026-08-26.
   Notes and deviations: claude/admin-console-2026-08-26.md
   ===================================================================== */

/* ---------- Sidebar: icons and the pending badge ---------- */

.admin-nav a { display: flex; align-items: center; gap: .55rem; }

.admin-nav-icon {
    display: inline-flex;
    flex: 0 0 auto;
    width: 1.05rem;
    height: 1.05rem;
    opacity: .8;
}

.admin-nav-icon svg { width: 100%; height: 100%; display: block; }
.admin-nav a:hover .admin-nav-icon { opacity: 1; }
.admin-nav a.active .admin-nav-icon { opacity: 1; color: var(--gold-600); }

/* Pushed to the far end of the row, so a two-word label and a one-word label
   still line their counts up. */
.admin-nav-badge {
    margin-left: auto;
    min-width: 1.35rem;
    padding: .05rem .4rem;
    border-radius: 999px;
    background: var(--gold-600);
    color: #2b2205;
    font-size: .72rem;
    font-weight: 700;
    line-height: 1.5;
    text-align: center;
}

.admin-sidebar-footer a { display: flex; align-items: center; gap: .55rem; }

/* ---------- Page titles ---------- */
/* Three variants, all the same row: an icon chip, a status badge, or a lock
   note sitting beside the heading rather than under it. */

.title-with-icon,
.title-with-badge {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-wrap: wrap;
}

.title-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 10px;
    background: var(--green-100);
    color: var(--green-700);
}

.title-icon svg { width: 1.35rem; height: 1.35rem; display: block; }

/* The same badge beside a panel's h2 rather than a page's h1. The full 2.4rem square is tuned to
   a 1.75rem title and overpowers a 1.15rem one. */
.title-icon-sm { width: 1.85rem; height: 1.85rem; border-radius: 8px; }
.title-icon-sm svg { width: 1.05rem; height: 1.05rem; }

/* .lock-note carries its own margin-left, so no gap here. */
.title-with-lock { display: flex; align-items: center; flex-wrap: wrap; }

.head-actions { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }

/* ---------- Breadcrumb on the dark topbar ---------- */
/* .breadcrumb was written for a cream page and uses --ink-soft, which is all but
   invisible on the admin chrome — the trail read as an empty bar. */
.admin-topbar .breadcrumb { margin: 0; color: rgba(255, 255, 255, .6); }
.admin-topbar .breadcrumb a { color: var(--gold-100); }
.admin-topbar .breadcrumb a:hover { color: #fff; }
.admin-topbar .breadcrumb [aria-current="page"] { color: #fff; font-weight: 600; }

.back-link {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin-bottom: .55rem;
    font-size: .9rem;
    color: var(--ink-soft);
    text-decoration: none;
}

.back-link:hover { color: var(--green-700); }

/* ---------- Panel head ---------- */
/* A title row across the top of a .panel-card, above a table it must not
   inherit the padding of — .panel-card zeroes its own padding so the table can
   reach the edges, so this row puts it back for itself only. */

.panel-head {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
    padding: .85rem 1rem;
    border-bottom: 1px solid var(--line);
}

.panel-head .panel-title { margin: 0; font-size: 1.05rem; }
.panel-head p { margin: 0; }
.panel-head .spacer { flex: 1 1 auto; }

/* For the loading and empty lines that sit directly in a .panel-card. */
.panel-pad { margin: 0; padding: 1rem; }

/* An alert inside a padded panel body.

   `.panel-card .alert { margin: 1rem }` exists so an alert dropped straight into an UNPADDED
   panel is inset from its edges. Inside .panel-pad that margin lands on top of the wrapper's own
   padding, so the alert sits 2rem in while the paragraph above it sits at 1rem — a visible step
   in and back out, on the one element meant to be read first. */
.panel-pad .alert { margin: 0 0 1rem; }

/* A .panel-pad used as a WRAPPER — a body of prose and controls under a .panel-head, as on the
   supplier release queue and the product import — rather than around a single paragraph. Its
   first and last children give up their outer margins, because a leading <p> or a trailing
   .actions-row otherwise adds its own 1rem on top of the padding and the block sits visibly
   lower inside the card than it sits to the sides.

   These two stay LAST of the .panel-pad rules on purpose: the alert rule above is equally
   specific, so anything placed after these would beat them and the reset would fail silently on
   a body that happens to end in an alert. */
.panel-pad > :first-child { margin-top: 0; }
.panel-pad > :last-child { margin-bottom: 0; }

/* The filter bar inside this one already has its padding from the existing
   .panel-card .filter-bar rule; the card only needs to stop clipping the focus
   ring on the controls sitting flush to its edge. */
.panel-card-padded { overflow: visible; }

.count-chip {
    display: inline-block;
    padding: .1rem .55rem;
    border-radius: 999px;
    background: var(--cream-dark);
    color: var(--ink-soft);
    font-size: .78rem;
    font-weight: 600;
    white-space: nowrap;
}

/* ---------- Segmented control ---------- */
/* Buttons, not links: these filter rows already on the page and must not add
   history entries. Each carries aria-pressed, so the visual state is not the
   only state. */

.segmented {
    display: inline-flex;
    gap: .15rem;
    padding: .15rem;
    background: var(--cream-dark);
    border: 1px solid var(--line);
    border-radius: 999px;
}

/* Scroll rather than disappear. .segment is nowrap and .segmented is an inline-flex that cannot
   shrink below its content, so on a tablet — where the 230px sidebar still eats the width — a
   tab set carrying counts ("Rejected (88)") overflows and is clipped outright by
   .panel-card's overflow:hidden, with no way to reach the last tab. */
.segmented { max-width: 100%; min-width: 0; overflow-x: auto; }

.segment {
    appearance: none;
    border: none;
    background: none;
    padding: .3rem .8rem;
    border-radius: 999px;
    font: inherit;
    font-size: .85rem;
    font-weight: 600;
    color: var(--ink-soft);
    cursor: pointer;
    white-space: nowrap;
}

.segment:hover { color: var(--green-700); }
.segment:focus-visible { outline: 2px solid var(--gold-600); outline-offset: 2px; }

.segment-on {
    background: #fff;
    color: var(--green-900);
    box-shadow: 0 1px 2px rgba(36, 40, 31, .14);
}

/* ---------- Sortable column headings ---------- */
/* The <th> keeps aria-sort; the arrow is decorative. */

.sort-btn {
    appearance: none;
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--green-900);
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    cursor: pointer;
    min-height: 24px;
}

.sort-btn:hover { color: var(--green-600); }

.sort-btn:focus-visible {
    outline: 2px solid var(--gold-600);
    outline-offset: 2px;
    border-radius: 4px;
}

.sort-on { color: var(--green-700); }
.sort-arrow { font-size: .8rem; line-height: 1; }
.sort-arrow-idle { opacity: .35; }

/* A numeric column is right-aligned, so its heading button has to fill the cell
   to be pushed over with it. */
.data-table th.num .sort-btn { display: flex; width: 100%; justify-content: flex-end; }

/* ---------- Buttons: menu trigger, file picker, quiet danger ---------- */

/* A <summary> styled as a button. Both marker rules are needed: the standard
   property and WebKit's older pseudo-element. */
.btn-menu {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    list-style: none;
    cursor: pointer;
}

.btn-menu::-webkit-details-marker { display: none; }

/* A <label> wrapping a visually hidden file input. */
.btn-file {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    cursor: pointer;
}

/* .btn is display:inline-block, so an <svg> inside it with no width of its own
   stretches to fill the button — the trash icon on Remove rendered 100px tall.
   Any .btn carrying an icon lays out as a flex row and sizes the icon here,
   rather than each button remembering to. */
.btn-file,
.btn-menu,
.btn-lead,
.btn-danger-quiet {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
}

.btn-file svg,
.btn-menu svg,
.btn-lead svg,
.btn-danger-quiet svg,
.link-btn svg { width: 1rem; height: 1rem; display: block; flex: 0 0 auto; }

/* Destructive, but not the loudest thing on the screen — these sit beside a
   primary action rather than replacing it. */
.btn-danger-quiet {
    background: #fff;
    color: var(--danger);
    border-color: #e3c8c3;
}

.btn-danger-quiet:hover:not(:disabled) {
    background: var(--danger-bg);
    border-color: var(--danger);
}

.btn-danger-quiet:disabled { opacity: .5; cursor: default; }

/* A button that reads as a link — used for "View activity history", which
   toggles a section rather than navigating. */
.link-btn {
    appearance: none;
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: .9rem;
    color: var(--green-700);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}

.link-btn:hover { color: var(--green-900); }
.link-btn svg { width: 1rem; height: 1rem; display: block; }

/* ---------- Product editor: image and save bar ---------- */

.image-preview {
    aspect-ratio: 4 / 3;
    margin-bottom: .85rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--cream);
}

.image-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The rail is narrow, so the drop zone stacks instead of sitting icon-beside-text. */
.dropzone-stacked {
    flex-direction: column;
    text-align: center;
    gap: .3rem;
    padding: 1.1rem .8rem;
}

.dropzone-stacked .dropzone-body { align-items: center; }

/* Replace and Remove are a pair and should be the same width, which flex-wrap
   cannot promise. */
.actions-row-even {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem;
}

.actions-row-even > * { width: 100%; }

/* Four status buttons that must be allowed to run onto a second line without
   the last one being pushed out of sight. */
.actions-row-wrap { row-gap: .5rem; }

.save-bar {
    display: flex;
    max-width: 1400px;
    align-items: center;
    justify-content: flex-end;
    gap: .6rem;
    margin-top: 1.25rem;
    padding: .9rem 1.1rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* ---------- Order review ---------- */

/* The detail tiles carry several lines of contact information rather than one
   number, so they align to the top instead of centring on the icon. */
.stat-tiles-detail .stat-tile { align-items: flex-start; }
/* An email address is longer than the tile is wide. overflow-wrap breaks it only
   when it genuinely will not fit, where word-break would break every long word. */
.stat-tiles-detail .stat-tile-caption {
    font-size: .8rem;
    line-height: 1.45;
    overflow-wrap: anywhere;
}

.stat-tile-strong {
    font-size: 1.02rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--green-900);
}

/* A table with seven columns and three controls in each row has no useful narrow
   form: below this the browser crushes the product name to one word per line
   rather than offering the scroll that .table-wrap is there to provide. */
.wide-table { min-width: 52rem; }

/* Left to itself the browser gives the first column whatever is left over, which
   on a tablet was one word per line. The product name is the row's identity. */
.wide-table th:first-child,
.wide-table td:first-child { min-width: 11rem; }

/* Unless the first column is a select checkbox, in which case the rule above opens an 11rem
   empty gutter down the left of every row. */
.wide-table-select th:first-child,
.wide-table-select td:first-child { min-width: 0; width: 1%; }

/* ---------- Frozen columns ---------- */
.table-wrap .sticky-first th:first-child,
.table-wrap .sticky-first td:first-child,
.table-wrap .sticky-actions th:last-child,
.table-wrap .sticky-actions td:last-child {
    position: sticky;
    z-index: 2;
    background: #fff;
}

.table-wrap .sticky-first th:first-child,
.table-wrap .sticky-first td:first-child { left: 0; }

.table-wrap .sticky-actions th:last-child,
.table-wrap .sticky-actions td:last-child { right: 0; }

.table-wrap .sticky-first th:first-child,
.table-wrap .sticky-first td:first-child {
    box-shadow: 1px 0 0 var(--line), 6px 0 8px -6px rgba(35, 40, 30, .18);
}

.table-wrap .sticky-actions th:last-child,
.table-wrap .sticky-actions td:last-child {
    box-shadow: -1px 0 0 var(--line), -6px 0 8px -6px rgba(35, 40, 30, .18);
}

.table-wrap .sticky-first thead th:first-child,
.table-wrap .sticky-actions thead th:last-child { z-index: 3; background: var(--cream-dark); }

.table-wrap .sticky-first tr:hover > td:first-child,
.table-wrap .sticky-actions tr:hover > td:last-child { background: #fbfaf5; }

.table-wrap .sticky-actions td:last-child:has(.row-menu[open]) { z-index: 61; }

.table-wrap .sticky-first td[colspan],
.table-wrap .sticky-actions td[colspan] { position: static; box-shadow: none; }


/* A short explanation under a control — narrower than the paragraph the form is made of, and
   attached to the field above it rather than floating between two. */
.field-hint { margin: .15rem 0 0; font-size: .84rem; line-height: 1.4; }

/* A field that is filled in and doing nothing. Not an error — the form still saves, and the
   value is not wrong so much as inert — so it takes the gold "look at this" tone rather than
   the red "you cannot proceed" one. The icon is the cue for anyone who cannot see the tint. */
.field-warning {
    display: flex;
    align-items: flex-start;
    gap: .35rem;
    margin-top: .35rem;
    padding: .4rem .55rem;
    background: var(--gold-100);
    border-radius: 6px;
    color: #7a5b12;
    font-size: .82rem;
    line-height: 1.4;
}

.field-warning::before {
    content: "!";
    flex: 0 0 auto;
    width: 1.05rem;
    height: 1.05rem;
    margin-top: .05rem;
    border-radius: 999px;
    background: var(--gold-600);
    color: #2b2205;
    font-weight: 700;
    font-size: .74rem;
    line-height: 1.05rem;
    text-align: center;
}

/* A one-time password an admin has to read out or transcribe. Monospaced with generous letter
   spacing because the cost of confusing l and 1 here is a support call. */
.temp-password {
    display: inline-block;
    padding: .3rem .6rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--cream-dark);
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 1rem;
    letter-spacing: .06em;
    user-select: all;
}

/* Full-height controls in a table row make it twice as tall as it needs to be. */
input.qty-input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]) {
    width: 5.5rem;
    padding: .35rem .5rem;
    font-size: .9rem;
    text-align: right;
}

.data-table select {
    padding: .35rem 1.9rem .35rem .55rem;
    font-size: .88rem;
    background-position: right .5rem center;
}

.icon-btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: #fff;
    color: var(--ink-soft);
    cursor: pointer;
}

.icon-btn svg { width: 1.05rem; height: 1.05rem; display: block; }
.icon-btn:hover { border-color: var(--green-600); color: var(--green-700); }
.icon-btn:disabled { opacity: .45; cursor: default; }

/* A secondary line under the main value in a table cell — a package size under a
   product name, a supply source under a badge. .offers-table already did this;
   the admin tables need the same and were running the two together on one line. */
.data-table td > .muted { display: block; font-size: .84rem; }

/* A note already exists on this line — the difference has to be visible without
   opening every row. */
.icon-btn-on {
    background: var(--gold-100);
    border-color: var(--gold-600);
    color: #7a5b12;
}

/* Expanded into the row below rather than a popover: this table scrolls
   horizontally, and .table-wrap's overflow-x clips on both axes. */
.note-editor {
    display: flex;
    flex-direction: column;
    gap: .3rem;
    padding: .3rem 0 .5rem;
}

.note-editor label { font-weight: 600; font-size: .85rem; color: var(--green-900); }
.note-editor textarea { width: 100%; }

.table-foot-total {
    display: flex;
    align-items: baseline;
    justify-content: flex-end;
    gap: .65rem;
    margin: 0;
    padding: .85rem 1rem;
    border-top: 1px solid var(--line);
    font-size: .9rem;
}

.table-foot-total strong { font-size: 1.1rem; color: var(--green-900); }
.table-foot-total .spacer { flex: 1 1 auto; }

/* The sums are a stack, not a word in a line, so the row's baseline alignment would hang the
   whole column off the first figure's baseline. Bottom-aligned instead, which puts the repeated
   Save button level with the Total it is about to change. */
.table-foot-total-sums { align-items: flex-end; flex-wrap: wrap; }

/* The money on an order, read down the way an invoice reads. Replaced a single sentence that
   made the reader do the arithmetic to reach the figure that matters (Jackie, 2026-09-17).
   min-width rather than a fixed one: the labels are short and the amounts are not, and the pair
   should not drift apart on a narrow card. */
.foot-sums {
    min-width: 15rem;
    max-width: 100%;
    font-size: .9rem;
    /* On the stack, not just the input: the whole point is a column of figures, and without it
       the derived amounts render proportional while the typed one does not. */
    font-variant-numeric: tabular-nums;
}

.foot-sum {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    padding: .12rem 0;
}

/* What the farm asked for. Muted and set above the rule, because it is context for the figures
   below rather than one of the figures being added up — a reader must never add it in. */
.foot-sum-aside { color: var(--ink-soft); }

.foot-sum-total {
    margin-top: .35rem;
    padding-top: .4rem;
    border-top: 1px solid var(--line);
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--green-900);
}

/* What the Total means, under the Total. It is the sentence the old run-on line ended with, and
   it is the part that says this figure is not charged yet. */
.foot-sums-note { margin: .45rem 0 0; font-size: .8rem; text-align: right; }

/* The one figure on this stack that is typed rather than derived. Centred rather than baseline,
   because a form control and a word do not share a sensible baseline, and the label drops the
   weight .form-field label would give it — it is a row label here, not a form heading. */
.foot-sum-edit { align-items: center; padding: 0; }
.foot-sum-edit label { margin: 0; }

.foot-sum-field { display: inline-flex; align-items: center; gap: .2rem; }

/* The global input rule scores (0,5,1) — one element plus five attribute selectors inside the
   :not() chain. This is (0,7,1): a class, the element, [type="number"], and the same five. Same
   trick as the .qty-stepper rule above; without it the box keeps .6rem/.8rem padding and renders
   as a wide pill in a column of right-aligned figures.

   Pulled right by its own inner padding and border, so the LAST DIGIT lands on the same vertical
   line as the amounts above and below it rather than sitting ~9px inside them. A column of
   figures that does not line up is not a column. */
.foot-sum-field
    input[type="number"]:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]) {
    width: 6.5rem;
    padding: .35rem .5rem;
    margin-right: calc(-.5rem - 1.5px);
    font-size: .9rem;
    text-align: right;
}

/* Says what just happened to the freight box. Reserves its own line rather than sitting beside
   the box, so the column of figures is not broken open and nothing shifts sideways when the text
   changes length between "Saving…" and "Shipping saved." */
.foot-sum-flash {
    margin: .4rem 0 0;
    /* Holds its line while empty, so the note below does not jump on every save — and neither
       does the Confirm/Reject row further down the page, where a shift between mousedown and
       mouseup eats the click. 1.55 is body's line-height (app.css:43); an em here resolves
       against this element's own .8rem, so 1.1em was ~6px short of the filled height. */
    min-height: 1.55em;
    font-size: .8rem;
    text-align: right;
}

/* A refusal is not a muted aside. It also carries a Try again button, so the line needs to hold
   one without the text riding above it. */
.foot-sum-flash-bad {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .4rem;
    flex-wrap: wrap;
    color: var(--danger);
}

/* Sits beside the repeated save button at the foot of the items table. Gold rather than red:
   nothing is wrong, there is just something not written down yet. */
.unsaved-note {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .2rem .6rem;
    border-radius: 999px;
    background: var(--gold-100);
    color: #7c6118;
    font-size: .82rem;
    font-weight: 700;
}

.unsaved-note svg { width: .95rem; height: .95rem; display: block; }

/* One row per supplier on this order, each ending in its release state. */
.supplier-list {
    list-style: none;
    margin: .5rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.supplier-list li {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .55rem .7rem;
    background: var(--cream);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

/* flex-basis rather than min-width:0 alone: with the row unable to wrap, the
   business name shrank to nothing and the release badge, which cannot wrap,
   was painted straight over it. */
.supplier-list li { flex-wrap: wrap; }
.supplier-list li > div { display: flex; flex-direction: column; flex: 1 1 9rem; min-width: 0; }
.supplier-list li > .badge { flex: 0 0 auto; }
.supplier-list li strong { color: var(--green-900); overflow-wrap: anywhere; }
.supplier-list li .muted { font-size: .83rem; }
.supplier-list .spacer { flex: 1 1 auto; }

/* The release number is a link wearing a badge. */
a.badge { text-decoration: none; }
a.badge:hover { filter: brightness(.95); }

/* ---------- Responsive ---------- */

@media (max-width: 720px) {
    .page-head-wide .head-actions { width: 100%; }
    .panel-head { align-items: flex-start; }
    .save-bar { flex-direction: column-reverse; align-items: stretch; }
    .save-bar .btn { width: 100%; }
    .title-icon { width: 2rem; height: 2rem; }
    .title-icon svg { width: 1.15rem; height: 1.15rem; }
}

/* ---------- Touch targets ---------- */
@media (max-width: 860px) {
    .btn { padding-block: .72rem; }
    .btn-sm { padding-block: .5rem; }

    .sort-btn,
    .back-link { min-height: 32px; }

    .row-menu-trigger { width: 2.5rem; height: 2.5rem; }
    .row-menu-panel button,
    .row-menu-panel a { padding-block: .6rem; }

    input[type="checkbox"],
    input[type="radio"] { width: 1.5rem; height: 1.5rem; }

    .alert-close { width: 40px; height: 40px; }
}


/* =====================================================================
   ADMIN DASHBOARD + SHELL CHROME — Jackie's mockup, 2026-08-26.
   Notes and deviations: claude/admin-console-2026-08-26.md
   ===================================================================== */

/* ---------- Topbar toggle and footer ---------- */

.admin-menu-toggle {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.1rem;
    height: 2.1rem;
    margin-right: .35rem;
    border: none;
    border-radius: 8px;
    background: none;
    color: inherit;
    cursor: pointer;
}

.admin-menu-toggle svg { width: 1.2rem; height: 1.2rem; display: block; }

/* The topbar is justify-content: space-between, which was fine with two children and centres
   the trail now there are three. The breadcrumb belongs beside the toggle, not in the middle. */
.admin-topbar .breadcrumb { margin-right: auto; }
.admin-menu-toggle:hover { background: rgba(255, 255, 255, .12); }
.admin-menu-toggle:focus-visible { outline: 2px solid var(--gold-600); outline-offset: 2px; }

/* ---------- Narrow screens: the public site's hamburger menu ---------- */
.admin-topbar-brand,
.admin-topbar .nav-menu { display: none; }

.admin-menu-panel { background: var(--admin-chrome); }
.admin-menu-panel a { display: flex; align-items: center; gap: .6rem; color: var(--admin-text); }
.admin-menu-panel a.active .admin-nav-icon { opacity: 1; color: var(--gold-600); }
.admin-menu-panel .admin-nav-section { padding: 1rem .75rem .35rem; font-size: .8rem; }

.nav-menu-panel a.admin-menu-public {
    margin-top: .5rem;
    border-top: 1px solid var(--admin-line);
    color: var(--admin-text-dim);
}

@media (max-width: 860px) {
    .admin-shell { --header-h: 64px; }

    .admin-topbar {
        position: relative;
        height: var(--header-h);
        padding-block: 0;
        padding-inline: 1rem .6rem;
        gap: .6rem;
    }

    .admin-topbar:has(.nav-menu[open]) { z-index: 70; }

    .admin-topbar-brand { display: flex; flex: 0 0 auto; }
    .admin-topbar-brand .admin-logo-chip img { height: 28px; }
    .admin-topbar .nav-menu { display: block; }

    .admin-topbar .breadcrumb { min-width: 0; overflow: hidden; white-space: nowrap; }
    .admin-topbar .breadcrumb [aria-current="page"] { overflow: hidden; text-overflow: ellipsis; }
}

@media (max-width: 560px) {
    .admin-topbar .breadcrumb:has(> :nth-child(3)) > :nth-child(-n+2) { display: none; }
}

/* The collapsed rail keeps the icons and drops everything that needs a label to make sense.
   Width is the only thing that animates — animating the sidebar's contents makes the whole
   page reflow on every frame. */
.admin-shell-collapsed .admin-sidebar { width: 62px; }
.admin-sidebar { transition: width .18s ease; }


.admin-shell-collapsed .admin-brand-text,
.admin-shell-collapsed .admin-sidebar .admin-nav-section,
.admin-shell-collapsed .admin-sidebar .admin-nav-badge,
.admin-shell-collapsed .admin-sidebar-footer a > :not(.admin-nav-icon) { display: none; }

.admin-shell-collapsed .admin-brand,
.admin-shell-collapsed .admin-nav a,
.admin-shell-collapsed .admin-sidebar-footer a { justify-content: center; }

.admin-shell-collapsed .admin-nav a { padding-left: 0; padding-right: 0; }

/* A collapsed item is an unlabelled icon, so the browser's own tooltip is the only thing telling
   the reader what it is. The accessible name still comes from the link text, which is present in
   the DOM and only visually hidden by the rule above — display:none would have removed it from
   the accessibility tree too, which is why the label is hidden on the CONTENTS, not the link. */
.admin-shell-collapsed .admin-nav a { font-size: 0; }
.admin-shell-collapsed .admin-nav a .admin-nav-icon { font-size: 1rem; }

.admin-footer {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .9rem 1.5rem;
    border-top: 1px solid var(--line);
    font-size: .82rem;
    color: var(--ink-soft);
}

.admin-footer .spacer { flex: 1 1 auto; }

/* ---------- Greeting ---------- */

.dash-greeting { margin-bottom: 1.5rem; }

/* The sun is the page's welcome, not a category marker, so it carries no chip behind it. */
.title-icon-plain {
    width: auto;
    height: auto;
    background: none;
    color: var(--gold-600);
}

.title-icon-plain svg { width: 1.6rem; height: 1.6rem; }

.dash-section { margin-bottom: 1.6rem; }

.dash-section-title {
    margin: 0 0 .8rem;
    font-size: 1.05rem;
    color: var(--green-900);
}

/* ---------- Needs attention ---------- */
/* auto-fit rather than a fixed four columns: there are five queues, and a fifth card stranded
   alone on a second row reads as an afterthought rather than as part of the set. */

.attention-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: .85rem;
}

.attention-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .3rem;
    padding: 1rem 1.05rem 1.05rem;
    background: #fff;
    border: 1px solid var(--line);
    border-left: 4px solid var(--green-600);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.attention-warning { border-left-color: var(--gold-600); }

.attention-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    margin-bottom: .35rem;
    border-radius: 10px;
    background: var(--green-100);
    color: var(--green-700);
}

.attention-warning .attention-icon { background: var(--gold-100); color: #8a6512; }
.attention-icon svg { width: 1.3rem; height: 1.3rem; display: block; }

.attention-count {
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.05;
    color: var(--green-900);
}

.attention-label { font-weight: 600; color: var(--green-900); line-height: 1.25; }

.attention-pill {
    padding: .1rem .55rem;
    border-radius: 999px;
    background: var(--green-100);
    color: var(--green-700);
    font-size: .76rem;
    font-weight: 700;
}

.attention-warning .attention-pill { background: var(--gold-100); color: #7c6118; }

.attention-note {
    margin: .25rem 0 .2rem;
    font-size: .85rem;
    color: var(--ink-soft);
    line-height: 1.4;
}

/* Five cards across leaves each about 215px wide, and "Review products" at the default button
   size wrapped onto a second line while its neighbours did not. */
.attention-action {
    margin-top: auto;
    align-self: stretch;
    text-align: center;
    padding-left: .6rem;
    padding-right: .6rem;
    font-size: .88rem;
}

/* Nothing to do: the card steps back rather than showing a control that cannot be pressed. */
.attention-card-clear {
    border-left-color: var(--line);
    background: #fcfcf8;
}

.attention-card-clear .attention-icon,
.attention-card-clear .attention-pill { background: #eceada; color: var(--ink-soft); }

.attention-card-clear .attention-count { color: var(--ink-soft); }

.attention-action-done {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    padding: .5rem 1rem;
    border: 1px dashed var(--line);
    border-radius: 8px;
    color: var(--ink-soft);
    font-size: .88rem;
    font-weight: 600;
}

.attention-action-done svg { width: .95rem; height: .95rem; display: block; }

/* The gold action, for the queues where somebody outside RFM is waiting on a reply. */
.btn-warning {
    background: #fff;
    color: #7c6118;
    border-color: var(--gold-600);
}

.btn-warning:hover { background: var(--gold-100); }

/* ---------- Overview tiles as links ---------- */

.stat-tile-link { text-decoration: none; color: inherit; }
.stat-tile-link:hover { border-color: var(--green-600); }
.stat-tile-link:focus-visible { outline: 2px solid var(--gold-600); outline-offset: 2px; }
.stat-tile-caption-warn { color: #8a6512; font-weight: 600; }

/* ---------- Quick actions ---------- */

.quick-actions {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-wrap: wrap;
    padding: .85rem 1.05rem;
    margin-bottom: 1.6rem;
}

.quick-actions-label {
    font-weight: 700;
    color: var(--green-900);
    margin-right: .35rem;
}

/* ---------- Two-column lower half ---------- */
/* 3fr / 2fr rather than equal halves: the demand table has six columns and the orders panel is
   a list, so an even split starves the table and leaves the list padded out. */

.dash-columns {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: 1.1rem;
    align-items: start;
}

/* The demand table has six columns inside a panel that is two thirds of the page at best, so it
   is set a size down and its first column is given a floor — otherwise the browser spends the
   width on the numeric columns and breaks the product name one word per line. */
.dash-columns .data-table { font-size: .88rem; }
.dash-columns .data-table th,
.dash-columns .data-table td { padding: .5rem .6rem; }
.dash-columns .data-table th:first-child,
.dash-columns .data-table td:first-child { min-width: 8.5rem; }

/* ---------- Presence ---------- */

.presence-notice { display: flex; align-items: flex-start; gap: .6rem; }

.presence-quiet {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 0 0 1rem;
    font-size: .88rem;
    color: var(--ink-soft);
}

.presence-icon { display: inline-flex; flex: 0 0 auto; }
.presence-icon svg { width: 1.05rem; height: 1.05rem; display: block; }
.presence-notice .presence-icon { margin-top: .1rem; }

/* On a row in the orders list. Sized down and kept on one line — it is a hint, not a status. */
.presence-chip {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .1rem .45rem;
    border-radius: 999px;
    background: var(--gold-100);
    color: #7c6118;
    font-size: .76rem;
    font-weight: 700;
    white-space: nowrap;
}

.presence-chip svg { width: .85rem; height: .85rem; display: block; }

/* ---------- Percentage meter ---------- */

/* Bar and number are one reading, so they must not be split across two lines. */
.meter-cell { white-space: nowrap; }

.meter {
    display: inline-block;
    vertical-align: middle;
    width: 3.4rem;
    height: .45rem;
    margin-right: .5rem;
    border-radius: 999px;
    background: var(--cream-dark);
    overflow: hidden;
}

.meter-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--green-600);
}

.meter-value { font-size: .85rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* ---------- Feed list ---------- */

.feed-list { list-style: none; margin: 0; padding: 0; }

.feed-list li {
    display: flex;
    align-items: flex-start;
    gap: .7rem;
    padding: .75rem 1rem;
    border-bottom: 1px solid var(--line);
}

.feed-list li:last-child { border-bottom: none; }
.feed-list li > div { display: flex; flex-direction: column; min-width: 0; }
.feed-list li > div:first-of-type { flex: 1 1 auto; }
.feed-list .muted { font-size: .83rem; }

.feed-title { font-weight: 600; color: var(--green-900); text-decoration: none; }
.feed-title:hover { text-decoration: underline; }

.feed-meta { align-items: flex-end; text-align: right; flex: 0 0 auto; gap: .25rem; }
.feed-meta .muted { white-space: nowrap; font-size: .8rem; }

/* A panel-head action that should read as a way out of the card, not as a button. */
.link-quiet {
    font-size: .87rem;
    color: var(--green-700);
    text-decoration: none;
    white-space: nowrap;
}

.link-quiet:hover { text-decoration: underline; }

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
    .dash-columns { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 860px) {
    /* The sidebar is hidden at this width; there is no rail to collapse, and the hamburger menu
       takes the toggle's job. */
    .admin-menu-toggle { display: none; }
}

@media (max-width: 560px) {
    .quick-actions { align-items: stretch; flex-direction: column; }
    .quick-actions .btn { width: 100%; }
    .feed-list li { flex-wrap: wrap; }
    .feed-meta { align-items: flex-start; text-align: left; flex-direction: row; gap: .5rem; }
    .admin-footer { flex-direction: column; align-items: flex-start; gap: .3rem; }
}


/* =====================================================================
   FARMER PRODUCT DETAIL — image, facts, spec list, reassurance band and
   the sticky buy rail. Built to Jackie's mockup, 2026-08-27.
   ===================================================================== */

.product-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 21rem;
    gap: 1.25rem;
    align-items: start;
}

/* gap owns the spacing here, so the cards' own margin has to go — .card carries
   margin-bottom: 1rem, which would otherwise add to the gap and leave a stray 1rem hanging
   under the last card before the reassurance band. */
.product-main { min-width: 0; display: flex; flex-direction: column; gap: 1.25rem; }
.product-main > .card { margin-bottom: 0; }

/* Sticky so the price and the Add button stay reachable while the details are read.
   A plain 1rem offset: the header is only taken out of flow on pages that open with a
   full-bleed hero (see the :has() rule above), and this is not one of them — it scrolls away
   like everything else, so reserving --header-h here would strand the rail a third of the way
   down the screen. */
.product-rail { position: sticky; top: 1rem; min-width: 0; }

/* ---------- Hero: picture beside the headline ---------- */

.product-hero {
    display: grid;
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
}

/* The picture and its reference note are one column, wrapped rather than placed into a second
   grid row. A two-row grid with the headline spanning both looked equivalent and was not:
   Chrome gave the empty note row a share of the spanning item's height, leaving ~95px of dead
   card under the image on every product that has its own photograph. */
.product-media { min-width: 0; }

.product-figure {
    aspect-ratio: 4 / 3;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .75rem;
    background: var(--cream);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
}

/* contain, not cover: this is the one place a farmer looks closely at the product, and
   cropping a jug's label off to fill a 4:3 box is the wrong trade here even though it is the
   right one on the marketplace card. */
.product-figure img { max-width: 100%; max-height: 100%; object-fit: contain; }

.figure-note {
    margin: .4rem 0 0;
    font-size: .78rem;
    color: var(--ink-soft);
    text-align: center;
    font-style: italic;
}

.product-headline { min-width: 0; }

.product-badges { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .5rem; }

.product-headline h1 { margin: 0 0 .5rem; font-size: 1.6rem; line-height: 1.25; }

.product-lede { margin: 0 0 1rem; color: var(--ink-soft); line-height: 1.55; }

/* ---------- Fact rows ---------- */

.product-facts { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }

/* Three columns rather than three inline spans, so the values line up down the page
   regardless of how long each label is. minmax(0,1fr) on the value column is what lets a long
   availability sentence wrap instead of forcing the grid wider than its container. */
.product-fact {
    display: grid;
    grid-template-columns: 1.1rem auto minmax(0, 1fr);
    align-items: baseline;
    gap: .5rem;
    font-size: .92rem;
}

/* start, not center: an icon centred in a row whose value wraps to two lines floats down
   between them and stops reading as a label for the first. The nudge lines it up with the
   cap height of the text beside it. */
.product-fact-icon {
    color: var(--green-600);
    display: inline-flex;
    align-self: start;
    margin-top: .12rem;
}

.product-fact-icon svg { width: 1.1rem; height: 1.1rem; display: block; }
.product-fact-label { color: var(--ink-soft); }
.product-fact-value { color: var(--ink); font-weight: 600; }

/* An order deadline earlier than the delivery window. Gold rather than red — it is a date to
   act on, not something that has gone wrong — and it carries its own clock icon and the words
   "Order by", so the tint is the third cue rather than the only one. */
.product-fact-alert .product-fact-label,
.product-fact-alert .product-fact-value { color: #7a5b12; }
.product-fact-alert .product-fact-icon { color: var(--gold-600); }

/* A qualifying line under a fact's value — currently only book-ahead. Green, not gold: it is
   an opportunity rather than a deadline, and the same card may carry the gold "Order by" line,
   where two warm tints side by side would flatten the difference between "you can act" and
   "you must act". */
.fact-note {
    display: block;
    margin-top: .1rem;
    font-size: .8rem;
    font-weight: 600;
    color: var(--green-700);
}

/* The one note in the rail that should be read before the button, not after it. Not built on
   .buy-note: that one is a quiet caveat under the actions, and inheriting its top margin would
   push this away from the stock lines it belongs with. */
.buy-note-lead {
    margin: .85rem 0 0;
    font-size: .82rem;
    line-height: 1.5;
    padding: .5rem .65rem;
    background: var(--green-100);
    border-radius: 6px;
    color: var(--green-900);
}

.buy-stock-alert { color: #7a5b12; }
.buy-stock-alert strong { color: #7a5b12; }
.buy-stock-alert .product-fact-icon { color: var(--gold-600); }

/* ---------- Spec list ---------- */

.spec-list { margin: 0; padding: 0; }

.spec-row {
    display: grid;
    grid-template-columns: 12rem minmax(0, 1fr);
    gap: .75rem;
    padding: .7rem 1rem;
    border-bottom: 1px solid var(--line);
}

.spec-row:last-child { border-bottom: none; }
.spec-row dt { margin: 0; color: var(--ink-soft); font-size: .9rem; }
.spec-row dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* A qualifying line under a value, not trailing along the same line as it. */
.spec-row dd .muted { display: block; margin-top: .15rem; font-size: .85rem; line-height: 1.45; }

/* ---------- Reassurance band ---------- */

.reassurance-band {
    list-style: none;
    margin: 0;
    padding: 1rem;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1rem;
    background: var(--green-100);
    border-radius: var(--radius);
}

.reassure { display: flex; align-items: flex-start; gap: .6rem; font-size: .88rem; line-height: 1.45; color: var(--ink-soft); }
.reassure strong { display: block; color: var(--green-900); font-size: .92rem; }

.reassure-icon { color: var(--green-700); flex: 0 0 auto; display: inline-flex; padding-top: .1rem; }
.reassure-icon svg { width: 1.15rem; height: 1.15rem; display: block; }

/* ---------- Buy rail ---------- */

.buy-card { padding: 1.1rem 1.15rem 1.2rem; }

.buy-price { display: flex; align-items: baseline; flex-wrap: wrap; margin: 0; }

.buy-perunit { margin: .15rem 0 0; font-size: .85rem; color: var(--ink-soft); }

.buy-stock {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: .7rem 0 0;
    font-size: .88rem;
    color: var(--ink-soft);
}

.buy-stock strong { color: var(--ink); }

.buy-qty {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin: 1rem 0 .75rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
}

.buy-qty-label { font-weight: 600; color: var(--green-900); }

.buy-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
    margin: 0 0 .85rem;
    font-size: .9rem;
    color: var(--ink-soft);
}

.buy-total strong { font-size: 1.2rem; color: var(--green-900); }

/* The two rail buttons stack; .btn-block gives the width, this gives the gap without a
   wrapper element. */
.buy-card .btn + .btn { margin-top: .5rem; }

.buy-note {
    margin: .9rem 0 0;
    font-size: .8rem;
    line-height: 1.5;
    color: var(--ink-soft);
}

@media (max-width: 1000px) {
    /* The rail drops below the details. It must also stop being sticky: a sticky box in a
       single-column flow pins itself to the viewport and covers the content above it. */
    .product-layout { grid-template-columns: minmax(0, 1fr); }
    .product-rail { position: static; }
}

@media (max-width: 700px) {
    .product-hero { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
    .spec-row { grid-template-columns: minmax(0, 1fr); gap: .15rem; }
    .product-headline h1 { font-size: 1.35rem; }
}


/* =====================================================================
   FARMER SUBMITTED ORDER — progress stepper, item table, activity and
   the summary rail. Built to Jackie's mockup, 2026-08-27.
   ===================================================================== */

/* A 17rem rail, narrower than the 20rem this started at and than .product-rail's, because this
   page sits INSIDE the account shell: the section nav has already taken 230px before the layout
   starts. At 20rem the five-column item table came out 65px short of its content on a 1280px
   screen and scrolled sideways inside its own card, hiding Confirmed and Status behind the
   edge — the two columns the farmer opened the page for. Measured, not guessed; re-measure
   before widening it. */
.order-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 17rem;
    gap: 1rem;
    align-items: start;
}

.order-main { min-width: 0; display: flex; flex-direction: column; gap: 1.25rem; }
.order-main > .card { margin-bottom: 0; }

.order-rail { position: sticky; top: 1rem; min-width: 0; }
.order-rail > .card { margin-bottom: 0; }

/* ---------- Stepper: the run that ended without a confirmation ---------- */
/* Not ticked and not green. An order RFM could not fill did reach the end of the process, but
   a green check beside "Not Approved" would tell the farmer the opposite of what happened. */

.stepper-item-stopped .stepper-dot {
    background: var(--danger-bg);
    border-color: var(--danger);
    color: var(--danger);
}

.stepper-item-stopped .stepper-label { color: var(--danger); font-weight: 600; }

/* Badges are nowrap everywhere else, and should be — a chip that wraps in a wide table looks
   broken. Here the column is genuinely narrow and "Partially Confirmed" is genuinely long, and
   two lines of chip beats a column pushed off the edge of the card. Scoped to this table.
   text-wrap: balance splits it evenly rather than leaving one word stranded on the second line. */
.order-main .data-table .badge {
    white-space: normal;
    text-wrap: balance;
}

/* The package and availability snapshot, under the product name in the same cell. */
.line-sub {
    display: block;
    margin-top: .15rem;
    font-size: .8rem;
    color: var(--ink-soft);
    font-weight: 400;
}

/* ---------- The stepper on a phone ---------- */
/* The shared .stepper hides its labels below 720px. On this page they are the point, so this
   variant lays the steps out vertically and keeps them. Scoped to .stepper-status so the
   offer-form wizard is untouched. */

@media (max-width: 720px) {
    .stepper-status {
        grid-auto-flow: row;
        grid-auto-columns: auto;
        gap: .55rem;
        margin: 1rem 0 1.25rem;
    }

    .stepper-status .stepper-item {
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
        text-align: left;
        gap: .65rem;
    }

    /* The connector is drawn between horizontal steps and is meaningless once they stack. */
    .stepper-status .stepper-item::before { display: none; }

    .stepper-status .stepper-label { display: block; }
}

/* The product column stays put while the rest of the row scrolls — a five-column table cannot fit
   a 390px phone, and a row whose label has slid off the edge is a row of numbers about nothing.
   This was a rule of its own here, scoped to .order-main and to 720px, before the same behaviour
   was needed on twenty other tables. It is now the shared `sticky-first` class, which the order
   items table carries; see the frozen columns block. The supplier breakdown that this rule used
   to point at is a different shape — a block pinned inside a cell, not a column — and keeps its
   own rule. */

/* ---------- Items table footnote ---------- */

.table-foot-note {
    margin: 0;
    padding: .75rem 1rem;
    border-top: 1px solid var(--line);
    font-size: .82rem;
    line-height: 1.5;
    color: var(--ink-soft);
}

/* The farmer's own words, set apart so it is obvious they are quoted back rather than
   paraphrased by RFM. */
.quoted-note {
    border-left: 3px solid var(--green-100);
    padding-left: .85rem;
    white-space: pre-wrap;
    line-height: 1.55;
}

/* ---------- Activity ---------- */

.activity-list {
    list-style: none;
    margin: 0;
    padding: .5rem 1rem 1rem;
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

.activity-item { display: flex; align-items: flex-start; gap: .65rem; font-size: .9rem; }
.activity-item strong { display: block; color: var(--green-900); }
.activity-item .muted { font-size: .84rem; }

.activity-icon {
    flex: 0 0 auto;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 999px;
    background: var(--green-100);
    color: var(--green-700);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.activity-icon svg { width: 1.05rem; height: 1.05rem; display: block; }

/* ---------- Summary rail ---------- */
/* .summary-card, .summary-row and .summary-total already exist and are reused as-is. Only the
   pieces this page adds are defined here. */

.summary-title { margin: 0 0 .8rem; font-size: 1.05rem; }

/* A field and the button that saves it, on one line. For a card holding a single value, a
   stacked field-then-actions-row spends three rows and a lot of height on one number. */
.inline-save-row {
    display: flex;
    align-items: flex-end;
    gap: .75rem;
    flex-wrap: wrap;
}

/* .form-field carries a bottom margin so fields stack; here it is a flex item on a baseline with
   a button, and that margin pushes the button up off the line. */
.inline-save-row .form-field { margin-bottom: 0; }

.summary-list { margin: 0; padding: 0; }

/* The goods/shipping breakdown above an order's total. A rule and some air, so it reads as the
   arithmetic behind the figure below it rather than two more rows of order detail. */
.summary-split {
    margin-top: .75rem;
    padding-top: .75rem;
    border-top: 1px solid var(--line);
}

/* .summary-row is a label-left / value-right flex row, which is right in the cart's wide
   summary and wrong in a 17rem rail: "Sep 2, 2026 · 9:14 AM" and "RFM-2026-118" both broke
   mid-value against the label. Stacked here — scoped to .summary-list, so the cart keeps the
   row it was written for. */
.summary-list .summary-row {
    flex-direction: column;
    align-items: stretch;
    gap: .1rem;
    padding: .35rem 0;
}

.summary-list dt { color: var(--ink-soft); font-size: .85rem; }

.summary-list dd {
    margin: 0;
    min-width: 0;
    color: var(--ink);
    font-weight: 600;
    /* break-word, not anywhere: `anywhere` splits "RFM-2026-118" across lines the moment the
       column is a few pixels tight, which reads as a different PO number. */
    overflow-wrap: break-word;
}

.summary-row-stack .muted { display: block; font-weight: 400; font-size: .8rem; margin-top: .15rem; }

.summary-note {
    margin: .55rem 0 1rem;
    font-size: .8rem;
    line-height: 1.5;
    color: var(--ink-soft);
}

/* Scoped to this rail. .summary-card is also the retailer offer form's rail card, whose
   .summary-actions stack is already a flex column with its own .5rem gap — unscoped, this
   added a second .5rem on top and loosened three buttons that were fine. */
.order-rail .summary-card .btn + .btn { margin-top: .5rem; }

/* ---------- Print ---------- */
/* The page is a record of an order, and farmers file paper. Everything that is chrome or a
   control comes off; what stays is the header, the items and the summary. */

@media print {
    .site-header,
    .site-footer,
    .account-nav,
    .head-actions,
    .breadcrumb,
    .order-rail .btn,
    .cart-link { display: none !important; }

    .account-shell,
    .order-layout { display: block; }

    .order-rail { position: static; margin-top: 1rem; }

    .card,
    .panel-card {
        box-shadow: none;
        border: 1px solid #ccc;
        break-inside: avoid;
    }

    /* The items table must not be inside a scroller on paper — a scrollbar prints as a clipped
       table with no indication that anything is missing. */
    .table-wrap { overflow: visible; }

    body { background: #fff; }
}

/* 1180, not the 1000 used by .product-rail. This layout starts 230px in — the account section
   nav is to its left — so it runs out of room for the item table sooner than a full-width page
   would. Below this the rail drops underneath and the table gets the whole column. */
@media (max-width: 1180px) {
    /* Static as well as single-column: a sticky box in a single-column flow pins itself to the
       viewport and covers the content above it. */
    .order-layout { grid-template-columns: minmax(0, 1fr); }
    .order-rail { position: static; }
}


/* =====================================================================
   BALANCE COLOUR — red when the farm owes, green when it is in credit.
   Jackie, 2026-08-27: "a negative balance should show as red."

   .amount-owed / .amount-paid already existed and already carried the right two colours; they
   were only being applied to per-row amounts, not to the balance itself. These rules are the
   places where a heading style set its own colour and won on specificity or source order, so
   the class landed and did nothing. All of them are stated explicitly rather than left to
   source order, because the amount block sits 1,200 lines earlier and a future reshuffle would
   silently turn the colour off again.

   The class is applied through LedgerWording.AmountClass, which is also where the note lives
   about the two opposite sign conventions in play.
   ===================================================================== */

/* .metric-value is NOT listed here. It sets its own colour, but at (0,1,0) and 130 lines before
   .amount-owed, so the class already wins on source order — an override would only restate it.
   If .amount-owed is ever moved earlier in this file, .metric-value starts winning and needs a
   pair like the ones below. */

/* .stat-tile-value sets color at (0,1,0) but is defined AFTER .amount-owed, so it was winning
   on source order alone. */
.stat-tile-value.amount-owed { color: #9a3324; }
.stat-tile-value.amount-paid { color: #2f6b3a; }

/* .summary-rows dd scores (0,1,1) and beat the class outright. */
.summary-rows dd.amount-owed { color: #9a3324; }
.summary-rows dd.amount-paid { color: #2f6b3a; }

/* The dashboard hero sits on --green-900. #9a3324 on that is unreadable — 1.4:1 — so the pair
   is re-tuned for the dark ground rather than inherited. Measured against #39441f: 6.1:1 for
   the red and 7.8:1 for the green, both comfortably past AA for large text and past AAA for
   the green. White stays the neutral. */
.hero-banner .hero-balance-value.amount-owed { color: #ffb4a2; }
.hero-banner .hero-balance-value.amount-paid { color: #cbe8b8; }

/* Says in words what the colour above says in hue. Scoped and qualified for the same reason
   .hero-balance-label is — `.hero p` sets font-size and max-width at (0,1,1) and would win
   against a bare class. */
.hero-banner .hero-balance-caption {
    margin: .1rem 0 0;
    font-size: .82rem;
    max-width: none;
    color: #cdd6bb;
}

/* ============================================================
   PRODUCT IMPORT
   The admin bulk-load screen. Placed before LATE OVERRIDES so that
   section keeps the last word, which is the point of it.
   ============================================================ */

/* Explanatory prose in a full-width admin card. Used by the product import and the order
   review screen; named for the job rather than for whichever page asked for it first.

   The admin content area is as wide as the widest TABLE on it, which is right for tables and
   wrong for sentences — at 1400px the eye loses its place returning to the next line. Capped at
   the same measure .page-head-wide already caps a page description to, so the two read as one
   rather than two. Only the prose is capped; the tables and the drop zone still use the full
   width, which is what they are there for.

   39rem, not 74ch: ch is font-relative, and the paragraphs on this page are at two different
   sizes, so the same character count rendered about 70px apart and the ragged edges did not line
   up. 39rem is what 74ch already came to at .muted size. */
.prose-measure { max-width: 39rem; }

.import-actions { margin-top: .75rem; }

/* An uploaded file's name in a panel head. A flex item's min-width is auto, so a long
   underscored name has no break opportunity and pushes the head — and the card — wider than the
   content area. */
.file-name { min-width: 0; overflow-wrap: anywhere; }

.import-callout { margin-top: 1rem; }

/* A short field in a .form-row — a two-word dropdown, a money box — that must neither grow
   to the panel's full width nor shrink below its own label.

   `.form-row .form-field { flex: 1 1 180px }` lets a lone field grow to the whole panel, which
   put a 1,300px dropdown on two words; `.form-row .form-field-sm { flex: 0 1 110px }` is
   narrower than the label and wrapped it onto two lines. This sits between the two: it does not
   grow, and its basis is wide enough to hold the label on one line. Selector is two classes deep
   to match the specificity of the rules it is overriding. */
.form-row .field-narrow { flex: 0 1 16rem; margin-bottom: 0; }

/* A .field-hint that is followed by another control rather than ending its field.

   .field-hint sets `margin: .15rem 0 0` because it is normally the last thing in a form field.
   Here the "skip the rejected rows" checkbox comes after it, and sat flush against the text. The
   width cap matches .prose-measure so the two paragraphs on this panel share one measure. */
.import-hint { max-width: 39rem; margin: .45rem 0 .95rem; }

/* The reasons a row was rejected, inside a table cell. A default <ul> puts a blank line above
   and below itself and indents 40px, which in a dense table reads as a layout fault rather than
   a list. */
.import-problems {
    margin: 0;
    padding-left: 1.05rem;
    font-size: .88rem;
    color: var(--ink-soft);
}

.import-problems li + li { margin-top: .2rem; }

/* ============================================================
   SUPPLIER ORDER: A BLOCK PER CUSTOMER
   The only layout for a supplier order, whichever way it is handled. What differs by handling is
   what a block can honestly assert about a destination — see ReleaseLines.razor.
   ============================================================ */

/* What to pull off the shelves before it is split up. Chips rather than a table: it is a
   reading aid above the real content, and a second table here competes with the ones below. */
.rollup {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: .9rem 1rem;
    margin-bottom: 1rem;
}

.rollup-label {
    display: block;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: .5rem;
}

.rollup-items { display: flex; flex-wrap: wrap; gap: .45rem; }

.rollup-item {
    background: var(--cream-dark);
    border-radius: 8px;
    padding: .35rem .7rem;
    font-size: .88rem;
}

.rollup-item strong { color: var(--green-900); }

/* The product's own value, carried here because the blocks below total by customer. Given its
   own weight rather than left muted: on a warehouse order this is the figure the supplier
   reconciles their invoice against. */
.rollup-value { font-weight: 600; color: var(--green-900); }

/* One customer's whole consignment. Padding is zero and the table runs edge to edge, the way
   .panel-card works — the head and the totals band bring their own gutters. */
.custblock {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    margin-bottom: 1.1rem;
    overflow: hidden;
}

/* The left edge carries what the supplier has to DO. Gold for a collection, because nothing
   ships and a truck loaded for it is a wasted run; green for an ordinary delivery. */
.custblock-collect { border-left: 4px solid var(--gold-600); }
.custblock-deliver { border-left: 4px solid var(--green-700); }

/* A block that asserts nothing about a destination — every block on a warehouse order, and a
   direct one whose farm row did not load. It still needs the 4px, or its contents sit three
   pixels left of every other block in the stack. */
.custblock-plain { border-left: 4px solid var(--line); }

/* The single destination of a warehouse order, drawn as a block so it reads as an address to
   ship to rather than as a notice to skim. It is head and nothing else — there are no lines
   going to a destination, they are split by customer below — so the head's own rule would
   otherwise double up against the block's bottom border. */
.custblock-dest .cb-head { border-bottom: 0; }

/* The warehouse address is free text an admin typed into a two-row textarea, so it legitimately
   arrives with line breaks in it — and HTML would otherwise collapse "123 Grain Rd\nPierre, SD"
   into one run-on line. A block, not a flex item, because .cb-contact is a wrapping row built
   for short contact chips and an address is one value with its own shape. */
.cb-dest-address { display: block; white-space: pre-line; }

.cb-head {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    flex-wrap: wrap;
    padding: .9rem 1rem;
    background: var(--cream);
    border-bottom: 1px solid var(--line);
}

.cb-who { flex: 1 1 320px; min-width: 0; }

/* The Customer ID leads the block, so it takes the weight a heading would. The label stays
   small beside it — it is what stops a bare number being ambiguous. */
.cb-cid {
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.cb-cid strong {
    font-size: 1.3rem;
    letter-spacing: 0;
    color: var(--green-900);
    margin-left: .4rem;
    vertical-align: -.05em;
}

.cb-name {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--green-900);
    margin: .1rem 0 0;
    overflow-wrap: anywhere;
}

.cb-contact {
    font-size: .88rem;
    color: var(--ink-soft);
    margin: .2rem 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: .1rem .85rem;
}

.cb-contact a { font-weight: 600; }

.cb-headright {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: .35rem;
    text-align: right;
}

/* The totals band. Two jobs at once: it restates where the goods are going, and it closes the
   block with its money — which also fills the width the old breakdown row left empty. */
.cb-foot {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
    align-items: flex-start;
    padding: .9rem 1rem;
    background: var(--cream-dark);
    border-top: 1px solid var(--line);
}

.cb-ship { flex: 1 1 300px; font-size: .88rem; min-width: 0; }

.cb-ship-label {
    display: block;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: .15rem;
}

.cb-ship strong { color: var(--green-900); }
.cb-address { display: block; margin-top: .1rem; }

.cb-sums { flex: 0 0 auto; min-width: 230px; }

/* A totals band with no ship-to beside it — a warehouse order, where the destination is stated
   once above the blocks instead of under each one. Without this the sums slide to the left edge
   and stop lining up with the blocks above and below. */
.cb-foot-sums-only { justify-content: flex-end; }

/* The block's head and totals band are inset 1rem; cell padding is .75rem. Same three visible
   pixels the .panel-card rule above exists to remove (Jackie, 2026-08-27) — repeated here
   because on the supplier's page this table has no .panel-card ancestor to catch it. */
.custblock .data-table th:first-child,
.custblock .data-table td:first-child { padding-left: 1rem; }

.custblock .data-table th:last-child,
.custblock .data-table td:last-child { padding-right: 1rem; }

.cb-sum {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    font-size: .9rem;
    padding: .1rem 0;
}

.cb-sum-total {
    border-top: 1.5px solid var(--line);
    margin-top: .35rem;
    padding-top: .4rem;
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--green-900);
}

/* The one figure that gets invoiced. Reversed out, because every block above now shows money
   of its own and this has to be unmistakably the total rather than one more subtotal. */
.order-total {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    align-items: center;
    background: var(--green-900);
    color: #fff;
    border-radius: var(--radius);
    padding: 1rem 1.15rem;
}

.order-total-label { font-weight: 700; font-size: 1.05rem; }

.order-total-sub {
    display: block;
    font-size: .84rem;
    color: #cdd6bb;
    font-weight: 400;
}

.order-total-figs { display: flex; gap: 2rem; align-items: baseline; }
.order-total-figs div { text-align: right; }
.order-total-figs span { display: block; font-size: .78rem; color: #cdd6bb; }
.order-total-figs strong { font-size: 1.35rem; }

@media (max-width: 620px) {
    /* Stacked, the right-aligned head column reads as a stray column of its own. */
    .cb-headright { align-items: flex-start; text-align: left; }
    .cb-sums { min-width: 0; flex: 1 1 100%; }
}

/* Framing. The supplier's page renders this stack at page level, where the cards want their own
   shadows and no gutter. The admin console renders the same component inside a .panel-card, under
   a "What the supplier sees" head — and .panel-card sets padding:0 so a table can run edge to
   edge, which would leave these cards flush against its border and shadowed inside its shadow. */
.panel-card .release-blocks { padding: 1rem; }
.panel-card .release-blocks > .alert { margin: 0 0 1rem; }

.panel-card .release-blocks .custblock,
.panel-card .release-blocks .rollup { box-shadow: none; }

/* A supplier prints this and walks the yard with it, so a customer must not straddle a page
   break. The order total is allowed to sit on its own sheet; a split consignment is not. */
@media print {
    /* overflow:visible on every block. .custblock clips so its rounded corners hold the table,
       but on paper a clipped table is a table with columns silently missing — the same reason
       .table-wrap is un-clipped for print higher up. */
    .custblock { box-shadow: none; overflow: visible; }

    /* Keeping a customer whole on one sheet is right for a DIRECT order: each block is a drop,
       and a consignment split across a page fold is how a driver leaves half of it behind.
       It is wrong for a warehouse order, where the whole lot goes out as one delivery and the
       rule would only turn a sixty-customer picking list into sixty sheets. */
    .release-blocks-direct .custblock {
        page-break-inside: avoid;
        break-inside: avoid;
    }

    /* The rule above is scoped to direct orders on purpose, so it does not cover the destination
       block on a warehouse one — and a ship-to whose heading prints on one sheet and whose
       address prints on the next is the one break in this stack that actually costs something. */
    .custblock-dest {
        page-break-inside: avoid;
        break-inside: avoid;
    }

    /* Browsers drop background paint when printing unless asked not to, and this band is white
       text on deep green — so the one figure the page tells a supplier to invoice against was
       the one figure that came out blank. Restyled rather than forced, because print-color-adjust
       is a request the printer may still refuse. */
    .order-total {
        background: transparent;
        color: var(--ink);
        border: 2px solid var(--green-900);
    }

    .order-total-label { color: var(--green-900); }
    .order-total-sub,
    .order-total-figs span { color: var(--ink-soft); }
    .order-total-figs strong { color: var(--green-900); }
}

/* ============================================================
   LATE OVERRIDES
   These must come last. Every one of them exists because an
   earlier rule uses the `margin` shorthand, which silently
   zeroes the horizontal margins .bleed depends on.
   ============================================================ */

/* .band, .band-dark and .cta-band all set `margin` shorthand, which resets the
   left/right margins .bleed sets. Same specificity means source order decides,
   so the utility has to be restated here to actually break out of the column.
   Without this a .bleed section renders inset — which is what it was doing. */
.bleed {
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
}

/* ---------- Narrow phones ----------
   The wordmark has already left the header row (860px block); here Sign In follows. */
@media (max-width: 560px) {
    .site-header .brand-logo-chip { margin: 0; }

    .nav-cta { padding: .45rem .7rem; font-size: .84rem; }

    /* Sign In leaves the header row here and moves into the hamburger panel. The logo,
       Sign In, the primary action and the menu button together need more than the
       288px content box of a 320px phone; something has to give, or .header-user wraps
       and the header grows a second row that --header-h (960px block) does not allow for.

       The CTA stays because a first-time visitor needs it and the pages without a hero
       CTA are reached from here. Sign In is still one tap away in the menu, in the hero
       note ("Already approved? Sign in…") and in the footer.

       Targeted at a.btn-outline specifically: when someone IS signed in, .header-user
       holds the account menu, which must not disappear. */
    .header-user a.btn-outline { display: none; }
    .nav-menu-panel a.nav-menu-signin { display: block; }

    .hero-home { padding: 2.5rem 1rem 2.25rem; }
    .hero h1 { font-size: 1.7rem; }

    /* Full-width actions read as buttons rather than as links squeezed into a corner.
       white-space is reset because .cta-band-actions .btn sets nowrap for the desktop
       row; with a fixed 100% width a long label would overflow its own box instead of
       wrapping. */
    .supplier-band .btn,
    .cta-band-actions .btn,
    .band-cta .btn-lg {
        width: 100%;
        text-align: center;
        white-space: normal;
    }

    .value-card { padding: 1.25rem 1.15rem; }

    /* Restated AFTER .value-card, which would otherwise flatten the top padding that
       leaves room for the icon punching through the card's top edge. Same reason
       .testimonial-card is restated below. */
    .value-card-centered { padding-top: 2.2rem; }

    .testimonial-card { padding-left: 2.7rem; }
    .testimonial-card::before { font-size: 2.8rem; left: .8rem; }
}

/* ============================================================
   REPORT AN ISSUE
   A corner affordance on every signed-in screen for the client review period, plus the page it
   opens.
   ============================================================ */

/* LEFT, not right. The cart and checkout summary rails are sticky on the right and can run the
   full height of the viewport, which puts their primary buttons — Proceed to Checkout, Submit
   Order Request — in exactly the band a bottom-right pill occupies on any screen narrower than
   about 1600px. Covering the most important control in the farmer workflow to offer a feedback
   link is a poor trade. */
.report-issue {
    position: fixed;
    left: 1rem;
    bottom: 1rem;
    z-index: 55;
    /* The wrapper is a positioning box, not a target. Without this it takes the full width it is
       given at narrow sizes and swallows taps on whatever sits behind it. */
    pointer-events: none;
}

.report-issue-trigger {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    pointer-events: auto;
    background: var(--green-900);
    color: #fff;
    text-decoration: none;
    border-radius: 999px;
    padding: .5rem .9rem;
    font-size: .85rem;
    font-weight: 600;
    box-shadow: 0 2px 10px rgba(35, 40, 30, .22);
}

.report-issue-trigger:hover { background: var(--green-700); color: #fff; }
.report-issue-trigger svg { width: 1rem; height: 1rem; }

/* Clear of the admin sidebar. The sidebar is sticky and full height with no z-index of its own,
   so a pill fixed at the viewport's left edge paints over its footer and swallows clicks on
   "View public site". Offset by the rail's width instead — both widths, since the rail collapses
   to 62px. Below the breakpoint the sidebar is hidden and the offset must go. */
.admin-shell .report-issue { left: calc(230px + 1rem); }
.admin-shell-collapsed .report-issue { left: calc(62px + 1rem); }

@media (max-width: 860px) {
    .admin-shell .report-issue,
    .admin-shell-collapsed .report-issue { left: 1rem; }
    .report-issue-label { display: none; }
    .report-issue-trigger { padding: .75rem; }
}

/* Print puts this nowhere useful, and a picked order sheet does not need a feedback button. */
@media print {
    .report-issue { display: none; }
}

.report-page {
    max-width: 44rem;
    margin: 0 auto;
    padding: 1.25rem 0 2rem;
}

/* ------------------------------------------------------------------------------------------
   Supplier product code on a released order line (2026-09-22)

   The supplier's own code for a product, which is what their warehouse actually picks against
   — RFM's SKU is a number they have never seen. It sits on its own line directly under the
   product name, above the muted subline that carries RFM's SKU and the availability window.

   Monospaced for the reason .temp-password is: these get read aloud and transcribed, and the
   cost of confusing l and 1 in a picking code is a wrong pallet. Not letter-spaced, though —
   these are read at a glance on a list, not dictated character by character.

   Nothing here renders when a line has no supplier code, which is most of them for now: the
   markup omits the whole block rather than reserving space for it, so an ordinary line looks
   exactly as it did before this existed.
   ------------------------------------------------------------------------------------------ */
.line-code {
    display: flex;
    align-items: baseline;
    gap: .45rem;
    flex-wrap: wrap;
    margin-top: .15rem;
}

.line-code-value {
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: .92rem;
    font-weight: 600;
    color: var(--green-900);
}

/* Small and quiet. The label exists so nobody mistakes this for RFM's SKU on a phone call; it
   is not itself information anyone needs to read twice. */
.line-code-label {
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--ink-soft);
}

/* ------------------------------------------------------------------------------------------
   ORDERING TIERS (2026-09-29)

   Three small additions, all additive: a grid of checkboxes for picking product groups on a
   tier (and tiers on a farm), and a quiet second line inside a table cell.

   The checkbox itself is deliberately left alone. The global input rule excludes
   [type="checkbox"] through its :not() chain, so anything styled here would be fighting
   nothing — and the :not() chain is exactly the specificity trap the quantity stepper walked
   into on 2026-08-26.
   ------------------------------------------------------------------------------------------ */
.check-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: .45rem .9rem;
}

.check-item {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .45rem .6rem;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: #fff;
    font-weight: 500;
    cursor: pointer;
}

.check-item:hover { border-color: #bcb69e; }

/* Not a <label> descendant rule: .check-item IS the label, and .form-field label sets the
   heavier form-label weight and colour, which reads as a heading rather than a choice. */
.form-field .check-item { font-weight: 500; font-size: .93rem; color: var(--ink); }

/* A second line under the main value in a table cell — a tier's description beside its name.
   Block so it drops below, quiet so it never competes with the value above it. */
.cell-note {
    display: block;
    font-size: .82rem;
    margin-top: .15rem;
}

/* The tier refusal inside the buy rail. Full width like the buttons it replaces, and spaced
   the same, so the rail does not reflow when a farm's access changes. */
.buy-blocked { margin: .9rem 0; }

/* ---------------------------------------------------------------------------
   Product filters — the facet panel beside the marketplace grid (2026-09-29).

   Two layouts from one piece of markup. On a phone the panel is collapsed
   behind a button: twenty choices stacked above the products would push the
   products off the screen entirely. From 900px it becomes a sidebar, with the
   button hidden and the body always shown — a sidebar you have to open first
   is friction for nothing.

   NOT a <details>. A closed one is hidden through
   `::details-content { content-visibility: hidden }`, which no display rule on
   a child can override, so a media query cannot force one open — the panel
   rendered as an empty box on every desktop. Plain classes work everywhere.
   --------------------------------------------------------------------------- */

.catalog-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.1rem;
    align-items: start;
}

.facet-shell {
    border: 1px solid var(--line);
    border-radius: 12px;
    background: #fff;
    padding: .2rem .9rem;
}

.facet-toggle {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    padding: .7rem .1rem;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 600;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.facet-body {
    display: none;
    padding: .2rem 0 .8rem;
}

.facet-shell.facet-open > .facet-body { display: block; }

/* Panel chrome — heading, Clear all, lede, footnote. Matches the mockup Jackie
   sent on 2026-10-01. */
.facet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
}

.facet-heading {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 0;
    font-size: 1.02rem;
    font-weight: 700;
}

.facet-heading-icon svg { width: 1rem; height: 1rem; display: block; }

.facet-lede { margin: .15rem 0 .9rem; font-size: .86rem; }

.facet-note {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin: .95rem 0 0;
    padding-top: .8rem;
    border-top: 1px solid var(--line);
    font-size: .82rem;
}

.facet-note-icon svg { width: .95rem; height: .95rem; display: block; }

.facet-group + .facet-group {
    margin-top: .9rem;
    padding-top: .9rem;
    border-top: 1px solid var(--line);
}

/* A button, so a long list can be folded away. Styled to read as a heading
   rather than a control — the chevron is what says it does something. */
.facet-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    width: 100%;
    margin: 0 0 .35rem;
    padding: .15rem 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: .95rem;
    font-weight: 700;
    color: var(--ink);
    text-align: left;
    cursor: pointer;
}

.facet-chevron svg {
    width: 1rem;
    height: 1rem;
    display: block;
    color: var(--muted);
    transition: transform .15s ease;
}

.facet-chevron-down svg { transform: rotate(180deg); }

.facet-list { list-style: none; margin: 0; padding: 0; }

.facet-item {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .25rem 0;
    font-size: .93rem;
    cursor: pointer;
}

/* The name takes the slack so every count lines up down the right edge, which is
   what makes the column scannable rather than something to read word by word. */
.facet-name { flex: 1 1 auto; }

.facet-count {
    flex: 0 0 auto;
    font-size: .82rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

@media (min-width: 900px) {
    .catalog-layout { grid-template-columns: 15rem 1fr; gap: 1.4rem; }
    /* No panel, so no sidebar column — otherwise the grid's only child sits in
       the 15rem track and the products render in a ribbon down the left. */
    .catalog-layout.catalog-layout-plain { grid-template-columns: 1fr; }
    .facet-toggle { display: none; }
    .facet-shell > .facet-body { display: block; }
    /* Scrolls within itself. Sticky inside a grid row pins the panel while the
       product column goes on past it, so without a height cap everything below
       the fold — and with Category and Brand there is more of it — can never be
       scrolled into view. */
    .facet-shell {
        position: sticky;
        top: 1rem;
        max-height: calc(100vh - 2rem);
        overflow-y: auto;
    }
}

/* The grid's own heading, so the products have a label of their own once a
   filter panel sits beside them. */
.results-head {
    margin: 0 0 .8rem;
    font-size: 1.02rem;
    font-weight: 700;
}

/* A brand badge that links to the rest of the range. Inherits the badge's own
   colours; only the affordance is added. */
.badge-link { text-decoration: none; }
.badge-link:hover { text-decoration: underline; }

/* Brand and tags on a product page. Quiet rows under the heading — facts about
   the goods, not something to compete with the price. */
.product-tags {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: .6rem 0 0;
    padding: 0;
    list-style: none;
}

.product-tag {
    display: inline-flex;
    align-items: baseline;
    gap: .3rem;
    padding: .22rem .55rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--cream);
    font-size: .84rem;
}

.product-tag-group { color: var(--muted); }

/* ---------------------------------------------------------------------------
   Contact page (2026-10-01).

   The details used to sit in a full-width card above the form, so the thing
   somebody came to do — write a message — started below the fold. They are
   secondary: a column beside it on wide screens, and underneath the form on a
   phone, where a farmer is far more likely to want the phone number first.
   --------------------------------------------------------------------------- */

.contact-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.1rem;
    align-items: start;
    margin-top: 1.4rem;
}

/* The form comes FIRST in the markup, so it is first for a screen reader and
   first under the thumb on a phone. `order` is visual only — it moves nothing
   for the keyboard — so the DOM has to carry the real order and the grid only
   puts the details back on the left above 900px. */
.contact-aside { display: grid; gap: 1.1rem; align-content: start; }

.contact-links { list-style: none; margin: .2rem 0 0; padding: 0; }

.contact-links li + li {
    margin-top: .7rem;
    padding-top: .7rem;
    border-top: 1px solid var(--line);
}

.contact-links a { font-weight: 600; }
.contact-links .muted { display: block; font-size: .86rem; margin-top: .1rem; }

/* Who the message will come from, when we already know. Replaces the name and
   email boxes rather than sitting above them. */
.sender-card {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .8rem .9rem;
    margin-bottom: 1rem;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--cream-dark);
}

.sender-avatar {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: 50%;
    background: var(--green-900);
    color: #fff;
    font-weight: 700;
    font-size: .9rem;
}

.sender-text { flex: 1 1 auto; display: grid; }
.sender-text .muted { font-size: .88rem; }
.sender-link { flex: 0 0 auto; font-size: .88rem; }

/* The topic-specific block. Spacing rather than a border: it is part of the
   same form, not a separate section. `hidden` is set by rfm.js. */
.contact-topic-fields { margin-bottom: .2rem; }
.contact-topic-fields[hidden] { display: none; }

@media (min-width: 900px) {
    .contact-layout { grid-template-columns: 21rem 1fr; gap: 1.6rem; }
    .contact-aside { order: 1; }
    .contact-main { order: 2; }
}

/* The contact rows were written for a full-width card: label left, value hard
   right, 46px badge, generous padding. In a 21rem aside that layout breaks in
   three ways at once — info@revolutionaryfarm.example is wider than the space
   left beside the badge and the label, the base `gap: 1rem` becomes a chasm
   once the value drops onto its own line, and `align-items: center` leaves the
   badge floating in the middle of a two-line row.

   The existing stacked treatment is keyed to a 560px VIEWPORT, which an aside
   that is narrow on a 2560px monitor never satisfies. So the aside gets its own
   proportions: the badge spans both lines at the size a sidebar wants, the
   label and value sit tight above one another, and the row loses most of its
   padding. Nothing here touches the full-width version on any other page. */
.contact-aside .contact-row {
    grid-template-columns: auto 1fr;
    align-items: start;
    column-gap: .7rem;
    row-gap: .05rem;
    padding: .7rem .15rem;
}

/* Spanning both lines, so it sits beside the pair rather than between them. */
.contact-aside .icon-badge {
    grid-row: span 2;
    width: 34px;
    height: 34px;
    margin-top: .1rem;
}

.contact-aside .contact-label {
    grid-column: 2;
    font-size: .78rem;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* 0.9rem keeps the longest value — the email address — on one line inside the
   content box, which is what stops it hyphenating itself mid-word. The wrap
   rule stays as a backstop for whatever address RFM actually supply. */
.contact-aside .contact-value {
    grid-column: 2;
    text-align: left;
    font-size: .92rem;
    font-weight: 600;
    color: var(--green-900);
}

.contact-aside .contact-value a { overflow-wrap: anywhere; }

.contact-aside .contact-list { margin: 1rem 0 1.1rem; }

/* The last rule in the list carries no information — the card edge is already
   the end of it. */
.contact-aside .contact-row:last-child { border-bottom: 0; padding-bottom: .2rem; }

/* The placeholder-details note. Quiet and in the palette rather than the blue
   info alert, which is the one cool colour on an otherwise earth-toned page and
   pulled the eye straight to the least important thing in the column. */
.contact-note {
    display: flex;
    gap: .6rem;
    padding: .7rem .8rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--gold-600);
    border-radius: 8px;
    background: var(--cream-dark);
    font-size: .86rem;
    color: var(--ink-soft);
}

.contact-note-icon { flex: 0 0 auto; color: var(--gold-600); }
.contact-note-icon svg { width: 1rem; height: 1rem; display: block; margin-top: .1rem; }

/* =====================================================================
   FARMER ACCOUNT AREA — 2026-10-01 mockup pass
   Section heads, read-only detail rows, card footers, the overview rail,
   order stat tiles and the planting-plan crop rows.
   ===================================================================== */

/* ---------- Section head inside a card ----------
   Glyph, title, one line of context. The glyph is decorative and sits OUTSIDE the heading
   so it is never part of the accessible name. */
.sec-head {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-bottom: 1.1rem;
}

.sec-head .panel-title { margin: 0; }
.sec-head p { margin: .1rem 0 0; }

.sec-head-icon { flex: 0 0 auto; color: var(--green-700); line-height: 0; }
.sec-head-icon svg { width: 24px; height: 24px; display: block; }

/* The variant whose action sits on the same line as the title — Sign-in & security. */
.sec-head-inline { margin-bottom: 0; }
.sec-head-inline > div { flex: 1 1 auto; min-width: 0; }
.sec-head-inline .btn { flex: 0 0 auto; }

@media (max-width: 560px) {
    /* The button would otherwise squeeze the heading into two or three words per line. */
    .sec-head-inline { flex-wrap: wrap; }
    .sec-head-inline .btn { margin-left: 2.7rem; }
}

/* ---------- "Managed by RFM" ----------
   A lock plus a word, in the neutral badge. Deliberately NOT a warning colour: nothing is
   wrong, it just is not the farmer's to edit. */
.badge-lock {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    background: var(--cream-dark);
    color: var(--ink-soft);
}

.badge-lock svg { width: .82rem; height: .82rem; flex: 0 0 auto; }
.badge-sm { font-size: .72rem; padding: .1rem .45rem; }

/* ---------- Read-only detail row ----------
   Three facts side by side with hairlines between. auto-fit rather than a fixed 3 so it
   folds to two and then one without a media query of its own. */
/* An explicit 3, NOT repeat(auto-fit, …). With auto-fit the track count changes on its own,
   so at an in-between width the third fact wraps onto a second row and keeps the divider it
   was given as a middle column — a hairline hanging off the left edge of the card. Three
   across or stacked, nothing between. */
.detail-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.25rem;
}

.detail-row > * + * {
    border-left: 1px solid var(--line);
    padding-left: 1.25rem;
}

/* 860px is where .account-shell drops its sidebar, so the card is at its narrowest just above
   this and at full width just below it. */
@media (max-width: 860px) {
    .detail-row { grid-template-columns: minmax(0, 1fr); gap: .9rem; }
    /* Stacked, a left border is a stray vertical line beside the text. */
    .detail-row > * + * { border-left: 0; padding-left: 0; }
}

/* ---------- Copy-to-clipboard ----------
   The button ships hidden and rfm.js reveals it; see the markup comment on Profile for why.
   .copy-pair keeps the glyph on the baseline of whatever it follows, including the 1.65rem
   metric value. */
.copy-pair { display: inline-flex; align-items: center; gap: .4rem; }

.copy-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    flex: 0 0 auto;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--ink-soft);
    cursor: pointer;
}

.copy-btn svg { width: .95rem; height: .95rem; display: block; }
.copy-btn:hover { background: var(--cream-dark); color: var(--green-700); border-color: var(--line); }
.copy-btn[hidden] { display: none; }

/* The confirmation rfm.js flips on for a moment after a successful copy. */
.copy-btn.is-copied { color: var(--green-700); border-color: var(--green-600); background: var(--green-100); }

/* ---------- Card footer strip ----------
   A full-bleed bar at the bottom of a card: the explanation on the left, the action on the
   right. The negative margins cancel .form-card's padding, so this must stay in step with it
   (1.25rem 1.35rem 1.35rem). */
.card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin: 1.25rem -1.35rem -1.35rem;
    padding: .85rem 1.35rem;
    border-top: 1px solid var(--line);
}

.card-foot p { margin: 0; }

/* .form-card > :last-child { margin-bottom: 0 } is equally specific and would flatten the
   negative margin above, leaving the strip floating inside the card. Hence the extra class. */
.form-card > .card-foot:last-child { margin-bottom: -1.35rem; }

/* The variant carrying a primary action — tinted so the save bar reads as the end of the form. */
.card-foot-action { background: var(--green-100); border-top-color: #cfd8bb; }

/* ---------- Overview: orders beside the farm's own details ---------- */
.overview-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr);
    gap: 1.25rem;
    align-items: start;
    margin-bottom: 1rem;
}

.overview-rail { min-width: 0; }
.overview-rail .card:last-child { margin-bottom: 0; }

@media (max-width: 900px) {
    /* The rail is reference material; it follows the orders in the DOM and stacks under them. */
    .overview-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- Row chevron ----------
   Replaces the per-row "View" button. The cell is sized to the glyph so the chevrons line up
   down the right edge of the table. */
.row-go-cell { width: 1px; white-space: nowrap; text-align: right; }

.row-go {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 6px;
    color: var(--ink-soft);
}

.row-go svg { width: 1rem; height: 1rem; display: block; }
.row-go:hover { background: var(--cream-dark); color: var(--green-700); }

/* ---------- Order stat tiles ----------
   A summary of the whole history above the filters. Not metric cards: these carry no link and
   no caption, and standing them at the same height as the four on Overview would overstate
   them. */
.stat-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 1rem;
    margin-bottom: 1.25rem;
}

.stat-tile {
    display: flex;
    align-items: center;
    gap: .8rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: .9rem 1rem;
}

.stat-value {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--green-900);
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.stat-label { font-size: .85rem; color: var(--ink-soft); }

/* Tinted to match the badge each count stands for, so the tile and the rows it describes
   read as the same thing. */
.stat-tile-wait { background: #fdfaf1; border-color: #e7d9b4; }
.stat-tile-done { background: #f7faf4; border-color: #cfdcc4; }

/* ---------- Search field with a leading glyph ---------- */
.input-icon { position: relative; display: block; }

.input-icon svg {
    position: absolute;
    left: .6rem;
    top: 50%;
    transform: translateY(-50%);
    width: 1rem;
    height: 1rem;
    color: var(--ink-soft);
    pointer-events: none;
}

.input-icon input { padding-left: 2.1rem; }

/* Safari paints its own clear button inside type="search" and squeezes the text; and the
   default appearance ignores the border radius the rest of the form uses. */
.input-icon input[type="search"] { -webkit-appearance: none; appearance: none; }

/* ---------- Notice strip ----------
   A standing statement of fact, not a response to anything the user did — so it is not an
   .alert. An alert that is present on every single render teaches people to stop reading
   alerts. */
.notice-strip {
    display: flex;
    align-items: center;
    gap: .7rem;
    background: var(--green-100);
    border: 1px solid #cfd8bb;
    border-radius: var(--radius);
    padding: .7rem 1rem;
    margin-bottom: 1.25rem;
}

.notice-strip p { margin: 0; font-size: .93rem; color: var(--green-900); }
.notice-strip-icon { flex: 0 0 auto; color: var(--green-700); line-height: 0; }
.notice-strip-icon svg { width: 1.2rem; height: 1.2rem; display: block; }

/* ---------- Planting plan: season bar ---------- */
.season-card { display: flex; align-items: flex-end; gap: 1rem; flex-wrap: wrap; }
.season-card .form-field { margin-bottom: 0; min-width: 190px; }
.season-card-note { margin: 0 0 .45rem; }
.head-note-text { color: var(--gold-600); font-size: .9rem; font-weight: 600; }

/* ---------- Planting plan: crop rows ----------
   Cards, not table rows: four controls plus a share bar is more than a cell should hold, and
   a four-column table of form controls is unusable on a phone. */
/* Short on the bottom because the "Add crop" row below brings .panel-pad's own 1rem plus
   .actions-row's margin; the full padding here stacked with both. */
.crop-rows { display: flex; flex-direction: column; gap: .75rem; padding: 1rem 1rem .25rem; }

/* One line per crop, aligned to the header strip above. Explicit tracks rather than flex so the
   header and every row share one column grid — a flex row would let each row size its own columns
   and the header would stop lining up the moment one crop name ran long. */
.crop-row {
    display: grid;
    grid-template-columns: 2rem minmax(0, 1.2fr) 7.5rem minmax(0, 1.6fr) 2.25rem;
    align-items: center;
    gap: .6rem;
    padding: .5rem .9rem;
}

.crop-row + .crop-row { border-top: 1px solid var(--line); }

/* Column labels. aria-hidden in the markup and each control keeps its own sr-only label, so a
   screen reader hears "Crop" on the select rather than having to hold a table header in mind. */
.crop-row-head {
    background: var(--cream-dark);
    border-radius: 8px;
    padding-top: .4rem;
    padding-bottom: .4rem;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.crop-row-head + .crop-row { border-top: 0; }
.crop-row .form-field { margin-bottom: 0; }

/* A bare icon, but a real target. .btn-sm alone is about 28px tall, and this list is used on
   tablets in a farm office — 44px is the size a finger can hit without magnifying the page. */
.crop-row-remove {
    justify-self: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
}

.crop-row-remove svg { width: 1.05rem; height: 1.05rem; }

.crop-glyph { color: var(--green-700); line-height: 0; align-self: center; }
.crop-glyph svg { width: 30px; height: 30px; display: block; }

.crop-col-crop,
.crop-col-acres,
.crop-col-notes { min-width: 0; }


@media (max-width: 760px) {
    /* A five-column table of form controls does not survive a phone. The row becomes a stack,
       the header strip goes (it would label nothing), and each control gets its own label back. */
    .crop-row-head { display: none; }

    .crop-row {
        grid-template-columns: 2rem minmax(0, 1fr) 2.25rem;
        align-items: start;
        row-gap: .5rem;
        border: 1px solid var(--line);
        border-radius: var(--radius);
        padding: .8rem .85rem;
    }

    .crop-row + .crop-row { border-top: 1px solid var(--line); }
    .crop-col-acres,
    .crop-col-notes { grid-column: 2 / 3; }

    /* Stacked, the sr-only labels are the only thing naming these controls, so they come back. */
    .crop-row .form-field .sr-only {
        position: static;
        width: auto;
        height: auto;
        clip: auto;
        clip-path: none;
        white-space: normal;
        font-weight: 600;
        font-size: .82rem;
        color: var(--green-900);
    }
}

/* ---------- Planting plan: save bar ----------
   NOT .save-bar. That name belongs to the admin console's action bar (seven editors use it), and
   borrowing it meant this inherited max-width: 1400px, justify-content: flex-end and a second
   border and background on top of its own .card — and, at 720px, a flex-direction: column-reverse
   that stacked the discard confirmation ABOVE the text explaining it, in reverse order. */
.plan-save-bar { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.plan-save-bar-text { flex: 1 1 280px; min-width: 0; }
.plan-save-bar-text strong { color: var(--green-900); }
.plan-save-bar-text p { margin: .15rem 0 0; }

@media (max-width: 620px) {
    /* Stacked, the actions go full width — but in DOM order, so "Keep editing" stays before
       "Yes, discard" and the status text stays above both. */
    .plan-save-bar { flex-direction: column; align-items: stretch; }
    .plan-save-bar .btn { width: 100%; }
}

/* =====================================================================
   CROP COLOURS — 2026-10-02
   The swatch a crop is drawn in, keyed by CropPalette. The KEY is what the
   database holds; these variables are the only place a colour value exists,
   so restyling every chart is an edit here and nothing else.

   Eighteen, ordered by CropPalette.Keys so a farm's first crops are maximally
   distinct. Separated by LIGHTNESS as well as hue: deuteranopia collapses the
   red-green axis, and these still read as light and dark bands when the hues
   merge. Legend text is set in ink beside the dot, never in the swatch colour,
   so these only have to be distinct from each other and from the cream — they
   carry no text.
   ===================================================================== */

:root {
    --crop-olive: #39441f;
    --crop-gold: #c8952c;
    --crop-teal: #2f6b5e;
    --crop-clay: #b5703f;
    --crop-sage: #9aa87b;
    --crop-rust: #8e3d1e;
    --crop-moss: #68763a;
    --crop-straw: #d9c48a;
    --crop-bark: #6b4423;
    --crop-fern: #4f6b2f;
    --crop-amber: #e0b45f;
    --crop-lichen: #8c9a4e;
    /* 2026-10-02: the six hues that took the palette from twelve to eighteen. */
    --crop-slate: #3f5a73;
    --crop-heather: #b493a8;
    --crop-plum: #6b3550;
    --crop-sky: #8fb2c9;
    --crop-denim: #5d7fa3;
    --crop-rose: #c27a85;
}

.crop-olive { --crop-fill: var(--crop-olive); }
.crop-gold { --crop-fill: var(--crop-gold); }
.crop-teal { --crop-fill: var(--crop-teal); }
.crop-clay { --crop-fill: var(--crop-clay); }
.crop-sage { --crop-fill: var(--crop-sage); }
.crop-rust { --crop-fill: var(--crop-rust); }
.crop-moss { --crop-fill: var(--crop-moss); }
.crop-straw { --crop-fill: var(--crop-straw); }
.crop-bark { --crop-fill: var(--crop-bark); }
.crop-fern { --crop-fill: var(--crop-fern); }
.crop-amber { --crop-fill: var(--crop-amber); }
.crop-lichen { --crop-fill: var(--crop-lichen); }
.crop-slate { --crop-fill: var(--crop-slate); }
.crop-heather { --crop-fill: var(--crop-heather); }
.crop-plum { --crop-fill: var(--crop-plum); }
.crop-sky { --crop-fill: var(--crop-sky); }
.crop-denim { --crop-fill: var(--crop-denim); }
.crop-rose { --crop-fill: var(--crop-rose); }

/* The two shapes that consume --crop-fill. Anything else a crop colours should
   set the variable the same way rather than naming a colour directly. */
.crop-dot {
    display: inline-block;
    width: .7rem;
    height: .7rem;
    border-radius: 50%;
    background: var(--crop-fill, var(--line));
    flex: 0 0 auto;
    /* A hairline so the palest swatches still have an edge against white. */
    box-shadow: inset 0 0 0 1px rgba(36, 40, 31, .18);
}

.crop-fill-bg { background: var(--crop-fill, var(--line)); }

/* ---------- Acreage mix ----------
   One segmented bar for the whole plan, and a legend that is the actual data. The bar is the
   picture; at ten crops several segments are slivers, which is fine because nothing depends on
   reading them — the legend is a list and stays legible however many crops a farm plans. */
.acreage-mix {
    padding: 1rem 1rem .25rem;
    border-bottom: 1px solid var(--line);
}

.acreage-mix-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
    flex-wrap: wrap;
    margin-bottom: .6rem;
}

.acreage-mix-title {
    margin: 0;
    font-size: .95rem;
    color: var(--green-900);
}

.mix-bar {
    display: flex;
    height: 20px;
    border-radius: 6px;
    overflow: hidden;
    background: var(--cream-dark);
}

.mix-seg {
    background: var(--crop-fill, var(--line));
    /* A sliver still has to be visible as its own segment rather than vanishing between its
       neighbours — a crop at 0.3% of a 1,600-acre plan is a real row somebody typed. */
    min-width: 3px;
}

.mix-seg + .mix-seg { box-shadow: inset 1px 0 0 rgba(255, 255, 255, .5); }

/* Ground the farm has not planned. Deliberately not a palette colour: it is the absence of a
   crop, not another crop, and a hatch says so without needing a colour of its own. */
.mix-seg-rest {
    --crop-fill: transparent;
    background:
        repeating-linear-gradient(135deg,
            var(--cream-dark) 0 6px,
            #e6dfc7 6px 12px);
}

.mix-legend {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 1.5rem;
    list-style: none;
    margin: .75rem 0 0;
    padding: 0;
}

.mix-legend li { display: flex; align-items: flex-start; gap: .45rem; }
.mix-legend-text { display: flex; flex-direction: column; line-height: 1.3; }
.mix-legend-name { font-weight: 600; color: var(--green-900); font-size: .9rem; }
.mix-legend-meta { font-size: .82rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* The dot sits on the first line of text, not centred against two lines of it. */
.mix-legend .crop-dot { margin-top: .3rem; }

/* ---------- Crop appearance picker (admin) ---------- */
.field-label-plain {
    display: block;
    font-weight: 600;
    font-size: .87rem;
    color: var(--green-900);
    margin-bottom: .35rem;
}

.swatch-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: .4rem;
}

.swatch {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .4rem .55rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: #fff;
    cursor: pointer;
    font-size: .88rem;
}

/* The radio itself is hidden from sight but NOT from the keyboard or a screen reader — this is
   the one input in the group that carries the choice. :focus-visible on it rings the label. */
.swatch input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.swatch:hover { background: var(--cream); }
.swatch.is-chosen { border-color: var(--green-700); box-shadow: inset 0 0 0 1px var(--green-700); }
.swatch:has(input:focus-visible) { outline: 2px solid var(--gold-600); outline-offset: 2px; }

.swatch-chip {
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 4px;
    flex: 0 0 auto;
    background: var(--crop-fill, var(--line));
    box-shadow: inset 0 0 0 1px rgba(36, 40, 31, .18);
}

.swatch-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.appearance-preview {
    display: flex;
    align-items: center;
    gap: .9rem;
    flex-wrap: wrap;
    padding: .7rem .85rem;
    border: 1px dashed var(--line);
    border-radius: 8px;
    background: var(--cream);
}

.preview-label { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; font-weight: 700; }
.crop-legend-item { display: flex; align-items: center; gap: .5rem; font-weight: 600; color: var(--green-900); }
.crop-legend-item .crop-glyph svg { width: 24px; height: 24px; }
