/* Copyright (c) 2026 Bizon, LLC. All rights reserved. */
/* Bizon VPD Dashboard - shared theme */

/* Inter, served from here rather than Google Fonts: two fewer connections
   before first paint (~750 ms on PageSpeed mobile). One variable file per
   script, as Google splits it; unicode-range means a page only downloads the
   scripts its text uses, which for most pages is latin alone. Pages preload
   inter-latin. OFL, see /fonts/LICENSE-Inter.txt. */
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url(/fonts/inter-latin.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url(/fonts/inter-latin-ext.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url(/fonts/inter-cyrillic-ext.woff2) format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url(/fonts/inter-cyrillic.woff2) format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url(/fonts/inter-greek-ext.woff2) format('woff2');
    unicode-range: U+1F00-1FFF; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url(/fonts/inter-greek.woff2) format('woff2');
    unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
    src: url(/fonts/inter-vietnamese.woff2) format('woff2');
    unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }

:root {
    /* Tells the browser the page is dark, so anything IT paints rather than we
       do - the iOS status bar strip on a home-screen PWA, the rubber-band area
       past the end of a scroll, form controls - uses dark defaults. Without it
       the UA assumes light and fills those with white, which is how the PWA's
       status bar went white under the nav's translucent background. */
    color-scheme: dark;

    /* Surfaces - warm espresso → leather */
    --bg:          #14100b;
    --bg-grad-a:   #1a140d;
    --bg-grad-b:   #100c08;
    --surface:     #1d1811;
    --surface-2:   #251e15;
    --surface-3:   #2e251a;

    /* Lines */
    --border:      #382d20;
    --border-soft: #2a2118;
    --border-warm: #4a3a28;

    /* Text */
    --text:        #f4ede0;
    --text-muted:  #b3a48d;
    --text-dim:    #7d6e5a;

    /* Brand - caramel / tan / leather */
    --brand:       #c08a4f;
    --brand-light: #dcad77;
    --brand-dark:  #9a6432;
    --brand-deep:  #6e4623;

    /* Metric accents (harmonized for warm dark bg).
       Intuitive per reading: leaf = green, temperature = red, water = blue.
       The trends chart reads these at paint time, so a scheme change repaints
       the graph, its chips and its bands from one place. Every scheme below
       restates the whole set: a line the eye can follow on espresso is a pale
       smear on white. */
    --c-canopy:    #8ab98a;  /* sage green: leaf-surface temp */
    --c-air:       #e07a5f;  /* terracotta red: air temp      */
    --c-humidity:  #6fa8dc;  /* soft blue: humidity           */
    --c-vpd:       #e8b04b;  /* amber/gold: VPD               */
    --c-airvpd:    #f0c987;  /* pale gold: the estimated pair */
    --c-co2:       #c58fd6;  /* violet: the one free hue      */
    /* Per-sensor lines in the room view, as one list the chart splits on the
       comma. Eight, because past that a room chart is unreadable anyway. */
    --room-lines:  #8ab98a, #6fa8dc, #e07a5f, #b58ad0, #7fc7bd, #d0a35f, #c58ab9, #9aa86f;

    /* Pro plan (homeowner tier) blue, and the only
       non-caramel accent in the chrome. It used to be six hardcoded hexes
       tuned for espresso, which meant the light and greenhouse schemes
       inherited a pale blue that reads at 1.5:1 on parchment and 1.6:1 on
       white: a heading nobody could see. Same contract as the metric accents
       above - every scheme restates the whole set.
         --home       borders and lines        --home-fill  button fill
         --home-text  headings and labels      --home-on    text ON that fill
         --home-soft  a chip's backing         --home-edge  the fill's border */
    --home:        #8fc7d8;
    --home-text:   #9fd4e2;
    --home-soft:   rgba(143,199,216,0.12);
    --home-line:   rgba(143,199,216,0.40);
    --home-fill:   linear-gradient(180deg, #9fd4e2, #6fb3c6);
    --home-on:     #0f2329;
    --home-edge:   #6fb3c6;

    /* States */
    --danger:      #d9774f;
    --danger-bg:   #2a1812;
    --ok:          #8ab98a;
    /* status chips: below / on / above target */
    --tone-low:    #8fbfe6;
    --tone-ideal:  #9fce9f;
    --tone-high:   #ec9579;

    --radius:      14px;
    --radius-sm:   9px;
    --shadow:      0 10px 30px -12px rgba(0,0,0,0.7);
    --shadow-sm:   0 4px 14px -8px rgba(0,0,0,0.6);
    /* hover lift: reads as raised without moving the element (see .tool-card:hover) */
    --shadow-lift: 0 10px 26px -10px rgba(0,0,0,0.78);
    --ring:        0 0 0 3px rgba(192,138,79,0.20);
}

/* ── Light theme ── warm cream / parchment, same caramel brand ── */
:root[data-theme="light"] {
    color-scheme: light;
    --bg:          #f1e9d9;
    --bg-grad-a:   #f4ece0;
    --bg-grad-b:   #e6dbc7;
    --surface:     #faf5ea;
    --surface-2:   #efe7d6;
    --surface-3:   #e3d6c1;

    --border:      #e4dac6;
    --border-soft: #efe7d8;
    --border-warm: #d6c1a1;

    --text:        #2c2316;
    --text-muted:  #6d5f47;
    --text-dim:    #75654c;   /* 4.6:1 or better on every light surface (was 3.1:1) */

    /* darker so caramel reads as text/accents on a light background */
    --brand:       #a8692f;
    --brand-light: #8a531e;
    --brand-dark:  #7a4a1d;
    --brand-deep:  #5c3917;

    --danger:      #c0512a;
    --danger-bg:   #fbe7dd;
    --ok:          #4f8a4f;
    --tone-low:    #2a5f99;
    --tone-ideal:  #2f6f37;
    --tone-high:   #a84522;

    /* The dark set is tuned to glow on espresso and washes out on parchment,
       so the chart gets its own. Same hues, dropped in value to clear 4.5:1
       on the page background (they label axes as well as draw lines). Air VPD
       sits a step lighter than VPD as the quieter half of the pair, at about
       4.5:1: a thin dotted line at 3.1:1 disappeared on parchment. */
    --c-canopy:    #376d34;
    --c-air:       #a8451d;
    --c-humidity:  #28658f;
    --c-vpd:       #7d5608;
    --c-airvpd:    #8f6414;
    --c-co2:       #744591;
    --room-lines:  #376d34, #28658f, #a8451d, #744591, #1c7166, #7d5608, #993c6f, #59651c;

    /* Dropped in value exactly as the metric accents above are, and for the
       same reason: this is read as TEXT on parchment. #1b6378 is 6.2:1 on
       --surface where the dark set's #9fd4e2 is 1.5:1. The fill keeps its
       dark-text-on-blue character but gets its own brighter blue, the way
       .btn-primary does below. */
    --home:        #2b7891;
    --home-text:   #1b6378;
    --home-soft:   rgba(43,120,145,0.12);
    --home-line:   rgba(43,120,145,0.45);
    --home-fill:   linear-gradient(180deg, #7fc2d6, #4f9fb6);
    --home-on:     #10262d;
    --home-edge:   #3f8ba2;

    --shadow:      0 12px 30px -16px rgba(90,60,25,0.28);
    --shadow-sm:   0 4px 14px -10px rgba(90,60,25,0.20);
    --shadow-lift: 0 10px 26px -12px rgba(90,60,25,0.34);
    --ring:        0 0 0 3px rgba(168,105,47,0.20);
}
:root[data-theme="light"] body {
    background:
        url("../img/leaves-light.svg") center center / cover no-repeat,
        radial-gradient(1100px 600px at 78% -8%, rgba(168,105,47,0.07), transparent 60%),
        radial-gradient(900px 500px at -5% 0%, rgba(120,76,31,0.05), transparent 55%),
        linear-gradient(180deg, var(--bg-grad-a), var(--bg-grad-b) 60%);
    background-attachment: fixed;
}
:root[data-theme="light"] .nav { background: rgba(248,242,231,0.82); }
:root[data-theme="light"] .message.success { color: #2f7a3a; }
:root[data-theme="light"] .message.error { color: #b04a28; }
/* The amber "no internet" tint needs darkening to stay legible on parchment. */
:root[data-theme="light"] .message.offline,
:root[data-theme="light"] .net-chip { color: #8a5a1c; background: rgba(168,105,47,0.10); border-color: rgba(168,105,47,0.34); }

/* ── Greenhouse ── a screen in direct sun ──
   Not a third taste, a third LIGHTING CONDITION. Under glare the screen's own
   light is swamped by what reflects off it, and the two themes above both fail
   in the same way: their quiet tiers (cream on cream, muted brown, hairline
   borders, shadow-as-separator) are exactly the differences that wash out
   first, so the page stops being readable while still looking designed.

   So this rides ON TOP of the light theme rather than beside it: the switcher
   sets data-theme="light" AND data-scheme="greenhouse", every light-mode rule
   in this file still applies, and what follows is only the parts that have to
   get louder. That is also why it is set with its own attribute — a fourth
   data-theme value would silently skip every ":root[data-theme=light] .foo"
   component rule and leave dark-theme widgets on a white page.

   The rules, all of them about surviving reflection rather than taste:
     - pure white ground and near-black text, for the widest luminance gap a
       screen can produce; no image or gradient behind the words
     - the muted tiers collapse upward (#000 / #262626 / #4a4a4a, all past
       8:1) so "de-emphasised" never means "gone"
     - borders carry the structure that shadows carry elsewhere, since a soft
       shadow is the first thing sunlight erases
     - accents darkened to hold 6:1+ on white while staying separable by hue,
       because the charts and status chips are read at a glance
   Contrast ratios above are against #ffffff and were checked, not guessed. */
:root[data-scheme="greenhouse"] {
    color-scheme: light;
    --bg:          #ffffff;
    --bg-grad-a:   #ffffff;
    --bg-grad-b:   #ffffff;
    --surface:     #ffffff;
    --surface-2:   #f0f0f0;
    --surface-3:   #e2e2e2;

    --border:      #6b6b6b;
    --border-soft: #9a9a9a;
    --border-warm: #3d3d3d;

    --text:        #000000;
    --text-muted:  #262626;
    --text-dim:    #4a4a4a;

    /* Caramel this dark stops reading as caramel, which is the trade: brand
       colour is the first thing to give up when the alternative is unreadable
       labels. It stays warm, just heavy. */
    --brand:       #6b3a0c;
    --brand-light: #522c08;
    --brand-dark:  #522c08;
    --brand-deep:  #3a1f05;

    --c-canopy:    #14601f;
    --c-air:       #a32c05;
    --c-humidity:  #0f4c8a;
    --c-vpd:       #6e4a00;
    --c-airvpd:    #8a6410;
    --c-co2:       #5f2a80;
    --room-lines:  #14601f, #0f4c8a, #a32c05, #5f2a80, #0d5b50, #6e4a00, #8a1f56, #40521a;

    /* 11.2:1 on white. The chip backing is a solid tint rather than an alpha
       fill for the same reason the nav is opaque here: in direct sun a
       translucent layer is a contrast problem that moves. */
    --home:        #06414f;
    --home-text:   #06414f;
    --home-soft:   #dff0f5;
    --home-line:   #06414f;
    --home-fill:   #7ec8dc;
    --home-on:     #000000;
    --home-edge:   #06414f;

    --danger:      #a3200a;
    --danger-bg:   #ffe3dc;
    --ok:          #14601f;
    --tone-low:    #0b4585;
    --tone-ideal:  #14601f;
    --tone-high:   #a3200a;

    /* Transparent no-op rather than `none`: some shadows are declared as a
       list ("inset ..., var(--shadow-sm)"), and `none` inside a list voids the
       whole declaration instead of just dropping the layer. */
    --shadow:      0 0 0 0 transparent;
    --shadow-sm:   0 0 0 0 transparent;
    --shadow-lift: 0 0 0 0 transparent;
    --ring:        0 0 0 3px rgba(0,0,0,0.45);
}
/* Flat white, no leaf art and no gradient: both sit under body text, and any
   texture there costs contrast exactly where it is needed most. */
:root[data-scheme="greenhouse"] body { background: #ffffff; }
/* Opaque. A translucent bar over scrolling content is a moving contrast
   problem, and blur cannot be read through in sunlight. */
:root[data-scheme="greenhouse"] .nav { background: #ffffff; backdrop-filter: none; -webkit-backdrop-filter: none; }
:root[data-scheme="greenhouse"] .message.success { color: #14601f; }
:root[data-scheme="greenhouse"] .message.error { color: #a3200a; }
:root[data-scheme="greenhouse"] .message.offline,
:root[data-scheme="greenhouse"] .net-chip { color: #5c3a00; background: #ffe9c4; border-color: #5c3a00; }
/* Light mode gives the primary button a gold gradient with dark text. Keep the
   gold (it is the one place the brand still shouts) but flatten it and pin the
   text to black: #e09a3f against #000 is ~8.9:1, and a flat fill has no light
   end for a reflection to blow out. */
:root[data-scheme="greenhouse"] .btn-primary {
    background: #e09a3f;
    color: #000000;
    border-color: #7a4a10;
}

*, *::before, *::after { box-sizing: border-box; }

/* The canvas colour. body's background would propagate here on its own, but
   it is a fixed-attachment stack, and the strip under the status bar is
   outside what that reliably paints on iOS; naming it on <html> means the
   safe area and any overscroll are the theme's colour, never the UA's white.
   var(--bg) tracks the theme, so this follows light and greenhouse too. */
html { -webkit-text-size-adjust: 100%; background-color: var(--bg); }

body {
    margin: 0;
    /* dvh tracks the visible viewport. On iOS 100vh is measured with the URL
       bar retracted, so it is taller than the screen and every page starts
       with a phantom scroll. vh stays first as the fallback. */
    min-height: 100vh;
    min-height: 100dvh;
    background:
        url("../img/leaves.svg") center center / cover no-repeat,
        radial-gradient(1100px 600px at 78% -8%, rgba(192,138,79,0.10), transparent 60%),
        radial-gradient(900px 500px at -5% 0%, rgba(110,70,35,0.10), transparent 55%),
        linear-gradient(180deg, var(--bg-grad-a), var(--bg-grad-b) 60%);
    background-attachment: fixed;
    color: var(--text);
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: 15px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--brand-light); text-decoration: none; transition: color .15s ease; }
a:hover { color: var(--text); }

/* ─────────────  Top bar  ───────────── */
.nav {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px clamp(16px, 4vw, 40px);
    /* Extends the nav's own background up through the status bar/notch on a
       home-screen PWA (viewport-fit=cover, set in theme.js) instead of
       leaving that strip blank; the 0px fallback keeps this a no-op
       everywhere else. */
    padding-top: calc(14px + env(safe-area-inset-top, 0px));
    background: rgba(16,12,8,0.72);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--border);
}
.brand { display: flex; align-items: center; gap: 13px; min-width: 0; }
/* The brand link is a flex item AND a flex container, and its inline style
   sets neither min-width. A flex item defaults to min-width:auto, so the
   link refused to shrink below the full title, the ellipsis on .brand-name
   never engaged, and a long tool name set a floor under the whole page:
   /gardenplanner scrolled sideways on any phone. */
.brand > a { min-width: 0; }
.brand-mark {
    display: grid;
    place-items: center;
    width: 40px; height: 40px;
    flex: 0 0 auto;
    border-radius: 11px;
    background: linear-gradient(150deg, var(--brand-light), var(--brand-dark));
    color: #1a120a;
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: -0.04em;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.35), var(--shadow-sm);
}
.brand-mark img { width: 100%; height: 100%; object-fit: contain; border-radius: 11px; }
.brand-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.brand-name {
    font-weight: 700;
    font-size: 1.02rem;
    letter-spacing: -0.01em;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.brand-sub {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--brand);
    font-weight: 600;
}
.nav-right { display: flex; align-items: center; gap: 18px; }
/* Between phone and wide desktop the signed-in nav used to run out of room
   around 1040px and fold into the menu. Tighten the spacing first, and let the
   tagline under the page name give way (ellipsis) before the links do. */
.brand-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 1280px) {
    .nav { padding-left: 24px; padding-right: 24px; }
    .nav-right { gap: 12px; }
    .brand-sub { letter-spacing: 0.08em; }
}

/* Collapse the nav's right side into a phone-app-style dropdown (every screen
   size). theme.js adds .has-menu + the trigger button; .nav-right becomes the
   panel, so anything mounted into it later (site switcher, logs, chips,
   sign-out) lands inside the menu automatically. */
.nav-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.nav-menu-btn, .nav-back-btn, .nav-refresh-btn {
    display: none; align-items: center; justify-content: center;
    width: 42px; height: 42px; border-radius: 11px; flex: none;
    background: var(--surface); border: 1px solid var(--border); color: var(--text); text-decoration: none; box-sizing: border-box;
    cursor: pointer; transition: border-color .15s ease, background .15s ease;
}
.nav.has-menu .nav-menu-btn, .nav.has-menu .nav-back-btn, .nav.has-menu .nav-refresh-btn { display: inline-flex; }
.nav-menu-btn:hover, .nav-back-btn:hover, .nav-refresh-btn:hover { border-color: var(--border-warm); }
.nav-refresh-btn:disabled { opacity: 0.6; cursor: default; }
.nav-refresh-btn.is-spinning svg { animation: bizon-spin 0.7s linear infinite; }
@keyframes bizon-spin { to { transform: rotate(360deg); } }
.nav.has-menu .nav-right {
    position: absolute; top: calc(100% + 8px); right: clamp(16px, 4vw, 40px);
    flex-direction: column; align-items: stretch; gap: 4px;
    background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
    padding: 10px; min-width: 224px; max-width: calc(100vw - 32px);
    box-shadow: 0 16px 40px -10px rgba(0,0,0,0.55); z-index: 60; display: none;
}
.nav.has-menu.menu-open .nav-right { display: flex; }
.nav.has-menu .nav-right > * { width: 100%; }
.nav.has-menu .nav-link { padding: 9px 12px; border-radius: 8px; }
.nav.has-menu .nav-link:hover { background: var(--surface-2); }
/* In the dropdown there's room to label the account (avatar + "Account"). */
.nav.has-menu .user-chip { padding: 5px 8px; border-radius: 8px; }
.nav.has-menu .user-chip:hover { background: var(--surface-2); }
.nav.has-menu .user-chip .uemail { display: inline; }
/* Same for the no-internet chip: icon-only in the header, worded in the menu. */
.nav.has-menu .net-chip { padding: 5px 8px; border-radius: 8px; }
.nav.has-menu .net-chip .net-chip-text { display: inline; }

/* ── Colour scheme switcher ──
   Three schemes in a pill, mounted into .nav-right by theme.js so every page
   gets one without touching its markup. Icon-only in the header where width is
   scarce, worded once the nav collapses into the menu — the same trick
   .user-chip and .net-chip use two rules up. */
.theme-seg {
    display: inline-flex; align-items: center; gap: 2px; flex: none;
    padding: 3px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
}
.theme-seg button {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    min-height: 40px; min-width: 40px; padding: 0 9px;
    border-radius: 999px; border: 1px solid transparent; background: transparent;
    color: var(--text-dim); cursor: pointer; font: inherit; font-size: 0.84rem;
    transition: color .12s ease, background .12s ease, border-color .12s ease;
}
.theme-seg button:hover { color: var(--text); }
.theme-seg button[aria-pressed="true"] {
    background: var(--surface); border-color: var(--border-warm); color: var(--text);
}
.theme-seg svg { flex: none; }
.theme-seg .theme-lbl { display: none; }
.nav.has-menu .theme-seg { flex-direction: column; align-items: stretch; gap: 2px; border-radius: 12px; }
.nav.has-menu .theme-seg button { justify-content: flex-start; border-radius: 8px; padding: 0 12px; }
.nav.has-menu .theme-seg .theme-lbl { display: inline; }
.nav-status {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.8rem;
    color: var(--text-muted);
    white-space: nowrap;
}
.pulse-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--ok);
    box-shadow: 0 0 0 0 rgba(138,185,138,0.6);
    animation: pulse 2.4s infinite;
}
/* Green is a claim: it says the room is reporting. The dot used to be green
   while the line right beside it read "0 of 1 sensors reporting", which is the
   one thing a sensor app must never do. Three states now, the same honesty the
   sensor cards below already had, and the pulse belongs to the live one only:
   a ring throbbing on a feed that stopped three days ago is the same lie in
   motion. */
.pulse-dot.dot-part { background: var(--brand-light); box-shadow: none; animation: none; }
.pulse-dot.dot-off  { background: var(--text-dim);   box-shadow: none; animation: none; }
@keyframes pulse {
    0%   { box-shadow: 0 0 0 0 rgba(138,185,138,0.5); }
    70%  { box-shadow: 0 0 0 7px rgba(138,185,138,0); }
    100% { box-shadow: 0 0 0 0 rgba(138,185,138,0); }
}
.nav-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border-radius: 9px;
    border: 1px solid var(--border-warm);
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: 0.85rem;
    font-weight: 500;
    /* A two-word label ("Irrigation Setup", "My Fertilizers") wrapped inside
       its pill, making that one 59px tall against its neighbours' 38px and
       leaving the row visibly uneven. The nav already collapses to a menu when
       it runs out of width, so a link is never the thing that has to give. */
    white-space: nowrap;
    transition: all .15s ease;
}
.nav-link:hover { color: var(--text); border-color: var(--brand); background: var(--surface-3); }
/* Coming-soon nav links (grayed, not clickable) */
.nav-soon { color: var(--text-dim); opacity: 0.6; cursor: default; }
.nav-soon:hover { color: var(--text-dim); border-color: var(--border-warm); background: var(--surface-2); }
.soon-pip {
    font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
    padding: 2px 6px; border-radius: 999px; margin-left: 2px;
    color: var(--brand-light); background: color-mix(in srgb, var(--brand) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--brand) 32%, transparent);
}

/* ─────────────  Layout  ───────────── */
.page {
    max-width: 1080px;
    margin: 0 auto;
    padding: clamp(20px, 3vw, 36px) clamp(16px, 4vw, 40px) 64px;
    /* A solid column so no text sits on the leaves. Cards are --surface,
       a step lighter, so they still read as cards on top of it. */
    background: var(--bg);
    /* A rounded box with a gap under the nav and at the sides, so it reads
       as floating over the leaves rather than hanging off the header. */
    width: calc(100% - 2 * clamp(8px, 2vw, 24px));
    margin: clamp(10px, 2vw, 22px) auto 28px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
/* The calculator's ingredient table runs past 1000px once a mix has a few
   nutrient columns; at 1080 it scrolled sideways on desktop with room to spare. */
.page.page-wide { max-width: 1320px; }
.section-label {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.13em;
    color: var(--text-dim);
    margin: 34px 0 16px;
}
.section-label::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--border), transparent);
}
/* A section label that folds the cards under it (js/foldsections.js). */
.section-label.fold-head { cursor: pointer; user-select: none; }
.section-label.fold-head:hover { color: var(--text-muted); }
.section-label.fold-head:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 4px; border-radius: 6px; }
.fold-chev { flex: 0 0 auto; margin-right: -2px; transition: transform 0.18s ease; }
.section-label.folded .fold-chev { transform: rotate(-90deg); }
.fold-hidden { display: none !important; }
@media (prefers-reduced-motion: reduce) { .fold-chev { transition: none; } }

/* ─────────────  Cards  ───────────── */
.card {
    background:
        linear-gradient(180deg, rgba(255,255,255,0.015), transparent 40%),
        var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: clamp(18px, 2.4vw, 26px);
    margin-bottom: 22px;
    box-shadow: var(--shadow);
}
.card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin-bottom: 18px;
    flex-wrap: wrap;
}
.card-title {
    margin: 0;
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-muted);
}

/* ─────────────  Stat cards  ───────────── */
.stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-bottom: 26px;
}
/* Column count follows the number of visible stat cards (canopy hides on
   air-only units; CO2 shows when enabled). Desktop only — the mobile rules
   further down force 2-up regardless of card count. */
@media (min-width: 769px) {
    .stats.cols-3 { grid-template-columns: repeat(3, 1fr); }
    .stats.cols-4 { grid-template-columns: repeat(4, 1fr); }
    .stats.cols-5 { grid-template-columns: repeat(5, 1fr); }
}
.stat {
    position: relative;
    overflow: hidden;
    background:
        linear-gradient(180deg, rgba(255,255,255,0.02), transparent 45%),
        var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px 18px 20px;
    box-shadow: var(--shadow-sm);
    transition: transform .18s ease, border-color .18s ease;
}
.stat:hover { box-shadow: var(--shadow-lift); border-color: var(--border-warm); }
.stat::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    background: var(--accent, var(--brand));
}
.stat-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
}
.stat-label {
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-muted);
}
.stat-icon {
    display: grid; place-items: center;
    width: 30px; height: 30px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--accent, var(--brand)) 18%, transparent);
    color: var(--accent, var(--brand));
}
.stat-icon svg { width: 16px; height: 16px; }
.stat-value {
    font-size: clamp(1.7rem, 3.4vw, 2.15rem);
    font-weight: 750;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.stat-unit { font-size: 0.92rem; font-weight: 600; color: var(--text-dim); margin-left: 3px; }
/* A fresh load's realtime listener can take a real 20-30s to hand back its
   first snapshot (App Check + Firestore's channel handshake, worst right
   after a deploy) -- the stat number sits on "–" that whole time with
   nothing saying whether it's working or stuck. Staged text stands in for
   the number meanwhile, at caption size since "Taking longer than usual"
   doesn't fit at 2rem the way "72.4" does; the unit hides so it doesn't
   read as "Taking longer than usual°F". */
.stat.is-loading .stat-unit { display: none; }
.stat.is-loading .stat-value {
    font-size: 0.92rem; font-weight: 600; letter-spacing: normal;
    color: var(--text-dim); line-height: 1.3;
}
.stat-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 12px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    background: color-mix(in srgb, var(--accent, var(--brand)) 16%, transparent);
    color: var(--accent, var(--brand));
    border: 1px solid color-mix(in srgb, var(--accent, var(--brand)) 35%, transparent);
}
.stat-badge .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.stat-foot { margin-top: 12px; font-size: 0.74rem; color: var(--text-dim); }

/* Status tones - shared by stat badge + scale marker. Text is mixed 30%
   toward --text: the pure tones on their own tint measured 4.0 to 4.4:1. */
.badge-low   { background: color-mix(in srgb, var(--tone-low) 14%, transparent);   color: color-mix(in srgb, var(--tone-low) 70%, var(--text));   border-color: color-mix(in srgb, var(--tone-low) 42%, transparent); }
.badge-ideal { background: color-mix(in srgb, var(--tone-ideal) 14%, transparent); color: color-mix(in srgb, var(--tone-ideal) 70%, var(--text)); border-color: color-mix(in srgb, var(--tone-ideal) 42%, transparent); }
.badge-high  { background: color-mix(in srgb, var(--tone-high) 14%, transparent);  color: color-mix(in srgb, var(--tone-high) 70%, var(--text));  border-color: color-mix(in srgb, var(--tone-high) 42%, transparent); }

/* ─────────────  VPD position scale  ───────────── */
.scale-pref { font-size: 0.82rem; color: var(--text-muted); }
.scale-pref strong { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }
.scale { padding: 30px 4px 4px; }
.scale-track {
    position: relative;
    height: 14px;
    border-radius: 999px;
    background: linear-gradient(90deg,
        #5e86ad 0%, #6fa8dc 14%, #8ab98a 42%, #c9bd66 62%, #e8b04b 80%, #e07a5f 100%);
    box-shadow: inset 0 1px 3px rgba(0,0,0,0.45);
}
.scale-band {
    position: absolute;
    top: -5px; bottom: -5px;
    border-radius: 7px;
    background: rgba(255,255,255,0.06);
    border: 1.5px solid rgba(255,255,255,0.55);
    box-shadow: 0 0 0 1px rgba(0,0,0,0.25), 0 0 14px rgba(159,206,159,0.30);
    pointer-events: none;
}
.scale-marker {
    position: absolute;
    top: 50%;
    width: 18px; height: 18px;
    margin-left: -9px;
    border-radius: 50%;
    background: var(--text);
    border: 3px solid var(--bg);
    transform: translateY(-50%);
    box-shadow: 0 2px 8px rgba(0,0,0,0.6);
    transition: left .5s cubic-bezier(.4,0,.2,1);
}
.scale-marker.m-low   { background: var(--tone-low); }
.scale-marker.m-ideal { background: var(--tone-ideal); }
.scale-marker.m-high  { background: var(--tone-high); }
.scale-bubble {
    position: absolute;
    bottom: calc(100% + 9px);
    left: 50%;
    transform: translateX(-50%);
    white-space: nowrap;
    background: var(--surface-3);
    border: 1px solid var(--border-warm);
    color: var(--text);
    font-size: 0.76rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    padding: 4px 9px;
    border-radius: 7px;
    box-shadow: var(--shadow-sm);
}
.scale-bubble::after {
    content: "";
    position: absolute;
    top: 100%; left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--border-warm);
}
.scale-ticks {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 12px;
    font-size: 0.74rem;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}
.scale-hint { letter-spacing: 0.02em; }
@media (max-width: 480px) { .scale-hint { display: none; } }

/* ─────────────  Controls  ───────────── */
.field-label {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: 7px;
}
.seg {
    display: inline-flex;
    /* Segment labels never wrap inside a button, so on a phone a three- or
       four-option control is wider than the screen and drags the whole page
       sideways with it. Let the buttons flow onto a second row instead. */
    flex-wrap: wrap;
    max-width: 100%;
    padding: 4px;
    gap: 3px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 11px;
}
.seg button {
    appearance: none;
    border: none;
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-size: 0.84rem;
    font-weight: 600;
    padding: 7px 14px;
    border-radius: 8px;
    cursor: pointer;
    transition: all .15s ease;
    white-space: nowrap;
}
.seg button:hover { color: var(--text); }
.seg button.active {
    background: linear-gradient(180deg, var(--brand), var(--brand-dark));
    color: #1a120a;
    box-shadow: var(--shadow-sm);
}
/* Same trade as .btn-primary, and for the same reason: the light schemes darken
   the brand vars because they are mostly used as TEXT there, and as a FILL they
   turn the selected segment into near-black on dark brown. Measured on the
   dashboard's own pickers: 4.17:1 in light and 1.97:1 in greenhouse, which is
   the scheme that exists to be read in sunlight. Restating the fill as the same
   gold the primary button uses keeps "selected" reading the same in all three. */
:root[data-theme="light"] .seg button.active {
    background: linear-gradient(180deg, #e6a960, #d1893f);
    color: #2b2115;
}
:root[data-scheme="greenhouse"] .seg button.active {
    background: #e09a3f;
    color: #000000;
}

/* Custom time-range picker (Environmental Dashboard) */
.custom-range {
    display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px;
    margin-top: 12px; padding: 12px 14px;
    background: var(--bg); border: 1px solid var(--border); border-radius: 11px;
}
.custom-range .cr-field {
    display: flex; flex-direction: column; gap: 4px;
    font-size: 0.78rem; color: var(--text-muted); font-weight: 600;
}
.custom-range input[type="datetime-local"] {
    font: inherit; font-size: 0.85rem;
    padding: 7px 10px; border-radius: 8px;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
    color-scheme: dark;
}
.custom-range .cr-apply { padding: 8px 18px; font-size: 0.85rem; }
/* Sits under the pickers, full width, so a truncated range is read next to
   the dates that were typed rather than somewhere off by the chart. */
.custom-range .cr-note {
    flex: 1 1 100%; margin: 0;
    font-size: 0.78rem; line-height: 1.5; color: var(--text-muted);
}
@media (max-width: 480px) {
    .custom-range .cr-field { flex: 1 1 100%; }
    .custom-range input[type="datetime-local"] { width: 100%; }
}

/* Saved-test tabs (Soil Amendment Calculator) */
.test-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.test-tab {
    appearance: none;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 7px 16px;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    transition: all .15s ease;
}
.test-tab:hover { color: var(--text); border-color: var(--border-warm); }
.test-tab.active {
    background: color-mix(in srgb, var(--brand) 18%, var(--bg));
    border-color: color-mix(in srgb, var(--brand) 55%, transparent);
    color: var(--brand-light);
}

select, input[type="date"], input[type="text"], input[type="search"] {
    width: 100%;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font: inherit;
    font-size: 0.92rem;
    transition: border-color .15s ease, box-shadow .15s ease;
}
select:focus, input:focus { outline: none; border-color: var(--brand); box-shadow: var(--ring); }
/* Our own chevron, inset from the edge: the browser's sits flush against the
   border and ignores padding (see .quick-views, which did this first). Literal
   brand colours per theme, since a data: URI can't see currentColor. */
select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 36px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23dcad77' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 14px;
}
:root[data-theme="light"] select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a8692f' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
select[multiple], select[size] { background-image: none; padding-right: 12px; }
/* iOS Safari draws its own date control: centred text and a shorter box
   than the other fields beside it. Make it a plain field the same height. */
@supports (-webkit-touch-callout: none) {
    input[type="date"] { -webkit-appearance: none; appearance: none; min-height: calc(1.2em + 22px); line-height: 1.2; }
    input[type="date"]::-webkit-date-and-time-value { text-align: left; margin: 0; }
}
/* Chrome pads the date's inner fields by a pixel top and bottom, which made
   it 2px taller than the text fields and selects beside it. */
input[type="date"]::-webkit-datetime-edit, input[type="date"]::-webkit-datetime-edit-fields-wrapper { padding-top: 0; padding-bottom: 0; }
input::-webkit-calendar-picker-indicator { filter: invert(0.7) sepia(1) saturate(2) hue-rotate(2deg); cursor: pointer; }

.btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 10px 18px;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: all .15s ease;
}
.btn-primary {
    background: linear-gradient(180deg, var(--brand), var(--brand-dark));
    color: #1a120a;
    box-shadow: var(--shadow-sm);
}
.btn-primary:hover { filter: brightness(1.08); transform: translateY(-1px); }
/* Light theme darkens the brand vars because they are mostly used as TEXT on a
   cream background. Used as a button FILL those same values make a muddy dark
   brown with near-black text (~2.7:1, fails AA). Give the primary button its
   own brighter gold here, keeping the dark-text-on-gold character of the dark
   theme but legible on light. */
:root[data-theme="light"] .btn-primary {
    background: linear-gradient(180deg, #e6a960, #d1893f);
    color: #2b2115;
}
.btn-home {
    background: var(--home-fill);
    color: var(--home-on);
    border: 1px solid var(--home-edge);
    box-shadow: var(--shadow-sm);
    font-weight: 700;
}
.btn-home:hover { filter: brightness(1.06); transform: translateY(-1px); color: var(--home-on); }
.btn-ghost {
    background: var(--surface-2);
    border-color: var(--border-warm);
    color: var(--text-muted);
}
.btn-ghost:hover { color: var(--text); border-color: var(--brand); }
.btn-danger {
    background: transparent;
    color: var(--danger);
    border-color: color-mix(in srgb, var(--danger) 45%, transparent);
}
.btn-danger:hover { background: var(--danger-bg); border-color: var(--danger); }
.btn-block { width: 100%; }
.btn:disabled { opacity: 0.5; cursor: default; transform: none; filter: none; }

/* ─────────────  Graph  ───────────── */
#plotly-graph { height: 470px; }
/* Dropping every y-axis's numbers on a phone (vpdpage.js themeGraph) frees up
   width, not height -- so the chart still deserves more vertical room now
   that it isn't sharing the screen with three columns of tick labels. */
@media (max-width: 640px) { #plotly-graph { height: 540px; } }
/* The site chart stacks one panel per metric, so it grows with them. */
#plotly-graph.panels-2 { height: 620px; }
#plotly-graph.panels-3 { height: 800px; }
#plotly-graph.panels-4 { height: 980px; }
#plotly-graph.panels-5 { height: 1160px; }
/* The soil chart needs its own height for the same reason the one above has
   one, and it went without: Plotly then falls back to its default 450 and
   sizes the container from its own drawing. That survives the first render
   and collapses on the second Plots.resize() -- the container measures the
   plot, the plot is then laid out to the container, and the pair settles at
   zero. The SVGs keep their old size, so the chart draws on top of whatever
   follows it down the page instead of inside its card. A height here is what
   breaks the circle: the container is measured from the stylesheet, and the
   plot is drawn to fit it. Shorter than the trends chart because it carries
   one axis and a handful of traces, not four axes. */
#plotly-soil-graph { height: 360px; }
@media (max-width: 640px) { #plotly-soil-graph { height: 420px; } }
.graph-empty { color: var(--text-dim); text-align: center; padding: 80px 0; }
/* While a new window loads, the chart on screen stays put and dims under a
   label, then redraws once, rather than drawing the old window and jumping. */
.graph-wrap { position: relative; }
.graph-wrap > [id^="plotly-"] { transition: opacity 0.2s ease; }
.graph-wrap.is-loading > [id^="plotly-"] { opacity: 0.35; pointer-events: none; }
.graph-loading {
    display: none;
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    padding: 8px 16px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text-muted); font-size: 0.85rem; font-weight: 600;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
}
.graph-wrap.is-loading .graph-loading { display: block; }
.graph-tip {
    margin: 4px 0 0;
    text-align: center;
    font-size: 0.76rem;
    color: var(--text-dim);
}

/* ─────────────  Manage grid  ───────────── */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; align-items: start; }
@media (max-width: 880px) { .grid-3 { grid-template-columns: 1fr; } }
.stack > * + * { margin-top: 14px; }
.danger-note { font-size: 0.85rem; color: var(--text-dim); margin: 0 0 16px; line-height: 1.55; }
.hint { font-size: 0.8rem; color: var(--text-dim); margin: -4px 0 0; line-height: 1.5; }

/* "Not sure?" under a soil type field. Every tool that asks for texture asks
   for something most people have never been told, and the county soil survey
   answers it in a couple of minutes without a lab. One class, four tools. */
.soil-lookup { display: block; margin-top: 6px; font-size: 0.76rem; color: var(--text-dim); }
.soil-lookup a { color: var(--brand-light); border-bottom: 1px dotted currentColor; }
.soil-lookup a:hover { color: var(--text); }

/* Same slot as .soil-lookup, but the answer is a procedure rather than a link:
   a field asking for a number nobody has been told how to measure. Closed by
   default so it costs the people who already know it nothing, and the browser
   owns the toggle. */
.field-help { display: block; margin-top: 6px; font-size: 0.76rem; color: var(--text-dim); }
.field-help > summary { list-style: none; display: inline; cursor: pointer; color: var(--brand-light); border-bottom: 1px dotted currentColor; }
.field-help > summary::-webkit-details-marker { display: none; }
.field-help > summary:hover { color: var(--text); }
.field-help > summary:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 3px; border-radius: 6px; }
.field-help p { margin: 8px 0 0; line-height: 1.55; }

/* ─── BizonTip: a deliberately obvious advisory callout ───
   Collapsible with NO JavaScript: the markup is <details class="bizon-tip" open>,
   so the browser owns the toggle and the open/closed state. <summary> is the
   always-visible header; every sibling after it is the collapsible body.
   `open` in the HTML is what makes it start expanded. */
.bizon-tip {
    background: linear-gradient(150deg, color-mix(in srgb, var(--brand) 16%, transparent), transparent 70%), var(--surface-2);
    border: 1px solid var(--brand); border-left-width: 4px;
    border-radius: 12px; padding: 14px 16px; margin: 16px 0;
}
/* The clickable header. The default disclosure triangle has to be removed
   TWICE: list-style covers Chrome/Firefox, ::-webkit-details-marker covers
   Safari, which ignores list-style on a summary. */
.bizon-tip > summary {
    display: flex; align-items: center; gap: 10px;
    cursor: pointer; list-style: none; user-select: none;
}
.bizon-tip > summary::-webkit-details-marker { display: none; }
.bizon-tip > summary:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 3px; border-radius: 6px; }
.bizon-tip .bizon-tip-ico { font-size: 1.25rem; line-height: 1.3; flex: 0 0 auto; }
.bizon-tip .bizon-tip-label { font-weight: 750; letter-spacing: 0.01em; color: var(--brand-light); }
/* Our own chevron, since the native marker is gone. The [open] attribute is a
   real CSS selector, so the flip needs no script. */
.bizon-tip .bizon-tip-chev { margin-left: auto; flex: 0 0 auto; color: var(--brand-light); transition: transform 0.18s ease; }
.bizon-tip[open] > summary .bizon-tip-chev { transform: rotate(180deg); }
.bizon-tip .bizon-tip-body {
    font-size: 0.9rem; line-height: 1.55; color: var(--text);
    margin-top: 9px; padding-left: 32px;   /* lines the text up past the icon */
}
.bizon-tip .bizon-tip-body strong { color: var(--brand-light); }
/* "Ignore BizonTips" profile switch: one body class hides every tip, so tips
   added later need no extra wiring. Functional warnings aren't .bizon-tip. */
body.hide-bizon-tips .bizon-tip { display: none !important; }
@media (prefers-reduced-motion: reduce) { .bizon-tip .bizon-tip-chev { transition: none; } }
@media (max-width: 480px) { .bizon-tip .bizon-tip-body { padding-left: 0; } }

/* ─────────────  Table  ───────────── */
.table-scroll {
    max-height: 360px;
    overflow: auto;          /* both axes: a wide table scrolls sideways instead of clipping off-screen */
    -webkit-overflow-scrolling: touch;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-soft);
}
.table-scroll::-webkit-scrollbar { width: 10px; }
.table-scroll::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 8px; border: 2px solid var(--surface); }
table { width: 100%; border-collapse: collapse; font-size: 0.88rem; font-variant-numeric: tabular-nums; }
thead th {
    position: sticky; top: 0;
    background: var(--surface-3);
    color: var(--text-muted);
    padding: 12px 14px;
    text-align: center;
    font-weight: 600;
    font-size: 0.74rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
    border-bottom: 1px solid var(--border);
}
tbody td {
    padding: 11px 16px;
    text-align: center;
    color: var(--text);
    border-bottom: 1px solid var(--border-soft);
    white-space: nowrap;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) td { background: rgba(255,255,255,0.012); }
tbody tr:hover td { background: rgba(192,138,79,0.07); }
tbody td:first-child { color: var(--text-muted); }

/* ─────────────  Messages  ───────────── */
.message {
    margin-top: 18px;
    padding: 12px 16px;
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
    display: flex;
    gap: 10px;
    align-items: flex-start;
}
.message.success { background: rgba(138,185,138,0.10); border: 1px solid rgba(138,185,138,0.35); color: #a9d3a9; }
.message.error   { background: var(--danger-bg); border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent); color: #e8a487; }
/* "Worth a second look", not wrong: an imported draft waiting to be checked. */
.message.warn    { background: rgba(214,170,80,0.10); border: 1px solid rgba(214,170,80,0.40); color: #e3c27a; display: block; }
:root[data-theme="light"] .message.warn { color: #7a5a12; }
/* A form field a label import couldn't fill: still the grower's to answer. */
.needs-fill:not(label) { outline: 2px dashed rgba(214,170,80,0.75); outline-offset: 5px; border-radius: 6px; }
label.needs-fill::after, .needs-fill > .field-label::after {
    content: "Fill in"; margin-left: 8px; padding: 1px 7px; border-radius: 999px; font-size: 0.72em; font-weight: 600;
    background: rgba(214,170,80,0.18); color: #e3c27a; vertical-align: middle;
}
:root[data-theme="light"] label.needs-fill::after, :root[data-theme="light"] .needs-fill > .field-label::after { color: #7a5a12; }
/* "No internet" notice — not an error the reader can fix by retyping. */
.message.offline { background: rgba(224,176,112,0.11); border: 1px solid rgba(224,176,112,0.38); color: #e0b070; }

/* ─── Confirm-your-email bar ───
   Sits at the top of the page content on every signed-in page until the
   address is confirmed, because that one click is what a team seat and a
   shared sensor hang off (firestore.rules), and the account looks like an
   ordinary Free one until it happens. Amber, not red: nothing is broken,
   something is unfinished. */
.verify-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
    margin: 0 0 22px; padding: 13px 16px;
    border-radius: var(--radius-sm);
    background: rgba(224,176,112,0.11);
    border: 1px solid rgba(224,176,112,0.38);
    color: #e0b070; font-size: 0.9rem; line-height: 1.55;
}
.verify-bar-txt { flex: 1 1 320px; }
.verify-bar-txt strong { color: #f0c88a; }
.verify-bar-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.verify-bar-acts .btn { padding: 6px 13px; font-size: 0.85rem; }
.message.offline svg { flex: 0 0 auto; margin-top: 1px; }

pre.console {
    display: none;
    margin-top: 16px;
    background: #0c0906;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 14px;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.82rem;
    color: var(--text-muted);
    white-space: pre-wrap;
    max-height: 220px;
    overflow-y: auto;
}

/* ─────────────  Footer  ───────────── */
.footer {
    text-align: center;
    color: var(--text-dim);
    font-size: 0.8rem;
    padding-top: 22px;
    margin-top: 8px;
    border-top: 1px solid var(--border-soft);
}
.footer strong { color: var(--text-muted); font-weight: 600; }

/* ─────────────  Overlay / spinner  ───────────── */
.overlay {
    display: none;
    position: fixed; inset: 0;
    background: rgba(10,7,4,0.6);
    backdrop-filter: blur(2px);
    z-index: 9998;
}
.spinner {
    display: none;
    position: fixed; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    z-index: 9999;
}
.spinner .ring {
    width: 46px; height: 46px;
    border: 3px solid rgba(192,138,79,0.25);
    border-top-color: var(--brand);
    border-radius: 50%;
    animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ─────────────  Settings page  ───────────── */
.settings-head { margin-bottom: 26px; }
.settings-head h1 {
    margin: 0 0 4px;
    font-size: 1.6rem;
    font-weight: 750;
    letter-spacing: -0.02em;
}
.settings-head p { margin: 0; color: var(--text-muted); font-size: 0.92rem; }
.card h2 {
    margin: 0 0 4px;
    font-size: 1.02rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}
.card-desc { color: var(--text-muted); font-size: 0.88rem; margin: 0 0 20px; }

/* ─────────────  Dual-range VPD slider  ───────────── */
.range-readout {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 22px;
    flex-wrap: wrap;
}
.ro-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 13px;
    border-radius: 999px;
    border: 1px solid;
    font-size: 0.84rem;
    font-weight: 600;
}
.ro-pill .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.ro-pill strong { color: var(--text); font-weight: 750; font-variant-numeric: tabular-nums; font-size: 0.95rem; }
.ro-unit { color: var(--text-dim); font-size: 0.82rem; }

.dual-range { position: relative; height: 34px; margin-top: 6px; }
.dr-rail, .dr-fill {
    position: absolute;
    top: 50%;
    height: 7px;
    transform: translateY(-50%);
    border-radius: 999px;
    pointer-events: none;
}
.dr-rail {
    left: 0; right: 0;
    background: var(--bg);
    border: 1px solid var(--border);
    box-shadow: inset 0 1px 2px rgba(0,0,0,0.4);
}
.dr-fill {
    background: linear-gradient(90deg, var(--brand-dark), var(--brand-light));
    box-shadow: 0 0 12px rgba(192,138,79,0.35);
}
.dual-range input[type="range"] {
    position: absolute;
    top: 0; left: 0;
    width: 100%;
    height: 34px;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    -webkit-appearance: none;
    appearance: none;
    pointer-events: none;
}
.dual-range input[type="range"]:focus { outline: none; box-shadow: none; }
.dual-range input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    pointer-events: auto;
    width: 22px; height: 22px;
    border-radius: 50%;
    background: linear-gradient(180deg, #f0d8b8, var(--brand));
    border: 3px solid var(--surface);
    box-shadow: 0 2px 7px rgba(0,0,0,0.6);
    cursor: grab;
    transition: transform .1s ease;
}
.dual-range input[type="range"]::-webkit-slider-thumb:active { cursor: grabbing; transform: scale(1.12); }
.dual-range input[type="range"]::-moz-range-thumb {
    pointer-events: auto;
    width: 22px; height: 22px;
    border-radius: 50%;
    background: var(--brand-light);
    border: 3px solid var(--surface);
    box-shadow: 0 2px 7px rgba(0,0,0,0.6);
    cursor: grab;
}
.dual-range input[type="range"]::-moz-range-track { background: none; border: none; }
.dr-scale {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 10px;
    font-size: 0.74rem;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

/* ─────────────  Responsive  ───────────── */
@media (max-width: 820px) {
    .stats { grid-template-columns: repeat(2, 1fr); }
    .grid-2 { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
    .stats { grid-template-columns: 1fr 1fr; gap: 12px; }
    /* The status pill was born in the nav, where a phone has no room for its
       text. It lives on the dashboard's LIVE bar now, where the text IS the
       point: "0 of 1 sensors reporting" is the first thing a grower opens the
       phone to check. Hiding it left a bare green dot saying nothing. So the
       rule stays scoped to the nav, and the LIVE bar wraps its line instead
       of running off the side of the screen. */
    .nav .nav-status span.txt { display: none; }
    .nav-status { white-space: normal; align-items: flex-start; }
    .nav-status .pulse-dot { margin-top: 5px; }
    /* The reading time is its own fact, and mid-phrase wrapping ("last
       reading Aug / 31") reads like a broken sentence. Give it the line. */
    .nav-status .live-sep { display: none; }
    .nav-status .live-last { display: block; }
    .brand-sub { display: none; }
}

/* ═══════════════════════════════════════════════════════════
   Bizon Ag Tools - additions (auth, hub, calculator)
   ═══════════════════════════════════════════════════════════ */

/* ─── Number inputs (extend base input styling) ─── */
input[type="number"], input[type="email"], input[type="password"] {
    width: 100%;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font: inherit;
    font-size: 0.92rem;
    transition: border-color .15s ease, box-shadow .15s ease;
}
input[type="number"]:focus, input[type="email"]:focus, input[type="password"]:focus {
    outline: none; border-color: var(--brand); box-shadow: var(--ring);
}
input[type="number"] { font-variant-numeric: tabular-nums; }
textarea {
    width: 100%; background: var(--bg); color: var(--text);
    border: 1px solid var(--border-warm); border-radius: var(--radius-sm);
    padding: 10px 12px; font: inherit; font-size: 0.92rem; resize: vertical; min-height: 120px;
    transition: border-color .15s ease, box-shadow .15s ease;
}
textarea:focus { outline: none; border-color: var(--brand); box-shadow: var(--ring); }

/* ─── Auth screen ─── */
.auth-wrap {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: 24px;
}
.auth-card {
    width: 100%;
    max-width: 500px;
    background: linear-gradient(180deg, rgba(255,255,255,0.015), transparent 40%), var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: clamp(26px, 4vw, 38px);
    box-shadow: var(--shadow);
}
.auth-brand { display: flex; align-items: center; gap: 13px; margin-bottom: 26px; }
.auth-brand .brand-mark { width: 46px; height: 46px; font-size: 1.2rem; }
.auth-title { margin: 0; font-size: 1.35rem; font-weight: 750; letter-spacing: -0.02em; }
.auth-sub { margin: 4px 0 0; color: var(--text-muted); font-size: 0.9rem; }
.auth-form { margin-top: 24px; }
.auth-form .field-label { margin-top: 16px; }
.auth-form .field-label:first-child { margin-top: 0; }
.toolshop-heading {
    margin: 22px 0 10px;
    font-size: 1rem;
    font-weight: 700;
    color: var(--text);
    letter-spacing: -0.01em;
}
.toolshop-heading::after {
    content: "";
    display: block;
    width: 40px;
    height: 2px;
    margin: 8px 0 0;
    background: var(--brand-light);
    border-radius: 999px;
}
.auth-switch { margin-top: 20px; text-align: center; font-size: 0.88rem; color: var(--text-muted); }
.auth-switch button {
    background: none; border: none; color: var(--brand-light);
    font: inherit; font-weight: 600; cursor: pointer; padding: 0;
}
.auth-switch button:hover { color: var(--text); }
.tool-list {
    margin: 22px 0 0;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px;
    padding: 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: rgba(255,255,255,0.04);
    color: var(--text-muted);
    font-size: 0.92rem;
}
.tool-list-title {
    margin-bottom: 10px;
    font-weight: 600;
    color: var(--text);
}
.tool-list ul {
    margin: 0;
    padding-left: 18px;
}
.tool-list li {
    margin-bottom: 8px;
    line-height: 1.55;
}
@media (max-width: 520px) {
    .tool-list { grid-template-columns: 1fr; }
}

/* ─── Split login: value hero beside the sign-in card ─── */
.auth-layout {
    width: 100%;
    max-width: 1000px;
    display: grid;
    grid-template-columns: 1.05fr 0.95fr;
    gap: 38px;
    align-items: center;
}
.auth-layout .auth-card { max-width: none; }
/* Same solid backing as .page, so the hero copy is not on the leaves. */
.auth-layout {
    background: var(--bg); border: 1px solid var(--border-soft);
    border-radius: var(--radius); padding: clamp(16px, 3vw, 32px);
}
.auth-wrap > .hint { background: var(--bg); padding: 6px 14px; border-radius: 999px; }
.auth-hero { min-width: 0; }
.auth-hero .auth-brand { margin-bottom: 22px; }
.auth-headline {
    margin: 0 0 12px;
    font-size: clamp(1.7rem, 3.4vw, 2.5rem);
    font-weight: 750;
    letter-spacing: -0.025em;
    line-height: 1.08;
}
.auth-headline .acc { color: var(--brand-light); }
.auth-lede { margin: 0 0 22px; color: var(--text-muted); font-size: 1rem; line-height: 1.6; max-width: 46ch; }
.auth-benefits { list-style: none; margin: 0 0 24px; padding: 0; display: grid; gap: 12px; }
.auth-benefits li { position: relative; padding-left: 30px; color: var(--text); font-size: 0.95rem; line-height: 1.5; }
.auth-benefits li::before {
    content: ""; position: absolute; left: 0; top: 1px;
    width: 19px; height: 19px; border-radius: 999px;
    background: color-mix(in srgb, var(--brand) 22%, transparent);
    border: 1px solid color-mix(in srgb, var(--brand-light) 50%, transparent);
}
.auth-benefits li::after {
    content: ""; position: absolute; left: 6px; top: 6px;
    width: 6px; height: 9px; border: solid var(--brand-light);
    border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.plan-pills { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.plan-pills .pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 14px; border-radius: 999px;
    font-size: 0.84rem; font-weight: 600;
    background: var(--surface-2); border: 1px solid var(--border-warm); color: var(--text);
}
a.pill { text-decoration: none; }
a.pill:hover { filter: brightness(1.12); }
.plan-pills .pill.free { border-color: rgba(181,228,140,0.45); color: #b5e48c; }
/* The dark theme's lime is 1.2:1 on parchment; this clears 4.5:1 on surface-2 and -3. */
:root[data-theme="light"] .plan-pills .pill.free { border-color: rgba(40,105,47,0.45); color: #28692f; }
.plan-pills .pill.home { border-color: var(--home-line); color: var(--home-text); }
.plan-pills .pill.comm { border-color: color-mix(in srgb, var(--brand) 45%, transparent); color: var(--brand-light); }
.auth-trial { margin: 0; font-size: 0.9rem; color: var(--text-muted); }
@media (max-width: 860px) {
    .auth-layout { grid-template-columns: 1fr; gap: 26px; max-width: 520px; }
    .auth-hero { text-align: left; }
}
/* Phones: sign-in right under the headline; the feature list and plans follow it. */
@media (max-width: 600px) {
    .auth-wrap { padding: 16px; place-items: start center; }
    .auth-layout { display: flex; flex-direction: column; gap: 0; }
    .auth-hero { display: contents; }
    .auth-hero > * { order: 2; }
    .auth-hero > .auth-brand, .auth-hero > .auth-headline, .auth-hero > .auth-lede { order: 0; }
    .auth-hero .auth-brand { margin-bottom: 14px; }
    .auth-headline { font-size: 1.55rem; margin-bottom: 8px; }
    .auth-lede { font-size: 0.92rem; margin-bottom: 16px; }
    .auth-layout .auth-card { order: 1; margin-bottom: 28px; }
}

/* ─── Modal / popup (reusable) ─── */
.modal-overlay {
    position: fixed; inset: 0; z-index: 1100;   /* above the graph's pseudo-fullscreen (1000) */
    /* Flex + scroll so a tall popup can be scrolled on small screens
       (iOS Safari clips a grid-centered modal that's taller than the
       viewport). margin:auto on the modal still centers short ones. */
    display: flex; align-items: flex-start; justify-content: center;
    /* `contain` stops the scroll handing off to the page underneath once the
       popup hits its end: on a phone these forms are taller than the screen,
       and one flick past the bottom used to carry the dashboard away behind
       the popup, leaving somewhere else entirely on close. */
    overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    padding: 24px;
    /* On a home-screen PWA the page runs under the status bar/notch
       (viewport-fit=cover), so a popup taller than the screen started its
       top edge - and its close X - behind the clock. The 0px fallback keeps
       this a no-op everywhere else. */
    padding-top: calc(24px + env(safe-area-inset-top, 0px));
    padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
    background: rgba(8, 6, 4, 0.62);
    backdrop-filter: blur(3px);
    opacity: 0; pointer-events: none;
    transition: opacity .2s ease;
}
.modal-overlay.open { opacity: 1; pointer-events: auto; }
.modal {
    width: 100%; max-width: 480px; margin: auto;
    background: linear-gradient(180deg, rgba(255,255,255,0.02), transparent 42%), var(--surface);
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: clamp(24px, 4vw, 34px); box-shadow: var(--shadow);
    transform: translateY(8px) scale(0.985);
    transition: transform .2s ease;
}
.modal-overlay.open .modal { transform: none; }
.modal-close {
    position: absolute; top: 14px; right: 16px;
    background: none; border: none; color: var(--text-dim);
    font-size: 1.4rem; line-height: 1; cursor: pointer; padding: 4px;
}
.modal-close:hover { color: var(--text); }
.modal h2 { margin: 14px 0 8px; font-size: 1.4rem; font-weight: 750; letter-spacing: -0.02em; }
.modal-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.modal-actions .btn { flex: 1 1 auto; }

/* ─── Demo videos library ─── */
.demo-link { color: var(--brand-light); font-weight: 600; display: inline-flex; align-items: center; }
.demo-link:hover { color: var(--text); }
.demo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; }
.demo-card {
    text-align: left; padding: 0; font: inherit; color: inherit; cursor: pointer;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    overflow: hidden; transition: transform .15s ease, border-color .15s ease;
}
.demo-card:hover { box-shadow: var(--shadow-lift); border-color: var(--brand); }
.demo-thumb { position: relative; aspect-ratio: 16 / 9; background: var(--bg); overflow: hidden; }
.demo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.demo-play { position: absolute; inset: 0; display: grid; place-items: center; }
.demo-play svg { width: 56px; height: 56px; color: #fff; opacity: 0.92; filter: drop-shadow(0 2px 10px rgba(0,0,0,0.55)); transition: transform .15s ease; }
.demo-card:hover .demo-play svg { transform: scale(1.08); }
.demo-dur { position: absolute; right: 8px; bottom: 8px; background: rgba(0,0,0,0.72); color: #fff; font-size: 0.74rem; font-weight: 600; padding: 2px 7px; border-radius: 6px; }
.demo-meta { padding: 14px 16px 16px; }
.demo-meta h3 { margin: 0 0 4px; font-size: 1rem; font-weight: 700; }
.demo-meta p { margin: 0; color: var(--text-muted); font-size: 0.86rem; line-height: 1.5; }
.demo-modal { max-width: 900px; }
.demo-player { position: relative; aspect-ratio: 16 / 9; background: #000; border-radius: var(--radius-sm); overflow: hidden; }
.demo-player video, .demo-player iframe { width: 100%; height: 100%; border: 0; display: block; object-fit: contain; }
.auth-divider {
    display: flex; align-items: center; gap: 12px;
    margin: 22px 0; color: var(--text-dim); font-size: 0.78rem;
    text-transform: uppercase; letter-spacing: 0.1em;
}
.auth-divider::before, .auth-divider::after {
    content: ""; flex: 1; height: 1px; background: var(--border);
}

/* ─── Page header ─── */
.page-head { margin-bottom: 26px; }
/* ─── Simple / Advanced mode (js/uimode.js, docs/Simple-Advanced.md) ───
   !important because the pages also show and hide these from JS with inline
   display, and the mode has to win over a section that JS just revealed. */
body.ui-simple [data-adv] { display: none !important; }
body:not(.ui-simple) [data-simple] { display: none !important; }
body:not(.ui-expert) [data-expert] { display: none !important; }
/* Its own line: inline beside the page's "About this tool" link it read as part of it. */
.uimode-seg { display: flex; width: fit-content; margin-top: 14px; }
/* Tools that are two pages (fertilizer: /simplefeed and /calculator) link to
   each other from the same control, so the other mode is an <a>. */
.uimode-seg a {
    color: var(--text-muted); font-size: 0.84rem; font-weight: 600;
    padding: 7px 14px; border-radius: 8px; text-decoration: none; white-space: nowrap;
    transition: all .15s ease;
}
.uimode-seg a:hover { color: var(--text); }
.plan-lock { display: flex; align-items: center; gap: 14px; padding: 14px 16px; border: 1px dashed var(--border); border-radius: 10px; }
.plan-lock > svg { width: 20px; height: 20px; flex: 0 0 auto; color: var(--brand-light); }
.plan-lock > div { flex: 1; min-width: 0; }
.plan-lock strong { display: block; margin-bottom: 2px; }
.plan-lock span { color: var(--text-muted); font-size: 0.9rem; line-height: 1.5; }
.plan-lock .btn { flex: 0 0 auto; }
@media (max-width: 560px) { .plan-lock { flex-wrap: wrap; } .plan-lock .btn { width: 100%; } }
.simple-glossary { margin: 0; }
.simple-glossary dt { font-weight: 700; color: var(--text); margin-top: 14px; }
.simple-glossary dt:first-child { margin-top: 0; }
.simple-glossary dd { margin: 4px 0 0; color: var(--text-muted); line-height: 1.55; }
/* Lighting: the first grid in a card sits under a segmented control that only
   Advanced shows, so the gap goes with it. */
.lighting-first-grid { margin-top: 18px; }
.simple-answer { padding: 16px 18px; border-radius: var(--radius-sm); background: var(--surface-2); margin-bottom: 16px; }
.simple-answer .simple-answer-big { font-size: 1.35rem; font-weight: 750; color: var(--text); margin: 0 0 6px; }
.simple-answer p { margin: 0; color: var(--text-muted); line-height: 1.6; }
.page-head h1 { margin: 0 0 5px; font-size: clamp(1.4rem, 3vw, 1.8rem); font-weight: 750; letter-spacing: -0.02em; }
.page-head p { margin: 0; color: var(--text-muted); font-size: 0.94rem; }
/* Points off the calculator at the article behind it. The tools state the
   number; the guides say where it comes from, and neither has to do both. */
.page-head .head-phone { display: inline-flex; align-items: center; gap: 7px; margin-top: 12px; padding: 6px 13px; font-size: 0.84rem; }
.page-head .head-phone[hidden] { display: none; }
.page-head .head-phone svg { width: 15px; height: 15px; }
.page-head p.page-head-soon { margin-top: 7px; font-size: 0.84rem; color: var(--text-dim); }
.page-head p.page-head-soon a { border-bottom: 1px dotted currentColor; }
.page-head .toolshop-title { margin: 0 0 5px; font-size: clamp(1.25rem, 2.6vw, 1.5rem); font-weight: 750; letter-spacing: -0.02em; }

/* ─── Hub hero: the Environmental Dashboard, ahead of the catalog ─── */
.dash-hero {
    position: relative;
    display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    gap: clamp(20px, 3.5vw, 40px); align-items: center;
    margin-bottom: 34px; padding: clamp(22px, 3.4vw, 38px);
    border: 1px solid color-mix(in srgb, var(--brand) 40%, var(--border));
    border-radius: var(--radius);
    background:
        radial-gradient(120% 90% at 100% 0%, color-mix(in srgb, var(--brand) 16%, transparent), transparent 60%),
        var(--surface);
    box-shadow: var(--shadow);
}
.dash-hero-x {
    position: absolute; top: 12px; right: 12px;
    display: grid; place-items: center; width: 30px; height: 30px;
    border: 0; border-radius: 50%; background: transparent; color: var(--text-dim); cursor: pointer;
}
.dash-hero-x:hover { background: var(--surface-2); color: var(--text); }
.dash-hero-kicker {
    margin: 0 0 10px; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.09em;
    text-transform: uppercase; color: var(--brand-light);
}
.dash-hero h1 {
    margin: 0 0 14px; font-size: clamp(1.7rem, 4vw, 2.5rem); line-height: 1.08;
    font-weight: 750; letter-spacing: -0.03em; text-wrap: balance;
}
.dash-hero-lede { margin: 0 0 22px; color: var(--text-muted); font-size: 0.98rem; line-height: 1.6; max-width: 52ch; }
.dash-hero-cta { display: flex; flex-wrap: wrap; gap: 10px; }

/* The preview: a looping demo clip (js/demo-video.js plays it). */
.dash-hero-preview {
    display: flex; flex-direction: column; gap: 12px;
    padding: 16px; border-radius: var(--radius-sm);
    background: var(--bg); border: 1px solid var(--border);
    color: var(--text); text-decoration: none;
    transition: border-color .15s ease, transform .15s ease;
}
.dash-hero-preview:hover { border-color: var(--brand); transform: translateY(-2px); }
.dh-room { display: flex; align-items: center; gap: 7px; font-size: 0.74rem; color: var(--text-dim); }
.dh-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent); }
.dh-video {
    display: block; width: 100%; height: auto; aspect-ratio: 16 / 10;
    border-radius: 10px; border: 1px solid var(--border-soft, var(--border));
    background: var(--surface);
}
.dh-foot { font-size: 0.78rem; color: var(--text-muted); }
.dash-hero-preview:hover .dh-foot { color: var(--brand-light); }

@media (max-width: 820px) {
    .dash-hero { grid-template-columns: minmax(0, 1fr); }
    .dash-hero h1 { padding-right: 28px; }
}
.dash-hero-switch { margin: 16px 0 0; font-size: 0.86rem; color: var(--text-dim); }
.dash-hero-switch a { color: var(--brand-light); font-weight: 600; }

/* Which door leads. Growers and "Everyone" (and no choice yet) get the sensor
   hero; the homeowner persona gets the home hero. index.html sets
   html[data-persona] before first paint, toolfilter.js keeps it current. */
#homeHero { display: none; }
html[data-persona="homeowner"] #homeHero { display: grid; }
html[data-persona="homeowner"] #dashHero,
html.hero-off #dashHero, html.hero-off #homeHero { display: none; }
.home-hero {
    border-color: color-mix(in srgb, var(--home-line) 70%, var(--border));
    background:
        radial-gradient(120% 90% at 100% 0%, color-mix(in srgb, var(--home) 14%, transparent), transparent 60%),
        var(--surface);
}
.home-hero .dash-hero-kicker { color: var(--home-text); }

/* ─── Audience landing page (/home) ─── */
.lp-price {
    align-self: stretch; display: flex; flex-direction: column; justify-content: center;
    padding: 22px; border-radius: var(--radius-sm); background: var(--bg); border: 1px solid var(--border);
}
.lp-price-name { margin: 0 0 6px; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); }
.lp-price-val { margin: 0 0 8px; font-size: 2.6rem; font-weight: 750; letter-spacing: -0.03em; line-height: 1; }
.lp-price-val span { font-size: 1rem; font-weight: 600; color: var(--text-muted); }
.lp-price-note { margin: 0; color: var(--text-muted); font-size: 0.88rem; line-height: 1.55; }
.lp-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.lp-card h2 { margin: 0 0 8px; font-size: 1.1rem; }
.lp-card p { margin: 0 0 12px; color: var(--text-muted); line-height: 1.6; }
.lp-list { margin: 0 0 12px; padding-left: 18px; color: var(--text-muted); line-height: 1.6; }
.lp-note { font-size: 0.86rem; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--surface-2); }
.lp-links { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 0 !important; }
.lp-links a { color: var(--brand-light); font-weight: 600; font-size: 0.9rem; }
.home-hero ~ .lp-grid .lp-links a { color: var(--home-text); }
.lp-switch { margin: 26px 0 8px; text-align: center; color: var(--text-dim); }
.lp-switch a { color: var(--brand-light); font-weight: 600; }
@media (max-width: 760px) { .lp-grid { grid-template-columns: minmax(0, 1fr); } }

/* The persona switch sits beside the Toolshop heading, where it reads as
   "whose shop is this" rather than as one more filter. */
.toolshop-head { display: flex; flex-wrap: wrap; gap: 14px 24px; align-items: flex-end; justify-content: space-between; }
.toolshop-head > div:first-child { flex: 1 1 320px; }
.persona-switch { display: flex; flex-direction: column; gap: 6px; }
.persona-switch .field-label { margin: 0; }
.persona-switch .quick-views { margin: 0; }
@media (max-width: 640px) { .persona-switch { width: 100%; } }

/* ─── Tools hub grid ─── */
.tool-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 18px;
}
.tool-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: linear-gradient(180deg, rgba(255,255,255,0.015), transparent 40%), var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 22px;
    box-shadow: var(--shadow-sm);
    transition: box-shadow .18s ease, border-color .18s ease;
    color: var(--text);
}
.tool-card:hover { box-shadow: var(--shadow-lift); border-color: var(--brand); color: var(--text); }
.tool-card.disabled { opacity: 0.5; pointer-events: none; }
.tool-ico {
    display: grid; place-items: center;
    width: 44px; height: 44px; border-radius: 11px;
    background: color-mix(in srgb, var(--brand) 18%, transparent);
    color: var(--brand-light);
}
.tool-ico svg { width: 22px; height: 22px; }
.tool-card h3 { margin: 0; font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em; }
.tool-card p { margin: 0; color: var(--text-muted); font-size: 0.86rem; line-height: 1.5; }
.tool-tag {
    align-self: flex-start; margin-top: auto;
    font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--brand); background: color-mix(in srgb, var(--brand) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--brand) 32%, transparent);
    padding: 5px 13px; border-radius: 999px;
}
.tool-tag.soon { color: var(--text-dim); background: var(--surface-2); border-color: var(--border); }
/* Coming-soon tools are hidden until they ship (toolfilter.js does the same so
   empty sections collapse); this covers the moment before it runs and the
   favorites grid, which clones cards. The /tools index drops its rows too. */
.tool-card.disabled, .all-tools li:has(.t-badge.soon) { display: none !important; }
.tool-tag.tier-free { color: #9ccb9c; background: rgba(156,203,156,0.12); border-color: rgba(156,203,156,0.32); }
.tool-tag.tier-home { color: var(--home-text); background: var(--home-soft); border-color: var(--home-line); }
.tool-tag.tier-commercial { color: var(--brand-light); background: color-mix(in srgb, var(--brand) 14%, transparent); border-color: color-mix(in srgb, var(--brand) 32%, transparent); }
.tool-tag.locked { opacity: 0.78; }
.tool-card.locked { opacity: 0.82; }
.tool-card.locked:hover { opacity: 1; }
/* Shown but not openable (needs an account, above your plan, or not built).
   Greyed rather than removed, so the catalogue reads as a catalogue. Still
   clickable where a real page exists: the badge says why, and the page it
   leads to explains the rest. ".disabled" keeps its own pointer-events:none,
   since a "Coming soon" card has nowhere to go. */
.tool-card.is-unavailable { opacity: 0.5; filter: grayscale(0.65); }
.tool-card.is-unavailable:hover { opacity: 0.78; filter: grayscale(0.25); }

/* ─── Compact list view for tool grids ─── */
/* minmax(0,1fr), not 1fr: a plain 1fr track keeps an automatic min of its
   content size, so a long list-view row inflates the column past the viewport
   and pushes the star/hide buttons off-screen on phones. */
.tool-grid.as-list { grid-template-columns: minmax(0, 1fr); gap: 8px; }
.tool-grid.as-list .tool-card { flex-direction: row; align-items: center; flex-wrap: wrap; gap: 8px 14px; padding: 11px 16px; }
.tool-grid.as-list .tool-card .tool-ico { width: 34px; height: 34px; flex: none; margin: 0; }
/* The title is never truncated: it takes the space the inline blurb used to
   occupy and wraps to a second line if needed. The description moves behind
   the info (ⓘ) button below. */
.tool-grid.as-list .tool-card h3 {
    font-size: 0.95rem; margin: 0; min-width: 0; flex: 1 1 0;
    white-space: normal; overflow: visible; text-overflow: clip;
}
/* Description: hidden until the info button reveals it, then a full-width row. */
.tool-grid.as-list .tool-card p {
    display: none; order: 99; flex-basis: 100%; margin: 4px 0 0;
    font-size: 0.85rem; line-height: 1.5; color: var(--text-dim);
}
.tool-grid.as-list .tool-card.info-open p { display: block; }
/* Short blurb: only in list view, and only for the chemigation stepper (its
   steps have no <p> to reveal). Regular cards drop it for the info button. */
.tool-blurb { display: none; }
.tool-grid.as-list .tool-card .tool-blurb { display: none; }
/* Stepper blurb behaves like the tool cards' <p>: hidden until the info
   button reveals it as a full-width row, so the step title never truncates. */
.system-flow.as-list .sys-step .tool-blurb {
    display: none; order: 99; flex-basis: 100%; margin: 4px 0 0; min-width: 0;
    color: var(--text-dim); font-size: 0.85rem; line-height: 1.5;
    white-space: normal; overflow: visible; text-overflow: clip;
}
.system-flow.as-list .sys-step.info-open .tool-blurb { display: block; }
/* The tier badge stays in list view: it is how you tell a free tool from a
   commercial one, and list is the default view, so hiding it here hid it from
   most visitors. Sits just before the info/star/eye controls. */
.tool-grid.as-list .tool-card .tool-tag {
    order: 5; flex: none; position: static; align-self: center; margin: 0;
}
/* On a phone the row is only ~340px wide, and an icon, a badge and three
   control buttons leave the title about 88px: enough to wrap "pH Adjustment
   Calculator" onto three lines. The tool's name is the thing you are actually
   scanning for, so below this width the badge gives up its space. Nothing is
   lost: a tool you cannot open is already greyed. The badge is still there on wider list
   rows and in grid view, where it costs nothing. */
@media (max-width: 640px) {
    .tool-grid.as-list .tool-card .tool-tag { display: none; }
    .tool-grid.as-list .tool-card { gap: 6px 10px; padding: 11px 13px; }
    /* The chemigation steps are list rows too, and they were left out of the
       rule above: an 80px "Sign in" badge took the width that "Initial
       Irrigation Setup" needed, wrapping a three-word title onto three lines. */
    .system-flow.as-list .sys-step .tool-tag { display: none; }
    .system-flow.as-list .sys-step { gap: 6px 10px; padding: 11px 13px; }
}
.tool-grid.as-list .tool-card .featured-ribbon { display: none; }
.tool-grid.as-list .fav-btn, .tool-grid.as-list .hide-btn, .tool-grid.as-list .info-btn,
.system-flow.as-list .info-btn, .system-flow.as-list .fav-btn, .system-flow.as-list .hide-btn {
    position: static; opacity: 1; width: 26px; height: 26px; flex: none;
}
/* Keep the right-hand controls in a stable order: info, then star, then eye. */
.tool-grid.as-list .info-btn, .system-flow.as-list .info-btn { order: 6; }
.tool-grid.as-list .fav-btn, .system-flow.as-list .fav-btn { order: 7; }
.tool-grid.as-list .hide-btn, .system-flow.as-list .hide-btn { order: 8; }

/* Info (ⓘ) button: reveals a card's description in list view. Hidden in grid
   view, where the full description already shows under the title. */
.info-btn {
    display: none; place-items: center; padding: 0;
    width: 26px; height: 26px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text-dim); cursor: pointer; flex: none;
    pointer-events: auto; z-index: 3;
    transition: color .15s ease, border-color .15s ease, background .15s ease, transform .12s ease;
}
.tool-grid.as-list .info-btn, .system-flow.as-list .info-btn { display: grid; }
.info-btn:hover { color: var(--brand-light); border-color: var(--brand); transform: scale(1.1); }
.tool-card.info-open .info-btn {
    color: var(--brand-light);
    border-color: color-mix(in srgb, var(--brand-light) 55%, transparent);
    background: color-mix(in srgb, var(--brand) 18%, var(--surface-2));
}

/* ─── Arrange mode (reorder tools) ─── */
.arrange-ctrls { display: none; flex: none; gap: 4px; }
body.arranging .arrange-ctrls { display: inline-flex; }
.tool-card > .arrange-ctrls { align-self: flex-start; }
.arrange-ctrls button {
    width: 26px; height: 26px; border-radius: 7px; line-height: 1; cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text-muted);
}
.arrange-ctrls button:hover { border-color: var(--brand); color: var(--text); }
.arrange-ctrls button:disabled { opacity: 0.3; cursor: default; }
body.arranging .tool-card { cursor: grab; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
body.arranging .arrange-ctrls button:not(:disabled) { cursor: pointer; }
body.arranging .tool-card.dragging {
    cursor: grabbing; border-color: var(--brand); box-shadow: var(--shadow-lift);
    opacity: 0.85; position: relative; z-index: 2;
}
.section-arrange { margin-right: 4px; }
.section-arrange button { width: 22px; height: 22px; font-size: 0.7rem; }

/* ─── Chemigation hero: hideable + collapses in list view ─── */
.system-hero { position: relative; }
/* Keep the section's hide control visible (the hero has no hover on touch,
   and it's easy to miss on desktop). */
.system-hero > .hide-btn { opacity: 0.55; }
.system-hero:hover > .hide-btn, .system-hero.is-hidden-preview > .hide-btn { opacity: 1; }
.system-hero.is-hidden-preview { opacity: 0.55; outline: 1px dashed var(--border-warm); outline-offset: -1px; }
/* Column layout must also drop the base rule's flex-wrap:wrap. A wrapping
   column flex container sizes its width to children's max-content and
   overflows its parent, pushing the fav/hide buttons off-screen on phones. */
.system-flow.as-list { flex-direction: column; flex-wrap: nowrap; gap: 8px; }
.system-flow.as-list .flow-arrow { display: none; }
.system-flow.as-list { align-items: stretch; }
.system-flow.as-list .sys-step { flex: none; flex-direction: row; align-items: center; flex-wrap: wrap; gap: 8px 12px; padding: 11px 14px; }
.system-flow.as-list .sys-step .step-no { position: static; margin: 0; flex: none; }
.system-flow.as-list .sys-step .tool-ico { width: 32px; height: 32px; flex: none; margin: 0; }
.system-flow.as-list .sys-step h3 { font-size: 0.95rem; margin: 0; flex: 1 1 0; min-width: 0; white-space: normal; }
.system-flow.as-list .sys-step p { display: none; }

/* ─── Log reorder controls (dropdown) ─── */
.system-item .li-right { display: inline-flex; align-items: center; gap: 6px; }
.log-move { display: inline-flex; gap: 2px; }
.log-move button {
    width: 22px; height: 22px; border-radius: 6px; line-height: 1; cursor: pointer; font-size: 0.8rem;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text-muted);
}
.log-move button:hover { border-color: var(--brand); color: var(--text); }
.log-move button:disabled { opacity: 0.3; cursor: default; }

/* System switcher (nav) */
.system-switch { position: relative; }
.system-current {
    display: inline-flex; align-items: center; gap: 7px;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
    border-radius: 999px; padding: 6px 12px; font: inherit; font-size: 0.84rem; font-weight: 600;
    cursor: pointer; transition: border-color .15s ease, background .15s ease;
}
.system-current:hover { border-color: var(--brand); }
.system-current .system-name { max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.system-menu {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 60; min-width: 270px;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
    box-shadow: var(--shadow); padding: 7px; display: none;
}
.system-switch.open .system-menu { display: block; }
.system-menu-head {
    margin: 4px 8px 6px; font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.08em; color: var(--text-dim);
}
.system-item {
    display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
    text-align: left; background: none; border: none; cursor: pointer; color: var(--text);
    font: inherit; font-size: 0.88rem; padding: 8px; border-radius: 8px;
}
/* Compact read-only chips for system volumes on the calculator. */
.setup-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.setup-chip {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 11px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
    font-size: 0.85rem; color: var(--text-muted); white-space: nowrap;
}
.setup-chip strong { color: var(--text); font-weight: 700; }
.setup-chip > *:not(:first-child) { position: relative; padding-left: 9px; }
.setup-chip > *:not(:first-child)::before {
    content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 3px; height: 3px; border-radius: 50%; background: var(--text-dim);
}
.system-item:hover { background: var(--surface-2); }
.system-item.active { color: var(--brand-light); font-weight: 700; }
.system-item.danger { color: var(--danger); }
.system-item.upgrade { color: var(--brand-light); font-weight: 600; }
.system-item.locked { opacity: 0.7; }
.system-item[disabled] { opacity: 0.4; cursor: not-allowed; }

/* ─── Journal / Log calendar ─── */
.cal-head { display: flex; align-items: center; justify-content: center; gap: 18px; margin-bottom: 14px; }
.cal-month { font-weight: 700; font-size: 1rem; min-width: 160px; text-align: center; }
.cal-nav {
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
    width: 32px; height: 32px; border-radius: 8px; font-size: 1.2rem; line-height: 1; cursor: pointer;
}
.cal-nav:hover { border-color: var(--brand); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-dow { text-align: center; font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); padding-bottom: 4px; }
.cal-cell {
    position: relative; min-height: 78px; display: flex; flex-direction: column; align-items: stretch; gap: 3px;
    padding: 5px 5px 6px; text-align: left; overflow: hidden;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
    color: var(--text-muted); font-size: 0.85rem; font-weight: 600; cursor: pointer;
    transition: border-color .12s ease, background .12s ease;
}
.cal-cell .cal-num { flex: 0 0 auto; line-height: 1; padding: 1px 2px; }
.cal-cell.empty { background: none; border: none; cursor: default; }
.cal-cell:not(.empty):hover { border-color: var(--brand); }
/* Today. A bold gold number alone was easy to lose in a grid of numbers, and
   "which one is today" is the first question anyone asks a calendar. Give it a
   dot: shape reads faster than weight. Shared by the Master Calendar and every
   log page, which both render .cal-cell. */
.cal-cell.today { color: var(--text); border-color: var(--brand); }
.cal-cell.today .cal-num {
    color: #14100b; font-weight: 800;
    background: var(--brand-light); border-radius: 20px;
    min-width: 17px; height: 17px; display: inline-flex;
    align-items: center; justify-content: center; padding: 0 4px;
    /* The cell is a flex column, so without this the pill stretches the full
       width and reads as a banner rather than a date. */
    align-self: flex-start;
}
.cal-cell.has { color: var(--text); }
.cal-cell.sel { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 16%, var(--surface-2)); color: var(--text); }
/* Google-calendar-style text event chips inside a day cell. */
.cal-events { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cal-ev {
    font-size: 0.64rem; font-weight: 600; color: var(--text); line-height: 1.35;
    padding: 1px 5px; border-radius: 3px; border-left: 3px solid var(--brand-light);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* A planned entry (from a tool's schedule, not done yet): outlined, not filled. */
.cal-ev.planned { background: transparent !important; border: 1px dashed var(--border-warm); border-left-style: dashed; color: var(--text-muted); }
.cal-more { font-size: 0.6rem; font-weight: 700; color: var(--text-dim); padding: 0 2px; }
.cal-dot { position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%); width: 5px; height: 5px; border-radius: 50%; background: var(--brand-light); }

/* ─── Journal entry list ─── */
.log-entry { display: flex; gap: 14px; align-items: flex-start; padding: 13px 0; border-top: 1px solid var(--border); }
.log-entry:first-child { border-top: none; }
.log-date { flex: none; width: 92px; font-size: 0.82rem; font-weight: 700; color: var(--text-dim); padding-top: 2px; }
.log-body { flex: 1; min-width: 0; }
.log-cat { font-weight: 700; }
.log-amount { color: var(--brand-light); font-weight: 600; }
.log-note { margin-top: 4px; color: var(--text-muted); font-size: 0.9rem; line-height: 1.5; }
.log-photo { flex: none; display: block; width: 64px; height: 64px; border-radius: 8px; overflow: hidden; background: var(--surface-2); }
.log-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.log-spot { color: var(--text-dim); font-size: 0.82rem; }
.spot-year { display: flex; gap: 12px; align-items: flex-start; padding: 10px 0; border-top: 1px solid var(--border); }
.spot-year:first-child { border-top: none; }
.spot-year-label { flex: none; width: 52px; font-weight: 700; padding-top: 4px; }
.spot-strip { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; }
.spot-shot { flex: none; width: 104px; text-decoration: none; color: inherit; }
.spot-shot img { width: 104px; height: 104px; object-fit: cover; border-radius: 8px; display: block; }
.spot-shot span { display: block; font-size: 0.78rem; color: var(--text-dim); margin-top: 3px; }
.log-entry.planned .log-date { font-weight: 600; }
.log-planned.missed { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 55%, transparent); }
.log-room { font-size: 0.72rem; color: var(--text-dim); }
.log-acts { flex: none; display: flex; gap: 6px; align-items: center; }
.log-act { font-size: 0.8rem; padding: 4px 10px; }
.log-panel { margin-top: 10px; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); }
.log-panel .field-label { display: block; margin: 8px 0 4px; }
.log-panel .field-label:first-child { margin-top: 0; }
.log-panel-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 10px; }
.log-panel-row input[type="date"] { max-width: 180px; }
.log-panel-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.log-panel-grid .field-label { margin-top: 0; }
.log-danger { color: var(--danger); }
@media (max-width: 560px) {
    .log-entry { flex-wrap: wrap; }
    .log-date { width: auto; flex-basis: 100%; }
    .log-acts { flex-basis: 100%; justify-content: flex-end; }
    .log-panel-grid { grid-template-columns: 1fr; }
}
.log-planned { display: inline-block; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); border: 1px dashed var(--border-warm); border-radius: 999px; padding: 1px 7px; margin-right: 6px; vertical-align: 1px; }
.log-done { flex: none; font-size: 0.8rem; padding: 4px 10px; }
.log-del { flex: none; background: none; border: none; color: var(--text-dim); font-size: 1.3rem; line-height: 1; cursor: pointer; padding: 0 4px; }
.log-del:hover { color: var(--danger); }
textarea { width: 100%; background: var(--bg); color: var(--text); border: 1px solid var(--border-warm); border-radius: var(--radius-sm); padding: 10px 12px; font: inherit; font-size: 0.92rem; resize: vertical; }
textarea:focus { outline: none; border-color: var(--brand); box-shadow: var(--ring); }
.system-menu-sep { height: 1px; background: var(--border); margin: 6px 4px; }

/* Auto-save status (next to a Save button) */
.autosave-status {
    margin-left: 12px; font-size: 0.82rem; color: var(--text-dim);
    font-weight: 600; vertical-align: middle;
}
.autosave-status[data-autosave="saved"] { color: var(--ok); }
.autosave-status[data-autosave="error"] { color: var(--danger); }
/* "Loaded, autosaving" under the open recipe in a saved list. Mixed toward
   the text color so the green holds contrast on every theme. */
.loaded-note {
    display: block; margin: 2px 0 0; font-size: 0.85rem; font-weight: 600;
    color: color-mix(in srgb, var(--ok) 70%, var(--text));
}

/* Toggle switch (settings) */
.switch { position: relative; display: inline-block; width: 42px; height: 24px; flex: none; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
    position: absolute; inset: 0; cursor: pointer;
    background: var(--surface-3); border: 1px solid var(--border); border-radius: 999px;
    transition: background .15s ease, border-color .15s ease;
}
.switch .track::before {
    content: ""; position: absolute; height: 16px; width: 16px; left: 3px; top: 3px;
    background: var(--text-muted); border-radius: 50%;
    transition: transform .15s ease, background .15s ease;
}
.switch input:checked + .track { background: color-mix(in srgb, var(--brand) 32%, transparent); border-color: var(--brand); }
.switch input:checked + .track::before { transform: translateX(18px); background: var(--brand-light); }
.switch input:focus-visible + .track { box-shadow: var(--ring); }

/* Small color swatch (trial treatments, etc.) */
.swatch {
    display: inline-block; width: 12px; height: 12px; border-radius: 3px;
    margin-right: 8px; vertical-align: -1px;
    border: 1px solid rgba(255,255,255,0.15);
}

/* ─── Favorite star (per-card pin) ─── */
.tool-card { position: relative; }
.fav-btn {
    position: absolute; right: 12px; bottom: 12px;
    display: grid; place-items: center; padding: 0;
    width: 30px; height: 30px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text-dim); cursor: pointer;
    pointer-events: auto;   /* stay clickable even on disabled (coming-soon) cards */
    transition: color .15s ease, border-color .15s ease, background .15s ease, transform .12s ease;
    z-index: 3;
}
.fav-btn:hover { color: var(--brand-light); border-color: var(--brand); transform: scale(1.1); }
.fav-btn.on {
    color: var(--brand-light);
    border-color: color-mix(in srgb, var(--brand-light) 55%, transparent);
    background: color-mix(in srgb, var(--brand) 18%, var(--surface-2));
}
/* ─── Tool filter bar (hub) ─── */
.tool-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 4px 0 24px; }
.tool-search { position: relative; flex: 1 1 240px; max-width: 360px; }
.tool-search svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--text-dim); pointer-events: none; }
.tool-search input { padding-left: 36px; }
.filter-hidden-btn { margin-left: auto; }
.tool-filter-empty { color: var(--text-dim); font-size: 0.92rem; padding: 8px 2px 24px; }
/* The button trio (List/Grid view, Arrange, Install app). `contents` on
   desktop so this wrapper has no layout effect at all: the buttons stay
   direct flex children of .tool-filter, same as before it existed. */
.tool-filter-actions { display: contents; }
/* Phones: search gets its own full-width line (already does, via max-width
   on .tool-search), and the three buttons drop as a group onto a line of
   their own below it. */
@media (max-width: 640px) {
    .tool-filter { flex-direction: column; align-items: stretch; }
    /* .tool-search carries `flex: 1 1 240px` for the desktop ROW layout, where
       that basis controls width. In this column layout the main axis is
       vertical, so the same rule would set a 240px HEIGHT basis and the grow
       factor would stretch the search box to fill the column. Neutralize it. */
    .tool-search { flex: 0 0 auto; max-width: none; }
    .tool-filter-actions {
        display: flex; flex-wrap: wrap; gap: 8px; width: 100%;
    }
    .tool-filter-actions .btn { flex: 1 1 auto; font-size: 0.85rem; padding: 9px 14px; }
}

/* ─── Per-card hide button (top-right; complements the favorite star) ─── */
.hide-btn {
    position: absolute; right: 12px; top: 12px;
    display: grid; place-items: center; padding: 0;
    width: 28px; height: 28px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text-dim); cursor: pointer; opacity: 0;
    pointer-events: auto;
    transition: color .15s ease, border-color .15s ease, opacity .15s ease, transform .12s ease;
    z-index: 3;
}
.tool-card:hover .hide-btn, .tool-card:focus-within .hide-btn { opacity: 1; }
.hide-btn:hover { color: var(--danger); border-color: var(--danger); transform: scale(1.1); }
.tool-card.is-hidden-preview { opacity: 0.55; outline: 1px dashed var(--border-warm); outline-offset: -1px; }
.tool-card.is-hidden-preview .hide-btn { opacity: 1; color: var(--brand-light); border-color: color-mix(in srgb, var(--brand-light) 55%, transparent); }
#favGrid .hide-btn { display: none; }

/* ─── Featured / flagship tool card (glowing border) ─── */
.tool-card.featured, .system-hero {
    position: relative;
    border-color: color-mix(in srgb, var(--brand-light) 70%, transparent);
    animation: featuredGlow 2.8s ease-in-out infinite;
}
.tool-card.disabled.featured { opacity: 1; }   /* the star never dims */
@keyframes featuredGlow {
    0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand-light) 35%, transparent),
                           0 0 16px -4px color-mix(in srgb, var(--brand) 55%, transparent); }
    50%      { box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand-light) 70%, transparent),
                           0 0 34px 0 color-mix(in srgb, var(--brand-light) 55%, transparent); }
}
.featured-ribbon {
    position: absolute; top: 14px; right: 14px;
    font-size: 0.62rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.12em;
    color: #1c1408;
    background: linear-gradient(180deg, #f0d8b8, var(--brand));
    padding: 3px 10px; border-radius: 999px;
    box-shadow: 0 2px 10px -2px color-mix(in srgb, var(--brand) 70%, transparent);
}
@media (prefers-reduced-motion: reduce) {
    .tool-card.featured, .system-hero { animation: none;
        box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand-light) 55%, transparent),
                    0 0 24px -4px color-mix(in srgb, var(--brand-light) 50%, transparent); }
    .boot-bison img, .boot-bison::before, .boot-bison::after { animation: none; }
}

/* ─── Settings inline grid ─── */
.settings-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
}
/* Three hard columns on a phone leave ~96px per field, which truncates every
   select to two words and squeezes the number inputs. Let them reflow. */
@media (max-width: 640px) {
    .settings-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
}
.settings-grid .field-label { margin-bottom: 7px; }

/* ─── Disclosure: a card section that opens from its own button ───
   0fr to 1fr animates to whatever height the content actually is. A max-height
   guess cannot: too small clips, too large eases against a number nothing on
   the page ever reaches, so the open looks like it stalls at the end. */
.disclosure {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.28s ease;
}
.disclosure > * { overflow: hidden; min-height: 0; }
.disclosure[data-open="true"] { grid-template-rows: 1fr; }
/* Same flip as the BizonTip chevron, driven by the button's own aria state so
   the visual and the thing screen readers announce cannot drift apart. */
.disclosure-chev { transition: transform 0.2s ease; }
[aria-expanded="true"] .disclosure-chev { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
    .disclosure, .disclosure-chev { transition: none; }
}

/* ─── Ingredient table ─── */
.ingredient-table { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.ingredient-table thead th {
    position: sticky; top: 0; z-index: 2;
    /* Mixed toward --text: --text-muted on --surface-3 measured 4.33:1, under AA. */
    background: var(--surface-3); color: color-mix(in srgb, var(--text-muted) 70%, var(--text));
    padding: 10px 10px; text-align: center; font-weight: 600;
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;
    white-space: nowrap; border-bottom: 1px solid var(--border);
}
/* Element symbols keep their case: "NA" reads as "not applicable", not sodium */
/* Links in a page intro sit in running text, so color alone does not mark them. */
.page-head p a { text-decoration: underline; text-underline-offset: 2px; }
.ingredient-table thead th.el-head { text-transform: none; font-size: 0.78rem; letter-spacing: 0; }
/* My Fertilizers: seventeen columns on a wide page. Names stay on one line
   while there's room and wrap only when the window narrows, so the table
   side-scrolls only on phones */
#fertTable thead th, #fertTable tbody td { padding-left: 5px; padding-right: 5px; }
#fertTable tbody td:first-child { white-space: normal; min-width: 130px; }
.ingredient-table tbody td {
    padding: 7px 8px; text-align: center; border-bottom: 1px solid var(--border-soft);
    color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ingredient-table tbody tr:hover td { background: rgba(192,138,79,0.05); }
/* Soilless "What to Buy": the About column carries notes (the lime line), so it wraps
   instead of stretching the table. */
#buyTable td:last-child { white-space: normal; min-width: 180px; }
.ingredient-table select, .ingredient-table input {
    padding: 7px 8px; font-size: 0.84rem; min-width: 70px;
}
.ingredient-table select { padding-right: 26px; background-position: right 7px center; background-size: 12px; }
.ingredient-table select.fert-select { min-width: 170px; text-align: left; }
.ingredient-table input.ppm-input { width: 78px; text-align: center; }
.ingredient-table td.calc-out { color: var(--text-muted); }
.ingredient-table td.row-tools { width: 34px; }
#mixTable select { min-width: 170px; }
@media (max-width: 520px) {
    #mixTable select { min-width: 0; width: 100%; max-width: 160px; }
    #mixTable input.ppm-input { width: 56px; }
    #mixTable th, #mixTable td { padding-left: 6px; padding-right: 6px; }
    #mixTable th { white-space: normal; }
}
/* On a phone the table scrolls sideways; pin the product column so the name
   stays in view while the numbers slide under it. The pinned cell has to stay
   opaque: the hover tint (a tap counts as hover) and the source-water tint are
   see-through, and with either one the numbers showed through the name. The
   tint is laid over a solid surface instead. Same 720px cut as the swipe hint. */
@media (max-width: 720px) {
    .ingredient-table tbody td:first-child, .ingredient-table thead th:first-child,
    .ingredient-table tfoot td:first-child {
        position: sticky; left: 0; z-index: 1; background: var(--surface);
        box-shadow: inset -1px 0 0 var(--border-soft);
    }
    .ingredient-table thead th:first-child { z-index: 3; background: var(--surface-3); }
    .ingredient-table tbody tr:hover td:first-child {
        background: linear-gradient(rgba(192,138,79,0.05), rgba(192,138,79,0.05)) var(--surface);
    }
    .ingredient-table tbody tr.source-row td:first-child {
        background: linear-gradient(rgba(111,168,220,0.06), rgba(111,168,220,0.06)) var(--surface);
    }
    .ingredient-table tfoot td:first-child { background: var(--surface-2); }
    .ingredient-table select.fert-select { min-width: 150px; max-width: 46vw; }
}
.row-del {
    display: grid; place-items: center; width: 26px; height: 26px;
    border-radius: 7px; border: 1px solid transparent;
    background: transparent; color: var(--text-dim); cursor: pointer;
    transition: all .15s ease;
}
.row-tool-pair { display: flex; gap: 2px; justify-content: flex-end; }
.ingredient-table td.row-tools:has(.row-tool-pair) { width: 62px; }
.row-lock {
    display: grid; place-items: center; width: 26px; height: 26px;
    border-radius: 7px; border: 1px solid transparent;
    background: transparent; color: var(--text-dim); cursor: pointer;
    transition: all .15s ease; opacity: 0.55;
}
.row-lock:hover { opacity: 1; border-color: var(--border-soft); }
.row-lock.on { opacity: 1; color: var(--brand); border-color: color-mix(in srgb, var(--brand) 40%, transparent); background: color-mix(in srgb, var(--brand) 10%, transparent); }
.ingredient-table tr.locked input.ppm-input { border-color: color-mix(in srgb, var(--brand) 45%, transparent); }
.row-del:hover { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: var(--danger-bg); }
.table-x-scroll { overflow-x: auto; border-radius: var(--radius-sm); border: 1px solid var(--border-soft); }
/* "Swipe the table" nudge: only where the table is likely to overflow (phones). */
.table-scroll-hint { display: none; color: var(--brand-light); }
@media (max-width: 720px) { .table-scroll-hint { display: block; } }
/* Product picker: a clearly tappable, expandable row (it's collapsed to save space). */
#prodPanel > summary { list-style: none; display: flex; align-items: center; gap: 8px;
    padding: 11px 13px; border: 1px solid var(--border); border-radius: 9px;
    background: var(--surface-2); cursor: pointer; }
#prodPanel > summary::-webkit-details-marker { display: none; }
#prodPanel > summary::after { content: "▾"; margin-left: auto; color: var(--text-dim); transition: transform .15s ease; }
#prodPanel[open] > summary::after { transform: rotate(180deg); }
/* pH adjustment: a whole card that collapses, so the summary is the card's own
   heading row rather than a button inside it. */
#phAdjust > summary { list-style: none; display: flex; align-items: center; gap: 8px; cursor: pointer; }
#phAdjust > summary::-webkit-details-marker { display: none; }
#phAdjust > summary::after { content: "▾"; margin-left: auto; color: var(--text-dim); transition: transform .15s ease; }
#phAdjust[open] > summary::after { transform: rotate(180deg); }
#phAdjust > summary:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 3px; border-radius: 6px; }
.table-x-scroll::-webkit-scrollbar { height: 10px; }
.table-x-scroll::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 8px; border: 2px solid var(--surface); }

/* ─── Totals / target match ─── */
.npk-targets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.npk-cell {
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--radius-sm); padding: 14px 16px;
}
.npk-cell .lbl { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); font-weight: 600; }
.npk-cell .val { font-size: 1.5rem; font-weight: 750; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; margin-top: 4px; }
.npk-cell .lbl .lower { text-transform: lowercase; }
/* A long unit ("% of bench canopy") that can't sit beside the number drops
   below it as one small block, rather than breaking into heading-sized lines. */
.npk-cell .val .stat-unit { display: inline-block; line-height: 1.25; }
.npk-cell .tgt { font-size: 0.78rem; color: var(--text-dim); margin-top: 3px; font-variant-numeric: tabular-nums; }

/* Lighting: crop-steering levers */
.steer-summary { font-size: 1.05rem; font-weight: 700; margin: 0 0 14px; }
.lever {
    border: 1px solid var(--border); border-left: 3px solid #6f9d6f;
    background: var(--surface-2); border-radius: var(--radius-sm);
    padding: 12px 15px; margin-bottom: 12px;
}
.lever-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.lever-dir { font-weight: 700; font-size: 0.85rem; }
.dir-up { color: #7bb37b; }
.dir-down { color: #d1955f; }
.lever-action { font-weight: 600; flex: 1 1 160px; }
.lever-conf { margin-left: auto; margin-top: 0; }
/* Results-style tiles: the light's number, then whether it's already there. */
.lever-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-top: 12px; }
.lever-tile { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 14px; }
.lever-tile .lbl, .steer-when .lbl { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); font-weight: 600; }
.lever-tile .val { font-size: 1.4rem; font-weight: 750; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; margin-top: 4px; line-height: 1.15; }
.lever-tile .sub { font-size: 0.84rem; color: var(--text-dim); margin-top: 3px; }
.lever-verdict.is-ok .val { color: var(--tone-ideal); }
.lever-verdict.is-go .val { color: var(--tone-high); }
.lever-empty { color: var(--text-muted); font-size: 0.9rem; }
.lever-why { color: var(--text-muted); font-size: 0.9rem; line-height: 1.5; margin: 9px 0 0; }
.lever-more { margin: 10px 0 0; font-size: 0.9rem; }
.lever-more summary { cursor: pointer; color: var(--text-muted); font-weight: 600; }
.lever-more p { color: var(--text-muted); line-height: 1.5; margin: 6px 0 0; }
.lever-note { font-size: 0.86rem; color: var(--text-dim); margin: 7px 0 0; }
.steer-when { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 14px; font-size: 0.92rem; line-height: 1.5; margin: 4px 0 0; }
.steer-sub { font-size: 1rem; font-weight: 700; margin: 26px 0 10px; }
.crop-notes { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; }
.crop-notes summary { cursor: pointer; font-weight: 600; }
.crop-notes ul { margin: 6px 0 0; padding-left: 18px; display: grid; gap: 6px; color: var(--text-muted); font-size: 0.9rem; line-height: 1.5; }
.crop-notes .cite { color: var(--text-dim); font-size: 0.82rem; white-space: nowrap; }
.sched-bar { display: flex; height: 12px; border-radius: 6px; overflow: hidden; background: var(--border); }
.sched-bar span { display: block; height: 100%; background: var(--accent, #9a6432); opacity: 0.45; }
.sched-bar span.finish { opacity: 1; }
.sched-rows { display: grid; gap: 6px; margin-top: 10px; font-size: 0.9rem; }
.sched-rows div { display: grid; grid-template-columns: 7.5em 1fr; gap: 10px; }
.sched-rows b { font-weight: 650; white-space: nowrap; }
.spd-import summary { cursor: pointer; font-weight: 600; }
.spd-chart-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 14px; }
.spd-chart-row .hint { margin: 0; }
.spd-chart-top { margin: 0 0 14px; }
/* Spectrum chart popup (spdchart.js) */
.spd-modal { max-width: 820px; position: relative; }
.spd-modal .seg { margin: 6px 0 14px; }
.spd-pane .hint { margin: 0 0 10px; }
.spd-cal { display: flex; flex-wrap: wrap; gap: 10px 18px; margin: 12px 0 4px; }
.spd-cal label { display: inline-flex; align-items: center; gap: 6px; font-size: 0.9rem; color: var(--text-muted); }
.spd-cal input { width: 84px; }
.spd-step { font-weight: 600; color: var(--text); margin: 10px 0; }
.spd-canvas-wrap { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--surface-2); }
.spd-canvas-wrap canvas { display: block; width: 100%; height: auto; touch-action: none; }
#spdImg { cursor: crosshair; }
#spdDraw { cursor: crosshair; }
.spd-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 12px; }
.spd-row label { font-size: 0.9rem; color: var(--text-muted); }
.spd-tol { display: inline-flex; align-items: center; gap: 8px; }
.spd-row select { width: auto; }
.spd-bands { margin: 14px 0 0; font-weight: 600; color: var(--text); min-height: 1.4em; }

/* Micro results chips */
.micro-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 10px;
}
.micro-chip {
    background: var(--surface-2); border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm); padding: 10px 12px; text-align: center;
}
.micro-chip .el { font-size: 0.74rem; font-weight: 700; color: var(--brand-light); letter-spacing: 0.03em; }
.micro-chip .ppm { font-size: 1.02rem; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; margin-top: 2px; }
.micro-chip .ppm.zero { color: var(--text-dim); font-weight: 600; }
.micro-chip .u { font-size: 0.68rem; color: var(--text-dim); }

/* ─── User menu in nav ─── */
/* Static pages: signed-out links and CTAs hide when theme.js finds a cached
   account chip; data-account-only is their signed-in stand-in. */
.has-account [data-anon-only] { display: none !important; }
html:not(.has-account) [data-account-only] { display: none !important; }

.user-chip {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: 0.82rem; color: var(--text-muted);
    padding: 7px 12px; border-radius: 9px;
    border: 1px solid var(--border-soft); background: var(--surface-2);
    max-width: 200px;
}
.user-chip .uavatar {
    display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%;
    background: linear-gradient(150deg, var(--brand-light), var(--brand-dark));
    color: #1a120a; font-weight: 800; font-size: 0.72rem; flex: 0 0 auto;
    position: relative; overflow: hidden;
}
/* The profile photo sits over the letter; if it fails to load it removes
   itself and the letter shows through (userchip.js). */
.user-chip .uavatar img {
    position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.user-chip .uemail { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* No-internet chip (netstatus.js). Amber rather than red: the work is safe and
   waiting to sync, so this is a heads-up, not an error. */
.net-chip {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: 0.78rem; font-weight: 650; white-space: nowrap;
    padding: 6px 11px; border-radius: 9px;
    color: #e0b070; background: rgba(224,176,112,0.13);
    border: 1px solid rgba(224,176,112,0.38);
    cursor: default;
}
.net-chip[hidden] { display: none; }
.net-chip svg { flex: 0 0 auto; }

.row-add { display:inline-flex; }
.recipe-bar { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.recipe-bar > div { flex: 1; min-width: 160px; }

@media (max-width: 600px) {
    .npk-targets { grid-template-columns: 1fr; }
    .user-chip .uemail { display: none; }
    .net-chip .net-chip-text { display: none; }
    .net-chip { padding: 6px 8px; }
}

/* ─── System bar (calculator) ─── */
.system-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sys-pill {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: 0.85rem; color: var(--text); font-weight: 600;
    padding: 7px 13px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border-warm);
}
.sys-pill .sys-k {
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
    color: var(--brand); 
}

/* ─── Concentrate tank summary lines ─── */
.tank-stat {
    display: flex; justify-content: space-between; align-items: center;
    padding: 8px 0; border-bottom: 1px solid var(--border-soft); font-size: 0.88rem;
}
.tank-stat:last-child { border-bottom: none; }
.tank-stat span { color: var(--text-muted); }
.tank-stat strong { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ─── Mixing guide ─── */
.mix-steps {
    margin: 0; padding-left: 1.35em; list-style: decimal;
    color: var(--text-muted); font-size: 0.9rem;
}
.mix-steps li { padding: 5px 0; line-height: 1.5; }
.mix-steps li::marker { color: var(--brand-light); font-weight: 700; }
.mix-steps strong { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }
/* Keep the step text to a readable measure even when its card is page-wide. */
.mix-steps li { max-width: 70ch; }

/* "Recipe loaded: X" bar at the top of the calculator. Autosave writes to a
   loaded recipe, so which one is open has to be visible while you work, not
   only down at the Save button. */
.recipe-banner {
    display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px;
    margin: 0 0 18px; padding: 11px 15px;
    background: var(--surface-2); border: 1px solid var(--border);
    border-left: 3px solid var(--brand); border-radius: var(--radius-sm);
    font-size: 0.9rem; color: var(--text);
}
.recipe-banner-k {
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em;
    text-transform: uppercase; color: var(--text-dim);
}
.recipe-banner-note { font-size: 0.82rem; color: var(--text-muted); }
/* Switch recipes / unload, held at the far end so the bar reads left to right
   as "this is open" then "here is what you can do about it". */
.recipe-banner-actions {
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
    margin-left: auto;
}
.recipe-banner-actions select {
    width: auto; max-width: 230px; padding: 6px 30px 6px 10px; background-position: right 9px center; background-size: 12px; font-size: 0.84rem;
    background-color: var(--card);
}
.recipe-banner-actions .btn { padding: 6px 14px; font-size: 0.84rem; }
/* Light mode's muted browns sat too close to the banner's beige, so the label,
   the N-P-K line and the button text read faint. Darker text on the card colour.
   Greenhouse sets data-theme="light" too, so it gets its own neutral greys. */
:root[data-theme="light"] .recipe-banner { background: var(--surface); border-color: var(--border-warm); border-left-color: var(--brand); }
:root[data-theme="light"] .recipe-banner-k { color: #4f412c; }
:root[data-theme="light"] .recipe-banner-note,
:root[data-theme="light"] .recipe-banner-actions .btn-ghost { color: #3d3221; }
:root[data-scheme="greenhouse"] .recipe-banner-k { color: var(--text-muted); }
:root[data-scheme="greenhouse"] .recipe-banner-note,
:root[data-scheme="greenhouse"] .recipe-banner-actions .btn-ghost { color: var(--text); }
@media (max-width: 640px) {
    .recipe-banner-actions { margin-left: 0; width: 100%; }
    .recipe-banner-actions select { flex: 1 1 auto; max-width: none; }
}

/* A section heading with buttons on the right (Mixing Guide: print / share). */
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin: 34px 0 16px; }
.section-head .section-label { margin: 0; flex: 1 1 220px; }
.section-head-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Mixing guide grid. One tank stretched a single step list across the whole
   page, which reads badly, so the column count follows the tank count instead
   of filling every available track. */
.mix-grid[data-count="1"] { grid-template-columns: minmax(0, 1fr); max-width: 760px; }
.mix-grid[data-count="2"] { grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); }

/* ─── pH adjustment readout ─── */
.ph-facts { max-width: 540px; }
.ph-dose-sub { margin: 8px 0 0; font-size: 0.82rem; color: var(--text-muted); }
.ph-notes { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-soft); }
.ph-notes p { margin: 0 0 8px; font-size: 0.82rem; color: var(--text-muted); line-height: 1.5; }
.ph-notes p:last-child { margin-bottom: 0; }

/* Verdict against the grower's target pH. Tinted by state, and deliberately
   louder than a .hint: it is the one line that answers "am I where I wanted
   to be?", which is the question people open this tool with. */
.ph-verdict {
    margin: 0 0 16px; padding: 13px 16px; border-radius: 10px;
    border: 1px solid var(--border); background: var(--card);
    font-size: 0.93rem; line-height: 1.55; color: var(--text-muted);
}
.ph-verdict strong { color: var(--text); }
.ph-verdict-head { display: block; font-weight: 700; margin-bottom: 3px; color: var(--text); }
.ph-verdict.is-on {
    border-color: color-mix(in srgb, var(--ok) 45%, transparent);
    background: color-mix(in srgb, var(--ok) 11%, transparent);
}
.ph-verdict.is-on .ph-verdict-head { color: var(--ok); }
.ph-verdict.is-off {
    border-color: color-mix(in srgb, var(--brand) 40%, transparent);
    background: color-mix(in srgb, var(--brand) 12%, transparent);
}
.ph-verdict.is-off .ph-verdict-head { color: var(--brand-light); }
.ph-verdict.is-bad {
    border-color: color-mix(in srgb, var(--danger) 50%, transparent);
    background: var(--danger-bg);
}
.ph-verdict.is-bad .ph-verdict-head { color: var(--danger); }

/* ─── Plan badges / paywall ─── */
.nav-plan { display: inline-flex; align-items: center; gap: 10px; }
.plan-badge {
    display: inline-flex; align-items: center;
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
    padding: 4px 9px; border-radius: 999px; border: 1px solid;
}
.plan-free  { color: var(--text-dim);    background: var(--surface-2); border-color: var(--border); }
.plan-pro   { color: var(--brand-light); background: color-mix(in srgb, var(--brand) 16%, transparent); border-color: color-mix(in srgb, var(--brand) 38%, transparent); }
.plan-home  { color: var(--home-text);   background: var(--home-soft); border-color: var(--home-line); }
.plan-admin { color: #9fce9f;            background: rgba(138,185,138,0.14); border-color: rgba(138,185,138,0.4); }
.nav-upgrade {
    display: inline-flex; align-items: center; padding: 6px 12px; border-radius: 9px;
    font-size: 0.8rem; font-weight: 700;
    color: #1a120a; background: linear-gradient(180deg, var(--brand), var(--brand-dark));
    box-shadow: var(--shadow-sm);
}
.nav-upgrade:hover { color: #1a120a; filter: brightness(1.08); }
/* Same fix as .btn-primary: the light schemes darken the brand vars, which
   turns this fill into dark brown under near-black text. */
:root[data-theme="light"] .nav-upgrade { background: linear-gradient(180deg, #e6a960, #d1893f); color: #2b2115; }
:root[data-scheme="greenhouse"] .nav-upgrade { background: #e09a3f; color: #000000; border: 1px solid #7a4a10; }
/* Pages with no sign-in script (the tour) never fill the account chip. */
.user-chip:empty { display: none; }

/* Pro lock affordance on controls */
.pro-lock {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em;
    color: var(--brand-light); background: color-mix(in srgb, var(--brand) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--brand) 38%, transparent);
    padding: 2px 7px; border-radius: 999px; margin-left: 8px;
}

/* Upgrade page feature lists */
.plan-list { list-style: none; margin: 0; padding: 0; }
.plan-list li {
    position: relative; padding: 9px 0 9px 28px; font-size: 0.92rem;
    color: var(--text-muted); border-bottom: 1px solid var(--border-soft);
}
.plan-list li:last-child { border-bottom: none; }
.plan-list li::before {
    content: ""; position: absolute; left: 4px; top: 50%; transform: translateY(-50%);
    width: 14px; height: 14px; border-radius: 50%;
    background: var(--surface-3); border: 1px solid var(--border-warm);
}
.plan-list.pro li::before { background: var(--brand); border-color: var(--brand-light); }
/* A sub-list inside one plan bullet (the sensor models on /upgrade): small
   dots, no rules between rows, and the parent's dot pinned to its first line. */
.plan-list li:has(> .plan-list)::before { top: 18px; }
.plan-list .plan-list li { padding: 3px 0 3px 18px; border-bottom: none; font-size: 0.88rem; }
.plan-list .plan-list li::before { width: 6px; height: 6px; left: 4px; }
.plan-list.pro li strong { color: var(--text); }

/* ─── Growing System hero (homepage) ─── */
.system-hero {
    background:
        linear-gradient(180deg, color-mix(in srgb, var(--brand) 7%, transparent), transparent 55%),
        var(--surface);
    border-color: var(--border-warm);
}
.system-lead { margin: 0; color: var(--text-muted); font-size: 0.95rem; line-height: 1.55; max-width: 760px; }
/* Header row: the lead paragraph and the collapse control. Laid out rather
   than absolutely positioned, so the button cannot land on top of the text
   at any width, and so it stays put when the lead is collapsed away. */
.hero-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 18px; }
/* The hide button is absolutely positioned in the card's corner, so the head
   row has to leave it a lane or the Collapse pill ends up underneath it. The
   collapsed layout already did this with a margin; expanded never did, and on
   a phone the eye sat squarely on top of the word "Collapse". */
.system-hero:not(.collapsed) .hero-head { padding-right: 34px; }
.hero-head .system-lead { flex: 1 1 auto; }
.hero-collapse {
    flex: none; display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 11px; border-radius: 999px; cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text-muted); font: inherit; font-size: 0.8rem; font-weight: 600;
    transition: color .15s ease, border-color .15s ease;
}
.hero-collapse:hover { color: var(--brand-light); border-color: var(--brand); }
.hero-collapse svg { transition: transform .18s ease; }
.system-hero.collapsed .hero-collapse svg { transform: rotate(-90deg); }
/* Collapsed: the four-step flow reduces to the two steps opened day to day.
   The irrigation setup and the fertilizer list are configured once; the recipe
   and the calculator are the working loop (set the target, hit it), so both
   survive the collapse. */
.system-hero.collapsed .system-lead,
.system-hero.collapsed .flow-arrow,
.system-hero.collapsed .sys-step:not([data-tool="calculator"]):not([data-tool="recipes"]),
.system-hero.collapsed .sys-step .step-no { display: none; }
/* Collapsed, the control shares the calculator's row instead of taking one of
   its own: a button alone on a line left a band of empty card above the only
   thing the collapse was supposed to leave you with. margin-right keeps it
   clear of the absolutely-positioned hide button in the corner. */
.system-hero.collapsed { display: flex; align-items: center; gap: 12px; }
.system-hero.collapsed .hero-head { order: 2; margin: 0 34px 0 0; }
.system-hero.collapsed .system-flow { order: 1; flex: 1 1 auto; min-width: 0; }
.system-flow { display: flex; align-items: stretch; gap: 14px; flex-wrap: wrap; }
.sys-step {
    position: relative; flex: 1 1 200px; min-width: 200px; margin: 0;
    background: var(--surface-2);
}
/* A step pinned to favorites sits among plain cards, not on the hero panel,
   so it takes their surface (surface-2 read as grayed out in light mode). */
#favGrid .sys-step { background: var(--surface); min-width: 0; }
.sys-step .step-no {
    position: absolute; top: 14px; right: 14px;
    display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%;
    font-size: 0.74rem; font-weight: 800; color: #1a120a;
    background: linear-gradient(150deg, var(--brand-light), var(--brand-dark));
}
/* Light brand tokens are dark text browns, so as a fill they put the
   near-black number on muddy brown. Use the light .btn-primary gold. */
:root[data-theme="light"] .sys-step .step-no,
:root[data-theme="light"] .tour-rail-step .step-no {
    background: linear-gradient(150deg, #e6a960, #d1893f);
    color: #2b2115;
}
.flow-arrow {
    display: flex; align-items: center; justify-content: center;
    color: var(--brand); font-size: 1.5rem; font-weight: 700; flex: 0 0 auto;
}
@media (max-width: 760px) {
    .flow-arrow { display: none; }
    .sys-step { flex-basis: 100%; }
    /* Collapsed row layout puts the Expand/Collapse pill beside the flow, sized
       to its own content - fine when the two remaining steps share one line,
       but they stack to two lines at this width (.sys-step above), and with
       nothing telling the row itself to stack too, the pill stayed a flex
       sibling vertically centered against both stacked cards, floating in the
       gap between them. Stack the pill above the flow instead, clear of the
       hide button in the card's corner. */
    .system-hero.collapsed { flex-direction: column; align-items: stretch; }
    .system-hero.collapsed .hero-head {
        order: 0; margin: 0 0 12px; padding-right: 34px; justify-content: flex-end;
    }
    .system-hero.collapsed .system-flow { order: 1; }
}

/* ─── Tank dilution row (Grow Setup) ─── */
.dilution-row { display: flex; align-items: center; gap: 10px; max-width: 360px; }
.dilution-prefix { color: var(--text-muted); font-weight: 700; font-size: 1.05rem; font-variant-numeric: tabular-nums; white-space: nowrap; flex: none; }
.dilution-row .tank-ratio { flex: 1; text-align: center; font-weight: 600; }
.dilution-pct {
    flex: 0 0 auto; font-size: 0.8rem; color: var(--brand-light); font-weight: 600;
    background: color-mix(in srgb, var(--brand) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--brand) 30%, transparent);
    padding: 5px 10px; border-radius: 999px; white-space: nowrap;
}

/* ─── Nutrient scales (calculator results) ─── */
.nut-row { padding: 13px 2px; border-bottom: 1px solid var(--border-soft); }
.nut-row:last-child { border-bottom: none; }
.nut-head { display: flex; align-items: center; gap: 10px; margin-bottom: 11px; flex-wrap: wrap; }
.nut-label { font-weight: 700; color: var(--text); min-width: 130px; }
.nut-label .el { color: var(--brand-light); }
.nut-read { color: var(--text-muted); font-size: 0.88rem; font-variant-numeric: tabular-nums; }
.nut-read strong { color: var(--text); font-weight: 700; }
.nut-head .stat-badge { margin-left: auto; }
.nut-track {
    position: relative; height: 10px; border-radius: 999px;
    background: var(--bg); border: 1px solid var(--border);
    box-shadow: inset 0 1px 2px rgba(0,0,0,0.4);
}
.nut-band {
    position: absolute; top: -3px; bottom: -3px; border-radius: 6px;
    background: rgba(138,185,138,0.16); border: 1px solid rgba(138,185,138,0.45);
}
.nut-marker {
    position: absolute; top: 50%; width: 16px; height: 16px; margin-left: -8px;
    border-radius: 50%; transform: translateY(-50%);
    border: 3px solid var(--surface); box-shadow: 0 2px 6px rgba(0,0,0,0.6);
    background: var(--text); transition: left .45s cubic-bezier(.4,0,.2,1);
}
.nut-marker.m-low   { background: var(--tone-low); }
.nut-marker.m-ideal { background: var(--tone-ideal); }
.nut-marker.m-high  { background: var(--tone-high); }
/* On light backgrounds the dark-mode track (page colour) and the faint green
   band all but disappeared. Greenhouse sets data-theme="light" too. */
:root[data-theme="light"] .nut-track {
    background: var(--surface-3); border-color: var(--border-warm);
    box-shadow: inset 0 1px 2px rgba(0,0,0,0.18);
}
:root[data-theme="light"] .nut-band {
    background: color-mix(in srgb, var(--tone-ideal) 26%, transparent);
    border: 1.5px solid color-mix(in srgb, var(--tone-ideal) 75%, transparent);
}
:root[data-theme="light"] .nut-marker { box-shadow: 0 1px 4px rgba(0,0,0,0.35); }
.nut-empty { color: var(--text-dim); font-size: 0.84rem; margin: 6px 0 0; }

/* Source-water row in the ingredient table */
.ingredient-table tr.source-row td { background: rgba(111,168,220,0.06); color: var(--text-muted); }
.ingredient-table tr.source-row td:first-child { color: var(--text); font-weight: 600; }
.src-tag {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--tone-low);
}

/* Totals + targets along the bottom of the ingredient table */
.ingredient-table tfoot td {
    padding: 7px 8px; text-align: center; white-space: nowrap;
    background: var(--surface-2); font-variant-numeric: tabular-nums;
    font-size: 0.82rem; color: var(--text-muted);
}
.ingredient-table tfoot tr.foot-total td { border-top: 2px solid var(--border); color: var(--text); font-weight: 700; }
.ingredient-table tfoot td:first-child { text-align: left; font-weight: 700; color: var(--text); text-transform: uppercase; font-size: 0.7rem; letter-spacing: 0.04em; }
.ingredient-table tfoot td.foot-mid { text-align: right; }
.foot-tone {
    display: inline-block; padding: 2px 7px; border-radius: 6px;
    border: 1px solid transparent; font-weight: 700; font-variant-numeric: tabular-nums;
}
/* Far off (under half or over twice the target): filled solid. */
.foot-tone.far.badge-low  { background: var(--tone-low);  color: var(--surface); border-color: var(--tone-low); }
.foot-tone.far.badge-high { background: var(--tone-high); color: var(--surface); border-color: var(--tone-high); }
/* Other targets: a small table of its own under the ingredients */
.other-wrap { margin-top: 12px; border: 0; border-radius: 0; }
.other-targets {
    border-collapse: separate; border-spacing: 0; font-variant-numeric: tabular-nums;
    font-size: 0.82rem; background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--radius-sm); overflow: hidden;
}
.other-targets caption {
    caption-side: top; text-align: left; padding: 0 0 6px; font-weight: 700;
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text);
}
.other-targets caption span { color: var(--text-dim); font-weight: 600; text-transform: none; letter-spacing: 0; margin-left: 4px; }
.other-targets th, .other-targets td { padding: 6px 10px; text-align: center; white-space: nowrap; }
.other-targets thead th { color: var(--text); font-weight: 700; text-transform: none; letter-spacing: 0; font-size: 0.82rem; border-bottom: 1px solid var(--border); }
.other-targets tbody th {
    text-align: left; font-weight: 700; font-size: 0.7rem; text-transform: uppercase;
    letter-spacing: 0.04em; color: var(--text-muted); position: sticky; left: 0; background: var(--surface-2);
}
.other-targets tr.ot-target td { color: var(--text-muted); border-top: 1px solid var(--border); }
.other-targets tr.ot-target th { border-top: 1px solid var(--border); }
.other-targets thead th:first-child { position: sticky; left: 0; background: var(--surface-2); }
.foot-note { color: var(--text-dim); font-weight: 600; }

/* Nitrogen by form, under the N scale */
.nform { margin: -2px 0 16px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--surface-2); }
.nform-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-bottom: 8px; font-size: 0.84rem; }
.nform-label { font-weight: 700; color: var(--text); margin-right: auto; }
.nform-share { color: var(--text-muted); }
.nform-share strong { color: var(--text); }
.nform-bar { display: flex; height: 10px; border-radius: 999px; overflow: hidden; background: var(--surface-3); }
.nform-bar i { display: block; height: 100%; }
.nform-keys { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; font-size: 0.8rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.nform-key { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
.nform-no3 { background: #2a78d6; }
.nform-nh4 { background: #eb6834; }
.nform-urea { background: #eda100; }
.nform-unk { background: repeating-linear-gradient(45deg, var(--text-dim) 0 3px, transparent 3px 6px); }
/* A foliar spray under its nutrient's scale: same box as the N forms, one line per spray */
.foliar-sub { border-left: 3px solid var(--brand); }
.foliar-sub .nform-head { margin-bottom: 4px; }
.foliar-sub .nform-keys { flex-direction: column; margin-top: 0; }
.foliar-sub .nform-keys strong { color: var(--text); }

/* Where each nutrient comes from */
.src-row { border-bottom: 1px solid var(--border-soft); }
.src-row:last-child { border-bottom: 0; }
.src-row summary {
    display: grid; grid-template-columns: minmax(96px, 9rem) 1fr 8.5rem; align-items: center; gap: 12px;
    padding: 9px 0; cursor: pointer; list-style: none; font-size: 0.86rem;
}
.src-row summary::-webkit-details-marker { display: none; }
.src-sym { font-weight: 600; color: var(--text); }
.src-sym .el { color: var(--text-dim); font-size: 0.75rem; margin-left: 3px; }
.src-stack { display: flex; height: 12px; border-radius: 4px; overflow: hidden; background: var(--surface-3); }
.src-stack i { display: block; height: 100%; box-shadow: inset -1px 0 0 var(--surface); }
.src-row summary > :last-child { justify-self: end; }
.src-total { color: var(--text-muted); font-variant-numeric: tabular-nums; font-weight: 600; }
.src-list { list-style: none; margin: 0 0 10px; padding: 0; font-size: 0.84rem; }
.src-list li { display: grid; grid-template-columns: 14px 1fr auto 3.2em; gap: 8px; align-items: center; padding: 4px 0; }
.src-key { width: 10px; height: 10px; border-radius: 2px; display: block; }
.src-name { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.src-ppm { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.src-pct { color: var(--text-dim); text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 520px) {
    .src-row summary { grid-template-columns: 1fr auto; }
    .src-row summary .src-stack { grid-column: 1 / -1; grid-row: 2; }
}

/* Wider tank selector so "Tank A/B/C" isn't clipped */
.ingredient-table select.tank-select { min-width: 104px; text-align: left; }

/* ─── Logo easter egg: click to GRAWRRR ─── */
.brand-mark { cursor: pointer; }
@keyframes roar-shake {
    0%   { transform: scale(1) rotate(0deg); }
    12%  { transform: scale(1.45) rotate(-9deg); }
    24%  { transform: scale(1.55) rotate(9deg); }
    36%  { transform: scale(1.5) rotate(-8deg); }
    48%  { transform: scale(1.55) rotate(7deg); }
    60%  { transform: scale(1.5) rotate(-6deg); }
    74%  { transform: scale(1.35) rotate(4deg); }
    88%  { transform: scale(1.18) rotate(-2deg); }
    100% { transform: scale(1) rotate(0deg); }
}
.brand-mark.roar-anim {
    animation: roar-shake .65s ease-in-out;
    position: relative; z-index: 200;
    filter: drop-shadow(0 6px 16px rgba(192,138,79,0.5));
}

/* ─── Flow action rows (Save / Save & continue -> Calculator) ─── */
.flow-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.flow-actions .btn-primary { margin-left: auto; }
@media (max-width: 520px) {
    .flow-actions { flex-direction: column; align-items: stretch; }
    .flow-actions .btn-primary { margin-left: 0; }
}

/* ─── Community library search results ─── */
.lib-results { display: flex; flex-direction: column; }
.lib-row {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 2px; border-bottom: 1px solid var(--border-soft);
}
.lib-row:last-child { border-bottom: none; }
.lib-row .lib-add { flex: 0 0 auto; }

/* Remove number-input stepper arrows everywhere (increment-by-1 is useless
   for ppm / % / ratios). */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* Footer contact links */
.footer-links {
    display: flex; gap: 18px; justify-content: center;
    margin-bottom: 8px; flex-wrap: wrap;
}
.footer-links a { color: var(--text-muted); font-weight: 600; }
.footer-links a:hover { color: var(--brand-light); }

/* ─── Legal pages (privacy / terms) ─── */
.legal { max-width: 780px; line-height: 1.7; color: var(--text-muted); }
.legal h2 { margin: 28px 0 8px; font-size: 1.15rem; font-weight: 750; color: var(--text); letter-spacing: -0.01em; }
.legal h2:first-child { margin-top: 0; }
.legal h3 { margin: 18px 0 6px; font-size: 1rem; font-weight: 700; color: var(--text); }
.legal p { margin: 0 0 12px; }
.legal ul { margin: 0 0 12px; padding-left: 20px; }
.legal li { margin-bottom: 7px; }
.legal a { color: var(--brand-light); font-weight: 600; }
.legal strong { color: var(--text); }
.legal .updated { font-size: 0.9rem; color: var(--text-dim); }

/* ─── Master calendar (multi-topic) ─── */
.topic-toggles { display: flex; flex-wrap: wrap; gap: 8px; }
.topic-toggle {
    display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
    padding: 7px 14px; border-radius: 999px; font-size: 0.85rem; font-weight: 600;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
}
.topic-toggle .swab { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.topic-toggle.off { opacity: 0.45; }
.topic-toggle.off .label { text-decoration: line-through; }
.topic-chip {
    display: inline-block; font-size: 0.68rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.05em; padding: 2px 8px; border-radius: 999px; vertical-align: 1px;
}

/* ─── All-tools landing list (auto-built from the hub) ─── */
.all-tools { list-style: none; margin: 0; padding: 0; }
.all-tools li { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 10px 0; border-top: 1px solid var(--border); font-size: 0.92rem; }
.all-tools li:first-child { border-top: none; }
.all-tools .t-name { font-weight: 600; color: var(--text); }
/* Live tools are links here (this page is the crawlable index); coming-soon
   ones stay plain <span>, so only the anchor picks up the hover. */
.all-tools a.t-name { text-decoration: none; }
.all-tools a.t-name:hover, .all-tools a.t-name:focus-visible { color: var(--brand-light); text-decoration: underline; }
.all-tools .t-badges { margin-left: auto; display: inline-flex; gap: 6px; flex: none; }
.t-badge { font-size: 0.64rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; padding: 2px 8px; border-radius: 999px; border: 1px solid; white-space: nowrap; }
.t-badge.live { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); background: color-mix(in srgb, var(--ok) 13%, transparent); }
.t-badge.soon { color: var(--text-dim); border-color: var(--border); background: var(--surface-2); }
.t-badge.free { color: #b5e48c; border-color: rgba(181,228,140,0.4); }
/* The pale green is for dark backgrounds; on cream or white it all but vanishes. */
:root[data-theme="light"] .t-badge.free, :root[data-scheme="greenhouse"] .t-badge.free { color: #3d6e2e; border-color: rgba(61,110,46,0.45); }
.t-badge.home { color: var(--home-text); border-color: var(--home-line); }
.t-badge.comm { color: var(--brand-light); border-color: color-mix(in srgb, var(--brand) 38%, transparent); }

/* ─── Boot reveal: hold the page until it's assembled, then show it at once ─── */
/* CSS-only loader (pseudo-elements on <html>), so no per-page markup is needed. */
.nav, .page { transition: opacity .28s ease; }
/* Signed-out fast path (theme.js): shown before the page's boot, so not yet
   clickable. Scrolling still works; it passes through to the document. */
html.boot-inert .page { pointer-events: none; }
/* Hidden AND inert: at opacity 0 the page still took clicks and typing, so
   input made during a slow boot landed in a form the boot then repainted. */
html.booting .nav, html.booting .page { opacity: 0; pointer-events: none; }
html.booting::before {
    content: ""; position: fixed; inset: 0; z-index: 200; background: var(--bg);
}
/* Sleek translucent "Bizon" wordmark washed across the backdrop. */
html.booting::after {
    content: "Bizon"; position: fixed; inset: 0; z-index: 200;
    display: flex; align-items: center; justify-content: center;
    font-family: "Inter", system-ui, sans-serif; font-weight: 700;
    font-size: clamp(72px, 24vw, 300px); letter-spacing: -0.045em; line-height: 1;
    color: color-mix(in srgb, var(--text) 7%, transparent);
    pointer-events: none; user-select: none;
    opacity: 0; transition: opacity .3s ease;
}
/* Wordmark, bison and status wait for theme.js to call the load slow (.boot-slow,
   ~0.9s in); a fast load only ever shows the plain veil. */
html.booting.boot-slow::after,
html.booting.boot-slow .boot-bison,
html.booting.boot-slow .boot-status { opacity: 1; }
/* The bison peeks up and tilts to look at you, with a shades glint. */
.boot-bison {
    position: fixed; top: 46%; left: 50%; z-index: 201;
    width: 112px; height: 112px; margin: -56px 0 0 -56px;
    opacity: 0; transition: opacity .25s ease;
}
.boot-bison img {
    width: 100%; height: 100%; object-fit: contain; display: block; border-radius: 22px;
    transform-origin: 50% 85%;
    animation: bisonrun .6s ease-in-out infinite;
}
/* Dust puffs kicking out behind the running bison. */
.boot-bison::before, .boot-bison::after {
    content: ""; position: absolute; bottom: 14px; left: -4px;
    width: 20px; height: 20px; border-radius: 50%; pointer-events: none;
    background: radial-gradient(circle, rgba(206,184,150,0.55), rgba(206,184,150,0) 70%);
    animation: bisondust .9s ease-out infinite;
}
.boot-bison::after { animation-delay: .45s; }
html:not(.booting) .boot-bison { opacity: 0; pointer-events: none; }
/* Status line under the wordmark (theme.js swaps its text as the wait goes
   on -- "Loading...", then reassurance, then a guess at why, rather than a
   silent hold that reads as broken past a few seconds). */
.boot-status {
    position: fixed; top: calc(46% + 78px); left: 0; right: 0; z-index: 201;
    text-align: center; padding: 0 24px; pointer-events: none; user-select: none;
    font-family: "Inter", system-ui, sans-serif; font-weight: 600;
    font-size: 14px; letter-spacing: 0.01em;
    color: color-mix(in srgb, var(--text) 55%, transparent);
    opacity: 0; transition: opacity .25s ease;
}
html:not(.booting) .boot-status { opacity: 0; pointer-events: none; }
@keyframes bisonrun {
    0%, 100% { transform: translateY(0) rotate(-1.5deg); }
    50%      { transform: translateY(-10px) rotate(2deg); }
}
@keyframes bisondust {
    0%   { transform: translate(0, 0) scale(0.4); opacity: 0; }
    25%  { opacity: 0.7; }
    100% { transform: translate(-40px, -18px) scale(1.8); opacity: 0; }
}

/* ─── "Best on desktop" tip (footer, shown on small screens only) ─── */
.footer-tip { display: none; }
.footer-report { margin: 32px auto 0; padding: 0 16px; max-width: 720px; text-align: center; font-size: 0.86rem; color: var(--text-dim); }
.footer-report a { color: var(--brand-light); border-bottom: 1px dotted currentColor; }
.footer-report a:hover { color: var(--text); }
@media (max-width: 760px) {
    .footer-tip {
        display: block; margin: 0 0 12px; font-size: 0.8rem; color: var(--text-muted);
    }
}

/* ─── Mobile nav ───
   This used to set flex-wrap:wrap, because the brand link could not shrink and
   the header overflowed. Now that .brand > a has min-width:0 the title
   ellipsizes instead, so the header stays on one row: wrapping only dropped
   the menu button onto a second line and made the bar 117px tall on the three
   pages with long tool names. */
@media (max-width: 720px) {
    .nav { gap: 10px 12px; padding: 12px 16px; padding-top: calc(12px + env(safe-area-inset-top, 0px)); }
    .nav-right { gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
    .nav-soon { display: none; }          /* hide coming-soon teaser links on small screens */
    .nav-link { padding: 7px 11px; font-size: 0.8rem; }
    .system-current { padding: 6px 10px; }
}
@media (max-width: 420px) {
    .cal-grid { gap: 4px; }
    .cal-cell { font-size: 0.78rem; min-height: 60px; padding: 3px; }
    /* Text chips can't fit a phone's day cell: each becomes a dot in the
       log's color (hollow while planned). Tapping the day lists them. */
    .cal-events { flex-direction: row; flex-wrap: wrap; gap: 3px; margin-top: 2px; }
    .cal-ev { font-size: 0; width: 0; height: 8px; padding: 0; border-radius: 50%; border-left-width: 8px; background: none !important; }
    .cal-ev.planned { width: 8px; height: 8px; border: 1.5px solid var(--text-muted) !important; }
    .cal-more { font-size: 0.55rem; line-height: 8px; }
    .cal-more-word { display: none; }
}

/* ─────────────  Home Gardening: companions + bed planner  ───────────── */
.pick-group { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); margin: 14px 2px 7px; }
.pick-group:first-child { margin-top: 4px; }
.pick-wrap { display: flex; flex-wrap: wrap; gap: 7px; }
.dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: none; }

/* Companion guide */
.companion-layout { display: grid; grid-template-columns: minmax(220px, 300px) 1fr; gap: 16px; align-items: start; }
.companion-picker { position: sticky; top: 16px; }
.plant-pick-list { max-height: 380px; overflow-y: auto; margin-top: 6px; padding-right: 4px; }
.plant-pick {
    display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text-muted);
    font: inherit; font-size: 0.84rem; cursor: pointer; transition: border-color .12s ease, color .12s ease;
}
.plant-pick:hover { border-color: var(--brand); color: var(--text); }
.plant-pick.active { border-color: var(--brand); color: var(--text); background: color-mix(in srgb, var(--brand) 16%, var(--surface-2)); font-weight: 600; }
/* Denser pills here than the filter chips elsewhere: ninety plants in a
   narrow column, so three to a row keeps the card near the detail card. */
.plant-pick-list .plant-pick { padding: 4px 9px; font-size: 0.78rem; gap: 6px; }
.plant-pick-list .plant-pick .dot { width: 8px; height: 8px; }
.plant-pick-list .pick-wrap { gap: 6px; }
.plant-pick-list .pick-group { margin: 11px 2px 6px; }
.companion-head { display: flex; align-items: center; gap: 14px; }
.companion-swatch { width: 34px; height: 34px; border-radius: 9px; flex: none; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); }
.companion-label { font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 9px; }
.companion-label.good { color: var(--ok, #6a994e); }
.companion-label.avoid { color: var(--danger); }
.companion-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.comp-chip {
    padding: 6px 12px; border-radius: 999px; font: inherit; font-size: 0.85rem; font-weight: 600;
    border: 1px solid transparent; cursor: default; color: var(--text);
}
.comp-chip[data-go] { cursor: pointer; }
.comp-chip.good { background: color-mix(in srgb, var(--ok, #6a994e) 18%, transparent); border-color: color-mix(in srgb, var(--ok, #6a994e) 40%, transparent); }
.comp-chip.avoid { background: color-mix(in srgb, var(--danger) 15%, transparent); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.comp-chip[data-go]:hover { border-color: currentColor; }

/* Add-your-own-plant form */
.custom-fields { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; }
.custom-comp-list {
    max-height: 320px; overflow-y: auto; padding: 8px 10px; border: 1px solid var(--border);
    border-radius: var(--radius-sm); background: var(--surface-2);
}
.cc-row { display: flex; align-items: center; gap: 9px; padding: 4px 2px; }
.cc-row .cc-name { flex: 1 1 auto; color: var(--text); font-size: 0.9rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-rel { flex: 0 0 104px; width: 104px; min-width: 104px; font-size: 0.84rem; padding: 5px 8px; }
.custom-comp-list .pick-group { margin: 12px 2px 5px; }
/* What is marked right now, in the same colors the guide uses for good and
   avoid. It sits outside the scroll box on purpose: the search filters rows
   away, and this is where you check what you have set. */
.cc-marks { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin: 10px 2px 0; }
.cc-marks:empty { display: none; }
.cc-marks .cc-marks-label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
.cc-marks .cc-marks-label.good { color: var(--ok, #6a994e); }
.cc-marks .cc-marks-label.avoid { color: var(--danger); }
.cc-marks .comp-chip { font-size: 0.8rem; padding: 4px 10px; }
.custom-comp-list .pick-group:first-child { margin-top: 2px; }

/* Planner controls */
.bed-controls { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px; }
.bed-field { display: flex; flex-direction: column; }
.bed-field select, .bed-field input { min-width: 92px; }
.bed-field:first-child select { min-width: 170px; }
.bed-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
.bed-actions .btn { padding: 8px 13px; font-size: 0.84rem; }

/* Add-toolbar (beds + plant) */
.gp-toolbar { display: flex; flex-wrap: wrap; gap: 22px; align-items: flex-end; }
.gp-tool-group { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; }
.gp-tool-group .btn { padding: 8px 13px; font-size: 0.84rem; }
.gp-plant-add select { min-width: 150px; }
/* Selects default to width 100%, which stretched these across the whole
   toolbar row on desktop. */
.gp-plant-add #plantSelect { width: auto; flex: 0 1 260px; }
.gp-plant-add #fillPattern { width: auto; }
.gp-plant-search { flex: 0 1 200px; min-width: 140px; }
.spacing-in { display: inline-flex; align-items: center; gap: 5px; }
.spacing-in input { width: 72px; }
.spacing-in span { color: var(--text-dim); font-size: 0.85rem; }
/* A select is as wide as its longest option, and some crop options carry a
   long "(not enough time to ...)" note. Without these, the crop pickers in the
   bed and block editors pushed the page 75 to 115px past a phone screen. */
#toolWrap select { max-width: 100%; text-overflow: ellipsis; }
.gp-edit-fields > *, .gp-bed-contents, .gp-block-crops, .gp-tool-group, .gp-mix-row, .bed-field { min-width: 0; }

/* Canvas / stage */
/* Full screen. Done with fixed positioning rather than the Fullscreen API,
   which iPhone Safari does not offer for anything but video, and a phone is
   exactly where the extra room is worth most. */
.gp-plot-fs {
    position: fixed; inset: 0; z-index: 60;
    margin: 0; border-radius: 0;
    display: flex; flex-direction: column;
    background: var(--surface);
}
.gp-plot-fs .gp-canvas-bar { flex: none; }
.gp-plot-fs .gp-stage-wrap { flex: 1 1 auto; min-height: 0; }
.gp-plot-fs .gp-stage { height: 100%; overflow: auto; }
body.gp-fs-open { overflow: hidden; }

.gp-canvas.grid-off { background-image: none; }
.gp-stage-wrap { position: relative; }
/* A wide, shallow plot leaves a stage only ~100px tall, which the warning card
   would hang out of and over the panel below. Hold the row open while it is up. */
.gp-stage-wrap.is-warned { min-height: 260px; }
/* The confirm step adds two buttons, and these labels wrap to two lines each. */
.gp-stage-wrap.is-warned:has(.gp-size-warn-actions:not([hidden])) { min-height: 330px; }
.gp-stage { position: relative; overflow-x: auto; padding: 4px 0; }
/* Oversized-plot warning. Covers the plot and eats the taps, so a field-scale
   layout cannot be nudged around by accident on a phone before the grower has
   decided they really do want to do this here. */
.gp-size-warn {
    position: absolute; inset: 0; z-index: 4;
    display: grid; place-items: center; padding: 16px;
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    backdrop-filter: blur(2px);
}
.gp-size-warn[hidden] { display: none; }
.gp-size-warn-card {
    position: relative; max-width: 320px; text-align: center;
    background: var(--surface-2); border: 1px solid var(--border-warm);
    border-radius: var(--radius-sm); padding: 20px 18px 18px;
    box-shadow: var(--shadow-sm);
}
.gp-size-warn-card p { margin: 0; color: var(--text); font-size: 0.92rem; line-height: 1.55; }
.gp-size-warn-x {
    position: absolute; top: 4px; right: 6px;
    width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
    background: none; border: none; color: var(--text-dim);
    font-size: 1.3rem; line-height: 1; cursor: pointer;
}
.gp-size-warn-x:hover { color: var(--text); }
.gp-size-warn-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.gp-size-warn-actions[hidden] { display: none; }
.gp-size-warn-actions .btn { flex: 1 1 auto; }
.gp-plot-wrap { position: relative; margin: 0 auto; padding: 4px 4px 24px 26px; box-sizing: border-box; }
.gp-canvas {
    position: relative; background: var(--surface-2);
    border: 1px solid var(--border-warm); border-radius: 6px;
    background-image:
        linear-gradient(to right, color-mix(in srgb, var(--border) 60%, transparent) 1px, transparent 1px),
        linear-gradient(to bottom, color-mix(in srgb, var(--border) 60%, transparent) 1px, transparent 1px);
    /* Let touch pan/scroll a large plot from empty space; items grab the drag. */
    touch-action: pan-x pan-y; user-select: none;
}
.gp-canvas-bar { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.gp-bar-left { display: flex; gap: 8px; flex-wrap: wrap; }
.gp-mode-btn { padding: 7px 13px; font-size: 0.84rem; }
/* Icon-only sibling of the mode buttons: square, same height as the row. */
.gp-help-btn { display: inline-flex; align-items: center; justify-content: center; padding: 7px 11px; min-height: 34px; }
.gp-compass { display: inline-flex; align-items: center; gap: 6px; padding-left: 8px; }
.gp-compass svg { width: 22px; height: 22px; }
.gp-compass-ring { fill: none; stroke: var(--border-warm); stroke-width: 1.6; }
.gp-compass-n { fill: #c8553d; }
.gp-compass-s { fill: var(--text-dim); }
.gp-seg { display: inline-flex; border: 1px solid var(--control-edge, var(--border)); border-radius: 10px; overflow: hidden; background: var(--control-bg, var(--surface)); }
.gp-seg-btn { font: inherit; font-size: 0.82rem; font-weight: 600; padding: 9px 12px; border: 0; background: transparent; color: var(--text-muted); cursor: pointer; }
.gp-seg-btn + .gp-seg-btn { border-left: 1px solid var(--control-edge, var(--border)); }
.gp-seg-btn[aria-pressed="true"] { background: color-mix(in srgb, var(--brand) 16%, transparent); color: var(--text); }
.gp-edit-sun { flex-basis: 100%; margin: 0; }
/* Bed contents + block crop list in the editor panel. */
.gp-bed-contents, .gp-block-crops { flex-basis: 100%; display: flex; flex-direction: column; gap: 8px; }
.gp-bed-contents { padding: 12px 0 2px; border-top: 1px solid var(--border-soft); }
.gp-bed-items { display: flex; flex-wrap: wrap; gap: 6px; }
.gp-bed-item {
    display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: 0.84rem; font-weight: 600;
    padding: 6px 10px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer;
}
.gp-bed-item:hover { border-color: var(--brand-light); }
.gp-bed-item .dot + .dot { margin-left: -9px; box-shadow: 0 0 0 2px var(--surface); }
.gp-bed-fill { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.gp-crop-add { width: auto; min-width: 170px; }
.gp-pattern-key { display: inline-flex; gap: 6px; }
.gp-pattern {
    display: inline-flex; align-items: center; gap: 6px; padding: 4px 9px 4px 5px; cursor: pointer;
    font: inherit; font-size: 0.78rem; font-weight: 600; color: var(--text-muted);
    background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
}
.gp-pattern svg { width: 24px; height: 24px; fill: var(--text-dim); }
.gp-pattern:hover { border-color: var(--border-warm); color: var(--text); }
.gp-pattern[aria-pressed="true"] { border-color: var(--brand); color: var(--text); background: color-mix(in srgb, var(--brand) 12%, var(--surface)); }
.gp-pattern[aria-pressed="true"] svg { fill: var(--brand); }
.gp-fill-check[hidden] { display: none; }
.gp-fill-check { display: inline-flex; align-items: center; gap: 6px; font-size: 0.86rem; color: var(--text-muted); cursor: pointer; white-space: nowrap; }
/* The bar itself wraps, but this right-hand group did not, so its six
   controls held a 358px line and pushed the planner sideways on a phone. */
.gp-zoom { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.gp-zoom-btn {
    width: 34px; height: 34px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--surface-2); color: var(--text); font: inherit; font-size: 1.1rem; font-weight: 700;
    cursor: pointer; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.gp-zoom-btn:hover { border-color: var(--brand); }
.gp-zoom-pct { min-width: 46px; text-align: center; font-size: 0.82rem; font-weight: 600; color: var(--text-muted); }
.gp-zoom-fit { padding: 7px 12px; font-size: 0.84rem; }
.gp-mode-btn.active { border-color: var(--brand-light); color: var(--brand-light); background: color-mix(in srgb, var(--brand) 16%, transparent); }
.gp-touch-note { display: none; }
@media (pointer: coarse), (max-width: 640px) { .gp-touch-note { display: block; } }
.gp-dim { position: absolute; color: var(--text-dim); font-size: 0.76rem; font-weight: 700; letter-spacing: 0.03em; pointer-events: none; }
.gp-dim-w { bottom: 4px; left: 26px; right: 4px; text-align: center; }
.gp-dim-h { left: 0; top: 4px; bottom: 24px; width: 20px; display: flex; align-items: center; justify-content: center; writing-mode: vertical-rl; transform: rotate(180deg); }

.gp-item { position: absolute; box-sizing: border-box; touch-action: none; cursor: grab; }
.gp-item.selected { z-index: 5; }
.gp-bed {
    background: color-mix(in srgb, var(--brand) 10%, transparent);
    border: 1px dashed color-mix(in srgb, var(--brand-light) 65%, transparent);
}
.gp-bed .gp-bed-dim {
    position: absolute; top: 3px; left: 5px; font-size: 0.68rem; font-weight: 700;
    color: color-mix(in srgb, var(--brand-light) 90%, var(--text)); pointer-events: none; white-space: nowrap;
}
.gp-bed.selected { border-style: solid; border-color: var(--brand-light); background: color-mix(in srgb, var(--brand) 18%, transparent); }
.gp-resize {
    position: absolute; right: -6px; bottom: -6px; width: 13px; height: 13px; border-radius: 3px;
    background: var(--brand-light); border: 2px solid var(--surface); cursor: nwse-resize; display: none; z-index: 6;
}
.gp-resize.tl { right: auto; bottom: auto; left: -6px; top: -6px; }
.gp-bed.selected .gp-resize,
.gp-group.selected .gp-resize { display: block; }
/* Fatter, easier-to-grab handle on touch devices. */
@media (pointer: coarse) {
    .gp-resize { width: 26px; height: 26px; right: -13px; bottom: -13px; border-radius: 6px; }
    .gp-resize.tl { right: auto; bottom: auto; left: -13px; top: -13px; }
}
.gp-plant {
    border-radius: 6px; border: 1px solid rgba(0,0,0,0.22); display: flex; align-items: center; justify-content: center;
    padding: 1px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.18);
}
.gp-plant-name {
    font-weight: 700; line-height: 1.06; text-align: center; max-width: 100%;
    pointer-events: none; overflow: hidden; overflow-wrap: anywhere; word-break: break-word;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}
.gp-plant.selected { outline: 2px solid var(--brand-light); outline-offset: 1px; }
.gp-plant.conflict { outline: 2px solid var(--danger); outline-offset: 1px; box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 30%, transparent); }

/* Filled plant block (a grouping) */
.gp-group {
    border-radius: 7px; border: 1px solid color-mix(in srgb, var(--gc) 70%, transparent);
    background: color-mix(in srgb, var(--gc) 16%, transparent);
}
.gp-group-dots { position: absolute; inset: 0; pointer-events: none; }
.gp-group-dot {
    position: absolute; transform: translate(-50%, -50%); border-radius: 50%;
    background: var(--gc); border: 1px solid rgba(0, 0, 0, 0.28);
}
.gp-group-dim {
    position: absolute; top: 3px; left: 5px; font-size: 0.68rem; font-weight: 700;
    color: var(--text); pointer-events: none; white-space: nowrap;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.gp-group.selected { outline: 2px solid var(--brand-light); outline-offset: 1px; border-style: solid; }
.gp-group.conflict { outline: 2px solid var(--danger); outline-offset: 1px; box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 30%, transparent); }
/* An intercropped block colours its dots per crop, so the block's own tint has
   to step back or it fights every dot sitting on it. */
.gp-group.mixed { background: color-mix(in srgb, var(--gc) 7%, transparent); border-style: dashed; }

/* Intercrop mix picker (toolbar). Takes a full line inside the wrapping tool
   group, so it sits between the fill options and the Add button rather than
   squeezing in beside them. */
.gp-mix-row { width: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 9px; }
/* Selects are width:100% site-wide, which would give this row a line each.
   Size it to its content so the row reads as one sentence. */
.gp-mix-row select { width: auto; flex: 0 1 240px; min-width: 150px; }
.gp-mix-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.gp-mix-chip {
    display: inline-flex; align-items: center; gap: 6px; padding: 5px 7px 5px 10px;
    border-radius: 999px; font-size: 0.82rem; white-space: nowrap;
    background: color-mix(in srgb, var(--mc) 16%, var(--surface-2));
    border: 1px solid color-mix(in srgb, var(--mc) 55%, transparent);
}
.gp-mix-chip .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--mc); flex: none; }
/* Each crop carries its own spacing right on the chip. The per-crop boxes used
   to live only in the editor panel, which appears after the block is created
   and further down the page, so the toolbar looked like it offered one shared
   spacing and nothing else. */
.gp-mix-chip .gp-mix-sp {
    width: 46px; padding: 2px 4px; margin: 0; text-align: right;
    font-size: 0.8rem; border-radius: 6px; background: var(--bg);
}
.gp-mix-chip .gp-mix-in { font-size: 0.72rem; color: var(--text-dim); margin-left: -2px; }
.gp-mix-x {
    background: none; border: 0; padding: 0 2px; cursor: pointer; line-height: 1;
    font-size: 1.05rem; color: var(--text-muted);
}
.gp-mix-x:hover { color: var(--danger); }
.gp-mix-warn {
    width: 100%; margin: 0; font-size: 0.85rem; color: var(--danger);
}
/* Companion-guide findings inside the editor panel */
.gp-mix-notes { width: 100%; margin: 2px 0 10px; }
.gp-mix-notes .ic-pair { margin-bottom: 10px; }

/* Locked beds ignore clicks so they can't be grabbed by accident. */
.gp-canvas.beds-locked .gp-bed { pointer-events: none; opacity: 0.85; }

/* Selected-item editor */
.gp-edit-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; font-size: 1.02rem; }
.gp-edit-link { margin-left: auto; font-size: 0.82rem; }
.gp-edit-fields { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
.gp-edit-note { color: var(--text-muted); font-size: 0.85rem; padding-bottom: 8px; }
.gp-edit-btns { margin-left: auto; display: flex; flex-wrap: wrap; gap: 8px; }

/* Right-click context menu */
.gp-context {
    position: fixed; z-index: 100; min-width: 152px; padding: 5px;
    background: var(--surface); border: 1px solid var(--border-warm); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.45);
}
.gp-context button {
    display: block; width: 100%; text-align: left; padding: 8px 11px; border: none; border-radius: 6px;
    background: none; color: var(--text); font: inherit; font-size: 0.88rem; cursor: pointer;
}
.gp-context button:hover { background: var(--surface-2); }
.gp-context button.danger { color: var(--danger); }

/* ─────────────  Native Garden Plants  ───────────── */
.np-filter { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.np-filter:last-of-type { margin-bottom: 0; }
.np-flabel { flex: 0 0 84px; margin: 0; }
.np-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 14px; }
.np-card { margin: 0; display: flex; flex-direction: column; }
.np-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.np-bot { color: var(--text-dim); font-size: 0.82rem; }
.np-colors { display: inline-flex; gap: 4px; flex: none; margin-top: 4px; }
.np-colors .dot { width: 11px; height: 11px; border: 1px solid rgba(0,0,0,0.25); }
.np-meta { color: var(--text-muted); font-size: 0.84rem; margin: 6px 0 10px; }
.np-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.np-badge {
    font-size: 0.7rem; font-weight: 700; padding: 3px 9px; border-radius: 999px;
    border: 1px solid var(--border); color: var(--text-muted); white-space: nowrap;
}
.np-badge.edible { color: #b5e48c; border-color: rgba(181,228,140,0.4); background: rgba(181,228,140,0.08); }
.np-badge.near { color: var(--brand-light); border-color: color-mix(in srgb, var(--brand) 50%, transparent); background: color-mix(in srgb, var(--brand) 12%, transparent); }
a.np-bot { text-decoration: underline dotted; text-underline-offset: 3px; }
a.np-bot:hover { color: var(--brand-light); }
.np-badge.pick { color: var(--text); border-color: color-mix(in srgb, var(--text) 30%, transparent); }
.np-badge.pollinator { color: #e58ea9; border-color: rgba(229,142,169,0.45); background: rgba(229,142,169,0.08); }
.np-badge.edible.warn { color: #f2c14e; border-color: rgba(242,193,78,0.45); background: rgba(242,193,78,0.08); }
.np-badge.water-low { color: var(--brand-light); border-color: color-mix(in srgb, var(--brand) 40%, transparent); }
.np-badge.water-high { color: var(--c-humidity); border-color: color-mix(in srgb, var(--c-humidity) 45%, transparent); }
.np-line { font-size: 0.86rem; color: var(--text-muted); line-height: 1.55; margin: 0 0 8px; }
.np-line strong { color: var(--text); }
.np-wild { margin-bottom: 0; }
.np-lock { margin: 0; text-align: center; display: flex; flex-direction: column; justify-content: center; align-items: center; border-style: dashed; }
/* Results header: count + cards/list toggle */
/* Wraps rather than squeezing. Without this the bar's children compete for a
   fixed width, and the first thing to give is button labels breaking mid-phrase
   ("+ Add a" / "sensor") while the device select truncates to "2 senso". */
.np-results-bar {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px 12px; margin: 26px 0 14px; flex-wrap: wrap;
}
/* Action buttons keep their label on one line and never shrink. */
.np-results-bar .btn { flex: none; white-space: nowrap; }
.np-results-bar .section-label { flex: 1 1 auto; }
.np-view-seg { flex: none; }
.np-view-seg button { padding: 6px 14px; font-size: 0.82rem; }
.np-disclaimer { margin: 0 2px 14px; }
/* Compact list view */
.np-list { display: flex; flex-direction: column; gap: 8px; }
.np-row {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
    padding: 11px 14px; background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.np-row-name { flex: 1 1 220px; min-width: 0; }
.np-row-name strong { color: var(--text); }
.np-row-name .np-bot { display: block; }
.np-row-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.np-lock-row { text-align: center; padding: 18px; }
@media (max-width: 640px) {
    .np-filter { flex-direction: column; gap: 6px; }
    .np-flabel { flex: none; margin: 0; }
}

/* Tally + warnings */
.tally-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px 18px; }
.tally-item { display: flex; align-items: center; gap: 9px; }
.tally-name { font-weight: 600; color: var(--text); }
.tally-num { margin-left: auto; color: var(--text-muted); font-size: 0.86rem; white-space: nowrap; }
.warn-line { margin: 0 0 8px; color: var(--text); line-height: 1.5; }
.warn-line:last-child { margin-bottom: 0; }

/* Planting calendar */
.gp-cal-head { margin-bottom: 14px; }
.gp-cal-head .field-label { display: block; }
.gp-cal-head input[type="date"] { max-width: 200px; }
.gp-cal-month { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-dim); margin: 16px 0 4px; }
.gp-cal-month:first-child { margin-top: 0; }
.gp-cal-yr { color: var(--brand-light); }
.gp-cal-row { display: grid; grid-template-columns: 54px 14px 1fr auto; align-items: center; gap: 10px; padding: 7px 0; border-top: 1px solid var(--border-soft); }
.gp-cal-date { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text-muted); font-size: 0.86rem; }
.gp-cal-crop { font-weight: 600; color: var(--text); }
.gp-cal-act { color: var(--text-muted); font-size: 0.86rem; text-align: right; }
.gp-cal-notes p { line-height: 1.5; }
.gp-cal-late { border-left: 3px solid var(--c-vpd); background: color-mix(in srgb, var(--c-vpd) 10%, transparent);
    border-radius: 8px; padding: 10px 14px; margin: 0 0 16px; font-size: 0.88rem; line-height: 1.5; color: var(--text); }
.gp-cal-late p { margin: 4px 0 0; }
.gp-cal-late .gp-cal-late-head { margin: 0; font-weight: 700; }
.gp-climate { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 18px; margin-top: 12px; }
.gp-climate .bed-field input[type="text"] { min-width: 180px; }
.gp-protect { display: inline-flex; align-items: center; gap: 8px; font-size: 0.88rem; color: var(--text); cursor: pointer; max-width: 420px; line-height: 1.35; padding-bottom: 8px; }
.gp-protect input { flex: 0 0 auto; }
/* Degree days per crop (gardencalendar.js heatPanel). */
.gp-gdd { display: grid; gap: 10px; }
.gp-gdd-row { display: grid; grid-template-columns: 150px minmax(0, 1fr); align-items: start; gap: 12px; }
.gp-gdd-cell { display: grid; gap: 4px; min-width: 0; }
.gp-gdd-bar { display: flex; height: 8px; border-radius: 4px; background: var(--border-soft); overflow: hidden; margin-top: 6px; }
.gp-gdd-bar i { display: block; height: 100%; background: var(--crop); }
.gp-gdd-bar i.is-est { background: repeating-linear-gradient(-45deg, var(--crop) 0 3px, transparent 3px 6px); opacity: 0.6; }
.gp-gdd-text { font-size: 0.82rem; color: var(--text-muted); line-height: 1.35; }
.gp-gdd-pitch { margin: 8px 0 0; padding: 10px 12px; border-left: 3px solid var(--brand); background: var(--surface-2); border-radius: var(--radius-sm); }
.gp-gdd-src { font-size: 0.85rem; }
.gp-gdd-box { flex: 1 0 100%; display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 18px; padding: 10px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.gp-gdd-box .gp-protect { padding-bottom: 8px; }
@media (max-width: 560px) { .gp-gdd-row { grid-template-columns: 1fr; gap: 2px; } }
#calFrostHint { display: block; margin-top: 6px; }

/* Phone layout. Without it every control kept its desktop size and wrapped
   wherever it ran out of room, so rows came out ragged. */
@media (max-width: 640px) {
    .bed-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
    .bed-controls .bed-field:first-child,
    .bed-controls .bed-field:nth-child(4),
    .bed-actions { grid-column: 1 / -1; }
    .bed-field select, .bed-field input, .bed-field:first-child select { min-width: 0; }
    .bed-actions { margin-left: 0; }
    .bed-actions .btn { flex: 1 1 auto; }
    .gp-climate { gap: 12px; }
    .gp-climate .bed-field, .gp-gdd-box .bed-field { flex: 1 1 100%; }
    .gp-climate .bed-field input[type="text"] { min-width: 0; }
    .gp-protect { max-width: none; }

    .gp-toolbar { gap: 18px; }
    .gp-tool-group { width: 100%; }
    .gp-tool-group > span.field-label { flex-basis: 100%; }
    .gp-tool-group .btn { flex: 1 1 calc(50% - 5px); }
    .gp-plant-search, .gp-plant-add select, .gp-plant-add #plantSelect, .gp-mix-row select { flex: 1 1 100%; min-width: 0; }
    .gp-tool-group #addPlant { flex-basis: 100%; }
    /* The Square / Diagonal buttons set this select, so on a phone it is the
       same choice shown twice. The JS sets its display inline. */
    .gp-plant-add #fillPattern { display: none !important; }

    /* Canvas bar on a six-column grid so the buttons line up in pairs. */
    .gp-canvas-bar { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; }
    .gp-bar-left, .gp-zoom { display: contents; }
    .gp-canvas-bar .btn, .gp-zoom-btn { width: auto; min-height: 36px; justify-content: center; }
    #multiToggle, #lockBeds, #zoomFit, #fullscreenBtn, #savePngBtn { grid-column: span 3; }
    #compassBtn { grid-column: span 5; order: 1; }
    #plotHelpBtn { order: 2; }
    #zoomOut, #zoomPct, #zoomIn, #zoomFit { order: 3; }
    #fullscreenBtn, #savePngBtn { order: 4; }
    .gp-zoom-pct { min-width: 0; align-self: center; }

    .gp-edit-fields { gap: 12px; }
    .gp-edit-fields > .bed-field { flex: 1 1 130px; }
    .gp-edit-fields > .bed-field:has(.gp-pattern-key),
    .gp-edit-fields > .bed-field:has(.gp-seg) { flex-basis: 100%; }
    .gp-edit-note { flex-basis: 100%; padding-bottom: 0; }
    .gp-seg { display: flex; width: 100%; }
    .gp-seg-btn { flex: 1 1 0; }
    .gp-crop-add { flex: 1 1 100%; }
    .gp-bed-fill > .btn { flex: 1 1 auto; }
    .gp-edit-btns { width: 100%; margin-left: 0; }
    .gp-edit-btns .btn { flex: 1 1 0; }

    .gp-cal-head input[type="date"] { max-width: none; }
}
.gp-cal-sub { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); margin: 22px 0 10px; }

/* Season timeline: one row per crop across the busy months (gardencalendar.js). */
.gp-tl { margin-top: 4px; }
.gp-tl-row { display: grid; grid-template-columns: 150px minmax(0, 1fr); align-items: center; gap: 12px; }
.gp-tl-head { display: grid; grid-template-columns: repeat(var(--months), 1fr); }
.gp-tl-head span { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); padding: 0 0 6px 4px; border-left: 1px solid var(--border-soft); white-space: nowrap; overflow: hidden; }
.gp-tl-head span.is-jan { border-left-color: var(--border-warm); }
.gp-tl-head i { font-style: normal; color: var(--brand-light); }
.gp-tl-body { position: relative; }
.gp-tl-body > .gp-tl-row:not(.gp-tl-overlay) { padding: 5px 0; border-top: 1px solid var(--border-soft); }
.gp-tl-name { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 0.88rem; color: var(--text); min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gp-tl-track {
    position: relative; height: 22px;
    background-image: linear-gradient(90deg, var(--border-soft) 1px, transparent 1px);
    background-size: calc(100% / var(--months)) 100%;
}
.gp-tl-grow { position: absolute; top: 50%; height: 6px; transform: translateY(-50%); border-radius: 999px; background: color-mix(in srgb, var(--crop) 35%, transparent); }
/* Perennial: the bar ends in an arrowhead at the chart's edge. */
.gp-tl-grow.is-ongoing { border-radius: 999px 0 0 999px; }
.gp-tl-grow.is-ongoing::after, .gp-tl-key i.k-grow.is-ongoing::after {
    content: ""; position: absolute; right: -1px; top: 50%; transform: translateY(-50%);
    border-style: solid; border-width: 7px 0 7px 10px; border-color: transparent transparent transparent var(--crop);
}
.gp-tl-key i.k-grow.is-ongoing { position: relative; border-radius: 999px 0 0 999px; --crop: var(--lawn-green, var(--brand)); }
.gp-tl-harvest { position: absolute; top: 3px; bottom: 3px; border-radius: 6px; background: var(--crop); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12); }
.gp-tl-mark { position: absolute; top: 50%; width: 12px; height: 12px; border-radius: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 0 2px var(--surface); }
.gp-tl-mark.is-indoor { background: var(--surface); border: 2.5px solid var(--crop); }
.gp-tl-mark.is-plant { background: var(--crop); }
.gp-tl-mark.is-caution { width: 18px; height: 18px; border-radius: 0; background: none; box-shadow: none; z-index: 1; }
.gp-caution { display: block; width: 100%; height: 100%; }
.gp-tl-key .gp-caution, .gp-cal-row .gp-caution { display: inline-block; width: 14px; height: 14px; flex: none; vertical-align: -2px; }
.gp-cal-warn { color: var(--danger); font-weight: 600; }
.gp-tl-overlay { position: absolute; inset: 0; pointer-events: none; }
.gp-tl-overlay .gp-tl-track { height: 100%; background: none; }
.gp-tl-today { position: absolute; top: -4px; bottom: 0; width: 0; border-left: 2px dashed #c8553d; }
.gp-tl-key { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 14px 0 0; font-size: 0.8rem; color: var(--text-muted); }
.gp-tl-key span { display: inline-flex; align-items: center; gap: 7px; }
.gp-tl-key i { display: inline-block; width: 12px; height: 12px; border-radius: 50%; background: var(--lawn-green, var(--brand)); }
.gp-tl-key i.k-indoor { background: transparent; border: 2.5px solid var(--lawn-green, var(--brand)); }
.gp-tl-key i.k-grow { width: 22px; height: 6px; border-radius: 999px; background: color-mix(in srgb, var(--lawn-green, var(--brand)) 35%, transparent); }
.gp-tl-key i.k-harvest { width: 22px; height: 12px; border-radius: 4px; }
.gp-tl-harvest.is-ongoing { background: color-mix(in srgb, var(--crop) 40%, transparent); box-shadow: none; }
.gp-tl-key i.k-harvest.is-ongoing { background: color-mix(in srgb, var(--lawn-green, var(--brand)) 40%, transparent); }
.gp-tl-key i.k-today { width: 0; height: 14px; border-radius: 0; background: none; border-left: 2px dashed #c8553d; }
.lawn-stats.gp-stats { margin-bottom: 14px; }
@media (max-width: 768px) {
    .gp-tl-row { grid-template-columns: 92px minmax(0, 1fr); gap: 8px; }
    .gp-stats .stat:last-child:nth-child(odd) { grid-column: span 2; }
    .gp-tl-name { font-size: 0.8rem; white-space: normal; line-height: 1.2; }
    .gp-tl-name .dot { display: none; }
    .gp-tl-head span { font-size: 0; padding-left: 2px; }
    .gp-tl-head span::first-letter { font-size: 0.66rem; }
    .gp-tl-head i { display: none; }
}
@media (max-width: 560px) {
    .gp-cal-row { grid-template-columns: 46px 12px 1fr; }
    .gp-cal-act { grid-column: 3; text-align: left; font-size: 0.82rem; padding-top: 2px; }
}

@media (max-width: 720px) {
    .companion-layout { grid-template-columns: 1fr; }
    .companion-picker { position: static; }
    .plant-pick-list { max-height: none; }
    .bed-actions { margin-left: 0; }
}

/* ── First-run Toolshop tour ─────────────────────────────────── */
body.tour-open { overflow: hidden; }

.tour-root { position: fixed; inset: 0; z-index: 9000; }

/* Four blurred panels tile around the crisp spotlight hole. Deliberately
   light: the ring and the crisp cut-out are what direct the eye, so the
   mask only has to knock the surroundings back a step. Darker than this
   and the walkthrough loses the context it is walking you through, which
   matters most on the public tour where the page IS the pitch. */
.tour-mask {
    position: fixed;
    background: rgba(10, 7, 4, 0.34);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    transition: all 0.18s ease;
}
:root[data-theme="light"] .tour-mask { background: rgba(40, 30, 15, 0.18); }
.tour-root.tour-centered .tour-mask[data-mask="right"],
.tour-root.tour-centered .tour-mask[data-mask="bottom"],
.tour-root.tour-centered .tour-mask[data-mask="left"] { display: none; }

/* Highlight ring around the spotlighted element. */
.tour-ring {
    position: fixed;
    border-radius: 12px;
    box-shadow: 0 0 0 2px var(--brand), 0 0 0 6px rgba(192, 138, 79, 0.25);
    pointer-events: none;
    transition: all 0.18s ease;
}

/* The tooltip / step card. */
.tour-pop {
    position: fixed;
    background: var(--surface-2);
    border: 1px solid var(--border-warm);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 18px 18px 16px;
    z-index: 1;
    transition: left 0.18s ease, top 0.18s ease;
}
.tour-arrow {
    position: absolute;
    width: 14px; height: 14px;
    background: var(--surface-2);
    border: 1px solid var(--border-warm);
    transform: rotate(45deg);
    margin-left: -7px;
}
.tour-arrow.up { top: -8px; border-right: none; border-bottom: none; }
.tour-arrow.down { bottom: -8px; border-left: none; border-top: none; }
.tour-count {
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-light);
    font-weight: 700;
    margin-bottom: 6px;
}
.tour-title { font-size: 1.06rem; margin: 0 0 7px; color: var(--text); }
.tour-body { font-size: 0.9rem; line-height: 1.5; color: var(--text-muted); margin: 0 0 16px; }
.tour-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
/* The CTA takes a line to itself; the opt-out keeps the left edge and the
   Back/Next pair is pushed to the right of whatever is left. */
.tour-cta { flex: 1 1 100%; justify-content: center; }
.tour-nav { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.tour-actions .btn { padding: 8px 15px; font-size: 0.85rem; }
.tour-skip {
    background: none; border: none; padding: 4px 0;
    color: var(--text-dim); font-size: 0.8rem; cursor: pointer;
    text-decoration: underline; text-underline-offset: 3px;
}
.tour-skip:hover { color: var(--text-muted); }

/* Force a card's hover-only hide button visible while the tour points at it. */
.tool-card.tour-reveal-hide .hide-btn { opacity: 1 !important; pointer-events: auto; }

@media (max-width: 560px) {
    .tour-pop { padding: 16px 15px 14px; }
    .tour-actions { flex-wrap: wrap; }
    .tour-nav { width: 100%; justify-content: flex-end; }
}

/* ── Quick views: one-tap persona filter on the Toolshop ────────
   A single select, not a pill row: five buttons wrapped onto three ragged
   lines on a phone, so one control that collapses cleanly at any width beats
   a phone-only alternate layout for the same picker. */
.quick-views {
    display: none;              /* revealed by toolfilter.js once wired */
    width: auto;
    min-width: 190px;
    max-width: 260px;
    margin: 0 0 16px;
    border-radius: 999px;
    font-weight: 600;
    cursor: pointer;
    /* A native <select> arrow sits flush against the border, and padding
       cannot move it (browsers draw it themselves, ignoring the box's
       padding). Swap it for a drawn chevron so it can be inset.
       The stroke is a literal colour, NOT currentColor: a data: URI is a
       separate document, so currentColor inside one resolves against that
       document's own initial colour (black) rather than this element's. On the
       dark theme that drew a black chevron on a dark pill, which is why the
       arrow looked missing. The # has to be percent-encoded as %23. */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23dcad77' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 16px center;
    background-size: 14px;
    padding-right: 40px;
}
:root[data-theme="light"] .quick-views {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a8692f' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
@media (max-width: 640px) {
    .quick-views { max-width: none; width: 100%; margin-bottom: 14px; }
}

/* ── Pill chips (the share modal's phone picker) ── */
.persona-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.persona-chip {
    appearance: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-muted);
    font: inherit; font-size: 0.92rem; font-weight: 600;
    padding: 10px 16px;            /* generous tap targets on mobile */
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.persona-chip:hover { border-color: var(--border-warm); color: var(--text); }
.persona-chip.active {
    background: var(--brand); border-color: var(--brand); color: #1a1206;
}

/* ── Share → phone-shortcut modal ── */
#scTool {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
    color: var(--text); font: inherit; font-size: 0.95rem; padding: 11px 14px;
}
.sc-steps { margin: 0; padding-left: 20px; color: var(--text-muted); line-height: 1.7; font-size: 0.9rem; }
.sc-steps li { margin-bottom: 6px; }
.sc-steps li::marker { color: var(--brand-light); font-weight: 700; }
.sc-steps strong { color: var(--text); word-break: break-word; }

/* ── Room mode: per-sensor cards on the Environmental Dashboard ── */
.room-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
    gap: 10px;
    margin: 0 0 18px;
}
.sensor-card {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    color: var(--text);
    font: inherit; text-align: left; cursor: pointer;
    transition: border-color 0.15s ease, transform 0.15s ease;
}
.sensor-card:hover { border-color: var(--brand); box-shadow: var(--shadow-lift); }
/* Pin (★): choose a room's top sensors when there are more than fit. Sits at
   the right of the header row; its own click pins rather than opening. */
.sensor-pin {
    margin-left: auto; flex: none; cursor: pointer; line-height: 1;
    font-size: 1.05rem; color: var(--text-dim);
    padding: 2px 4px; border-radius: 6px;
    transition: color 0.15s ease, background 0.15s ease;
}
.sensor-pin:hover { background: var(--surface-2); color: var(--brand); }
.sensor-pin.on { color: var(--brand); }
/* Show-all / show-top toggle under a room's sensor cards. Spans the grid. */
.room-toggle {
    grid-column: 1 / -1;
    justify-self: start;
    background: none; border: none; cursor: pointer;
    color: var(--brand); font: inherit; font-weight: 600; font-size: 0.85rem;
    padding: 4px 2px;
}
.room-toggle:hover { text-decoration: underline; }
/* Ghost cards fill out the last row when the sensors do not, so a two-sensor
   room stops trailing off into empty grid. Deliberately quiet: dashed and
   translucent, no hover lift, not clickable, and only the FIRST one carries
   any words - repeating the same sentence across three slots would turn a
   tidy edge into an advert. */
.sensor-card.ghost {
    background: transparent;
    border-style: dashed;
    border-color: color-mix(in srgb, var(--border) 65%, transparent);
    color: var(--text-dim);
    cursor: default;
    justify-content: center;
    align-content: center;
    text-align: center;
}
.sensor-card.ghost:hover {
    transform: none;
    box-shadow: none;
    border-color: color-mix(in srgb, var(--border) 65%, transparent);
}
.ghost-msg { font-size: 0.84rem; line-height: 1.5; opacity: 0.75; }
.ghost-msg a { color: var(--text-muted); text-decoration: underline; text-underline-offset: 2px; }
.ghost-msg a:hover { color: var(--brand-light); }
/* Every card breaks the same way. As a plain wrapping row, whether the readings
   sat beside the name or below it depended on how many there were, so a
   3-reading Climate card and a 4-reading L2A card in the same row looked like
   two different components. Readings and timestamp now always take their own
   line, and the timestamp is pinned to the bottom so cards of unequal content
   still line up. */
.sensor-card { align-content: flex-start; height: 100%; }
.sensor-card .sensor-vals { flex: 1 1 100%; }
.sensor-card .sensor-ts { flex: 1 1 100%; margin-top: auto; text-align: right; }
.sensor-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.sensor-dot.dot-ideal { background: #7fbf7f; box-shadow: 0 0 6px rgba(127,191,127,0.6); }
.sensor-dot.dot-low   { background: #6fa8dc; }
.sensor-dot.dot-high  { background: var(--danger); }
.sensor-dot.dot-off   { background: var(--text-dim); }
/* Reporting, but with no VPD to place in a band: a climate unit, or an L2A
   whose radiometer is unplugged. Deliberately NOT the ideal green, which
   would claim an in-band reading that was never taken; deliberately not the
   dim grey either, which reads as "this sensor is dead". The manage list has
   referenced dot-ok for a while and was rendering it unstyled. */
.sensor-dot.dot-ok    { background: var(--brand-light); box-shadow: 0 0 6px rgba(192,138,79,0.45); }
.sensor-name { font-weight: 700; font-size: 0.92rem; }
.sensor-type {
    font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em;
    color: var(--brand-light); border: 1px solid var(--border-warm);
    border-radius: 999px; padding: 1.5px 8px;
    /* The chip now lists a sensor set ("Climate · CO2"), so keep it on one
       line rather than letting it break around the separator. */
    white-space: nowrap;
}
/* A unit that has not reported yet: the chip is a placeholder, not a claim,
   so it drops the brand colour that makes the real labels look decided. */
.sensor-type.type-pending,
.manage-type.type-pending {
    color: var(--text-dim); border-color: var(--border-soft); font-style: italic;
}
.sensor-vals { display: flex; gap: 11px; font-size: 0.85rem; color: var(--text-muted); margin-left: auto; flex-wrap: wrap; }
.sensor-vals strong { color: var(--text); }
.sensor-wait { color: var(--text-dim); font-style: italic; }
.sensor-ts { font-size: 0.75rem; color: var(--text-dim); flex-basis: 100%; text-align: right; margin-top: -4px; }

/* ── "About this tool" explainer ──────────────────────────────
   Sits AFTER the tool so it never stands between someone and the thing they
   came to use, and collapsed so it costs nothing visually. It is plain markup
   in the page, not fetched on click: a search engine indexes content inside a
   closed <details> normally, but content that only arrives after a click is
   not reliably crawled at all. Keeping it in the HTML is the whole point. */
.tool-about { margin-top: 26px; }
.tool-about > summary {
    cursor: pointer; list-style: none; display: flex; align-items: center; gap: 9px;
    padding: 13px 18px; border: 1px solid var(--border); border-radius: 12px;
    background: var(--surface); color: var(--text);
    font-weight: 700; font-size: 0.95rem;
}
.tool-about > summary::-webkit-details-marker { display: none; }
.tool-about > summary::after {
    content: ""; width: 8px; height: 8px; margin-left: auto;
    border-right: 2px solid var(--text-dim); border-bottom: 2px solid var(--text-dim);
    transform: rotate(45deg); transition: transform 0.15s ease;
}
.tool-about[open] > summary::after { transform: rotate(-135deg); }
.tool-about > summary:hover { border-color: var(--brand); }
.tool-about .about-body {
    padding: 4px 2px 0; color: var(--text-muted); line-height: 1.65;
}
.tool-about .about-body h3 {
    color: var(--text); font-size: 1rem; margin: 22px 0 8px;
}
.tool-about .about-body h3:first-child { margin-top: 18px; }
.tool-about .about-body p { margin: 0 0 12px; }
.tool-about .about-body ul { margin: 0 0 12px; padding-left: 20px; }
.tool-about .about-body li { margin-bottom: 6px; }
.tool-about .about-body table {
    width: 100%; border-collapse: collapse; margin: 0 0 14px; font-size: 0.9rem;
}
.tool-about .about-body th, .tool-about .about-body td {
    text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border);
    /* These are prose tables, not numeric ones. The global `nowrap` that keeps
       data columns tidy makes a sentence-length cell push the table past the
       viewport on a phone, so let the text wrap here. */
    white-space: normal;
}
/* The global th style is a small uppercase label, which reads as a caption
   rather than a column heading at this size. Reset it to match the cells. */
.tool-about .about-body th {
    color: var(--text); font-weight: 700;
    font-size: 0.9rem; text-transform: none; letter-spacing: normal;
}
/* Header link that jumps down to it. */
.about-jump {
    display: inline-block; margin-top: 10px; font-size: 0.85rem;
    color: var(--brand-light); text-decoration: none; border-bottom: 1px dotted currentColor;
}
.about-jump:hover { color: var(--brand); }

/* ── Footer tool links ────────────────────────────────────────
   Real anchors to every tool that opens without an account. The hub's grid is
   built by JavaScript, so before this there was no markup path from any page
   to the tools: Search Console reported "Referring page: None detected" for
   them, having found them only through the sitemap. Clean URLs, because the
   .html form 301-redirects and there is no reason to pay that hop. */
.footer-tools {
    display: flex; flex-wrap: wrap; justify-content: center;
    gap: 6px 14px; margin: 0 auto 16px; max-width: 900px;
    padding-bottom: 16px; border-bottom: 1px solid var(--border);
}
.footer-tools-label {
    flex-basis: 100%; text-align: center; margin-bottom: 4px;
    color: var(--text-dim); font-size: 0.78rem;
    text-transform: uppercase; letter-spacing: 0.07em; font-weight: 700;
}
.footer-tools a {
    color: var(--text-muted); font-size: 0.82rem; text-decoration: none;
}
.footer-tools a:hover { color: var(--brand-light); text-decoration: underline; }

/* ── "or use the free tools" divider on the sign-in card ──────
   A visitor who does not want an account yet needs a way out of the login
   screen. Thirteen tools open without one, but the only place that was said
   was the first-visit modal, which never comes back once dismissed. */
.auth-nofuss {
    display: flex; align-items: center; gap: 12px; margin: 22px 0 14px;
}
.auth-nofuss-line { flex: 1; height: 1px; background: var(--border); }
.auth-nofuss-or {
    color: var(--text-dim); font-size: 0.78rem;
    text-transform: uppercase; letter-spacing: 0.09em; font-weight: 700;
}

/* ── Intercrop planner ────────────────────────────────────────
   A cross-section, because "root zone compatibility" and "canopy occupancy"
   are claims about space: better shown as space than described in a table. */
/* Search box plus the plants chosen, rather than the whole catalogue as chips:
   ninety of those buried the drawing they feed under a wall of pills. */
.ic-search { position: relative; max-width: 340px; margin-bottom: 14px; }
.ic-results {
    position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; right: 0;
    max-height: 300px; overflow-y: auto;
    background: var(--surface); border: 1px solid var(--border-warm);
    border-radius: var(--radius-sm); box-shadow: 0 14px 34px rgba(0,0,0,0.32);
    padding: 5px;
}
.ic-result {
    display: flex; align-items: center; gap: 9px; width: 100%;
    padding: 10px 10px; min-height: 42px; border: 0; border-radius: 8px;
    background: none; color: var(--text-muted); font: inherit; font-size: 0.9rem;
    text-align: left; cursor: pointer;
}
.ic-result:hover, .ic-result.hl { background: color-mix(in srgb, var(--brand) 16%, transparent); color: var(--text); }
.ic-result-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ic-result.picked .ic-result-name { color: var(--text); font-weight: 600; }
.ic-tag {
    flex: none; font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
    padding: 3px 8px; border-radius: 999px; border: 1px solid var(--border);
    color: var(--text-dim);
}
.ic-tag.on { color: var(--brand-light); border-color: color-mix(in srgb, var(--brand) 45%, transparent); }
.ic-results-more { margin: 4px 6px 3px; font-size: 0.78rem; color: var(--text-dim); }
.ic-noresult { padding: 10px 8px 8px; }
.ic-noresult p { margin: 0 0 10px; color: var(--text-muted); font-size: 0.88rem; }

.ic-chosen { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 16px; }
.ic-x { font-size: 1.05rem; line-height: 1; opacity: 0.6; margin: -1px 0 0 3px; }
.ic-chip:hover .ic-x { opacity: 1; }
.ic-chip {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 6px 12px; border-radius: 999px; cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text-muted); font-size: 0.85rem; font-weight: 600;
}
.ic-chip:hover { border-color: var(--brand); color: var(--text); }
.ic-chip.on { background: color-mix(in srgb, var(--brand) 18%, var(--surface-2)); border-color: var(--brand); color: var(--text); }
.ic-chip .dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }

.ic-plot { margin: 4px 0 18px; }
.ic-svg { width: 100%; height: auto; display: block; overflow: visible; }

.ic-pair {
    border: 1px solid var(--border); border-left-width: 3px;
    border-radius: 10px; padding: 13px 16px; margin-bottom: 10px;
    background: var(--surface-2);
}
.ic-pair.good   { border-left-color: #8ab98a; }
.ic-pair.avoid  { border-left-color: var(--danger); }
.ic-pair.neutral{ border-left-color: var(--border-warm); }
.ic-pair h3 {
    margin: 0 0 8px; font-size: 0.98rem; font-weight: 750; color: var(--text);
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.ic-verdict {
    font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em;
    padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border);
    color: var(--text-dim);
}
.ic-verdict.good  { color: #8ab98a; border-color: rgba(138,185,138,0.4); }
.ic-verdict.avoid { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.ic-pair ul { margin: 0; padding: 0; list-style: none; }
.ic-finding {
    display: flex; gap: 10px; align-items: baseline;
    color: var(--text-muted); line-height: 1.6; font-size: 0.9rem; margin-bottom: 7px;
}
.ic-finding:last-child { margin-bottom: 0; }
.ic-axis {
    flex: none; width: 104px; font-size: 0.68rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim);
}
.ic-finding.good .ic-axis  { color: #8ab98a; }
.ic-finding.watch .ic-axis { color: var(--brand-light); }
.ic-finding.bad .ic-axis   { color: var(--danger); }
@media (max-width: 640px) {
    .ic-finding { flex-direction: column; gap: 2px; }
    .ic-axis { width: auto; }
}

/* Large-plant scale toggle on the intercrop cross-section. */
.ic-compress {
    display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
    margin: 0 2px 10px; cursor: pointer;
    color: var(--text-muted); font-size: 0.86rem;
}
.ic-compress input { width: auto; margin: 0; accent-color: var(--brand); flex: none; }
.ic-compress .hint { margin: 0; font-size: 0.8rem; color: var(--brand-light); }

/* ── Manage sensors ───────────────────────────────────────────
   A list, not a dashboard: the destructive actions live here precisely so
   they are not next to a readout someone checks daily. */
/* While Manage is open, nothing from the dashboard shows, whatever a renderer
   does. Hiding each card by hand in enterManage lost to any render that
   finished afterwards: opening /dashboard#manage on an account with rooms
   painted the room's VPD scale under the Manage list once its data arrived.
   !important beats the inline display those renderers set. Kept: the Manage
   view itself, the modals (Settings opens from Manage) and
   the demo banner. */
body.managing #dashSection > :not(#manageWrap):not(.modal-overlay):not(#demoBanner),
body.managing #eduLede,
body.managing #eduSection { display: none !important; }
.manage-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 14px; flex-wrap: wrap;
    padding: 13px 0; border-bottom: 1px solid var(--border);
}
.manage-row:last-child { border-bottom: 0; }
.manage-main { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1 1 260px; }
.manage-name { font-weight: 700; color: var(--text); display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.manage-type {
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em;
    color: var(--text-dim); border: 1px solid var(--border); border-radius: 999px; padding: 2px 8px;
    white-space: nowrap;
}
.manage-meta { font-size: 0.8rem; color: var(--text-dim); margin-top: 3px; word-break: break-word; }
.manage-actions { display: flex; gap: 8px; flex: none; }
@media (max-width: 560px) {
    .manage-actions { flex-basis: 100%; flex-wrap: wrap; }
}

/* ── Light map designer (lighting page) ───────────────────────
   The draggable fixture layout + PPFD heat map. Same interaction
   grammar as the garden planner: absolute chips over a stage, grab
   cursor, snap on drag. The canvas below the chips is the map. */
.lm-controls { margin-bottom: 14px; }
.lm-add-pair { display: flex; gap: 8px; }
.lm-add-pair select { flex: 1; }
.lm-bar { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.lm-bar-right { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.lm-seg button { display: inline-flex; align-items: center; gap: 6px; }
.lm-values-btn { padding: 7px 13px; font-size: 0.84rem; }
/* :root and .btn-ghost outrank the light theme's ghost button colours. */
:root .btn.btn-ghost.lm-values-btn.active,
:root .btn.btn-ghost.lm-values-btn.active:hover { border-color: var(--ok); color: #fff; background: var(--ok); }
.lm-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: none; }

.lm-stagewrap { overflow-x: auto; padding: 2px 0; }
.lm-stage {
    position: relative; margin: 0 auto; border-radius: 8px; overflow: hidden;
    border: 1px solid var(--border-warm);
    touch-action: pan-x pan-y; user-select: none;
    background: rgb(24, 20, 14);   /* the map's zero-light dark, both themes */
}
.lm-stage canvas { display: block; position: absolute; inset: 0; }
.lm-border-pair { display: flex; gap: 10px; align-items: center; }
.lm-border-pair input[type="number"] { flex: 1; min-width: 0; }
.lm-border-pair input:disabled { opacity: 0.75; cursor: not-allowed; }
.lm-check { display: inline-flex; align-items: center; gap: 6px; font-size: 0.88rem; white-space: nowrap; cursor: pointer; }
#lmTileRow { margin-top: 8px; }
#lmSpaceSeg { display: flex; width: fit-content; }
#lmSpaceHint { display: block; }
/* Ruler: a band along the top and left of the map, toggled by lmRulerBtn. */
.lm-ruled { position: relative; width: fit-content; margin: 0 auto; }
.lm-ruled.on { padding: 24px 0 0 30px; }
.lm-ruler { display: none; position: absolute; inset: 0; pointer-events: none; }
.lm-ruled.on .lm-ruler { display: block; }
.lm-overlay { position: absolute; inset: 0; }
.lm-tip {
    position: absolute; z-index: 8; pointer-events: none; white-space: nowrap;
    background: rgba(16, 13, 9, 0.92); color: #f3ead9; font-size: 0.76rem; font-weight: 700;
    padding: 3px 8px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.14);
}
.lm-dims { margin-top: 8px; text-align: center; }

.lm-fx { position: absolute; box-sizing: border-box; touch-action: none; cursor: grab; z-index: 4; }
.lm-fx.ghost { opacity: 0.35; }
.lm-fx.selected { z-index: 6; }
.lm-fx-body {
    position: absolute; inset: 0; border-radius: 6px;
    border: 2px solid; background: rgba(255, 255, 255, 0.07);
    box-shadow: 0 1px 6px rgba(0,0,0,0.4);
}
.lm-fx.selected .lm-fx-body { background: rgba(255, 255, 255, 0.16); outline: 2px solid rgba(255,255,255,0.55); outline-offset: 2px; }
/* Overlapping fixtures get a warning ring + a soft pulse so the eye lands on them. */
.lm-fx.overlap .lm-fx-body { border-color: var(--danger) !important; box-shadow: 0 0 0 2px var(--danger), 0 0 10px rgba(214,74,42,0.6); animation: lm-overlap-pulse 1.4s ease-in-out infinite; }
@keyframes lm-overlap-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.55; } }
/* Zones and benches (Expert), the selection box, and the map menu */
.lm-stage:focus { outline: none; }
.lm-stage.lm-drawing { cursor: crosshair; touch-action: none; }
.lm-area { position: absolute; box-sizing: border-box; pointer-events: auto; cursor: move; touch-action: none; z-index: 3; }
.lm-zone { border: 2px dashed rgba(255,255,255,0.85); }
.lm-bench { border: 2px solid rgba(120,200,255,0.9); background: repeating-linear-gradient(45deg, rgba(120,200,255,0.12) 0 6px, transparent 6px 12px); }
.lm-area-tag {
    position: absolute; left: 8px; top: 0; pointer-events: auto; cursor: move; z-index: 7;
    padding: 2px 6px; font-size: 0.72rem; font-weight: 650; white-space: nowrap; border-radius: 0 0 6px 0;
    background: rgba(0,0,0,0.65); color: #fff; touch-action: none;
}
.lm-area-grip {
    position: absolute; width: 12px; height: 12px; pointer-events: auto; z-index: 8;
    border-radius: 3px; background: #fff; border: 1px solid rgba(0,0,0,0.5); touch-action: none;
}
.lm-grip-tl { left: -6px; top: -6px; cursor: nwse-resize; }
.lm-grip-tr { right: -6px; top: -6px; cursor: nesw-resize; }
.lm-grip-bl { left: -6px; bottom: -6px; cursor: nesw-resize; }
.lm-grip-br { right: -6px; bottom: -6px; cursor: nwse-resize; }
.lm-area.omit { border-style: dotted; border-color: rgba(255,255,255,0.5); background: repeating-linear-gradient(-45deg, rgba(0,0,0,0.25) 0 4px, transparent 4px 10px); }
.lm-area.selected { outline: 2px solid #fff; outline-offset: 2px; }
.lm-area.selected .lm-area-tag { background: #fff; color: #000; }
.lm-draw { position: absolute; pointer-events: none; z-index: 8; box-sizing: border-box; }
.lm-draw-marquee { border: 1px solid rgba(255,255,255,0.9); background: rgba(255,255,255,0.12); }
.lm-draw-zone { border: 2px dashed #fff; }
.lm-draw-bench { border: 2px solid rgba(120,200,255,0.95); background: rgba(120,200,255,0.15); }
.lm-areas { margin-top: 16px; }
.lm-dist { margin-top: 12px; }
.lm-dist:empty { display: none; }
.lm-dist-plot { position: relative; height: 56px; padding-top: 16px; touch-action: pan-y; cursor: crosshair; }
.lm-dist-plot svg { display: block; width: 100%; height: 40px; overflow: visible; }
.lm-dist-under, .lm-dist-over, .lm-dist-all { stroke-width: 1.5; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.lm-dist-under { fill: color-mix(in srgb, var(--danger) 28%, transparent); stroke: var(--danger); }
.lm-dist-over { fill: color-mix(in srgb, var(--ok) 30%, transparent); stroke: var(--ok); }
.lm-dist-all { fill: color-mix(in srgb, var(--brand) 25%, transparent); stroke: var(--brand); }
.lm-dist-base { stroke: var(--border); stroke-width: 1; vector-effect: non-scaling-stroke; }
.lm-dist-tgt { position: absolute; top: 12px; bottom: 0; width: 0; border-left: 1.5px dashed var(--text); pointer-events: none; }
.lm-dist-tgt span { position: absolute; top: -14px; left: 0; transform: translateX(-50%); font-size: 0.74rem; font-weight: 600; white-space: nowrap; color: var(--text); }
.lm-dist-avg { position: absolute; bottom: -4px; width: 8px; height: 8px; margin-left: -4px; border-radius: 50%; background: var(--text); box-shadow: 0 0 0 2px var(--surface); pointer-events: none; }
.lm-dist-guide { position: absolute; top: 16px; bottom: 0; width: 0; border-left: 1px solid var(--text-muted); pointer-events: none; }
.lm-dist-guide span { position: absolute; top: -18px; left: 6px; font-size: 0.74rem; white-space: nowrap; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 2px 7px; }
.lm-dist-guide.flip span { left: auto; right: 6px; }
.lm-dist-axis { position: relative; display: flex; justify-content: space-between; margin-top: 6px; font-size: 0.74rem; color: var(--text-muted); }
.lm-dist-avgl { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; color: var(--text); background: var(--surface); padding: 0 4px; }
.lm-dist-tgt.at-r span, .lm-dist-avgl.at-r { transform: translateX(-100%); }
.lm-dist-tgt.at-l span, .lm-dist-avgl.at-l { transform: none; }
.lm-dist-key { margin: 6px 0 0; font-size: 0.78rem; color: var(--text-muted); display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.lm-dist-key i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; }
.lm-dist-key i.u { background: var(--danger); }
.lm-dist-key i.o { background: var(--ok); margin-left: 8px; }
.lm-statrow { margin-top: 16px; }
.lm-statrow > summary { cursor: pointer; margin-bottom: 10px; width: fit-content; list-style: none; display: flex; align-items: center; gap: 8px; }
.lm-statrow > summary::-webkit-details-marker { display: none; }
.lm-statrow > summary::before { content: "+"; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border: 1px solid var(--border); border-radius: 4px; font-size: 14px; line-height: 1; color: var(--text-muted); }
.lm-statrow[open] > summary::before { content: "\2212"; }
.lm-statrow > summary:hover::before { color: var(--text); border-color: var(--text-muted); }
.lm-area-scroll { overflow-x: auto; }
.lm-area-table { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.lm-area-table th, .lm-area-table td { padding: 4px 8px; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.lm-area-table th { color: var(--text-muted); font-weight: 600; font-size: 0.78rem; }
.lm-area-table .lm-rise { width: 64px; padding: 4px 6px; }
.lm-area-band { display: inline-flex; align-items: center; gap: 8px; }
.lm-area-bar { display: inline-block; width: 56px; height: 6px; border-radius: 3px; overflow: hidden; background: color-mix(in srgb, var(--danger) 30%, transparent); }
.lm-area-bar i { display: block; height: 100%; background: var(--ok); }
.lm-areas-sum { font-weight: 400; color: var(--text-muted); text-transform: none; letter-spacing: 0; }
#lmAreasWrap > .lm-areas { margin-top: 0; }
.lm-area-del, .lm-saved-item button {
    border: none; background: none; color: var(--text-muted); font-size: 1.05rem; line-height: 1; cursor: pointer; padding: 2px 6px;
}
.lm-area-del:hover, .lm-saved-item button:hover { color: var(--danger); }
.lm-saved-list { display: flex; flex-wrap: wrap; gap: 6px; }
.lm-saved-item {
    display: inline-flex; align-items: center; gap: 2px; padding: 3px 4px 3px 10px; font-size: 0.85rem;
    border: 1px solid var(--border); border-radius: 999px; background: var(--surface-2);
}
.lm-save-row { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.lm-save-row input { flex: 1 1 160px; min-width: 0; }
.lm-keys { margin-top: 10px; font-size: 0.85rem; color: var(--text-muted); }
.lm-keys summary { cursor: pointer; }
.lm-keys ul { margin: 8px 0 0; padding-left: 18px; }
.lm-keys li { margin: 3px 0; }
.lm-overlap-warn {
    margin-top: 12px; border: 1px solid var(--danger); border-radius: 10px; padding: 10px 14px;
    background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--text);
    font-size: 0.86rem; line-height: 1.45;
}
.lm-overlap-warn strong { color: var(--danger); }
.lm-fx-tag {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; z-index: 2;
    background: rgba(16, 13, 9, 0.86); color: #f3ead9; font-size: 0.68rem; font-weight: 700;
    padding: 2px 7px; border-radius: 999px; pointer-events: none;
    box-shadow: 0 0 0 1.5px rgba(255,255,255,0.5);
}
/* Unselected fixtures show just a corner dot, so packed rows don't collide. */
.lm-fx-dot {
    position: absolute; top: 3px; left: 3px; width: 9px; height: 9px; border-radius: 50%;
    border: 1.5px solid rgba(16,13,9,0.6); pointer-events: none;
}

/* Click-a-fixture inspector under the map. */
.lm-inspector { margin-top: 12px; border: 1px solid var(--brand); border-radius: 10px; padding: 12px 14px; background: color-mix(in srgb, var(--brand) 7%, transparent); }
.lm-insp-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.lm-insp-close { border: 1px solid var(--border); background: var(--surface-2); color: var(--text); border-radius: 8px; padding: 5px 12px; font: inherit; font-size: 0.82rem; font-weight: 600; cursor: pointer; }
.lm-insp-close:hover { border-color: var(--brand); }
.lm-insp-fields { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.lm-insp-btns { display: flex; gap: 8px; align-items: flex-end; }
.lm-insp-btns .btn { padding: 8px 12px; font-size: 0.82rem; }
.lm-prim-gen { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 10px; padding: 8px 10px; border: 1px dashed var(--border); border-radius: 10px; font-size: 0.9rem; }
.lm-prim-gen span { flex: 1 1 200px; }
.lm-prim-saved-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.lm-prim-saved-row select { flex: 1 1 200px; min-width: 0; }
.lm-prim-saved .lm-save-row { margin-top: 8px; }
#lmFixBox { margin-top: 18px; }
.lm-opt-bars { margin-top: 8px; }
.lm-fix-box-title { display: none; }
#lmFixBox.lm-fix-box { border: 1.5px solid var(--border); border-radius: 12px; padding: 14px 16px 16px; }
#lmFixBox.lm-fix-box .lm-fix-box-title { display: block; margin: 0 0 12px; font-weight: 600; }
#lmFixBox.lm-fix-box .lm-prim-saved { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
@media (max-width: 600px) { #lmFixBox.lm-fix-box { padding: 12px; } }
body.ui-simple #lmFixBox.lm-fix-box { border: 0; padding: 0; }
.lm-opt { margin-top: 16px; padding: 14px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface, transparent); }
.lm-opt-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; margin-top: 12px; align-items: start; }
.lm-opt-grid select, .lm-opt-grid input { width: 100%; box-sizing: border-box; }
.lm-opt-grid .field-label { display: flex; align-items: center; min-height: 26px; margin: 0 0 6px; }
.lm-opt-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 26px; margin-bottom: 6px; }
.lm-opt-head .field-label { margin: 0; }
/* Power limit on/off: unticked, its fields grey out and the optimizer runs without one. */
.lm-opt-check { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.lm-opt-check input { width: 16px; height: 16px; margin: 0; accent-color: var(--brand); }
.lm-opt-off { opacity: 0.45; pointer-events: none; }
.lm-opt-mini { padding: 2px; gap: 2px; border-radius: 8px; margin: 0; }
.lm-opt-mini button { padding: 3px 9px; font-size: 0.76rem; border-radius: 6px; min-height: 0; }
.lm-opt-circ { display: grid; grid-template-columns: 1fr 1fr 56px; gap: 6px; }
.lm-opt-circ select { padding-left: 10px; padding-right: 26px; background-position: right 8px center; }
.lm-opt-circ input { padding-left: 10px; padding-right: 6px; }
.lm-opt-range { display: flex; align-items: center; gap: 8px; }
.lm-opt-range input { flex: 1; min-width: 0; }
.lm-opt-range span { font-size: 0.85rem; color: var(--text-muted); }
.lm-opt-aim { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.lm-opt-aim .field-label { margin: 0; }
.lm-opt-aim .hint { flex: 1 1 180px; margin: 0; }
.lm-opt-go { margin-left: auto; }
.lm-opt-span2 { grid-column: span 2; }
.lm-opt-run { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 10px; }
.lm-opt-run:empty { display: none; }
.lm-opt-bar { flex: 1 1 160px; height: 6px; border-radius: 3px; background: var(--border); overflow: hidden; }
.lm-opt-bar span { display: block; height: 100%; width: 0; background: var(--accent, var(--brand)); transition: width 0.1s linear; }
@media (max-width: 860px) { .lm-opt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .lm-opt-grid { grid-template-columns: 1fr; } .lm-opt-span2 { grid-column: auto; } .lm-opt-go { margin-left: 0; width: 100%; } }
.lm-opt-table { margin-top: 14px; }
.lm-opt-table td:nth-child(3) { font-weight: 650; }
.lm-opt-picks td:nth-child(3) { font-weight: inherit; }
.lm-opt-picks tr.on td { font-weight: 650; }
.lm-opt-picks td:first-child { width: 28px; }
.lm-opt-actions { display: flex; gap: 8px; margin-top: 12px; }
.lm-insp-check { display: flex; align-items: center; gap: 6px; font-size: 0.85rem; padding-bottom: 9px; cursor: pointer; }

.lm-legend { max-width: 320px; margin: 12px auto 0; }
.lm-legend canvas { width: 100%; height: 10px; border-radius: 5px; display: block; }
.lm-legend-labels { display: flex; justify-content: space-between; font-size: 0.74rem; color: var(--text-dim); font-weight: 600; margin-top: 4px; }

.lm-size-pair { display: flex; gap: 6px; }
.lm-spacing-pair { display: flex; gap: 8px; }
.lm-spacing-pair input { flex: 1; min-width: 0; }
.lm-spacing-pair .btn { white-space: nowrap; flex: none; }

/* Fixture roster (Supplemental Fixtures card). */
.lm-roster-head { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 4px; }
.lm-step-group { display: inline-flex; align-items: center; gap: 6px; margin-left: 8px; }
.lm-step {
    width: 26px; height: 26px; border-radius: 7px; border: 1px solid var(--border);
    background: var(--surface-2); color: var(--text); font: inherit; font-size: 0.95rem; font-weight: 700;
    cursor: pointer; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.lm-step:hover:not(:disabled) { border-color: var(--brand); }
.lm-step:disabled { opacity: 0.4; cursor: default; }
.lm-step-count { min-width: 18px; text-align: center; font-size: 0.86rem; font-weight: 700; color: var(--brand-light); }
.lm-roster-row {
    display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end;
    padding: 12px 0; border-bottom: 1px solid var(--border);
}
.lm-roster-row:last-child { border-bottom: 0; }
.lm-roster-name {
    display: inline-flex; align-items: center; gap: 7px; font-weight: 700;
    font-size: 0.88rem; color: var(--text); flex: 1 1 130px; min-width: 130px; padding-bottom: 8px;
}
.lm-band { display: flex; flex-direction: column; gap: 3px; }
/* No text-transform here: uppercasing turns µmol into "Mmol". */
.lm-band label { font-size: 0.7rem; font-weight: 700; color: var(--text-dim); letter-spacing: 0.02em; }
.lm-band input { width: 76px; padding: 8px 9px; }
.lm-roster-del { padding: 8px 12px; font-size: 0.82rem; flex: none; }
@media (max-width: 640px) {
    .lm-band input { width: 64px; }
    .lm-roster-name { flex-basis: 100%; padding-bottom: 0; }
}

/* Locked teaser: a soft fake glow, no real math behind it. */
.lm-teaser { position: relative; border-radius: 10px; overflow: hidden; padding: 38px 20px; text-align: center; }
.lm-teaser-glow {
    position: absolute; inset: 0; opacity: 0.5; filter: blur(2px);
    background:
        radial-gradient(circle at 30% 40%, rgba(255, 190, 90, 0.75), transparent 42%),
        radial-gradient(circle at 70% 55%, rgba(255, 140, 60, 0.65), transparent 45%),
        radial-gradient(circle at 52% 80%, rgba(168, 12, 64, 0.55), transparent 38%),
        rgb(24, 20, 14);
}
.lm-teaser-copy { position: relative; z-index: 1; }
.lm-teaser-copy h2, .lm-teaser-copy p { text-shadow: 0 1px 8px rgba(0,0,0,0.55); }
.lm-teaser-copy h2 { color: #f6efe1; }
.lm-teaser-copy p { color: #d8cdb8 !important; }

/* ═══════════════════════════════════════════════════════════
   Mobile polish
   Phone layout should read like the desktop one, not a squeezed
   copy of it. Three things break that, and all three are here:
   a page that slides sideways, iOS zooming on every input, and
   controls too small to hit with a thumb.
   ═══════════════════════════════════════════════════════════ */

/* Label / value readouts. Two columns of prose, so the global numeric
   `nowrap` only serves to push them off-screen. */
.kv-table td, .kv-table th { white-space: normal; }
/* Partner recipe tables: three or four short columns that have to fit a phone
   without scrolling, so tighter padding and a product name that wraps. */
.pr-table td, .pr-table th { padding: 10px 10px; }
.pr-table td:first-child { white-space: normal; }

@media (max-width: 640px) {
    /* iOS Safari zooms the page whenever a focused control renders below
       16px, and it does not zoom back out. Every field on the site was
       0.92rem, so tapping any input left the layout stranded mid-zoom.
       16px is the threshold, not a preference. */
    select,
    textarea,
    input[type="text"], input[type="search"], input[type="date"],
    input[type="datetime-local"], input[type="number"],
    input[type="email"], input[type="password"], input[type="tel"] {
        /* `!important` because several components set their own smaller size
           by class, and class beats element here. The 16px floor is a device
           constraint rather than a style choice, so it has to win. */
        font-size: 16px !important;
    }

    /* A select is as wide as its column but its options are not: at two
       columns on a phone the longer labels ("Coco coir / perlite blend") get
       cut mid-word. Give any field holding one the full row. */
    .settings-grid > *:has(> select) { grid-column: 1 / -1; }

    /* A range input carries a default intrinsic width that is wider than a
       phone-sized grid cell, so it leans a few pixels into the gap. */
    input[type="range"] { max-width: 100%; min-width: 0; }
    .settings-grid input[type="range"] { width: 100%; }
}

@media (pointer: coarse) {
    /* Icon-only controls are drawn at their glyph size, which is well under
       a fingertip. Grow the hit area without moving the glyph. */
    .modal-close, .drawer-x, .row-del, .row-lock {
        min-width: 40px; min-height: 40px;
        display: inline-flex; align-items: center; justify-content: center;
    }
    /* Same treatment for the reorder controls. They are 22-26px squares on a
       desktop, which is fine for a cursor and a coin flip for a thumb. The
       min-* pair beats the fixed width/height in the base rules, so the glyph
       stays put and only the box grows. */
    .arrange-ctrls button, .section-arrange button, .log-move button {
        min-width: 40px; min-height: 40px;
    }
    input[type="checkbox"], input[type="radio"] {
        width: 24px; height: 24px;
    }
    /* A range is grabbed by its thumb, so the thumb is the target. The track
       grows with it to keep the thumb centred on the rail. */
    .dual-range { height: 44px; }
    .dual-range input[type="range"] { height: 44px; }
    .dual-range input[type="range"]::-webkit-slider-thumb { width: 28px; height: 28px; }
    .dual-range input[type="range"]::-moz-range-thumb { width: 28px; height: 28px; }
    /* The card's hide control is opacity:0 until hover, which a touch screen
       never delivers: invisible, but still tappable, so the only way to meet
       it was to hide a tool by accident. Same treatment the hero already
       gets, dimmed rather than absent. */
    .tool-card .hide-btn { opacity: 0.55; }
    /* The segmented pickers are the most-tapped things in the app: the
       dashboard's Air/RH and 6H..1W sit right under the chart and get poked
       every time someone looks at it. At 34px they were a coin flip for a
       thumb, and worse for a gloved one in a greenhouse. Padding, not height,
       so the label stays centred and the control still wraps to two rows. */
    .seg button { min-height: 44px; padding-top: 0; padding-bottom: 0; }
    /* Same for the graph's band toggles, which were smaller still at 31px. */
    .band-chip { min-height: 44px; }
}

/* ─────────────────────────────────────────────────────────────
   Public guided walkthrough (/fertilizer-calculator-tour)

   Each step is a mock of a real signed-in page, so it gets framed
   as a screen: a browser-chrome strip with the URL it stands in for
   and a "Sample data" tag. Without the frame the sample numbers read
   as the visitor's own, which is the one thing this page must not do.
   ───────────────────────────────────────────────────────────── */
.tour-launch { border-color: var(--brand); }
.tour-launch-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 14px; flex-wrap: wrap; margin-bottom: 16px;
}
.tour-launch-head .btn { flex: none; }
.tour-rail { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; }
.tour-rail .flow-arrow { align-self: center; }
.tour-rail-step {
    flex: 1 1 200px; display: flex; align-items: center; gap: 11px;
    padding: 12px 14px; text-align: left; cursor: pointer;
    background: var(--surface-2); color: inherit; font: inherit;
    border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
    transition: border-color 0.15s, background 0.15s;
}
.tour-rail-step:hover { border-color: var(--brand); background: var(--surface-3); }
.tour-rail-step.is-on { border-color: var(--brand); background: var(--surface-3); }
/* .step-no is only styled under .sys-step, so the rail restates it. */
.tour-rail-step .step-no {
    flex: none; display: grid; place-items: center;
    width: 24px; height: 24px; border-radius: 50%;
    font-size: 0.74rem; font-weight: 800; color: #1a120a;
    background: linear-gradient(150deg, var(--brand-light), var(--brand-dark));
}
.tour-rail-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tour-rail-text strong { font-size: 0.93rem; color: var(--text); }
.tour-rail-text span { font-size: 0.78rem; color: var(--text-dim); }

.demo-screen {
    margin: 22px 0 0; border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm); overflow: hidden; background: var(--surface);
}
.demo-screen[hidden] { display: none; }
.demo-chrome {
    display: flex; align-items: center; gap: 7px;
    padding: 9px 13px; background: var(--surface-3);
    border-bottom: 1px solid var(--border-soft);
}
.demo-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--border-soft); flex: none; }
.demo-url {
    margin-left: 8px; font-size: 0.78rem; color: var(--text-dim);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.demo-badge {
    margin-left: auto; flex: none; font-size: 0.7rem; font-weight: 700;
    letter-spacing: 0.04em; text-transform: uppercase;
    color: var(--brand-light); border: 1px solid var(--brand);
    border-radius: 999px; padding: 2px 9px;
}
/* The mock pages carry their own cards, so the frame only pads them. */
.demo-body { padding: 4px 18px 20px; }
/* The mocks use h2 where the real pages use h1, so the page hierarchy
   stays honest; restate the h1 look so they still read as page titles. */
.demo-body .page-head h2 {
    margin: 0 0 5px; font-size: clamp(1.4rem, 3vw, 1.8rem);
    font-weight: 750; letter-spacing: -0.02em;
}
@media (max-width: 640px) {
    .demo-body { padding: 4px 11px 16px; }
    .tour-rail .flow-arrow { display: none; }
}

/* ─────────────────────────────────────────────────────────────
   Printable mixing sheet (#printSheet, calculator page)

   Hidden on screen; @media print hides everything else and shows
   only this, so the browser's "Save as PDF" produces a branded
   sheet instead of a screenshot of the dark app. Colors are
   hard-coded rather than themed: this always lands on paper.
   ───────────────────────────────────────────────────────────── */
.print-sheet { display: none; }

@media print {
    @page { margin: 14mm 12mm; }
    body > *:not(.print-sheet) { display: none !important; }
    /* <html> carries the theme colour on screen, so it has to be unpainted
       here too - otherwise "print backgrounds" floods every page espresso. */
    html, body { background: #fff !important; }
    html { color-scheme: light; }
    .print-sheet {
        display: block;
        background: #fff; color: #241c14;
        font-size: 10.5pt; line-height: 1.45;
        -webkit-print-color-adjust: exact; print-color-adjust: exact;
    }
    .print-sheet a { color: inherit; text-decoration: none; }

    .ps-head {
        display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
        padding-bottom: 10px; border-bottom: 2.5pt solid #a8692f;
    }
    .ps-brand { display: flex; align-items: center; gap: 9px; }
    .ps-brand img { width: 30px; height: auto; }
    .ps-brand-name { display: block; font-size: 15pt; font-weight: 750; letter-spacing: -0.01em; color: #7a4a1d; }
    .ps-brand-sub { display: block; font-size: 7.5pt; letter-spacing: 0.08em; text-transform: uppercase; color: #8a7663; }
    .ps-stamp { text-align: right; }
    .ps-stamp strong { display: block; font-size: 11pt; }
    .ps-stamp span { font-size: 8.5pt; color: #8a7663; }

    .ps-title { margin: 14px 0 2px; font-size: 17pt; font-weight: 750; letter-spacing: -0.02em; color: #241c14; }
    .ps-lede { margin: 0 0 10px; font-size: 9pt; font-weight: 600; color: #8a5a29; }
    .ps-h2 {
        margin: 18px 0 8px; font-size: 8pt; font-weight: 700;
        letter-spacing: 0.13em; text-transform: uppercase; color: #8a5a29;
        border-bottom: 0.5pt solid #e2d6c6; padding-bottom: 4px;
    }
    .ps-note { margin: 0 0 10px; font-size: 8.5pt; color: #6b5a49; }

    /* Run context: what this sheet was calculated for. */
    .ps-chips { display: flex; flex-wrap: wrap; gap: 6px 8px; margin-bottom: 4px; }
    .ps-chip {
        border: 0.5pt solid #e2d6c6; background: #faf6f0;
        border-radius: 4pt; padding: 4px 9px; font-size: 8.5pt;
    }
    .ps-chip span { color: #8a7663; margin-right: 6px; text-transform: uppercase; letter-spacing: 0.06em; font-size: 7pt; }

    .ps-table { width: 100%; border-collapse: collapse; font-size: 9pt; }
    /* The app's global table styling is dark-theme and centered; restate every
       inherited property here so the sheet stays a paper document. */
    .ps-table th {
        position: static; background: none; text-align: left;
        font-size: 7.5pt; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em;
        color: #8a7663; border-bottom: 0.5pt solid #e2d6c6; padding: 5px 8px 5px 0;
    }
    .ps-table td, .ps-table td:first-child {
        padding: 4px 8px 4px 0; text-align: left; color: #241c14;
        background: none; border-bottom: 0.5pt solid #f0e8dd;
    }
    .ps-table tbody tr:last-child td { border-bottom: 0.5pt solid #f0e8dd; }
    .ps-table .ps-num { text-align: right; font-variant-numeric: tabular-nums; padding-right: 14px; }
    .ps-sym { color: #8a7663; font-size: 8pt; }
    .ps-tone { font-size: 8pt; font-weight: 700; }
    .ps-tone-ideal { color: #2f6b3a; }
    .ps-tone-low { color: #8a5a29; }
    .ps-tone-high { color: #9b3b2f; }
    .ps-tone-none { color: #8a7663; font-weight: 500; }

    /* The numbers worth reading before anything else. */
    .ps-stats {
        display: flex; flex-wrap: wrap; gap: 0;
        border: 0.5pt solid #e2d6c6; border-radius: 5pt; background: #faf6f0;
        margin: 10px 0 4px; overflow: hidden;
    }
    .ps-stat { flex: 1 1 22%; padding: 7px 11px; border-left: 0.5pt solid #e8ddcd; }
    .ps-stat:first-child { border-left: 0; }
    .ps-stat span {
        display: block; font-size: 6.5pt; font-weight: 700; letter-spacing: 0.07em;
        text-transform: uppercase; color: #8a7663;
    }
    .ps-stat strong { display: block; font-size: 12pt; font-weight: 700; color: #241c14; }

    /* The ingredient table: a weigh-out list, so the amount column carries the
       weight and everything else stays out of its way. */
    .ps-ing .ps-no { width: 3%; color: #a8692f; font-weight: 700; }
    .ps-ing .ps-name { font-weight: 700; }
    .ps-ing .ps-name span { display: block; font-weight: 400; font-size: 7.5pt; color: #8a7663; }
    .ps-ing .ps-dim { color: #6b5a49; font-size: 8pt; }
    .ps-ing .ps-strong { font-weight: 700; }
    .ps-ing .ps-extra td { color: #6b5a49; }
    .ps-ing .ps-extra .ps-no { color: #b5a693; }
    /* A foliar spray under its nutrient in the profile: a note, not a result */
    .ps-spray-sub td { padding-top: 0; font-size: 7.5pt; color: #6b5a49; font-style: italic; }
    .ps-ing .ps-total td {
        background: #f6ece0; font-weight: 700; color: #241c14;
        border-bottom: 0; padding-top: 6px; padding-bottom: 6px;
    }

    /* Result against target, on one axis: the track runs to twice the target
       with the dotted rule at target. */
    .ps-bar-cell { width: 22%; }
    .ps-bar {
        position: relative; display: block; height: 7pt; width: 100%;
        background: #efe7d7; border-radius: 3pt; overflow: hidden;
    }
    .ps-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 3pt; }
    .ps-fill-ideal { background: #4f7a4f; }
    .ps-fill-low { background: #a8692f; }
    .ps-fill-high { background: #9b3b2f; }
    .ps-fill-none { background: #c3b4a0; }
    .ps-bar-target { position: absolute; left: 50%; top: -1pt; bottom: -1pt; width: 0.8pt; background: #a8692f; }

    /* Where each nutrient came from: one stacked bar per nutrient, one colour
       per product, held down the whole chart. */
    .ps-legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 0 0 10px; }
    .ps-key { display: inline-flex; align-items: center; gap: 5px; font-size: 8pt; color: #4a3d2f; }
    .ps-key i { width: 8pt; height: 8pt; border-radius: 2pt; display: inline-block; }
    .ps-stacks { margin-bottom: 8px; }
    .ps-srow { display: flex; align-items: center; gap: 9px; padding: 2.5px 0; }
    .ps-srow-lab { flex: 0 0 26%; font-size: 8.5pt; font-weight: 600; color: #241c14; }
    .ps-srow-lab i { font-style: normal; font-weight: 600; font-size: 7.5pt; color: #8a7663; }
    .ps-stack {
        flex: 1; display: flex; height: 9pt; border-radius: 3pt;
        background: #efe7d7; overflow: hidden;
    }
    .ps-stack i { display: block; height: 100%; box-shadow: 0.4pt 0 0 #fdfaf3 inset; }
    .ps-srow-val { flex: 0 0 14%; text-align: right; font-size: 8pt; font-weight: 600; color: #6b5a49; }

    /* Water, pH, tanks: a titled box of label/value rows. */
    .ps-panels { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 6px; }
    .ps-panel {
        flex: 1 1 46%; min-width: 44%; break-inside: avoid; page-break-inside: avoid;
        background: #faf6f0; border: 0.5pt solid #e2d6c6; border-left: 2.5pt solid #a8692f;
        border-radius: 4pt; padding: 9px 12px;
    }
    .ps-panel h3 {
        margin: 0 0 6px; font-size: 10pt; font-weight: 700; color: #241c14;
        display: flex; justify-content: space-between; gap: 8px;
    }
    .ps-ratio { font-size: 8pt; font-weight: 600; color: #8a7663; }
    .ps-panel-sub {
        margin: 8px 0 3px; font-size: 6.5pt; font-weight: 700; letter-spacing: 0.07em;
        text-transform: uppercase; color: #a8692f;
    }
    .ps-panel-note { margin: 7px 0 0; font-size: 8pt; color: #6b5a49; }
    .ps-inline { margin: 0; font-size: 8.5pt; color: #241c14; }
    .ps-inline span { display: inline-block; margin: 0 9px 2px 0; }
    .ps-inline b { color: #8a7663; font-weight: 700; }
    .ps-kv {
        display: flex; justify-content: space-between; gap: 10px;
        padding: 3px 0; border-bottom: 0.5pt solid #efe6d8; font-size: 8.5pt;
    }
    .ps-kv:last-child { border-bottom: 0; }
    .ps-kv span { color: #6b5a49; }
    .ps-kv strong { color: #241c14; font-weight: 700; font-variant-numeric: tabular-nums; }
    .ps-warn { margin: 7px 0 0; font-size: 8pt; color: #6b5a49; }
    .ps-warn.is-high { color: #9b3b2f; font-weight: 700; }

    /* Water/pH and the charts get their own sheet, ahead of the steps. */
    .ps-page { break-before: page; page-break-before: always; }

    /* The steps start their own page: growers post them in the mix room on
       their own, away from the targets and the profile table. The small
       header keeps that loose page identifiable. */
    .ps-steps-page { break-before: page; page-break-before: always; }
    .ps-page-head {
        display: flex; justify-content: space-between; gap: 12px;
        padding-bottom: 6px; margin-bottom: 4px;
        border-bottom: 1.5pt solid #a8692f;
        font-size: 8pt; color: #8a7663;
    }
    .ps-page-brand { font-weight: 700; color: #7a4a1d; letter-spacing: 0.04em; }

    /* One block per reservoir / concentrate tank / acid line. Keep a block
       whole on one page: half a mixing procedure is a mixing mistake. */
    .ps-block {
        break-inside: avoid; page-break-inside: avoid;
        border: 0.5pt solid #e2d6c6; border-left: 2.5pt solid #a8692f;
        border-radius: 4pt; padding: 10px 13px; margin-bottom: 10px;
    }
    .ps-block-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
    .ps-block-head h3 { margin: 0; font-size: 11pt; font-weight: 700; }
    .ps-step-n {
        flex: none; width: 17px; height: 17px; border-radius: 50%;
        background: #a8692f; color: #fff; font-size: 8pt; font-weight: 700;
        display: inline-flex; align-items: center; justify-content: center;
    }
    .ps-block-tag { margin-left: auto; font-size: 8pt; color: #8a7663; }
    .ps-steps { margin: 0; padding-left: 1.3em; font-size: 9.5pt; color: #3d3226; }
    .ps-steps li { padding: 2.5px 0; }
    .ps-steps li::marker { color: #a8692f; font-weight: 700; }
    .ps-steps strong { color: #241c14; font-weight: 700; font-variant-numeric: tabular-nums; }
    /* A box to tick: this sheet is worked through with a pen in hand. */
    .ps-box {
        display: inline-block; width: 8pt; height: 8pt; margin-right: 7px;
        border: 0.8pt solid #c9b99f; border-radius: 1.5pt; vertical-align: baseline;
    }

    .ps-safety {
        break-inside: avoid; page-break-inside: avoid;
        margin-top: 12px; padding: 9px 12px; border-radius: 4pt;
        background: #faf1e6; border: 0.5pt solid #e2d6c6;
        font-size: 8.5pt; color: #4a3d2f;
    }
    .ps-safety strong { color: #241c14; }

    /* What actually happened, in the grower's own hand. A recipe is a plan
       until somebody writes down what the meter read, so the log gets its own
       sheet and boxes big enough to write in. (The PDF makes these same boxes
       fillable; on paper they are ruled.) */
    .ps-log-page { display: flex; flex-direction: column; min-height: 8in; }
    .ps-log { display: flex; flex-wrap: wrap; gap: 14px 18px; }
    .ps-log-f { flex: 1 1 44%; min-width: 42%; }
    .ps-log-f span, .ps-log-notes span {
        display: block; font-size: 6.5pt; font-weight: 700; letter-spacing: 0.07em;
        text-transform: uppercase; color: #8a7663; margin-bottom: 4px;
    }
    .ps-log-f i {
        display: block; height: 26pt; border: 0.8pt solid #ded2bc;
        border-radius: 3pt; background: #fbf7ef;
    }
    .ps-log-notes { margin-top: 16px; flex: 1; display: flex; flex-direction: column; }
    .ps-log-notes i {
        display: block; flex: 1; min-height: 150pt;
        border: 0.8pt solid #ded2bc; border-radius: 3pt; background: #fbf7ef;
        background-image: repeating-linear-gradient(#fbf7ef 0 25pt, #ece2d0 25pt 25.7pt);
    }

    .ps-foot {
        display: flex; justify-content: space-between;
        margin-top: 14px; padding-top: 7px; border-top: 0.5pt solid #e2d6c6;
        font-size: 7.5pt; color: #8a7663;
    }
}

/* ─────────────  Tree Planting Tool  ───────────── */
/* A long page of short answers, so every panel is the same shape: a row of
   figures at the top, the reasoning under it. The picker is the only thing
   that scrolls independently. */
/* Input rows are a grid, not a flex row. Flexed and content-sized, the four
   site fields came out 187, 139, 96 and 92px wide and the row trailed off into
   whitespace; worse, a text input is 44px tall against a select's 41, so
   bottom-aligning them staggered every label by 3px. Equal tracks fix the
   widths, and aligning to the top of the cell puts the labels on one line and
   leaves the 3px where nobody reads. Tracks rather than columns because these
   rows hold two, three or four fields depending on the panel and the stock
   type, and a fixed count leaves a hole or a half-width select behind. */
.tp-site, .tp-stock {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 16px;
    align-items: start;
}
.tp-site .bed-field input, .tp-site .bed-field select,
.tp-stock .bed-field input, .tp-stock .bed-field select { width: 100%; }
.tp-check { display: inline-flex; align-items: center; gap: 8px; color: var(--text-muted); font-size: 0.88rem; cursor: pointer; }
/* Its own row under the fields: pushed into a track it reads as a fifth field
   with a missing label. */
.tp-site .tp-check { grid-column: 1 / -1; }
.tp-check input { flex: none; }

.tp-layout { display: grid; grid-template-columns: minmax(240px, 320px) 1fr; gap: 16px; align-items: start; }
/* The right of this row: species card, then when-to-plant. min-width:0 because
   a grid track sized 1fr still refuses to shrink below its content, and the
   window panel's two blocks would push the picker narrower than its minmax. */
.tp-detail-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.tp-detail-col .section-label { margin: 4px 0 0; }
.tp-picker { position: sticky; top: 16px; }
.tp-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.tp-tab {
    padding: 5px 11px; border-radius: 999px; font: inherit; font-size: 0.78rem; font-weight: 600;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--text-muted);
    cursor: pointer; transition: border-color .12s ease, color .12s ease;
}
.tp-tab:hover { border-color: var(--brand); color: var(--text); }
.tp-tab.on { border-color: var(--brand); color: var(--text); background: color-mix(in srgb, var(--brand) 16%, var(--surface-2)); }

.tp-list { max-height: 58vh; overflow-y: auto; margin-top: 8px; padding-right: 4px; display: flex; flex-direction: column; gap: 4px; }
.tp-pick {
    display: flex; flex-direction: column; align-items: flex-start; gap: 1px; width: 100%;
    padding: 8px 11px; border-radius: var(--radius-sm); text-align: left;
    background: transparent; border: 1px solid transparent; color: var(--text-muted);
    font: inherit; cursor: pointer; position: relative;
}
.tp-pick:hover { background: var(--surface-2); border-color: var(--border); color: var(--text); }
.tp-pick.on { background: color-mix(in srgb, var(--brand) 14%, var(--surface-2)); border-color: var(--brand); color: var(--text); }
.tp-pick-name { font-weight: 600; font-size: 0.92rem; color: inherit; }
.tp-pick-meta { font-size: 0.76rem; color: var(--text-dim); }
/* Sits between the search box and the list, so it reads as a second filter on
   the same list rather than a site input like the ones above. */
.tp-only-rec { padding: 8px 2px 0; font-size: 0.82rem; }
/* Result count, directly over the list it counts. Three filters (the tabs, the
   search and the zone) can each empty this list, and without a count the only
   signal that one of them is on is a list that looks short. */
.tp-count {
    margin: 10px 2px 0; font-size: 0.76rem; font-weight: 600;
    color: var(--text-dim); letter-spacing: 0.01em;
}
.tp-hidden-note { margin: 8px 2px 0; font-size: 0.76rem; }
/* Out of zone stays selectable once "Only show recommended" is off: someone
   planting in a protected courtyard or a pot knows something the ZIP does not.
   Shown that way it is dimmed, not removed. */
.tp-pick.out { opacity: 0.55; }
.tp-zone-flag {
    position: absolute; top: 8px; right: 10px; font-size: 0.66rem; font-weight: 700;
    color: var(--danger); white-space: nowrap;
}

.tp-detail-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; margin-bottom: 16px; }
.tp-detail-head h2 { margin: 0; font-size: 1.3rem; font-weight: 750; flex: 1 1 auto; }
.tp-cat { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--brand); flex: 1 1 100%; }
/* The out-of-zone case is a sentence ("Rated zones 4 to 8, outside zone 10"),
   not a word, so this chip wraps. Held on one line it runs past a 360px
   viewport, which is the whole reason check-ui flags nowrap. */
.tp-verdict {
    font-size: 0.74rem; font-weight: 700; padding: 4px 10px; border-radius: 999px;
    border: 1px solid var(--border); color: var(--text-muted); max-width: 100%;
}
.tp-verdict.good { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); background: color-mix(in srgb, var(--ok) 12%, transparent); }
.tp-verdict.bad { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); background: color-mix(in srgb, var(--danger) 12%, transparent); }

.tp-facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px 18px; margin: 0 0 16px; }
.tp-facts dt { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); margin-bottom: 2px; }
.tp-facts dd { margin: 0; color: var(--text); font-size: 0.92rem; }

.tp-callout {
    padding: 12px 14px; border-radius: var(--radius-sm); background: var(--surface-2);
    border: 1px solid var(--border); border-left: 3px solid var(--brand);
    color: var(--text-muted); font-size: 0.89rem; line-height: 1.55; margin-bottom: 10px;
}
.tp-callout:last-child { margin-bottom: 0; }
.tp-callout strong { color: var(--text); }
.tp-callout.warn { border-left-color: var(--danger); }

/* Planting window: the answer, then the fallback, then why. */
.tp-window { display: flex; flex-wrap: wrap; gap: 16px 28px; align-items: flex-start; }
.tp-window-main { flex: 1 1 300px; }
.tp-window-alt { flex: 0 1 220px; }
.tp-window strong { display: block; color: var(--text); font-size: 1.05rem; font-weight: 700; margin-top: 3px; }
.tp-window .hint { margin: 8px 0 0; }
.tp-label { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
.tp-notes { margin: 16px 0 0; padding-left: 18px; color: var(--text-muted); font-size: 0.88rem; line-height: 1.6; }
.tp-notes li { margin-bottom: 6px; }

/* Ordered steps, numbered by the browser so a reordering cannot desync them. */
.tp-steps { list-style: none; counter-reset: tp; margin: 0; padding: 0; }
.tp-steps li { counter-increment: tp; position: relative; padding: 0 0 18px 42px; }
.tp-steps li:last-child { padding-bottom: 0; }
.tp-steps li::before {
    content: counter(tp); position: absolute; left: 0; top: 0;
    width: 28px; height: 28px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--brand) 18%, var(--surface-2));
    border: 1px solid color-mix(in srgb, var(--brand) 45%, transparent);
    color: var(--brand-light); font-size: 0.8rem; font-weight: 700;
}
.tp-steps h3 { margin: 3px 0 5px; font-size: 0.98rem; font-weight: 700; color: var(--text); }
.tp-steps p { margin: 0; color: var(--text-muted); font-size: 0.89rem; line-height: 1.6; }
.tp-when { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--brand); }
.tp-links { margin: 8px 0 0 !important; display: flex; flex-wrap: wrap; gap: 14px; }
.tp-links a { font-size: 0.85rem; color: var(--brand-light); border-bottom: 1px solid color-mix(in srgb, var(--brand) 45%, transparent); }
.tp-links a:hover { color: var(--brand); border-bottom-color: var(--brand); }
.tp-steps-tight { margin-top: 20px; border-top: 1px solid var(--border-soft); padding-top: 20px; }

/* The figure row every panel opens with. */
.tp-hole-figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
.tp-figure {
    padding: 14px 12px; border-radius: var(--radius-sm); text-align: center;
    background: var(--surface-2); border: 1px solid var(--border);
}
.tp-num { display: block; font-size: 1.4rem; font-weight: 750; color: var(--brand-light); line-height: 1.15; font-variant-numeric: tabular-nums; }
.tp-cap { display: block; margin-top: 5px; font-size: 0.74rem; color: var(--text-dim); line-height: 1.35; }

.tp-diagram { display: block; width: 100%; max-width: 460px; margin: 20px auto 4px; color: var(--text-muted); }
.tp-diagram-t { fill: var(--text-dim); font-size: 10px; font-family: inherit; }

/* Perc-test verdict, and staking, which reads the same way. */
.tp-drain {
    margin-top: 18px; padding: 14px 16px; border-radius: var(--radius-sm);
    border: 1px solid var(--border); border-left: 3px solid var(--text-dim);
    background: var(--surface-2); color: var(--text-muted); font-size: 0.89rem; line-height: 1.6;
}
.tp-drain strong { color: var(--text); }
.tp-drain p { margin: 8px 0 0; }
.tp-drain.good { border-left-color: var(--ok); }
.tp-drain.slow { border-left-color: var(--brand); }
.tp-drain.fast { border-left-color: var(--c-vpd); }
.tp-drain.poor { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, var(--surface-2)); }

.tp-wires {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
    padding: 12px 15px; border-radius: var(--radius-sm); margin-bottom: 16px;
    border: 1px solid var(--border); background: var(--surface-2);
    font-size: 0.88rem; color: var(--text-muted); line-height: 1.55;
}
.tp-wires strong { color: var(--text); }
.tp-wires.ok { border-left: 3px solid var(--ok); }
.tp-wires.bad { border-left: 3px solid var(--danger); background: color-mix(in srgb, var(--danger) 8%, var(--surface-2)); }

.tp-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.tp-table th {
    text-align: left; padding: 0 12px 8px 0; font-size: 0.7rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim);
    border-bottom: 1px solid var(--border);
}
.tp-table td { padding: 10px 12px 10px 0; border-bottom: 1px solid var(--border-soft); color: var(--text-muted); line-height: 1.55; vertical-align: top; }
.tp-table td:first-child { color: var(--text); font-weight: 600; }
.tp-td-num { color: var(--brand-light) !important; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tp-table tr:last-child td { border-bottom: none; }

.tp-stages { list-style: none; margin: 18px 0 0; padding: 0; }
.tp-stages li { padding: 12px 0; border-top: 1px solid var(--border-soft); }
.tp-stages li:first-child { border-top: none; padding-top: 0; }
.tp-stage-when { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 14px; }
.tp-stage-when strong { color: var(--text); font-size: 0.94rem; }
.tp-stage-when span { color: var(--brand-light); font-size: 0.86rem; font-weight: 600; }
.tp-stages p { margin: 5px 0 0; color: var(--text-muted); font-size: 0.87rem; line-height: 1.6; }

.tp-timeline { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 22px; }
.tp-year h3 { margin: 0 0 10px; font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--brand); }
.tp-year ul { margin: 0; padding-left: 18px; color: var(--text-muted); font-size: 0.88rem; line-height: 1.6; }
.tp-year li { margin-bottom: 8px; }

@media (max-width: 860px) {
    .tp-layout { grid-template-columns: 1fr; }
    .tp-picker { position: static; }
    .tp-list { max-height: 320px; }
}
@media (max-width: 560px) {
    /* One field per row. auto-fill would still fit two 190px tracks at 430px,
       and a hardiness zone select half a phone wide is not a control. */
    .tp-site, .tp-stock { grid-template-columns: 1fr; }
    /* The Why column is the first thing to go: the distance and the number
       carry the row on their own, and three columns at this width wrap every
       cell to four lines. */
    .tp-table th:nth-child(3), .tp-table td:nth-child(3) { display: none; }
}

/* ═════════════  Guides  ══════════════════════════════════════
   The long-form half of the site. A calculator page states the number and
   the caveats that stop an expensive mistake; the mechanism, the worked
   example and the background live here, where someone arriving from a
   search has come to read rather than to compute.

   Two page shapes share this block:
       /guides           the index          web/guides.html
       /guides/<slug>    one article        web/guides/<slug>.html

   Article pages sit one directory down, so every asset they reference is an
   ABSOLUTE path. A relative "css/style.css" resolves to /guides/css/style.css
   and 404s, silently, with the page still rendering unstyled.

   The kit an article draws on, tokens only, so both themes come free:
       .g-lede      opening paragraph, larger and quieter than the body
       .g-body      the prose itself: measure, rhythm, numbered sections
       .g-callout   an aside; .warn tints it toward danger
       .g-report    label / value / meaning rows, for reading a document
       .g-range     a one-dimensional band chart (pH, EC, DLI) with targets
       .g-key       the takeaways box
       .g-tool      hands the reader the calculator for the job
   ───────────────────────────────────────────────────────────── */

/* Anchor jumps land under the sticky nav, not behind it. Scoped with :has so
   the rest of the site keeps its instant jumps. */
@media (prefers-reduced-motion: no-preference) {
    :root:has(.g-body) { scroll-behavior: smooth; }
}

/* ── Reading progress ──
   Sits above the nav (z-index 50) and is painted by guide.js. Width, not
   transform: a 1px bar has nothing to scale from. */
.g-progress {
    position: fixed; top: 0; left: 0; z-index: 60;
    width: 0; height: 3px;
    background: linear-gradient(90deg, var(--brand-dark), var(--brand-light));
    transition: width .1s linear;
}

/* ── Hub header ── */
.g-hero { margin-bottom: 28px; }
.g-eyebrow {
    margin: 0 0 10px; font-size: 0.72rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.14em; color: var(--brand);
}
.g-hero h1 {
    margin: 0 0 12px; line-height: 1.08; font-weight: 750; letter-spacing: -0.025em;
    font-size: clamp(1.9rem, 5vw, 2.8rem);
}
.g-hero p { margin: 0; max-width: 62ch; color: var(--text-muted); font-size: 1.04rem; line-height: 1.65; }

/* ── Featured guide ──
   One card, two panels: an art block that carries the category, and the
   text. Stacked on a phone, side by side once there is room for both. */
.g-feature {
    display: grid; grid-template-columns: minmax(0, 1fr);
    margin: 26px 0 8px; overflow: hidden;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: linear-gradient(180deg, rgba(255,255,255,0.015), transparent 40%), var(--surface);
    box-shadow: var(--shadow); color: var(--text);
    transition: border-color .18s ease, box-shadow .18s ease;
}
.g-feature:hover { border-color: var(--brand); box-shadow: var(--shadow-lift); color: var(--text); }
.g-feature-body { padding: clamp(20px, 3vw, 30px); }
.g-feature h2 {
    margin: 12px 0 10px; font-size: clamp(1.3rem, 3.2vw, 1.75rem);
    font-weight: 750; letter-spacing: -0.02em; line-height: 1.2;
}
.g-feature p { margin: 0 0 16px; color: var(--text-muted); line-height: 1.6; max-width: 58ch; }
@media (min-width: 860px) {
    .g-feature { grid-template-columns: 264px minmax(0, 1fr); }
}

/* The art panel. No photography anywhere on this site, so a guide is marked
   by a tinted field and its category icon. */
.g-art {
    display: grid; place-items: center; min-height: 152px; padding: 20px;
    color: var(--brand-light);
    background:
        radial-gradient(120% 90% at 20% 0%, color-mix(in srgb, var(--brand) 26%, transparent), transparent 70%),
        linear-gradient(150deg, color-mix(in srgb, var(--brand-deep) 30%, transparent), transparent 78%),
        var(--surface-2);
    border-bottom: 1px solid var(--border);
}
.g-art svg { width: 66px; height: 66px; opacity: 0.92; }
@media (min-width: 860px) {
    .g-art { border-bottom: 0; border-right: 1px solid var(--border); }
}

/* ── Category pill ──
   One accent for every category. The site is a single warm hue and a rainbow
   of topic colours would fight it; the word does the sorting. */
.g-cat {
    display: inline-block;
    padding: 5px 12px; border-radius: 999px;
    font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
    color: var(--brand); background: color-mix(in srgb, var(--brand) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--brand) 32%, transparent);
}
.g-cat.soon { color: var(--text-dim); background: var(--surface-2); border-color: var(--border); }

/* ── Card grid ── */
.g-grid {
    display: grid; gap: 18px;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 268px), 1fr));
}
.g-card {
    display: flex; flex-direction: column; gap: 11px; padding: 22px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: linear-gradient(180deg, rgba(255,255,255,0.015), transparent 40%), var(--surface);
    box-shadow: var(--shadow-sm); color: var(--text);
    transition: border-color .18s ease, box-shadow .18s ease;
}
.g-card:hover { border-color: var(--brand); box-shadow: var(--shadow-lift); color: var(--text); }
.g-card h3 { margin: 0; font-size: 1.04rem; font-weight: 700; letter-spacing: -0.015em; line-height: 1.3; }
.g-card p { margin: 0; color: var(--text-muted); font-size: 0.88rem; line-height: 1.55; }
.g-card .g-cat { align-self: flex-start; }
/* Written, not published. Greyed rather than hidden, the way the toolshop
   shows a tool it has not built yet: the shelf should read as a shelf. */
/* Dimmed, but not so far that the blurb stops being readable: on parchment
   the muted text was washing out at 0.62. The grey pill carries the state. */
.g-card.planned { box-shadow: none; background: var(--surface); opacity: 0.75; }
.g-card.planned:hover { border-color: var(--border); box-shadow: none; }
.g-meta {
    display: flex; flex-wrap: wrap; gap: 6px 14px;
    color: var(--text-dim); font-size: 0.8rem;
}
.g-meta span { display: inline-flex; align-items: center; gap: 6px; }
.g-more {
    margin-top: auto; padding-top: 2px;
    color: var(--brand-light); font-size: 0.86rem; font-weight: 600;
}

/* ── Article layout ──
   Narrower than the 1080px the tools get. A calculator is a control panel and
   wants the width; prose at that measure is tiring to read. */
.g-layout {
    display: grid; grid-template-columns: minmax(0, 1fr);
    column-gap: 34px; row-gap: 0;
    max-width: 880px; margin: 0 auto; align-items: start;
}
/* Source order is headline, contents, prose, related. That is the order a
   phone reads them in. On a wide screen the grid moves the contents into a
   rail beside the prose and lets the headline and the related cards span the
   full width, without the markup having to change. */
@media (min-width: 940px) {
    .g-layout { grid-template-columns: minmax(0, 1fr) 200px; }
    .g-head { grid-column: 1 / -1; grid-row: 1; }
    .g-article { grid-column: 1; grid-row: 2; }
    .g-toc { grid-column: 2; grid-row: 2; position: sticky; top: 86px; }
    .g-end { grid-column: 1 / -1; grid-row: 3; }
}

/* Light mode floats the article on a sheet.
   The parchment background carries leaf art (img/leaves-light.svg, fixed and
   cover). That is quiet behind a dashboard and not quiet behind six minutes of
   prose: a leaf crossing a paragraph competes with the words for the eye. A
   translucent panel in the page's own surface colour lifts the text off the art
   without flattening it away, which is why it is a mix rather than a flat fill.
   Dark mode is left alone; the same art at dark-theme contrast never reaches
   the text in the first place.
   The padding is ADDED to max-width rather than taken out of it, so the measure
   the prose is set to (880px, chosen for line length) does not change. */
:root[data-theme="light"] .g-layout {
    --g-sheet: clamp(18px, 3vw, 30px);
    max-width: calc(880px + 2 * var(--g-sheet));
    padding: var(--g-sheet);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--surface) 82%, transparent);
    box-shadow: var(--shadow-sm);
}

/* ── Article header ── */
.g-head { margin-bottom: 26px; padding-bottom: 22px; border-bottom: 1px solid var(--border); }
.g-crumbs { margin: 0 0 14px; font-size: 0.8rem; color: var(--text-dim); }
.g-crumbs a { color: var(--text-muted); }
.g-crumbs a:hover { color: var(--brand); }
.g-crumbs .sep { padding: 0 7px; opacity: 0.6; }
.g-head h1 {
    margin: 12px 0 14px; line-height: 1.12; font-weight: 750; letter-spacing: -0.025em;
    font-size: clamp(1.75rem, 4.6vw, 2.5rem);
}
/* The header spans both columns on a wide screen, so the lede needs its own
   measure or it runs the full 880px and reads twice as wide as the body. */
.g-lede { margin: 0 0 16px; max-width: 62ch; color: var(--text-muted); font-size: 1.1rem; line-height: 1.6; }

/* ── Table of contents ──
   Two shapes out of one list. On a phone it is a wrapping row of pills under
   the headline: seven jump links for about a fifth of a screen, instead of the
   third of a screen a stacked list would cost. On a wide screen it straightens
   out into the sticky rail. */
.g-toc {
    margin-bottom: 28px; padding: 16px 18px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface);
}
.g-toc-t {
    margin: 0 0 11px; font-size: 0.72rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.11em; color: var(--text-dim);
}
.g-toc ol {
    display: flex; flex-wrap: wrap; gap: 8px;
    margin: 0; padding: 0; list-style: none; counter-reset: gtoc;
}
.g-toc li { counter-increment: gtoc; }
.g-toc a {
    display: inline-block; padding: 11px 14px; border-radius: 999px;
    border: 1px solid var(--border-warm); background: var(--surface-2);
    color: var(--text-muted); font-size: 0.84rem; line-height: 1.35;
}
.g-toc a::before {
    content: counter(gtoc, decimal-leading-zero);
    margin-right: 8px; color: var(--brand); font-size: 0.74rem; font-weight: 700;
}
.g-toc a:hover { color: var(--text); border-color: var(--brand); }
.g-toc a.current {
    color: var(--brand-light); border-color: var(--brand);
    background: color-mix(in srgb, var(--brand) 12%, transparent);
}
@media (min-width: 940px) {
    .g-toc { margin-bottom: 0; padding: 18px 20px; }
    .g-toc ol { display: block; }
    .g-toc a {
        display: block; padding: 7px 0; border: 0; border-radius: 0;
        background: none; font-size: 0.87rem; line-height: 1.4;
    }
    .g-toc a::before { color: var(--text-dim); margin-right: 9px; }
    .g-toc a:hover { border-color: transparent; }
    .g-toc a.current { background: none; }
    .g-toc a.current::before { color: var(--brand); }
}

/* ── Prose ──
   Bigger and looser than the rest of the site on purpose: 15px at 1.5 is a
   dashboard, not something anyone reads for six minutes. */
.g-body { counter-reset: gsec; color: var(--text-muted); font-size: 1.05rem; line-height: 1.75; }
.g-body p { margin: 0 0 18px; }
/* Diagrams are drawn about 900px wide. On a phone that shrinks their labels
   past reading, so there they keep a minimum width and scroll sideways. */
.g-wide > div { overflow-x: auto; }
@media (max-width: 600px) { .g-wide img { min-width: 640px; } }

/* Figure images open larger in a box (guide.js). */
.g-body figure img.g-zoomable { cursor: zoom-in; }
.g-zoom {
    width: min(1100px, 94vw); max-width: none; max-height: 92vh; padding: 22px;
    background: #F7F1E6; color: #5C5247; border: 1px solid var(--border-warm);
    border-radius: 14px; box-shadow: 0 24px 70px rgba(20, 16, 12, 0.4); overflow: auto;
}
.g-zoom::backdrop { background: rgba(20, 16, 12, 0.6); backdrop-filter: blur(3px); }
.g-zoom-img img { display: block; width: 100%; height: auto; max-height: calc(92vh - 110px); object-fit: contain; border-radius: 6px; }
.g-zoom-cap { margin: 12px 4px 0; font-size: 0.95rem; text-align: center; }
.g-zoom-x {
    position: sticky; top: 0; float: right; margin: -10px -10px 0 0; z-index: 1;
    width: 36px; height: 36px; border-radius: 50%; border: 1px solid #D9CCB5;
    background: #FFFDF8; color: #2E2A24; font-size: 22px; line-height: 1; cursor: pointer;
}
.g-zoom-x:hover { background: #fff; }
@media (max-width: 600px) {
    .g-zoom { padding: 14px; }
    /* Full size on a phone, scrolled sideways inside the box. */
    .g-zoom.is-diagram .g-zoom-img { overflow-x: auto; }
    .g-zoom.is-diagram .g-zoom-img img { width: 900px; max-width: none; max-height: none; }
}
.g-body > h2 {
    margin: 42px 0 14px; color: var(--text); line-height: 1.2;
    font-size: clamp(1.3rem, 3.2vw, 1.6rem); font-weight: 750; letter-spacing: -0.02em;
    scroll-margin-top: 88px;
}
.g-body > h2::before {
    counter-increment: gsec;
    content: counter(gsec, decimal-leading-zero);
    display: block; margin-bottom: 7px;
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em; color: var(--brand);
}
.g-body h3 {
    margin: 28px 0 8px; color: var(--text);
    font-size: 1.05rem; font-weight: 700; letter-spacing: -0.01em;
}
.g-body strong { color: var(--text); font-weight: 650; }
.g-body a { color: var(--brand-light); border-bottom: 1px solid color-mix(in srgb, var(--brand) 45%, transparent); }
.g-body a:hover { color: var(--brand); border-bottom-color: var(--brand); }
.g-body ul, .g-body ol { margin: 0 0 18px; padding-left: 22px; }
.g-body li { margin-bottom: 9px; }
.g-body li::marker { color: var(--brand); }
.g-body hr { margin: 34px 0; border: 0; border-top: 1px solid var(--border); }
.g-body figcaption, .g-cap { margin: 10px 2px 0; color: var(--text-dim); font-size: 0.82rem; line-height: 1.5; }

/* ── Aside ── */
.g-callout {
    margin: 26px 0; padding: 16px 18px;
    border-left: 3px solid var(--brand); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    background: color-mix(in srgb, var(--brand) 7%, transparent);
}
.g-callout p:last-child { margin-bottom: 0; }
.g-callout-t {
    display: block; margin-bottom: 6px; color: var(--text);
    font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.11em;
}
.g-callout.warn { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 9%, transparent); }
.g-callout.warn .g-callout-t { color: var(--danger); }

/* ── Label / value / meaning rows ──
   For walking someone through a document they are holding: the line as it is
   printed, the number, and what the number is actually telling them. */
.g-report {
    margin: 26px 0; overflow: hidden;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.g-report-row {
    display: grid; grid-template-columns: minmax(0, 1fr); gap: 3px 16px;
    padding: 14px 16px; border-bottom: 1px solid var(--border-soft);
}
.g-report-row:last-child { border-bottom: 0; }
.g-report-k { color: var(--text); font-weight: 700; font-size: 0.92rem; }
.g-report-v {
    color: var(--brand-light); font-weight: 700; font-size: 0.95rem;
    font-variant-numeric: tabular-nums;
}
.g-report-m { color: var(--text-muted); font-size: 0.9rem; line-height: 1.55; }
@media (min-width: 620px) {
    .g-report-row { grid-template-columns: 156px 96px minmax(0, 1fr); align-items: baseline; }
}

/* ── Band chart ──
   One row per crop: a muted track for the whole scale, a filled span for the
   range that crop wants. Positions are inline percentages, since they are
   data rather than styling. */
.g-range { margin: 26px 0; }
.g-range-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 5px; margin-bottom: 14px; }
.g-range-k { color: var(--text); font-size: 0.9rem; font-weight: 600; }
.g-range-track {
    display: block; position: relative; height: 10px; border-radius: 999px;
    background: color-mix(in srgb, var(--text-dim) 24%, transparent);
}
.g-range-fill {
    position: absolute; top: 0; bottom: 0; border-radius: 999px;
    background: linear-gradient(90deg, var(--brand-dark), var(--brand-light));
}
.g-range-v { color: var(--text-muted); font-size: 0.86rem; font-variant-numeric: tabular-nums; }
.g-range-axis {
    display: flex; justify-content: space-between;
    color: var(--text-dim); font-size: 0.76rem; font-variant-numeric: tabular-nums;
}
@media (min-width: 560px) {
    .g-range-row { grid-template-columns: 168px minmax(0, 1fr) 88px; align-items: center; gap: 14px; }
    .g-range-axis-row .g-range-k, .g-range-axis-row .g-range-v { visibility: hidden; }
}

/* ── Takeaways ── */
.g-key {
    margin: 36px 0; padding: 22px;
    border: 1px solid var(--border-warm); border-radius: var(--radius);
    background: color-mix(in srgb, var(--brand) 6%, transparent);
}
.g-key h2 { margin: 0 0 12px; color: var(--text); font-size: 1.05rem; font-weight: 700; }
.g-key ul { margin: 0; padding-left: 20px; }
.g-key li:last-child { margin-bottom: 0; }

/* ── The calculator this section is about ── */
.g-tool {
    display: flex; flex-wrap: wrap; align-items: center; gap: 16px;
    margin: 28px 0; padding: 18px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
}
.g-tool-ico {
    display: grid; place-items: center; flex: none;
    width: 44px; height: 44px; border-radius: 11px;
    color: var(--brand-light); background: color-mix(in srgb, var(--brand) 18%, transparent);
}
.g-tool-ico svg { width: 22px; height: 22px; }
.g-tool-txt { flex: 1 1 220px; min-width: 0; }
.g-tool-txt strong { display: block; color: var(--text); font-size: 0.98rem; }
.g-tool-txt span { color: var(--text-muted); font-size: 0.88rem; }

/* ── Try it: a lab inside the article ──
   Inputs on top, readouts under them. guide-play.js fills the readouts; the
   HTML ships them filled for the default inputs, so the figure reads as a
   worked example without the script. */
.g-lab {
    margin: 28px 0; padding: 18px;
    border: 1px solid var(--border-warm); border-radius: var(--radius);
    background: linear-gradient(180deg, color-mix(in srgb, var(--brand) 8%, var(--surface)), var(--surface));
}
.g-lab-t {
    display: flex; align-items: center; gap: 8px; margin: 0 0 4px;
    color: var(--brand-light); font-size: 0.74rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.12em;
}
.g-lab-t::before {
    content: ""; width: 8px; height: 8px; border-radius: 50%;
    background: var(--brand); box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 22%, transparent);
}
.g-lab-h { margin: 0 0 14px; color: var(--text); font-size: 1rem; font-weight: 650; }
.g-lab-in { display: grid; gap: 12px; margin-bottom: 16px; }
.g-slider { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; align-items: center; }
.g-slider > span { color: var(--text-muted); font-size: 0.86rem; }
.g-slider output { color: var(--text); font-weight: 650; font-size: 0.9rem; font-variant-numeric: tabular-nums; text-align: right; }
.g-slider input[type="range"] { grid-column: 1 / -1; width: 100%; margin: 0; accent-color: var(--brand); cursor: pointer; }
.g-field { display: grid; gap: 5px; }
.g-field > span { color: var(--text-muted); font-size: 0.86rem; }
.g-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 12px; }
.g-lab-out { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.g-stat {
    padding: 12px 14px; border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--bg);
}
.g-stat-k { display: block; margin-bottom: 3px; color: var(--text-dim); font-size: 0.76rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.g-stat output, .g-stat-v { display: block; color: var(--text); font-size: 1.25rem; font-weight: 750; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.g-stat.hi { border-color: color-mix(in srgb, var(--brand) 55%, transparent); background: color-mix(in srgb, var(--brand) 10%, var(--bg)); }
.g-stat.hi output { color: var(--brand-light); }
.g-lab-note { margin: 12px 0 0; color: var(--text-muted); font-size: 0.9rem; line-height: 1.55; }
.g-lab-note output { color: var(--text); }
.g-lab .g-range { margin: 16px 0 4px; }
.g-lab .g-range-track { overflow: visible; }
.g-lab [data-band] .g-range-k { transition: color .2s ease; }
.g-lab [data-band].on .g-range-k { color: var(--brand-light); }
.g-lab [data-band]:not(.on) .g-range-fill { opacity: .45; }
.g-lab-mark {
    position: absolute; top: -4px; bottom: -4px; width: 3px; margin-left: -1.5px;
    border-radius: 2px; background: var(--text); box-shadow: 0 0 0 2px var(--bg);
    transition: left .12s ease-out;
}
.g-meter-bar { height: 12px; border-radius: 999px; background: color-mix(in srgb, var(--text-dim) 24%, transparent); overflow: hidden; margin-top: 8px; }
.g-meter-bar span { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--brand-dark), var(--brand-light)); transition: width .12s ease-out; }
.g-draw { display: block; width: 100%; height: auto; max-height: 260px; margin: 4px 0 14px; }
.g-draw .soil { fill: color-mix(in srgb, var(--brand-deep) 42%, var(--surface)); }
.g-draw .backfill { fill: color-mix(in srgb, var(--brand-deep) 25%, var(--surface-2)); }
.g-draw .ball { fill: var(--brand-dark); }
.g-draw .bark { fill: color-mix(in srgb, var(--brand-deep) 60%, var(--text-dim)); }
.g-draw .leaf { fill: color-mix(in srgb, var(--ok) 70%, transparent); }
.g-draw .grade { stroke: var(--text-muted); stroke-width: 1.2; stroke-dasharray: 4 4; fill: none; }
.g-draw text { fill: var(--text-muted); font-size: 10px; font-family: inherit; }
.g-draw .tag { fill: var(--brand-light); font-weight: 700; }

/* ── Line chart ──
   A plotted curve or two, drawn as inline SVG with the metric accents, so a
   chart about air and humidity uses the same colors as the dashboard. */
.g-chart { display: block; width: 100%; height: auto; margin: 4px 0 10px; }
.g-chart .grid { stroke: var(--border-soft); stroke-width: 1; }
.g-chart .ax { stroke: var(--border-warm); stroke-width: 1.2; }
.g-chart text { fill: var(--text-muted); font-size: 18px; font-family: inherit; }
.g-chart .sat { fill: none; stroke: var(--c-air); stroke-width: 2.6; }
.g-chart .act { fill: none; stroke: var(--c-humidity); stroke-width: 2.6; }
.g-chart .gap { fill: color-mix(in srgb, var(--c-vpd) 20%, transparent); }
.g-chart .vpd { stroke: var(--c-vpd); stroke-width: 3; stroke-linecap: round; }
.g-chart .t-sat { fill: var(--c-air); font-weight: 700; }
.g-chart .t-act { fill: var(--c-humidity); font-weight: 700; }
.g-chart .t-vpd { fill: var(--c-vpd); font-weight: 700; }
/* Seed starting timeline: weeks indoors, hardening off, the set-out day,
   and the frost date across all of them. */
.g-chart .in { fill: color-mix(in srgb, var(--c-humidity) 70%, transparent); }
.g-chart .hard { fill: color-mix(in srgb, var(--c-vpd) 70%, transparent); }
.g-chart .out { fill: var(--c-canopy); stroke: var(--bg); stroke-width: 2; }
.g-chart .frost { stroke: var(--c-air); stroke-width: 2.4; stroke-dasharray: 6 5; }
.g-chart .t-frost { fill: var(--c-air); font-weight: 700; }
.g-sow text { font-size: 22px; }
/* Degree day curves: a third line in gold, a target total, and where each
   curve crosses it. */
.g-chart .gold { fill: none; stroke: var(--c-vpd); stroke-width: 2.6; }
.g-chart .mark { stroke: var(--text-muted); stroke-width: 1.4; stroke-dasharray: 5 5; }
.g-chart .t-mark { fill: var(--text); font-weight: 650; }
.g-chart .dot-air { fill: var(--c-air); stroke: var(--bg); stroke-width: 2; }
.g-chart .dot-gold { fill: var(--c-vpd); stroke: var(--bg); stroke-width: 2; }
.g-chart .dot-blue { fill: var(--c-humidity); stroke: var(--bg); stroke-width: 2; }
/* Chill day (chill hours guide): the 32 to 45 band, the Utah band above 60, and the day's temperature. */
.g-chart .band-chill { fill: color-mix(in srgb, var(--c-humidity) 22%, transparent); }
.g-chart .band-warm { fill: color-mix(in srgb, var(--c-air) 16%, transparent); }
.g-chart .temp { fill: none; stroke: var(--text); stroke-width: 2.6; }
.g-chill text { font-size: 22px; }
.g-rates text { font-size: 26px; }

/* ── Mulder's wheel ──
   Twelve nutrients on a ring. Colour is what a line claims (blocks, helps),
   the dash is whether we found a source. guide-play.js adds .pick to the
   wheel and .on to the tapped node's lines. */
.mx-wheel { display: block; width: 100%; max-width: 540px; height: auto; margin: 0 auto; }
.mx-wheel .mx { fill: none; stroke-width: 2.6; stroke-linecap: round; transition: opacity 0.15s, stroke-width 0.15s; }
.mx-wheel .mx.block { stroke: var(--c-air); }
.mx-wheel .mx.help { stroke: var(--c-canopy); }
.mx-wheel .mx.nosrc { stroke-dasharray: 8 7; opacity: 0.5; }
.mx-wheel.pick .mx { opacity: 0.07; }
.mx-wheel.pick .mx.on { opacity: 1; stroke-width: 4; }
.mx-node { cursor: pointer; outline: none; }
.mx-node circle { fill: var(--surface); stroke: var(--border-warm); stroke-width: 2; }
.mx-node text { fill: var(--text); font-size: 25px; font-weight: 700; text-anchor: middle; pointer-events: none; }
.mx-node:hover circle, .mx-node:focus-visible circle { stroke: var(--brand); stroke-width: 3; }
.mx-node.on circle { stroke: var(--brand); stroke-width: 3; fill: color-mix(in srgb, var(--brand) 22%, var(--surface)); }
.mx-key { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 18px; margin: 10px 0 0; color: var(--text-muted); font-size: 0.86rem; }
.mx-key span { display: inline-flex; align-items: center; gap: 7px; }
.mx-key .sw { display: inline-block; width: 26px; height: 0; border-top: 3px solid var(--c-air); }
.mx-key .sw.help { border-top-color: var(--c-canopy); }
.mx-key .sw.nosrc { border-top: 3px dashed var(--text-muted); }
.mx-list { margin-top: 12px; }
@media (max-width: 480px) { .mx-lab { padding: 16px 8px; } .mx-lab .g-lab-t, .mx-lab .g-lab-h, .mx-lab .g-lab-note, .mx-lab figcaption { padding: 0 8px; } }
.mx-row.block.src .g-report-v { color: var(--c-air); }
.mx-row.help.src .g-report-v { color: var(--c-canopy); }
.mx-row.nosrc .g-report-v { color: var(--text-dim); font-weight: 600; }
.mx-src { color: var(--text-dim); font-size: 0.84rem; }

/* ── Data table ──
   A grid of numbers, tinted by band. Scrolls sideways inside its own box on
   a phone rather than pushing the page wider. */
.g-table-wrap { margin: 26px 0 0; overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); }
.g-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; font-variant-numeric: tabular-nums; }
.g-table th, .g-table td { padding: 9px 10px; text-align: center; border-bottom: 1px solid var(--border-soft); }
.g-table th { color: var(--text-muted); font-weight: 650; background: var(--surface); white-space: nowrap; }
.g-table td { color: var(--text); font-weight: 600; }
/* Row labels can be crop names, which must wrap at phone width. */
.g-table tbody th { white-space: normal; }
.g-table tr:last-child td, .g-table tr:last-child th { border-bottom: 0; }
.g-table .z1 { background: color-mix(in srgb, var(--c-humidity) 26%, transparent); }
.g-table .z2 { background: color-mix(in srgb, var(--c-canopy) 26%, transparent); }
.g-table .z3 { background: color-mix(in srgb, var(--c-vpd) 26%, transparent); }
.g-table .z4 { background: color-mix(in srgb, var(--c-air) 26%, transparent); }
.g-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 12px 2px 0; color: var(--text-muted); font-size: 0.82rem; }
.g-legend span::before {
    content: ""; display: inline-block; width: 11px; height: 11px; margin-right: 6px;
    border-radius: 3px; vertical-align: -1px; background: var(--sw);
}
@media (prefers-reduced-motion: reduce) {
    .g-lab-mark, .g-meter-bar span { transition: none; }
}

/* ── Spectrum strip ──
   A wavelength scale with the PAR and far-red bands bracketed under it. The
   colors are the light itself, so they are literal rather than theme tokens. */
.g-spectrum { margin: 26px 0; }
.g-spectrum-bar {
    height: 22px; border-radius: 6px;
    background: linear-gradient(90deg, #4b1a8c 0%, #3446ff 17.5%, #00b8f0 27.5%, #2fc24a 37.5%, #e6dc12 47.5%, #ff9a10 55%, #ff3a10 67.5%, #a8000c 80%, #3a0006 100%);
}
.g-spectrum-marks, .g-spectrum-axis { position: relative; height: 22px; }
.g-spectrum-marks span {
    position: absolute; top: 4px; padding-top: 4px; border-top: 2px solid var(--brand-light);
    color: var(--text); font-size: 0.78rem; font-weight: 650; text-align: center; white-space: nowrap;
}
.g-spectrum-marks span.fr { border-top-color: var(--text-dim); color: var(--text-muted); }
.g-spectrum-axis span {
    position: absolute; transform: translateX(-50%);
    color: var(--text-dim); font-size: 0.74rem; font-variant-numeric: tabular-nums;
}

/* ── Quick check ──
   One question, a few answers, the explanation under them. Shipped with the
   explanation visible; guide-play.js hides it until an answer is picked. */
.g-quiz {
    margin: 28px 0; padding: 18px;
    border: 1px dashed var(--border-warm); border-radius: var(--radius);
}
.g-quiz-t { margin: 0 0 4px; color: var(--brand); font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; }
.g-quiz-q { margin: 0 0 12px; color: var(--text); font-weight: 650; }
.g-quiz-opts { display: grid; gap: 8px; }
.g-quiz-opt {
    display: block; width: 100%; padding: 11px 14px; text-align: left;
    border: 1px solid var(--border-warm); border-radius: var(--radius-sm);
    background: var(--surface); color: var(--text); font: inherit; font-size: 0.93rem;
    cursor: pointer; transition: border-color .15s ease, background .15s ease, transform .15s ease;
}
.g-quiz:not(.live) .g-quiz-opt { cursor: default; }
.g-quiz.live:not(.done) .g-quiz-opt:hover { border-color: var(--brand); transform: translateX(2px); }
.g-quiz-opt:disabled { cursor: default; color: var(--text-muted); }
.g-quiz-opt.right { border-color: var(--ok); background: color-mix(in srgb, var(--ok) 14%, transparent); color: var(--text); }
.g-quiz-opt.wrong { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--text); }
.g-quiz-opt.right::after { content: " ✓"; color: var(--ok); font-weight: 700; }
.g-quiz-opt.wrong::after { content: " ✕"; color: var(--danger); font-weight: 700; }
.g-quiz-why { margin: 14px 0 0; color: var(--text-muted); font-size: 0.92rem; line-height: 1.6; }
.g-quiz-why strong:first-child { color: var(--text); }
.g-quiz.done .g-quiz-why { animation: g-quiz-in .25s ease-out; }
@keyframes g-quiz-in { from { opacity: 0; transform: translateY(-4px); } }
@media (prefers-reduced-motion: reduce) {
    .g-quiz-opt { transition: none; }
    .g-quiz.done .g-quiz-why { animation: none; }
}
.g-quiz-tally {
    margin: 0 0 18px; padding: 10px 14px; border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--brand) 10%, transparent);
    color: var(--brand-light); font-weight: 650; font-size: 0.92rem;
}

/* ── Foot of the article ── */
.g-end { margin-top: 44px; padding-top: 26px; border-top: 1px solid var(--border); }
.g-end h2 { margin: 0 0 16px; font-size: 1.1rem; font-weight: 700; letter-spacing: -0.01em; }

/* ── Guides: controls, reading list, list view ────────────────
   Three behaviours, all layered on top of a page that is complete without
   them (see the header of js/guides.js). Nothing here hides content until
   JavaScript decides to. */
.g-controls {
    display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
    margin: 4px 0 24px;
}
.g-topics { display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; }
.g-view { margin-left: auto; }

/* Topic chips. Same shape as the category pill on a card, deliberately: the
   thing you press and the thing it matches should look related. */
.g-topic {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 10px 15px; border-radius: 999px;
    border: 1px solid var(--border-warm); background: var(--surface-2);
    color: var(--text-muted); font-family: inherit;
    font-size: 0.8rem; font-weight: 600; letter-spacing: 0.02em;
    cursor: pointer; transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.g-topic:hover { color: var(--text); border-color: var(--brand); }
.g-topic.on {
    color: var(--brand-light); border-color: var(--brand);
    background: color-mix(in srgb, var(--brand) 15%, transparent);
}
.g-count:empty { display: none; }
.g-count {
    display: inline-grid; place-items: center; min-width: 18px; height: 18px;
    padding: 0 5px; border-radius: 999px;
    background: color-mix(in srgb, var(--brand) 26%, transparent);
    color: var(--brand-light); font-size: 0.68rem; font-weight: 700;
}

.g-empty { margin: 22px 2px; color: var(--text-dim); font-size: 0.92rem; }

/* ── Save to reading list ──
   The bookmark on a card. It sits inside a card that is itself a link, so
   guides.js stops the click; the z-index keeps it above the card's own
   hover state. */
.g-card, .g-feature { position: relative; }
.g-save {
    position: absolute; top: 14px; right: 14px; z-index: 2;
    display: grid; place-items: center;
    width: 38px; height: 38px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text-dim); cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background .15s ease, transform .12s ease;
}
.g-save svg { fill: none; }
.g-save:hover { color: var(--brand-light); border-color: var(--brand); transform: scale(1.08); }
.g-save.on {
    color: var(--brand-light);
    border-color: color-mix(in srgb, var(--brand-light) 55%, transparent);
    background: color-mix(in srgb, var(--brand) 18%, var(--surface-2));
}
.g-save.on svg { fill: currentColor; }
/* A thumb is not a mouse pointer. */
@media (pointer: coarse) {
    .g-save { width: 44px; height: 44px; }
}
/* The card's own padding has to clear the button, or a long title runs under
   it. Only where the button can appear: the planned cards have no slug and
   never get one. */
.g-card[data-slug] h3, .g-feature h2 { padding-right: 44px; }

/* The same thing on an article, worded rather than iconic: there is one of it
   on the page and no card to infer meaning from. */
.g-save-cta {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 7px 13px; border-radius: 999px;
    border: 1px solid var(--border-warm); background: var(--surface-2);
    color: var(--text-muted); font-family: inherit; font-size: 0.8rem; font-weight: 600;
    cursor: pointer; transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.g-save-cta svg { fill: none; }
.g-save-cta:hover { color: var(--text); border-color: var(--brand); }
.g-save-cta.on {
    color: var(--brand-light); border-color: var(--brand);
    background: color-mix(in srgb, var(--brand) 15%, transparent);
}
.g-save-cta.on svg { fill: currentColor; }

/* ── List view ──
   One row per guide, blurbs dropped. The featured card collapses into a row
   of the same shape rather than staying a billboard above a list, so the
   view is actually a list. */
.g-guides.as-list .g-grid { grid-template-columns: minmax(0, 1fr); gap: 8px; }
.g-guides.as-list .g-card,
.g-guides.as-list .g-feature {
    display: flex; flex-direction: row; align-items: center; flex-wrap: wrap;
    gap: 8px 14px; padding: 13px 16px; min-height: 0;
}
.g-guides.as-list .g-feature { margin: 0 0 8px; box-shadow: var(--shadow-sm); }
.g-guides.as-list .g-art { display: none; }
.g-guides.as-list .g-feature-body { display: contents; }
.g-guides.as-list .g-card h3,
.g-guides.as-list .g-feature h2 {
    flex: 1 1 0; min-width: 0; margin: 0; padding-right: 0;
    font-size: 0.98rem; font-weight: 700; letter-spacing: -0.01em;
}
.g-guides.as-list .g-card p,
.g-guides.as-list .g-feature p { display: none; }
.g-guides.as-list .g-cat { order: -1; flex: none; }
.g-guides.as-list .g-meta,
.g-guides.as-list .g-more { flex: none; margin: 0; color: var(--text-dim); font-size: 0.8rem; }
.g-guides.as-list .g-save {
    position: static; order: 9; flex: none; width: 30px; height: 30px;
}
@media (pointer: coarse) {
    .g-guides.as-list .g-save { width: 38px; height: 38px; }
}
/* At phone width the row is ~330px: the pill, the title and the button are
   what matters, and the read time is the first thing that can go. */
@media (max-width: 560px) {
    .g-guides.as-list .g-meta,
    .g-guides.as-list .g-more { display: none; }
    .g-guides.as-list .g-card,
    .g-guides.as-list .g-feature { padding: 12px 13px; gap: 8px 10px; }
}
/* On a phone the chips take two rows and the toggle takes a third; pushed
   right by margin it reads as orphaned, so it lines up under them instead. */
@media (max-width: 560px) {
    .g-view { margin-left: 0; }
}
/* The account note under the reading list. Quiet: it appears once someone has
   saved something, and says what they would get, not what they are missing. */
.g-nudge {
    margin: -14px 2px 22px; color: var(--text-dim); font-size: 0.85rem; line-height: 1.5;
}
.g-nudge a { color: var(--brand-light); border-bottom: 1px solid color-mix(in srgb, var(--brand) 45%, transparent); }
.g-nudge a:hover { color: var(--brand); border-bottom-color: var(--brand); }
/* ── Why this needs !important ──
   The filter hides cards by setting the `hidden` attribute. The UA stylesheet
   backs that with `[hidden] { display: none }`, but a UA rule loses to any
   author rule, and .g-card, .g-feature, .g-grid and .section-label all set
   `display` themselves. So every card stayed on screen with the attribute
   politely set on it: the DOM said filtered, the page said otherwise.
   Scoped to the section rather than global, since it is the only place here
   that hides anything this way. */
.g-guides [hidden] { display: none !important; }

/* ── Pheno Tracker ──────────────────────────────────────────── */
.pheno-panel[hidden] { display: none !important; }
.pheno-public-list { margin: 0; padding-left: 20px; display: grid; gap: 8px; color: var(--text-muted); line-height: 1.55; }
.pheno-public-list strong { color: var(--text); }
.pheno-bar-row { display: flex; align-items: flex-end; gap: 12px 16px; flex-wrap: wrap; }
.pheno-bar-pick { flex: 1 1 220px; min-width: 0; }
.pheno-bar-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.pheno-usage { margin: 12px 0 0; }
.pheno-tabs { margin: 18px 0 14px; }
.pheno-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.pheno-toolbar select { width: auto; }
.pheno-spacer { flex: 1 1 auto; }

.pheno-table tbody tr { cursor: pointer; }
.pheno-table tbody tr:hover, .pheno-table tbody tr:focus-visible { background: var(--surface-2); outline: none; }
.pheno-table tr.is-culled td { color: var(--text-dim); }
.pheno-parents { font-size: 0.82rem; color: var(--text-muted); }

.pheno-status {
    display: inline-block; padding: 2px 9px; border-radius: 999px;
    font-size: 0.74rem; font-weight: 700; white-space: nowrap;
    border: 1px solid var(--border-warm); color: var(--text-muted);
}
.pheno-status.st-keeper { color: var(--brand-light); border-color: var(--brand); }
.pheno-status.st-mother { color: var(--c-canopy); border-color: var(--c-canopy); }
.pheno-status.st-culled { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 55%, transparent); }
.pheno-status.st-archived { color: var(--text-dim); border-color: var(--border); }

.pheno-plant-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.pheno-plant-head h2 { margin: 0; font-size: 1.25rem; font-weight: 750; }
.pheno-facts { display: grid; gap: 8px; margin-top: 16px; }
.pheno-facts > div { display: flex; gap: 12px; font-size: 0.9rem; }
.pheno-fact-k { flex: none; width: 140px; color: var(--text-dim); font-weight: 600; }
.pheno-facts a { color: var(--brand-light); }

.pheno-score-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 7px 0; flex-wrap: wrap; }
.pheno-score-label { font-weight: 600; font-size: 0.9rem; }
.pheno-score button { min-width: 38px; padding: 6px 10px; }

#stText:disabled { opacity: .5; }
.pheno-upload-preview { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); margin-top: 0; max-height: 220px; overflow-y: auto; }
.pheno-trait-head { font-size: 0.74rem; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: .06em; }
.pheno-trait-head span:first-child { flex: 1 1 auto; }
.pheno-trait-head span:nth-child(2) { width: 80px; flex: none; }
.pheno-trait-head span:last-child { width: 28px; flex: none; }
.pheno-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; margin-top: 14px; }
.pheno-photo { margin: 0; }
.pheno-photo > button { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
.pheno-photo img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 9px; display: block; background: var(--surface-2); }
.pheno-photo figcaption { display: flex; justify-content: space-between; align-items: center; font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; }

.pheno-trait-row { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.pheno-trait-row input[type="text"] { flex: 1 1 auto; }
.pheno-trait-row input[type="number"] { width: 80px; flex: none; }

.pheno-picks { display: flex; flex-wrap: wrap; gap: 6px; }
.pheno-pick {
    appearance: none; font: inherit; font-size: 0.82rem; font-weight: 600; cursor: pointer;
    padding: 6px 12px; border-radius: 999px; border: 1px solid var(--border);
    background: var(--bg); color: var(--text-muted);
}
.pheno-pick.on { background: color-mix(in srgb, var(--brand) 22%, transparent); border-color: var(--brand); color: var(--text); }
.pheno-pick:disabled { opacity: .4; cursor: not-allowed; }
.pheno-compare th { text-align: left; color: var(--text-dim); font-weight: 600; white-space: nowrap; }
.pheno-compare tbody tr, .pheno-compare tr { cursor: default; }
.pheno-compare tr:hover { background: none; }
.pheno-best { color: var(--c-canopy); font-weight: 750; }

.pheno-lineage-title { font-weight: 700; }
.pheno-legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 0.78rem; color: var(--text-dim); margin-bottom: 10px; }
.pheno-legend i { display: inline-block; width: 22px; height: 0; margin-right: 6px; vertical-align: middle; border-top: 2px solid; }
.pheno-legend .lg-mother { border-color: var(--brand); }
.pheno-legend .lg-father { border-color: var(--c-humidity); border-top-style: dashed; }
.pheno-legend .lg-clone { border-color: var(--c-canopy); border-top-style: dotted; }
.pheno-legend .lg-self { border-color: var(--c-co2); }
.pheno-legend .lg-bus { border-color: var(--text-dim); }
.pheno-lineage svg { display: block; }
.pheno-lineage path { fill: none; stroke-width: 1.6; }
.pheno-lineage .ed-mother { stroke: var(--brand); }
.pheno-lineage .ed-father { stroke: var(--c-humidity); stroke-dasharray: 5 4; }
.pheno-lineage .ed-clone { stroke: var(--c-canopy); stroke-dasharray: 2 3; }
.pheno-lineage .ed-self { stroke: var(--c-co2); }
.pheno-lineage .ed-bus { stroke: var(--text-dim); }
.pheno-lineage .ed-joint { fill: var(--text-dim); }
.ln-node { cursor: pointer; }
.ln-node rect { fill: var(--surface-2); stroke: var(--border-warm); stroke-width: 1.2; }
.ln-node:hover rect, .ln-node:focus rect { stroke: var(--brand-light); }
.ln-node:focus { outline: none; }
.ln-node.st-keeper rect { stroke: var(--brand); stroke-width: 2; }
.ln-node.st-mother rect { stroke: var(--c-canopy); stroke-width: 2; }
.ln-node.is-focus rect { stroke: var(--brand-light); stroke-width: 2.5; }
.ln-node.is-other rect { stroke-dasharray: 4 3; }
.ln-node.is-culled { opacity: .45; }
.ln-code { fill: var(--text); font-size: 13px; font-weight: 700; }
.ln-sub { fill: var(--text-dim); font-size: 10.5px; }

.pheno-check { display: flex; gap: 10px; align-items: flex-start; margin-top: 8px; font-size: 0.88rem; line-height: 1.45; cursor: pointer; }
.pheno-check input { width: auto; margin-top: 3px; flex: none; }
.pheno-check.is-locked { color: var(--text-dim); cursor: default; }
.pheno-share-row { display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
.pheno-share-row input { flex: 1 1 240px; min-width: 0; }
.pheno-share-kind { flex: 1 1 auto; font-weight: 600; font-size: 0.88rem; }
.pheno-share-new { padding: 12px; border-radius: 10px; background: color-mix(in srgb, var(--brand) 12%, transparent); border: 1px solid var(--brand); }

.pheno-cross-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; margin-top: 16px; }
.pheno-cross-stats > div { padding: 10px 12px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--border); }
.pheno-cross-stats .stat-label { display: block; font-size: 0.74rem; color: var(--text-dim); font-weight: 600; margin-bottom: 2px; }
.pheno-cross-stats strong { font-size: 1.15rem; }
.pheno-flag { font-size: 0.76rem; font-weight: 700; color: var(--danger); }
.pheno-cross { padding: 14px 0; border-top: 1px solid var(--border); }
.pheno-cross:first-child { border-top: none; padding-top: 0; }
.pheno-cross-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.pheno-cross-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

/* Labels: the same markup is the on-screen preview and the printed sheet. */
.pheno-labels, .pheno-print { display: flex; flex-wrap: wrap; gap: 8px; }
.pheno-print { display: none; }
.pheno-label {
    display: flex; align-items: center; gap: 8px; box-sizing: border-box; overflow: hidden;
    background: #fff; color: #111; border: 1px dashed #999; border-radius: 4px; padding: 6px 8px;
}
.pheno-label.size-small { width: 2in; height: 1in; }
.pheno-label.size-large { width: 3in; height: 2in; }
.pheno-label-qr { flex: none; height: 100%; aspect-ratio: 1; }
.pheno-label-qr svg { width: 100%; height: 100%; display: block; }
.pheno-label-text { display: flex; flex-direction: column; min-width: 0; font-size: 9pt; line-height: 1.25; }
.pheno-label-text strong { font-size: 12pt; }
.pheno-label.size-large .pheno-label-text strong { font-size: 16pt; }
.pheno-label-text span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pheno-label.size-large { align-items: flex-start; padding: 10px 12px; }
.pheno-label.size-large .pheno-label-qr { height: 1.35in; }
.pheno-label.size-large .pheno-label-text span { white-space: normal; }
@media print {
    .print-sheet.pheno-print { display: flex; gap: 0.08in; }
    .pheno-print .pheno-label { break-inside: avoid; }
}
@media (max-width: 560px) {
    .pheno-fact-k { width: 110px; }
}
.pheno-meter { display: inline-block; width: 120px; height: 8px; border-radius: 999px; background: var(--surface-3); position: relative; overflow: hidden; vertical-align: middle; }
.pheno-meter::after { content: ""; position: absolute; inset: 0 auto 0 0; width: calc(var(--v) * 20%); background: var(--brand); border-radius: inherit; }

/* ── Live demos (dashboard, pheno tracker) ── */
.demo-banner { background: color-mix(in srgb, var(--brand) 14%, var(--surface-2)); border: 1px solid color-mix(in srgb, var(--brand-light) 40%, var(--border)); border-radius: 12px; padding: 12px 16px; margin-bottom: 18px; display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.demo-banner .demo-tag { font-weight: 700; color: var(--brand-light); }
.demo-banner .demo-msg { color: var(--text-muted); flex: 1; min-width: 180px; }
.demo-banner .btn { padding: 6px 14px; }
/* Controls a demo can't drive stay on screen, dimmed, so a visitor sees the
   feature they'd be getting instead of a page with it quietly removed. */
.demo-locked { opacity: 0.55; cursor: not-allowed; }
.demo-nudge {
    position: fixed; left: 50%; bottom: 22px; transform: translate(-50%, 16px);
    z-index: 60; max-width: min(520px, calc(100vw - 32px));
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    background: var(--surface-2); border: 1px solid color-mix(in srgb, var(--brand-light) 40%, var(--border));
    border-radius: 12px; padding: 12px 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.35);
    color: var(--text-muted); font-size: 0.9rem; line-height: 1.45;
    opacity: 0; visibility: hidden; transition: opacity 0.18s ease, transform 0.18s ease;
}
.demo-nudge.open { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
.demo-nudge .btn { padding: 6px 14px; white-space: nowrap; }

/* The pH dose: the amount to pour, first and largest, above the facts. */
.ph-dose { max-width: 540px; margin-bottom: 14px; padding: 14px 18px; border-radius: var(--radius-sm); background: var(--surface-2); border: 1px solid var(--border-soft); }
.ph-dose-k { display: block; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-dim); }
.ph-dose-v { margin-top: 2px; font-size: 1.6rem; font-weight: 750; letter-spacing: -0.01em; color: var(--text); font-variant-numeric: tabular-nums; }
.ph-dose .ph-dose-sub { margin-top: 2px; }
.ph-notes p:first-child { color: var(--text); }

/* ── Plant Problem Diagnosis Key (/planthealthkey) ── */
.pk-cropbar { max-width: 520px; margin-bottom: 16px; }
.pk-describe { max-width: 820px; margin-bottom: 16px; scroll-margin-top: 88px; }
.pk-describe textarea { font-size: 1rem; }
.pk-describe-acts { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 12px; }
.pk-rate { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 0 auto; padding: 0; border: 0; min-width: 0; }
.pk-rate-label { float: left; padding: 0; margin: 0; min-width: 8.5em; text-align: right; font-size: .85rem; color: var(--text-muted); }
.pk-stars { display: inline-flex; }
.pk-stars input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.pk-stars label { font-size: 1.6rem; line-height: 1; padding: 4px 3px; color: var(--text-dim); cursor: pointer; transition: color .12s; }
.pk-stars label.on { color: var(--brand); }
.pk-stars input:focus-visible + label { outline: 2px solid var(--brand); outline-offset: 1px; border-radius: 4px; }
.pk-rate:disabled .pk-stars label { cursor: default; opacity: .45; }
.pk-crop-from { margin: 10px 2px 0; }
.pk-static-list { columns: 2 220px; column-gap: 24px; margin: 4px 0 12px; }
.pk-static-list li { break-inside: avoid; }
.pk-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
@media (max-width: 560px) { .pk-rate { margin-left: 0; } .pk-rate-label { text-align: left; } }
.pk-nomatch { margin: 14px 2px 0; }
.pk-noai { margin: 14px 2px 0; font-size: 0.82rem; }
.pk-matches { display: flex; flex-direction: column; gap: 10px; }
.pk-match { display: flex; gap: 14px; align-items: flex-start; padding: 12px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.pk-match-body { min-width: 0; flex: 1; }
.pk-match-name { margin: 6px 0 2px; font-weight: 750; font-size: 1.05rem; }
.pk-match-why { margin: 0 0 10px; }
.pk-match-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.pk-close { margin: 16px 0 0; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); }
.pk-close-q { margin: 0 0 10px; font-weight: 700; }
.pk-close-opt { display: block; width: 100%; text-align: left; font: inherit; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; cursor: pointer; }
.pk-close-name { display: block; margin-top: 4px; font-size: .85rem; color: var(--text-muted); }
.pk-close-opt + .pk-close-opt { margin-top: 8px; }
.pk-close-opt:hover, .pk-close-opt:focus-visible { border-color: var(--brand); outline: none; }
.pk-best { margin-left: 8px; font-size: 0.72rem; font-weight: 700; color: var(--ok); }

/* The ride: answered questions fold to one line, the open card sits below. */
.pk-ride { list-style: none; margin: 0; padding: 0; max-width: 820px; display: flex; flex-direction: column; gap: 8px; }
/* Clears the sticky nav and leaves the last answer in view above the open card. */
.pk-card { scroll-margin-top: 150px; }
.pk-card:first-child { scroll-margin-top: 88px; }
.pk-open { min-height: 200px; margin-top: 4px; }
.pk-open h2:focus { outline: none; }
.pk-done-btn {
    display: flex; align-items: center; gap: 12px; width: 100%; min-height: 48px; padding: 10px 14px;
    text-align: left; font: inherit; color: var(--text); cursor: pointer;
    background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
    transition: border-color 0.15s ease;
}
.pk-done-btn:hover, .pk-done-btn:focus-visible { border-color: var(--brand); outline: none; }
.pk-done-n {
    flex: none; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
    font-size: 0.75rem; font-weight: 700; color: var(--text-dim); border: 1px solid var(--border);
}
.pk-done-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.pk-done-q { font-size: 0.8rem; color: var(--text-dim); line-height: 1.35; }
.pk-done-a { font-size: 0.95rem; font-weight: 650; line-height: 1.35; }
.pk-done-change { flex: none; font-size: 0.8rem; font-weight: 650; color: var(--brand-light); }
@media (prefers-reduced-motion: no-preference) {
    .pk-enter { animation: pk-enter 0.38s ease-out both; }
    @keyframes pk-enter { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
}

.pk-thumb { flex: none; object-fit: cover; border-radius: var(--radius-sm); background: var(--surface-3); }
.pk-opt-photo { align-items: center; }
.pk-opt-photo .pk-opt-body { flex: 1; }
.pk-opt-leads { font-size: 0.8rem; font-weight: 650; color: var(--brand-light); }
.pk-alike .pk-thumb { width: 28px; height: 28px; border-radius: 50%; margin-left: -6px; }
.pk-credit-note { margin: 10px 2px 0; font-size: 0.8rem; }
.pk-result-top { display: flex; gap: 18px; align-items: flex-start; }
.pk-result-head { flex: 1; min-width: 0; }
.pk-photo { flex: none; width: 300px; margin: 0; }
.pk-photo img { display: block; width: 100%; height: auto; border-radius: var(--radius-sm); background: var(--surface-3); }
.pk-photo figcaption { margin-top: 6px; display: flex; flex-direction: column; gap: 2px; }
.pk-photo-cap { font-size: 0.85rem; line-height: 1.4; color: var(--text-muted); }
.pk-photo-credit { font-size: 0.74rem; line-height: 1.4; color: var(--text-dim); }
.pk-photo-strip { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.pk-photo-pick { padding: 0; width: 56px; height: 56px; border: 2px solid transparent; border-radius: var(--radius-sm); background: none; cursor: pointer; overflow: hidden; }
.pk-photo-pick img { display: block; width: 100%; height: 100%; object-fit: cover; }
.pk-photo-pick.is-on, .pk-photo-pick:hover, .pk-photo-pick:focus-visible { border-color: var(--brand); outline: none; }
/* /planthealthkey/<problem> pages (tools/plantkey-pages) */
.pk-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
.pk-gallery .pk-photo { width: 100%; }
.pk-page .pk-lock { margin-top: 26px; }
.pk-page h2.pk-h3 { margin: 24px 0 8px; }
.pk-photo figcaption a { color: inherit; text-decoration: underline; }
@media (max-width: 640px) {
    .pk-result-top { flex-direction: column; }
    .pk-photo { width: 100%; }
    .pk-match .pk-thumb { width: 56px; height: 56px; }
    .pk-opt-photo .pk-thumb { width: 64px; height: 64px; }
}
.pk-eyebrow { margin: 0 0 4px; font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-dim); }
.pk-q { margin: 0 0 8px; font-size: 1.25rem; font-weight: 750; line-height: 1.3; color: var(--text); }
.pk-hint { margin: 0 0 14px; }
.pk-opts { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.pk-opt {
    display: flex; align-items: flex-start; gap: 12px; width: 100%; min-height: 48px;
    padding: 12px 14px; text-align: left; font: inherit; color: var(--text); cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
    transition: border-color 0.15s ease, background 0.15s ease;
}
.pk-opt:hover, .pk-opt:focus-visible { border-color: var(--brand); background: var(--surface-3); outline: none; }
.pk-opt-n {
    flex: none; width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
    font-size: 0.8rem; font-weight: 700; color: var(--brand-light);
    background: color-mix(in srgb, var(--brand) 16%, transparent); border: 1px solid color-mix(in srgb, var(--brand) 36%, transparent);
}
.pk-opt-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.pk-opt-label { font-weight: 650; line-height: 1.35; }
.pk-opt-detail { font-size: 0.88rem; color: var(--text-muted); line-height: 1.4; }
.pk-nav { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border-soft); }
.pk-kind {
    display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 0.72rem; font-weight: 700;
    letter-spacing: 0.04em; text-transform: uppercase; border: 1px solid var(--border); color: var(--text-muted);
}
.pk-kind-pest { color: #e0a96d; border-color: rgba(224,169,109,0.4); background: rgba(224,169,109,0.10); }
.pk-kind-disease { color: #d79a9a; border-color: rgba(215,154,154,0.4); background: rgba(215,154,154,0.10); }
.pk-kind-deficiency { color: var(--home-text); border-color: var(--home-line); background: var(--home-soft); }
.pk-kind-disorder { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); background: color-mix(in srgb, var(--ok) 10%, transparent); }
:root[data-theme="light"] .pk-kind-pest { color: #8a5214; }
:root[data-theme="light"] .pk-kind-disease { color: #9a3434; }
:root[data-scheme="greenhouse"] .pk-kind-pest { color: #5c3a00; }
:root[data-scheme="greenhouse"] .pk-kind-disease { color: #7a1010; }
.pk-latin { margin: 0; font-style: italic; color: var(--text-muted); font-size: 0.92rem; }
.pk-summary { margin: 12px 0 0; line-height: 1.55; }
.pk-h3 { margin: 22px 0 8px; font-size: 1rem; font-weight: 750; }
.pk-h4 { margin: 16px 0 6px; font-size: 0.82rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-dim); }
.pk-list { margin: 0; padding-left: 20px; line-height: 1.55; }
.pk-list li + li { margin-top: 5px; }
.pk-alikes { display: flex; flex-wrap: wrap; gap: 8px; }
.pk-alike {
    display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 6px 12px;
    font: inherit; font-size: 0.9rem; color: var(--text); cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px;
}
.pk-alike .pk-kind { padding: 0 6px; font-size: 0.62rem; }
.pk-alike:hover, .pk-alike:focus-visible { border-color: var(--brand); outline: none; }
.pk-care { margin-top: 8px; }
.pk-lock {
    margin-top: 22px; padding: 18px; border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--brand) 8%, var(--surface-2));
    border: 1px dashed color-mix(in srgb, var(--brand-light) 45%, var(--border));
}
.pk-lock-title { margin: 0 0 6px; font-weight: 750; }
.pk-cropnote { margin: 4px 0 12px; padding: 12px 14px; border-left: 3px solid var(--brand); background: var(--surface-2); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; line-height: 1.5; }
.pk-cropnote-k { display: block; font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--brand-light); margin-bottom: 3px; }
.pk-caution { margin: 0 0 12px; padding: 10px 14px; border-radius: var(--radius-sm); background: var(--danger-bg); color: var(--text); border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent); font-size: 0.92rem; line-height: 1.5; }
.pk-favors { margin: 12px 0 0; line-height: 1.55; }
.pk-treat { margin-top: 10px; }
.pk-how { display: inline-block; margin-bottom: 4px; font-size: 0.78rem; font-weight: 700; color: var(--text-muted); }
.pk-label-note { margin-top: 14px; }
.pk-other { margin-top: 16px; }
.pk-sources { margin: 0; padding-left: 20px; line-height: 1.5; font-size: 0.9rem; }
.pk-sources li + li { margin-top: 4px; }
.pk-src-org { display: block; color: var(--text-muted); font-size: 0.8rem; }
.pk-other summary { cursor: pointer; font-weight: 650; min-height: 40px; display: flex; align-items: center; }
.pk-other p { margin: 8px 0 0; line-height: 1.5; }
.pk-index { margin-top: 16px; }
.pk-index > summary { cursor: pointer; font-weight: 700; min-height: 40px; display: flex; align-items: center; }
.pk-index-list { display: flex; flex-wrap: wrap; gap: 8px; }
.pk-index-item {
    min-height: 40px; padding: 6px 12px; font: inherit; font-size: 0.9rem; color: var(--text); cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px;
}
.pk-index-item:hover, .pk-index-item:focus-visible { border-color: var(--brand); outline: none; }

/* Auto-adjust: a soft brand-coloured glow that breathes, so the button reads
   as the page's headline feature without shouting. Still under reduced motion. */
.btn-glow {
    border-color: var(--brand);
    color: var(--brand-light);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 35%, transparent), 0 0 14px color-mix(in srgb, var(--brand) 35%, transparent);
    animation: btn-glow 2.8s ease-in-out infinite;
}
.btn-glow:hover { box-shadow: 0 0 0 1px var(--brand), 0 0 20px color-mix(in srgb, var(--brand) 55%, transparent); }
@keyframes btn-glow {
    0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent), 0 0 8px color-mix(in srgb, var(--brand) 25%, transparent); }
    50%      { box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 55%, transparent), 0 0 18px color-mix(in srgb, var(--brand) 50%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .btn-glow { animation: none; } }
/* A field a link sent the user to change (Calculator "Switch" to Setup) */
.field-flash { border-color: var(--brand) !important; animation: btn-glow 1.4s ease-in-out 3; }
@media (prefers-reduced-motion: reduce) { .field-flash { animation: none; box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand) 50%, transparent); } }

/* ─── Light and greenhouse: fixed pale colours ───
   These rules hard-code pastel text meant for the dark theme, and on a cream
   or white page they fell to 1.3–2.4:1. Greenhouse sets data-theme="light"
   too, so the tone tokens pick its darker values up on their own. */
:root[data-theme="light"] .tool-tag.tier-free,
:root[data-theme="light"] .plan-admin,
:root[data-theme="light"] .np-badge.edible {
    color: var(--tone-ideal);
    background: color-mix(in srgb, var(--tone-ideal) 12%, transparent);
    border-color: color-mix(in srgb, var(--tone-ideal) 42%, transparent);
}
:root[data-theme="light"] .ic-verdict.good { color: var(--tone-ideal); border-color: color-mix(in srgb, var(--tone-ideal) 42%, transparent); }
:root[data-theme="light"] .ic-finding.good .ic-axis,
:root[data-theme="light"] .dir-up { color: var(--tone-ideal); }
:root[data-theme="light"] .dir-down { color: var(--tone-high); }
:root[data-theme="light"] .message.warn,
:root[data-theme="light"] .verify-bar,
:root[data-theme="light"] .verify-bar-txt strong,
:root[data-theme="light"] .np-badge.edible.warn { color: #6e500c; }
:root[data-scheme="greenhouse"] .message.warn,
:root[data-scheme="greenhouse"] .verify-bar,
:root[data-scheme="greenhouse"] .verify-bar-txt strong,
:root[data-scheme="greenhouse"] .np-badge.edible.warn { color: #5c3a00; }
:root[data-theme="light"] .np-badge.edible.warn { border-color: rgba(110,80,12,0.45); background: rgba(214,170,80,0.12); }
:root[data-theme="light"] .np-badge.pollinator { color: #9a2f55; border-color: rgba(154,47,85,0.4); background: rgba(154,47,85,0.07); }
:root[data-scheme="greenhouse"] .np-badge.pollinator { color: #7a1f42; }

/* ── Extension search (/extension) and support page ── */
.ext-searchcard form { margin: 0; }
.ext-fields { display: grid; grid-template-columns: 2fr 1fr; gap: 12px; }
.ext-fields input { width: 100%; }
.ext-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; }
.ext-acts .hint { margin: 0; flex: 1 1 220px; }
.ext-try { margin: 12px 0 0; }
.ext-local, .ext-res { margin-top: 16px; }
.ext-local .card-title, .ext-res .card-title { margin: 0 0 12px; font-size: 1.05rem; }
.ext-progs { display: grid; gap: 10px; }
.ext-prog { padding: 12px 14px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.ext-prog-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.ext-prog-name { font-weight: 700; }
.ext-prog-school { color: var(--text-muted); font-size: 0.85rem; margin-top: 2px; }
.ext-prog-links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.ext-prog-links a {
    display: inline-flex; align-items: center; min-height: 34px; padding: 4px 11px; font-size: 0.84rem;
    border: 1px solid var(--border); border-radius: 999px; color: var(--text); background: var(--surface);
}
.ext-prog-links a:hover, .ext-prog-links a:focus-visible { border-color: var(--brand); }
.ext-kind {
    font-size: 0.72rem; font-weight: 600; letter-spacing: 0.02em; color: var(--text-muted);
    border: 1px solid var(--border); border-radius: 999px; padding: 1px 8px;
}
.ext-ask { margin: 12px 0 0; }
.ext-intl { margin: 0 0 10px; padding-left: 20px; line-height: 1.55; }
.ext-tribal-head { margin: 14px 0 4px; }
.ext-intl-who, .ext-dir-who { color: var(--text-muted); font-size: 0.84rem; }
.ext-more { margin: 0 0 10px; }
.ext-more > summary { cursor: pointer; font-weight: 600; min-height: 36px; display: flex; align-items: center; }
.ext-hits { list-style: none; margin: 0; padding: 0; }
.ext-hit { padding: 10px 0; border-top: 1px solid var(--border); }
.ext-hit:first-child { border-top: 0; padding-top: 2px; }
.ext-hit-title { font-weight: 600; line-height: 1.35; }
.ext-hit-src { display: flex; align-items: center; gap: 8px; margin-top: 3px; color: var(--text-dim); font-size: 0.8rem; overflow-wrap: anywhere; }
.ext-st {
    flex: none; min-width: 26px; text-align: center; font-size: 0.7rem; font-weight: 700; color: var(--text-muted);
    border: 1px solid var(--border); border-radius: 5px; padding: 0 4px;
}
.ext-st-mine { color: var(--brand); border-color: var(--brand); }
.ext-more-row:not(:empty) { margin-top: 12px; }
.ext-fixed { margin: -4px 0 12px; }
.ext-fixed button {
    background: none; border: 0; padding: 0; font: inherit; color: var(--brand); cursor: pointer; text-decoration: underline;
}
/* Your Extension: folded above the results on phones, a column beside
   them on wide screens (extensionpage.js keeps it open there). */
.ext-local > summary {
    cursor: pointer; font-weight: 700; min-height: 44px; display: flex; align-items: center; color: var(--brand);
}
.ext-local[open] > summary { margin-bottom: 10px; }
.ext-local-hits { margin-top: 14px; }
.ext-local-hits h3 { margin: 0 0 6px; font-size: 0.95rem; }
.ext-local-hits .ext-more-row .btn { width: 100%; }
@media (min-width: 900px) {
    .ext-layout.has-side.has-res {
        display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 0 20px; align-items: start;
    }
    .ext-layout.has-side.has-res .ext-main { grid-column: 1; grid-row: 1; }
    .ext-layout.has-side.has-res .ext-side {
        grid-column: 2; grid-row: 1; position: sticky; top: 88px;
        max-height: calc(100vh - 104px); overflow-y: auto; overscroll-behavior: contain;
    }
    .ext-layout.has-side.has-res .ext-prog { padding: 10px 12px; }
    .ext-local > summary { display: none; }
}
.ext-google { margin: 12px 0 0; }
.ext-dir { margin-top: 16px; }
.ext-dir > summary { cursor: pointer; font-weight: 700; min-height: 40px; display: flex; align-items: center; }
.ext-dir h3 { margin: 18px 0 6px; font-size: 0.95rem; }
.ext-dir-list { margin: 0; padding-left: 20px; line-height: 1.5; font-size: 0.9rem; }
.ext-dir-list li + li { margin-top: 6px; }
.ext-dir-links { font-size: 0.82rem; }
.ext-dir-links a { white-space: nowrap; }
.ext-banner { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 20px; margin-top: 26px; border-color: var(--brand); }
.ext-banner-copy { flex: 1 1 320px; }
.ext-banner-copy h2 { margin: 0 0 6px; font-size: 1.1rem; }
.ext-banner-copy p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
.ext-banner-acts { display: flex; flex-wrap: wrap; gap: 8px; }
/* Wide screens: a card in the empty margin beside the toolshop, in view
   while scrolling. Narrower screens keep it at the bottom of the page.
   Pinned only while .is-pinned is on: js/pages/index-side-rail.js adds it
   when the whole rail fits in the window, width and height, so a zoom or a
   short window that would cut it off leaves it at the bottom instead. */
@media (min-width: 1460px) {
    /* The rail is what's pinned (index.html .side-rail), so Popular This Week
       and this card stack in one column. */
    .side-rail.is-pinned {
        position: fixed; top: 140px; left: calc(50% + 515px); width: 200px; z-index: 5;
        display: flex; flex-direction: column; gap: 14px;
    }
    .side-rail.is-pinned .ext-banner {
        margin: 0; flex: none;
        flex-direction: column; align-items: stretch; gap: 12px; padding: 18px;
    }
    .side-rail.is-pinned .ext-banner-copy { flex: none; }
    .side-rail.is-pinned .ext-banner-copy h2 { font-size: 1rem; }
    .side-rail.is-pinned .ext-banner-copy p { font-size: 0.84rem; }
    .side-rail.is-pinned .ext-banner-acts { flex-direction: column; }
    .side-rail.is-pinned .ext-banner-acts .btn { width: 100%; margin: 0; }
}
@media (max-width: 600px) {
    .ext-fields { grid-template-columns: 1fr; }
    .ext-acts .btn { width: 100%; }
}
.pk-ext { margin: 16px 0 0; }
/* /extension/<state> pages */
.ext-crumb { margin: 0 0 6px; font-size: 0.85rem; color: var(--text-dim); }
.ext-st-row { display: flex; gap: 10px; }
.ext-st-row input { flex: 1; min-width: 0; }
.ext-topics { margin: 0 0 12px; padding-left: 20px; line-height: 1.6; }
.ext-topic { color: var(--text-muted); }
.ext-states { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 0 0 12px; padding: 0; list-style: none; }
.ext-dir h3 a { color: inherit; }
/* /diagnostic-labs and /diagnostic-labs/<state> */
.lab-tests { display: flex; flex-wrap: wrap; gap: 5px; margin: 8px 0 0; padding: 0; list-style: none; }
.lab-tests li {
    font-size: 0.76rem; color: var(--text-muted); background: var(--surface-2);
    border: 1px solid var(--border); border-radius: 999px; padding: 1px 9px;
}
.lab-list { margin: 0; padding-left: 20px; line-height: 1.55; }
.lab-list li + li { margin-top: 6px; }
.lab-notes { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--text-muted); }
.lab-tips { margin: 0 0 12px; padding-left: 20px; line-height: 1.55; }
.lab-tips li + li { margin-top: 6px; }
.lab-filter { margin: 0 0 14px; }
.lab-filter .field-label { margin: 0 0 8px; }
.lab-filter-btns { display: flex; flex-wrap: wrap; gap: 6px; }
.lab-filter-btn {
    min-height: 36px; padding: 4px 12px; font: inherit; font-size: 0.84rem; cursor: pointer;
    color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
}
.lab-filter-btn:hover, .lab-filter-btn:focus-visible { border-color: var(--brand); }
.lab-filter-btn[aria-pressed="true"] { background: var(--brand); border-color: var(--brand); color: var(--bg); font-weight: 600; }
.lab-filter .hint { margin: 10px 0 0; }
.lab-rows { list-style: none; margin: 0; padding: 0; }
.lab-row { display: flex; gap: 10px; align-items: flex-start; padding: 12px 0; border-top: 1px solid var(--border); }
.lab-row:first-child { border-top: 0; padding-top: 2px; }
.lab-row[hidden] { display: none; }
.lab-dist { margin-left: 6px; font-size: 0.78rem; font-weight: 600; color: var(--brand); white-space: nowrap; }
.lab-near { margin: 0 0 16px; padding-bottom: 14px; border-bottom: 1px solid var(--border); }
.lab-toggle { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; margin-top: 6px; font-size: 0.9rem; cursor: pointer; }
.lab-toggle input { width: 18px; height: 18px; margin: 0; accent-color: var(--brand); }
.lab-near .hint { margin: 2px 0 0; }
.lab-row .ext-st { margin-top: 3px; }
.lab-row-main { min-width: 0; }
.lab-row-name { font-weight: 700; }

/* ── Light theme: controls that read as controls ──
   Fields used --bg (the page colour) and ghost buttons and header pills
   used --surface-2, all with a border near 1.4:1 against the cream page,
   so a search box or dropdown looked like a faint outline. Here they get a
   near-white fill and an edge of at least 3:1 (WCAG 1.4.11). Greenhouse
   already has black-on-white controls, so it is left out. Focus and hover
   are restated because this block outranks the base rules. */
:root[data-theme="light"]:not([data-scheme="greenhouse"]) {
    --control-bg:   #fffdf8;
    --control-edge: #9a8058;   /* 3.1:1 on --bg, 3.7:1 on the fill */
}
:root[data-theme="light"]:not([data-scheme="greenhouse"]) :is(
    select, textarea,
    input[type="text"], input[type="search"], input[type="date"], input[type="number"],
    input[type="email"], input[type="password"], input[type="tel"], input[type="url"],
    .btn-ghost, .nav-link, .user-chip, .theme-seg,
    .nav-menu-btn, .nav-back-btn, .nav-refresh-btn
) {
    background-color: var(--control-bg);
    border-color: var(--control-edge);
    box-shadow: 0 1px 2px rgba(70, 48, 18, 0.08);
}
:root[data-theme="light"]:not([data-scheme="greenhouse"]) :is(.btn-ghost, .nav-link, .user-chip) { color: var(--text); }
:root[data-theme="light"]:not([data-scheme="greenhouse"]) :is(select, textarea, input):focus {
    border-color: var(--brand); box-shadow: var(--ring);
}
:root[data-theme="light"]:not([data-scheme="greenhouse"]) :is(.btn-ghost, .nav-link, .nav-menu-btn, .nav-back-btn, .nav-refresh-btn):hover {
    border-color: var(--brand); background-color: #fff;
}
/* The selected theme button sits inside the pill, so it needs to stand
   apart from the new white pill. */
:root[data-theme="light"]:not([data-scheme="greenhouse"]) .theme-seg button[aria-pressed="true"] {
    background: var(--surface-3); border-color: var(--control-edge);
}
/* "Sign in" is a .nav-link inside the .user-chip: one box, not two. */
:root[data-theme="light"]:not([data-scheme="greenhouse"]) .user-chip .nav-link {
    background: none; border-color: transparent; box-shadow: none;
}

/* ── Lawn tools (/lime, /lawncare, /watering, /seeding): shared result
   graphics, one-row tiles and illustrated steps ── */
.lawn-page { --lawn-green: #5f8f2f; --lawn-soil: #8a6a48; --lawn-water: #3f86b0; }
:root[data-theme="dark"] .lawn-page, :root:not([data-theme]) .lawn-page { --lawn-green: #9cc46a; --lawn-soil: #b08a62; --lawn-water: #7fb6d6; }
.lawn-result { border-color: color-mix(in srgb, var(--brand-light) 45%, var(--border)); }
.lawn-summary { color: var(--text-muted); line-height: 1.65; margin: 0; font-size: 0.98rem; max-width: 80ch; }

/* pH scale: 4.5 to 8.0 across the bar (limepage.js places the marks). */
.ph-scale { margin: 22px 0 4px; }
.ph-bar {
    position: relative; height: 16px; border-radius: 999px; margin: 44px 0 6px;
    background: linear-gradient(90deg, #c8553d 0%, #df8a3a 22%, #e5bb45 38%, #7fb04f 58%, #5e9f5a 72%, #3f8f9a 86%, #3b6ea8 100%);
    box-shadow: inset 0 1px 2px rgba(0,0,0,0.18);
}
.ph-band {
    position: absolute; top: -6px; bottom: -6px; border: 2px dashed color-mix(in srgb, var(--text) 55%, transparent);
    border-radius: 8px; pointer-events: none;
}
.ph-band span {
    position: absolute; top: calc(100% + 4px); left: 50%; transform: translateX(-50%);
    font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); white-space: nowrap;
}
.ph-move {
    position: absolute; top: 50%; height: 4px; transform: translateY(-50%);
    background: repeating-linear-gradient(90deg, #fff 0 6px, transparent 6px 10px); opacity: 0.9;
}
.ph-mark {
    position: absolute; bottom: 50%; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center;
    padding-bottom: 14px; transition: left .35s ease;
}
.ph-mark::after {
    content: ""; position: absolute; bottom: -9px; left: 50%; width: 18px; height: 18px; transform: translateX(-50%);
    border-radius: 50%; border: 3px solid var(--surface); box-shadow: 0 1px 4px rgba(0,0,0,0.35);
}
.ph-now::after { background: #c8553d; }
.ph-target::after { background: var(--lawn-green); }
.ph-mark b { font-size: 1.05rem; line-height: 1; color: var(--text); font-variant-numeric: tabular-nums; }
.ph-mark span { font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); white-space: nowrap; }
.ph-scale.same .ph-target { display: none; }
.ph-ticks, .ph-ends { display: flex; justify-content: space-between; font-size: 0.72rem; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.ph-ticks { margin-top: 26px; }
.ph-ends { margin-top: 2px; font-weight: 600; }

/* Six tiles (four when no lawn size), one row on wide screens. */
.lawn-stats { margin-bottom: 0; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
@media (min-width: 1100px) { .lawn-stats { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 1fr; } }
.lawn-stats .stat { --accent: var(--lawn-green); }
.lawn-stats .stat:nth-child(2) { --accent: var(--brand); }
.lawn-stats .stat:nth-child(4) { --accent: var(--lawn-water); }
.lawn-stats .stat:nth-child(5) { --accent: #c8553d; }
.lawn-stats .stat-top { margin-bottom: 10px; }

/* How to lime: five illustrated steps joined by a line. */
.lawn-how { margin: 30px 0 26px; }
.lawn-steps {
    list-style: none; margin: 0; padding: 0; position: relative;
    display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px;
}
.lawn-steps::before {
    content: ""; position: absolute; top: 15px; left: 10%; right: 10%; height: 2px;
    background: repeating-linear-gradient(90deg, var(--border-warm) 0 8px, transparent 8px 14px);
}
.lawn-step {
    position: relative; display: flex; flex-direction: column; gap: 8px; padding: 0 16px 18px; margin-top: 0;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm);
    margin-top: 15px;
}
.lawn-step-n {
    position: absolute; top: -15px; left: 50%; transform: translateX(-50%);
    width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
    background: var(--lawn-green); color: #fff; font-weight: 800; font-size: 0.9rem; box-shadow: 0 0 0 4px var(--bg);
}
.lawn-step-art { width: 100%; height: auto; margin: 22px auto 2px; display: block; max-width: 170px; }
.lawn-step h3 { margin: 0; font-size: 1rem; letter-spacing: -0.01em; }
.lawn-step p { margin: 0; color: var(--text-muted); font-size: 0.86rem; line-height: 1.55; }
.lawn-step strong { color: var(--text); }
.art-line { fill: none; stroke: var(--text-muted); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.art-bag { fill: color-mix(in srgb, var(--surface-3) 70%, transparent); }
.art-fill { fill: var(--lawn-green); }
.art-soil { fill: color-mix(in srgb, var(--lawn-soil) 55%, transparent); }
.art-grass { fill: none; stroke: var(--lawn-green); stroke-width: 3; stroke-linecap: round; }
.art-lawn { fill: color-mix(in srgb, var(--lawn-green) 22%, transparent); stroke: var(--lawn-green); stroke-width: 2; }
.art-pass1 { stroke: var(--brand); stroke-width: 2.5; stroke-dasharray: 5 4; }
.art-pass2 { stroke: var(--lawn-water); stroke-width: 2.5; stroke-dasharray: 5 4; }
.art-arrow1 { fill: var(--brand); }
.art-arrow2 { fill: var(--lawn-water); }
.art-water { fill: color-mix(in srgb, var(--lawn-water) 75%, transparent); }
.art-text { fill: var(--text); font: 800 13px/1 system-ui, sans-serif; letter-spacing: 0.06em; }
.art-small { font-size: 9px; }
.art-on { fill: #fff; font-size: 9px; letter-spacing: 0.02em; }
.art-arrowline { stroke: var(--brand); }
.art-rain { stroke: var(--lawn-water); }
.art-sun { fill: color-mix(in srgb, #e5bb45 70%, transparent); stroke: #d9a441; stroke-width: 2; }
.art-sunray { stroke: #d9a441; }
.art-thick { stroke-width: 6; }
.art-seed { fill: #c9a24a; }

/* 12-month strip: feeding months (/lawncare), sowing windows (/seeding). */
.lawn-cal { display: grid; grid-template-columns: repeat(12, 1fr); gap: 6px; }
.lawn-cal-m {
    position: relative; display: flex; flex-direction: column; align-items: center; gap: 8px;
    padding: 10px 2px 12px; border-radius: 10px; border: 1px solid var(--border);
    background: color-mix(in srgb, var(--season) 10%, var(--surface)); min-width: 0;
}
.lawn-cal-m::before { content: ""; position: absolute; top: 0; left: 8px; right: 8px; height: 3px; border-radius: 0 0 3px 3px; background: var(--season); opacity: 0.55; }
.lawn-cal-m.s-w  { --season: #6f8fb0; }
.lawn-cal-m.s-sp { --season: #7fb04f; }
.lawn-cal-m.s-su { --season: #d9a441; }
.lawn-cal-m.s-f  { --season: #c4733a; }
.lawn-cal-name { font-size: 0.74rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
.lawn-cal-dot { width: 22px; height: 22px; border-radius: 50%; border: 2px dashed var(--border-warm); }
.lawn-cal-lbl { font-size: 0.7rem; font-weight: 700; color: var(--text); text-align: center; line-height: 1.2; min-height: 1.7em; font-variant-numeric: tabular-nums; }
.lawn-cal-m.is-main { border-color: var(--lawn-green); background: color-mix(in srgb, var(--lawn-green) 16%, var(--surface)); }
.lawn-cal-m.is-main .lawn-cal-name { color: var(--text); }
.lawn-cal-m.is-main .lawn-cal-dot { border: 0; background: var(--lawn-green); box-shadow: 0 0 0 4px color-mix(in srgb, var(--lawn-green) 25%, transparent); }
.lawn-cal-m.is-alt { border-style: dashed; border-color: var(--lawn-green); }
.lawn-cal-m.is-alt .lawn-cal-dot { border-color: var(--lawn-green); background: color-mix(in srgb, var(--lawn-green) 30%, transparent); }
.lawn-cal-key { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 14px 0 0; font-size: 0.8rem; color: var(--text-muted); }
.lawn-cal-key span { display: inline-flex; align-items: center; gap: 7px; }
.lawn-cal-key i { width: 12px; height: 12px; border-radius: 50%; background: var(--lawn-green); }
.lawn-cal-key i.alt { background: color-mix(in srgb, var(--lawn-green) 30%, transparent); border: 1.5px dashed var(--lawn-green); }

/* Watering: the week's need split into rain and sprinkler, and the days. */
.lawn-need { margin: 20px 0 0; }
.lawn-need-bar { display: flex; height: 16px; border-radius: 999px; overflow: hidden; background: var(--surface-3); box-shadow: inset 0 1px 2px rgba(0,0,0,0.12); }
.lawn-need-bar span { height: 100%; transition: width .35s ease; }
.lawn-need-rain { background: repeating-linear-gradient(135deg, #6f8fb0 0 6px, #7f9fc0 6px 12px); }
.lawn-need-sprk { background: var(--lawn-water); }
.lawn-need-key { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 16px; margin-top: 8px; font-size: 0.8rem; color: var(--text-muted); }
.lawn-need-key b { color: var(--text); font-variant-numeric: tabular-nums; }
.lawn-need-key i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
.lawn-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-top: 18px; }
.lawn-day {
    display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 2px 12px; min-width: 0;
    border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
}
.lawn-day-name { font-size: 0.74rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
.lawn-day svg { width: 26px; height: 26px; }
.lawn-day .drop { fill: none; stroke: var(--border-warm); stroke-width: 1.6; stroke-dasharray: 3 3; }
.lawn-day-lbl { font-size: 0.7rem; font-weight: 700; color: var(--text); text-align: center; line-height: 1.25; min-height: 2.5em; }
.lawn-day.is-water { border-color: var(--lawn-water); background: color-mix(in srgb, var(--lawn-water) 14%, var(--surface)); }
.lawn-day.is-water .lawn-day-name { color: var(--text); }
.lawn-day.is-water .drop { fill: var(--lawn-water); stroke: none; }

@media (max-width: 1100px) {
    .lawn-steps { grid-template-columns: repeat(3, 1fr); row-gap: 30px; }
    .lawn-steps::before { display: none; }
}
@media (max-width: 768px) {
    .lawn-stats { grid-template-columns: 1fr 1fr; }
    .lawn-cal { grid-template-columns: repeat(6, 1fr); }
    .lawn-week { gap: 4px; }
    .lawn-day-lbl { font-size: 0.64rem; }
    .lawn-steps { grid-template-columns: 1fr 1fr; }
    .ph-ticks span:nth-child(even) { visibility: hidden; }
}
@media (max-width: 480px) {
    .lawn-steps { grid-template-columns: 1fr; }
    .lawn-step { display: grid; grid-template-columns: 96px 1fr; column-gap: 14px; align-items: start; padding-top: 16px; }
    .lawn-step-art { grid-row: span 2; margin: 0; }
    .lawn-step-n { left: 18px; transform: none; }
}

/* Degree Day & Chill Hour Calculator (degreedayspage.js). */
/* Labels wrap to different line counts on a phone; sit each field at the
   bottom of its cell so the boxes in a row line up. */
.dd-settings > div:not([hidden]) { display: flex; flex-direction: column; justify-content: flex-end; }
.dd-settings > div:first-child { justify-content: flex-start; }
.dd-big { display: flex; align-items: baseline; gap: 8px; }
.dd-num { font-size: 2.2rem; font-weight: 800; letter-spacing: -0.02em; color: var(--text); }
.dd-unit { font-size: 1rem; font-weight: 600; color: var(--text-muted); }
.dd-sub { margin: 2px 0 0; color: var(--text-muted); font-size: 0.9rem; }
.dd-compare { margin: 8px 0 0; font-size: 0.92rem; color: var(--text); }
.dd-chart { margin: 16px 0 6px; }
.dd-chart svg { display: block; width: 100%; height: auto; }
.dd-chart .dd-grid { stroke: var(--border-soft); stroke-width: 1; }
.dd-chart .dd-axis { fill: var(--text-muted); font-size: 11px; }
.dd-chart .dd-mark { stroke: var(--danger); stroke-width: 1.2; stroke-dasharray: 2 4; opacity: 0.7; }
.dd-chart .dd-avg { fill: none; stroke: var(--text-dim); stroke-width: 2; }
.dd-chart .dd-season { fill: none; stroke: var(--brand); stroke-width: 3; }
.dd-chart .dd-ahead { fill: none; stroke: var(--brand); stroke-width: 2; stroke-dasharray: 6 5; opacity: 0.7; }
.dd-chart .dd-dot { fill: var(--brand); }
.dd-key { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 6px; font-size: 0.78rem; color: var(--text-muted); }
.dd-key span { display: inline-flex; align-items: center; gap: 6px; }
.dd-key i { display: inline-block; width: 20px; height: 0; border-top: 3px solid var(--brand); }
.dd-key i.k-ahead { border-top: 2px dashed var(--brand); }
.dd-key i.k-avg { border-top: 2px solid var(--text-dim); }
.dd-key i.k-mark { border-top: 2px dotted var(--danger); }
.dd-table-wrap { overflow-x: auto; margin-top: 14px; }
.dd-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.dd-table th { text-align: left; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); padding: 6px 8px; border-bottom: 1px solid var(--border); }
.dd-table td { padding: 8px; border-bottom: 1px solid var(--border-soft); vertical-align: top; color: var(--text); }
.dd-table .dd-m-dd { font-weight: 700; white-space: nowrap; }
.dd-table .dd-m-avg { color: var(--text-muted); white-space: nowrap; }
.dd-done { color: var(--ok); font-weight: 600; }
.dd-cold { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border-soft); }
.dd-cold h3 { margin: 0 0 6px; font-size: 1rem; }
.dd-cold p:not(.hint) { margin: 0; font-size: 0.92rem; }
.dd-cold ul { margin: 8px 0 0; padding-left: 20px; font-size: 0.92rem; }
.dd-cold li { margin: 3px 0; }
.dd-cold-ok::marker { color: var(--ok); }
.dd-cold-some::marker { color: var(--text-dim); }
.dd-cold-bad::marker { color: var(--danger); }
.dd-cold-bad strong { color: var(--danger); }
.dd-alert-form { margin-bottom: 4px; }
.remind-row { display: flex; align-items: center; gap: 8px; font-size: 0.95rem; font-weight: 600; color: var(--text); cursor: pointer; }
.dd-alert-news { display: flex; align-items: center; gap: 8px; margin-top: 10px; font-size: 0.9rem; color: var(--text); }
.dd-alert-list { list-style: none; margin: 16px 0 0; padding: 12px 0 0; border-top: 1px solid var(--border-soft); }
.dd-alert-list li { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 8px 0; font-size: 0.9rem; }
.dd-alert-list li + li { border-top: 1px solid var(--border-soft); }
.dd-alert-list .btn { padding: 5px 12px; flex-shrink: 0; }
#alerts { scroll-margin-top: 90px; }

/* "Against target" bars (targetbars.js), shared by the soil test and the
   soilless mix lab report. The soil test adds its line-chart parts inline. */
.hist-svg { width:100%; min-width:520px; height:auto; display:block; }
.hist-svg .hx-grid { stroke:var(--border); stroke-width:1; }
.hist-svg .hx-target { stroke:var(--ok); stroke-width:1.5; stroke-dasharray:6 5; opacity:0.85; }
.hist-svg text { fill:var(--text-muted); font-size:13px; font-family:inherit; }
.hist-svg text.hx-target-lbl { fill:var(--ok); }

/* Line charts and Compare, shared by the soil test and the soilless mix
   lab history. Series colours run in fixed order, each stepped for the
   surface it sits on: the dark set for the espresso theme, the light set for
   cream. Past the eighth series the soil test runs them again dashed, so a
   hue is never invented. */
.hist-svg .hx-axis { stroke:var(--border); stroke-width:1.5; }
.hist-svg .hx-line { fill:none; stroke:var(--brand-light); stroke-width:2.5; stroke-linejoin:round; stroke-linecap:round; }
.cmp-pick { display:flex; flex-wrap:wrap; gap:6px 18px; margin-bottom:12px; }
.cmp-group { min-width:200px; }
.cmp-group .field-label { margin:0 0 4px; }
.cmp-opt { display:flex; align-items:center; gap:7px; font-size:0.9rem; color:var(--text-muted); padding:3px 0; cursor:pointer; }
.cmp-legend { display:flex; flex-wrap:wrap; gap:6px 16px; margin:4px 0 10px; font-size:0.85rem; color:var(--text-muted); }
.cmp-legend span::before { content:""; display:inline-block; width:12px; height:12px; border-radius:3px; background:var(--c); margin-right:6px; vertical-align:-1px; }
.cmp-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(250px, 1fr)); gap:14px; }
.cmp-grid .hist-svg { min-width:0; }
.cmp-grid .cmp-title { font-size:0.85rem; font-weight:700; color:var(--text); margin:0 0 2px; }
.hist-svg .cmp-bar { fill:var(--c); }
.hist-svg .cmp-target { stroke:var(--ok); stroke-width:2; stroke-dasharray:4 3; }
:root {
    --sx1:#3987e5; --sx2:#d95926; --sx3:#199e70; --sx4:#c98500;
    --sx5:#d55181; --sx6:#008300; --sx7:#9085e9; --sx8:#e66767;
}
:root[data-theme="light"] {
    --sx1:#2a78d6; --sx2:#eb6834; --sx3:#1baf7a; --sx4:#eda100;
    --sx5:#e87ba4; --sx6:#008300; --sx7:#4a3aa7; --sx8:#e34948;
}

/* Soil test: a container mix ends at the hand-off card (see soiltest.html). */
#toolWrap.is-container #containerHandoff ~ :not([data-keep]) { display: none !important; }

/* ── Top Dress Calculator (topdress.html) ──
   Reuses the lawn step cards and art classes; .td-page supplies their colors. */
.td-page { --lawn-green: #5f8f2f; --lawn-soil: #8a6a48; --lawn-water: #3f86b0; --td-dress: #6b4a2e; }
:root[data-theme="dark"] .td-page, :root:not([data-theme]) .td-page { --lawn-green: #9cc46a; --lawn-soil: #b08a62; --lawn-water: #7fb6d6; --td-dress: #d7a46c; }
.td-result { display: grid; grid-template-columns: minmax(200px, 280px) 1fr; gap: 22px; align-items: center; }
.td-viz svg { width: 100%; height: auto; display: block; }
.td-layer { fill: var(--td-dress); }
.td-dim { stroke: var(--text-dim); stroke-width: 1.5; }
.td-callout { stroke: var(--td-dress); stroke-width: 1.5; fill: none; }
.td-callout-text { fill: var(--td-dress); font: 800 13px/1 system-ui, sans-serif; }
.td-dim-text { fill: var(--text-muted); font: 600 11px/1 system-ui, sans-serif; }
.td-stats { margin-bottom: 0; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.td-stats .stat { --accent: var(--td-dress); }
.td-stats .stat:nth-child(2) { --accent: var(--lawn-green); }
.td-stats .stat:nth-child(3) { --accent: var(--lawn-water); }
.td-measure { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 6px; margin-top: 16px; }
.td-measure-lbl { width: 100%; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); }
.td-scoop { display: flex; flex-direction: column; align-items: center; gap: 3px; font-size: 0.68rem; color: var(--text-muted); }
.td-scoop svg { stroke: var(--td-dress); fill: color-mix(in srgb, var(--td-dress) 22%, transparent); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.td-scoop.cup svg { width: 34px; height: 34px; }
.td-scoop.tbsp svg { width: 24px; height: 24px; }
.td-scoop.tsp svg { width: 18px; height: 18px; }
@media (max-width: 640px) {
    .td-result { grid-template-columns: 1fr; }
    .td-result .td-stats { grid-template-columns: 1fr 1fr; gap: 10px; }
    .td-viz { max-width: 280px; margin: 0 auto; }
}

/* Favorites header dropdown (favmenu.js). On phones the star alone says it,
   which keeps the header on one row next to the other dropdowns. */
.fav-switch .system-current svg:first-child { color: var(--brand-light); }
@media (max-width: 480px) {
    .fav-switch .system-current .system-name { display: none; }
}

/* Live result numbers (livenumbers.js): a tile whose number just changed
   pulses once in its own accent colour while the number counts to the new
   value. Never added under prefers-reduced-motion. */
@keyframes statBump {
    0%   { border-color: var(--accent, var(--brand)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--brand)) 28%, transparent), var(--shadow-sm); }
    100% { box-shadow: 0 0 0 0 transparent, var(--shadow-sm); }
}
@keyframes statPop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.06); }
    100% { transform: scale(1); }
}
.stat.num-bump { animation: statBump .7s ease-out; }
.stat.num-bump .stat-value { animation: statPop .38s ease-out; transform-origin: left center; }

/* Press feedback: buttons sink a touch while held, so a tap feels like it
   landed. Transform only, so nothing around them moves. */
.btn:active:not(:disabled), .system-current:active, .fav-btn:active { transform: translateY(1px) scale(0.97); transition-duration: .05s; }

/* Favorite star: a quick pop and a ring when a tool is starred (favorites.js
   adds .just-on). */
@keyframes favPop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.35) rotate(-12deg); }
    70%  { transform: scale(0.92) rotate(4deg); }
    100% { transform: scale(1); }
}
@keyframes favRing {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand-light) 55%, transparent); }
    100% { box-shadow: 0 0 0 10px transparent; }
}
.fav-btn.just-on { animation: favPop .45s ease-out, favRing .6s ease-out; }

/* Short confirmation note, bottom centre (favorites.js toast()). */
.bz-toast {
    position: fixed; left: 50%; bottom: calc(22px + env(safe-area-inset-bottom, 0px)); z-index: 200;
    transform: translate(-50%, 12px); opacity: 0; pointer-events: none;
    display: inline-flex; align-items: center; gap: 8px; max-width: calc(100vw - 32px);
    background: var(--surface); color: var(--text); border: 1px solid var(--border-warm, var(--border));
    border-radius: 999px; padding: 10px 16px; box-shadow: var(--shadow);
    font-size: 0.88rem; font-weight: 600;
    transition: opacity .2s ease, transform .2s ease;
}
.bz-toast.show { opacity: 1; transform: translate(-50%, 0); }
.bz-toast svg { color: var(--brand-light); flex: none; }

@media (prefers-reduced-motion: reduce) {
    .btn:active:not(:disabled), .system-current:active, .fav-btn:active { transform: none; }
    .fav-btn.just-on { animation: none; }
    .bz-toast { transition: opacity .2s ease; transform: translate(-50%, 0); }
}

/* Popular This Week (popular.js): sits above the Extension box, in the same
   brand-edged card, as a short ranked list of tools. */
.popular-box { margin-top: 26px; border-color: var(--brand); }
.popular-box h2 { margin: 0 0 12px; font-size: 1.1rem; }
.popular-list { display: grid; gap: 8px; }
.popular-item {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px; border-radius: var(--radius-sm);
    background: var(--surface-2); border: 1px solid var(--border);
    color: var(--text); text-decoration: none;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
    animation: popularIn .35s ease-out both; animation-delay: calc(var(--i, 0) * 60ms);
}
.popular-item:hover { transform: translateX(3px); border-color: var(--brand); box-shadow: var(--shadow-sm); }
.popular-rank {
    flex: none; width: 26px; height: 26px; border-radius: 999px; display: grid; place-items: center;
    font-weight: 750; font-size: 0.85rem; font-variant-numeric: tabular-nums;
    background: color-mix(in srgb, var(--brand) 20%, transparent); color: var(--brand-light);
}
.popular-item:first-child .popular-rank { background: linear-gradient(180deg, var(--brand), var(--brand-dark)); color: #1a120a; }
.popular-item .tool-ico { flex: none; width: 32px; height: 32px; margin: 0; }
.popular-text { display: flex; flex-direction: column; min-width: 0; }
.popular-name { font-weight: 650; font-size: 0.95rem; }
.popular-blurb { color: var(--text-muted); font-size: 0.82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@keyframes popularIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .popular-item { animation: none; } .popular-item:hover { transform: none; } }
.popular-item .tool-ico svg { width: 18px; height: 18px; }

/* Popular This Week in the pinned side rail (wide screens): compact rows,
   name only, so five fit above the Extension card. */
@media (min-width: 1460px) {
    .side-rail.is-pinned .popular-box { margin: 0; flex: none; padding: 16px; }
    .side-rail.is-pinned .popular-box h2 { font-size: 1rem; margin-bottom: 10px; }
    .side-rail.is-pinned .popular-list { gap: 6px; }
    .side-rail.is-pinned .popular-item { padding: 7px 8px; gap: 8px; }
    .side-rail.is-pinned .popular-item .tool-ico, .side-rail.is-pinned .popular-blurb { display: none; }
    .side-rail.is-pinned .popular-rank { width: 22px; height: 22px; font-size: 0.78rem; }
    .side-rail.is-pinned .popular-name { font-size: 0.86rem; line-height: 1.25; }
}

/* /upgrade: Homeowner sample tiles (images from tools/upgrade-samples/build.mjs). */
.up-samples { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin: 0 0 24px; }
.up-sample { display: flex; flex-direction: column; gap: 8px; padding: 8px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface, var(--card)); color: var(--text); text-decoration: none; font-weight: 650; font-size: 0.88rem; }
.up-sample:hover { border-color: var(--brand); }
.up-sample img { width: 100%; height: 150px; object-fit: cover; object-position: top; border-radius: 8px; border: 1px solid var(--border); background: #faf6ef; }
@media (max-width: 720px) {
    .up-samples { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
    .up-sample img { height: 120px; }
}

/* ─── Tool page demo clip (js/demo-video.js) ───
   Beside the intro on wide screens, so the tool doesn't move down; on phones
   a button after the intro opens it in place. */
.tool-demo { float: right; width: min(340px, 40%); margin: 4px 0 14px 28px; }
.tool-demo video {
    display: block; width: 100%; height: auto; aspect-ratio: 16 / 10;
    border-radius: 10px; border: 1px solid var(--border);
    background: var(--surface); box-shadow: var(--shadow);
}
.tool-demo-btn { display: none; }
.page-head:has(.tool-demo)::after { content: ""; display: block; clear: both; }
@media (max-width: 820px) {
    .page-head:has(.tool-demo) { display: flex; flex-direction: column; }
    .tool-demo { order: 2; float: none; width: auto; margin: 12px 0 0; }
    .tool-demo-btn { display: inline-flex; align-self: flex-start; }
    .tool-demo video { display: none; margin-top: 12px; }
    .tool-demo.open video { display: block; }
}

/* ─────────────  Airflow Planner  ───────────── */
.af-house { display: flex; flex-wrap: wrap; gap: 12px; }
.af-house > div { flex: 1 1 120px; max-width: 180px; }
.af-house > .af-house-save { flex: 0 0 auto; max-width: none; display: flex; flex-direction: column; }
.af-house-save .btn { white-space: nowrap; }
/* Studio layout (js/airflowstudio.js): the house fields move to the Room window. */
body.studio .af-house { display: none; }
body.studio #afToolbar #savePlanTop { margin-left: auto; }
.af-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 14px 0 8px; }
.af-toolbar .btn { padding: 8px 12px; font-size: 0.88rem; }
.af-toolbar .btn:disabled { opacity: 0.45; cursor: default; }
.af-sep { width: 1px; align-self: stretch; background: var(--border); margin: 2px 4px; }
.af-toolbar-adv { margin-top: 0; }
:root .btn.btn-ghost.af-toggle[aria-pressed="true"] { border-color: #e0703a; color: color-mix(in srgb, #e0703a 70%, var(--text)); background: color-mix(in srgb, #e0703a 16%, transparent); }
:root .btn.btn-ghost.af-toggle[aria-pressed="false"] { color: var(--text-dim); }
.af-mapbar { display: flex; align-items: center; justify-content: space-between; gap: 8px 12px; flex-wrap: wrap; margin: 0 0 10px; }
.af-mapbar .hint { flex: 1 1 260px; }
.af-mapbar-btns { display: flex; gap: 6px; }
.af-view-btn { padding: 5px 11px; font-size: 0.82rem; }
/* :root and .btn-ghost outrank the light theme's ghost button colours. */
:root .btn.btn-ghost.af-view-btn[aria-pressed="true"] { border-color: var(--ok); color: var(--ok); background: color-mix(in srgb, var(--ok) 16%, transparent); }
.af-table tr.af-off td { opacity: 0.55; }
.af-unit-in { display: inline-flex; align-items: center; gap: 4px; }
.af-table .af-unit-in input { width: 44px; }
.af-table-wrap { margin-top: 16px; }
.af-table input, .af-table select { min-width: 0; width: 100%; padding: 6px 8px; }
.af-table td { vertical-align: middle; }
.af-table th, .af-table td { padding-left: 6px; padding-right: 6px; }
.af-table td:nth-child(2) select { width: 76px; }
.af-table td:nth-child(3) input { width: 56px; }
.af-table td:nth-child(4) input { width: 68px; }
.af-table td:nth-child(5) input { width: 56px; }
.af-table td:nth-child(6) input, .af-table td:nth-child(7) input, .af-table td:nth-child(8) input { width: 64px; }
.af-dir { display: flex; gap: 6px; width: 196px; }
.af-dir select { flex: 1 1 auto; }
.af-dir.is-custom select { flex: 0 0 140px; }
.af-dir input { flex: 1 1 0; }
.af-remove { padding: 4px 10px; font-size: 1.1rem; line-height: 1; }
.af-edit { margin-top: 12px; padding: 12px 14px; border: 1px solid var(--border-soft); border-radius: var(--radius-sm); background: var(--surface-2); }
.af-edit[hidden] { display: none; }
.af-edit-title { margin: 0 0 8px; font-weight: 600; font-size: 0.9rem; }
.af-edit-group + .af-edit-group { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border-soft); }
.af-edit-grid { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.af-edit-field { flex: 1 1 78px; min-width: 0; }
.af-edit-grid input, .af-edit-grid select { width: 100%; min-width: 0; padding: 6px 8px; }
.af-edit-dir { flex: 2 1 180px; }
.af-edit-act { flex: 0 0 auto; }
.af-edit-act .btn { white-space: nowrap; padding: 7px 12px; font-size: 0.88rem; }
.af-edit .af-dir { width: 100%; }
.af-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-bottom: 12px; font-size: 0.88rem; }
.af-key { display: inline-flex; align-items: center; gap: 6px; }
.af-swatch { width: 14px; height: 14px; border-radius: 3px; display: inline-block; }
.af-map-wrap { width: 100%; }
.af-map { display: block; touch-action: none; cursor: crosshair; border-radius: var(--radius-sm); }
.af-checks .message { margin-top: 10px; }
/* Airflow Planner right-click menu: shortcut on the right of each item. */
.af-context button { display: flex; align-items: center; gap: 18px; }
.af-key { margin-left: auto; color: var(--text-dim); font-size: 0.78rem; white-space: nowrap; }
@media (pointer: coarse) { .af-keys { display: none; } }
/* Airflow Planner side view */
.af-side { margin-top: 22px; }
.af-side-head { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 16px; margin-bottom: 10px; }
.af-side-head h3 { margin: 0 auto 8px 0; font-size: 1rem; }
.af-side-cut { width: 200px; }
.af-side-cut input { width: 100%; }
.af-slider { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px; margin: 10px 0 0; }
.af-slider .field-label { margin: 0; }
.af-slider input[type="range"] { flex: 1 1 220px; min-width: 160px; accent-color: var(--brand); }
.af-side .af-slider { margin: 0 0 10px; }
.af-slider .hint { flex: 0 1 auto; }
/* One-question feedback box (js/poll.js). In the page by default; pinned
   in the right margin on wide screens when it fits. */
.poll-box { margin-top: 22px; border-color: var(--brand); }
.poll-box:not(.is-pinned) { position: relative; }
.poll-box[hidden] { display: none; }
.poll-box h2 { padding-right: 24px; }
.poll-close { position: absolute; top: 6px; right: 6px; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--text-muted); font-size: 1.3rem; line-height: 1; cursor: pointer; }
.poll-close:hover, .poll-close:focus-visible { color: var(--text); background: var(--surface-2); }
.poll-box h2 { margin: 0 0 6px; font-size: 1.05rem; }
.poll-box p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
.poll-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.poll-acts .btn[aria-pressed="true"] { border-color: var(--brand); color: var(--brand); }
.poll-box .poll-thanks { margin-top: 10px; color: var(--text); }
.poll-box.is-pinned {
    position: fixed; top: 140px; width: 190px; z-index: 5; margin: 0; padding: 16px;
}
.poll-box.is-pinned h2 { font-size: 1rem; }
.poll-box.is-pinned p { font-size: 0.84rem; }
.poll-box.is-pinned .poll-acts { flex-direction: column; }
.poll-box.is-pinned .poll-acts .btn { width: 100%; margin: 0; }
.poll-acts .btn[aria-pressed="true"]:disabled { opacity: 1; }

/* A newly released tool on the Toolshop: a soft breathing glow and a tilted
   "New!" sticker on the top edge (clear of the hide button). */
.tool-card-new {
    border-color: color-mix(in srgb, var(--brand) 60%, var(--border));
    animation: new-card-glow 3.2s ease-in-out infinite;
}
@keyframes new-card-glow {
    0%, 100% { box-shadow: var(--shadow-sm), 0 0 0 1px color-mix(in srgb, var(--brand) 25%, transparent), 0 0 10px color-mix(in srgb, var(--brand) 18%, transparent); }
    50%      { box-shadow: var(--shadow-sm), 0 0 0 1px color-mix(in srgb, var(--brand) 50%, transparent), 0 0 24px color-mix(in srgb, var(--brand) 42%, transparent); }
}
.new-badge {
    position: absolute; top: -10px; right: 48px; z-index: 2;
    padding: 3px 10px; border-radius: 999px;
    /* A shade under --brand: white on --brand is 4.44:1, just under AA. */
    background: color-mix(in srgb, var(--brand) 90%, #000); color: #fff;
    font-size: 0.72rem; font-weight: 800; letter-spacing: 0.03em;
    box-shadow: 0 2px 8px color-mix(in srgb, var(--brand) 45%, transparent);
    transform: rotate(-6deg);
    animation: new-badge-wiggle 4s ease-in-out infinite;
    pointer-events: none;
}
@keyframes new-badge-wiggle {
    0%, 86%, 100% { transform: rotate(-6deg) scale(1); }
    90%           { transform: rotate(4deg) scale(1.08); }
    94%           { transform: rotate(-10deg) scale(1.04); }
}
/* List view: the sticker sits right after the tool name instead. */
.tool-grid.as-list .tool-card-new h3 { flex: 0 1 auto; }
.tool-grid.as-list .tool-card-new .new-badge { position: static; margin-right: auto; flex: none; }
/* Phones: no room in the row, so the sticker goes back on the top edge,
   over the name, and the buttons stay on one line. */
@media (max-width: 640px) {
    .tool-grid.as-list .tool-card-new .new-badge { position: absolute; top: -9px; left: 58px; right: auto; margin: 0; padding: 2px 8px; font-size: 0.66rem; }
    .tool-grid.as-list .tool-card-new h3 { flex: 1 1 0; }
}
/* The same sticker on the Bizon Off-Grid header link (theme.js), smaller,
   on the link's top-right corner. In the phone menu it sits after the label. */
.nav-offgrid { position: relative; }
.nav-offgrid .new-badge { top: -9px; right: -8px; padding: 1px 6px; font-size: 0.6rem; }
.nav.has-menu .nav-offgrid .new-badge { position: static; }
@media (prefers-reduced-motion: reduce) {
    .tool-card-new { animation: none; box-shadow: var(--shadow-sm), 0 0 0 1px color-mix(in srgb, var(--brand) 45%, transparent), 0 0 16px color-mix(in srgb, var(--brand) 30%, transparent); }
    .new-badge { animation: none; }
}
.lm-keys kbd {
    display: inline-block; padding: 0 5px; font: inherit; font-size: 0.8rem; line-height: 1.5;
    border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 4px; background: var(--surface-2); color: var(--text);
}

/* Optimize button: a soft pulsing glow so the optimizer gets noticed. */
#lmOptOpen { position: relative; border-color: var(--accent, var(--brand)); color: var(--accent, var(--brand)); box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent, var(--brand)) 55%, transparent); animation: lm-opt-glow 2.4s ease-in-out infinite; }
@keyframes lm-opt-glow {
    0%, 100% { box-shadow: 0 0 8px 1px color-mix(in srgb, var(--accent, var(--brand)) 55%, transparent); }
    50% { box-shadow: 0 0 22px 6px color-mix(in srgb, var(--accent, var(--brand)) 85%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
    #lmOptOpen { animation: none; box-shadow: 0 0 10px 2px color-mix(in srgb, var(--accent, var(--brand)) 45%, transparent); }
}

/* ─── Studio layout (js/studio.js) ───
   A tool's main view fills the page, with setting tiles above it. A tile
   opens the side window that holds its group of fields. Plan and per-tool
   list: docs/studio-layout-todo.md. Page-specific rules follow each tool's
   own block. */
body.studio [data-studio-hide] { display: none !important; }
body:not(.studio) .studio-only { display: none !important; }
/* Water Quality: the four-column ion table fits the window without a scroll. */
.studio-drawer-body #ionTable :is(td, th) { padding-left: 6px; padding-right: 6px; }
.studio-drawer-body #ionTable input { width: 56px; min-width: 0; padding-left: 8px; padding-right: 6px; }
body.studio .page { max-width: 1320px; }
.studio-bar { display: flex; gap: 10px; align-items: stretch; margin: 6px 0 14px; }
.studio-bar[hidden] { display: none; }
.studio-tiles { flex: 1; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.studio-tile {
    display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0;
    padding: 11px 13px 12px; text-align: left; cursor: pointer; font: inherit; color: var(--text);
    background: var(--surface); border: 1.5px solid var(--border); border-radius: 12px;
    transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.studio-tile:hover { transform: translateY(-2px); border-color: var(--brand); box-shadow: var(--shadow); }
.studio-tile:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--brand); }
.studio-tile.active { border-color: var(--brand); box-shadow: var(--ring); }
.studio-tile-head {
    display: flex; align-items: center; gap: 6px; font-size: 0.72rem; font-weight: 650;
    letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted);
}
.studio-tile-head svg { width: 15px; height: 15px; flex: none; color: var(--brand); }
.studio-tile-main { font-weight: 700; font-size: 0.98rem; line-height: 1.25; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.studio-tile-sub { font-size: 0.78rem; color: var(--text-muted); line-height: 1.3; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The side window. A bottom sheet on phones. */
.studio-drawer {
    position: fixed; top: 0; right: 0; bottom: 0; z-index: 60; width: min(440px, 100vw);
    display: flex; flex-direction: column; background: var(--surface);
    border-left: 1px solid var(--border); box-shadow: -18px 0 40px -24px rgba(0,0,0,0.45);
    transform: translateX(104%); transition: transform 0.22s ease;
}
.studio-drawer[hidden] { display: none; }
.studio-drawer.open { transform: none; }
.studio-drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.studio-drawer-head h2 { margin: 0; font-size: 1.12rem; font-weight: 750; }
.studio-drawer-x { display: inline-flex; padding: 6px; border: 0; border-radius: 8px; background: none; color: var(--text-muted); cursor: pointer; }
.studio-drawer-x:hover { color: var(--text); background: var(--surface-2); }
.studio-drawer-body { flex: 1; overflow-y: auto; padding: 18px 20px 24px; overscroll-behavior: contain; }
.studio-drawer-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 14px 20px; border-top: 1px solid var(--border); }
.studio-drawer-foot[hidden], .studio-drawer-body > [hidden] { display: none !important; }
.studio-drawer-grid { grid-template-columns: 1fr 1fr; }
.studio-drawer-grid > * { grid-column: 1 / -1; }
.studio-drawer-grid > .studio-half { grid-column: auto; }
/* Forms inside a window take one field per line: the window is narrow and tall. */
.studio-drawer-body .settings-grid:not(.studio-drawer-grid) { grid-template-columns: 1fr; }
.studio-drawer-body .settings-grid:not(.studio-drawer-grid) > * { grid-column: 1 / -1; }
/* A card borrowed into a window drops its own frame; the window is the frame. */
.studio-drawer-plain > .card, .studio-drawer-grid > .card { padding: 0; border: 0; box-shadow: none; background: none; margin: 0; }

@media (min-width: 1100px) {
    body.studio.studio-open .page { max-width: min(1320px, calc(100vw - 480px)); margin-right: 464px; margin-left: max(16px, calc((100vw - 464px - 1320px) / 2)); }
}
@media (max-width: 760px) {
    .studio-bar { flex-direction: column; }
    .studio-tiles { grid-template-columns: repeat(2, 1fr); }
    /* An odd tile out takes the whole last row instead of half of it. */
    .studio-tiles > .studio-tile:last-child:nth-child(odd) { grid-column: 1 / -1; }
    .studio-drawer {
        top: auto; left: 0; width: 100%; height: min(82vh, 720px); border-left: 0;
        border-top: 1px solid var(--border); border-radius: 18px 18px 0 0;
        box-shadow: 0 -18px 40px -20px rgba(0,0,0,0.45); transform: translateY(104%);
    }
    .studio-drawer-head { position: relative; padding-top: 22px; }
    .studio-drawer-head::before { content: ""; position: absolute; top: 8px; left: 50%; width: 40px; height: 4px; margin-left: -20px; border-radius: 2px; background: var(--border); }
}
@media (prefers-reduced-motion: reduce) {
    .studio-drawer, .studio-tile { transition: none; }
    .studio-tile:hover { transform: none; }
}

/* Grow Light Planner in the studio layout (js/lightstudio.js). */
body.studio #lmOptOpen { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 650; }
body.studio #lmOptOpen:hover { filter: brightness(1.06); }
body.studio:not(.ui-expert) #lmOptOpen { display: none; }
.studio-tile-spec { display: flex; width: 100%; height: 12px; margin: 4px 0 2px; border-radius: 6px; overflow: hidden; gap: 2px; background: var(--surface); }
.studio-tile-spec i { display: block; min-width: 0; }
.studio-tile-spec .s-b { background: #4a7bd8; }
.studio-tile-spec .s-g { background: #5aa85b; }
.studio-tile-spec .s-r { background: #d4493f; }
.studio-tile-spec .s-fr { background: #7d1f2b; }
body.studio .lm-map-card { padding-top: 16px; }
.studio-drawer-body #lmFixBox { margin-top: 4px; }
.studio-drawer-body #lmFixBox.lm-fix-box { border: 0; padding: 0; }
.studio-drawer-body #lmFixBox .lm-fix-box-title { display: none; }
.studio-drawer-body .lm-opt-grid { grid-template-columns: 1fr 1fr; }
.studio-drawer-body .lm-opt-grid > :has(#lmOptPowerSeg) { grid-column: 1 / -1; }
.studio-drawer-body .lm-opt-mini { flex-wrap: nowrap; }
.studio-drawer-body .lm-opt-mini button { white-space: nowrap; }
.studio-drawer-body .lm-opt-circ { grid-template-columns: 1fr 1fr 72px; }
.studio-drawer-body #lmOptBars + .hint { display: block; margin-top: 8px; }
.studio-drawer-body .lm-opt { margin: 0; padding: 0; border: 0; background: none; }
.studio-drawer-body .lm-opt .lm-insp-head { display: none; }
.studio-drawer-body #lmSaveCard, .studio-drawer-body #lmRecipeGrid > .card { padding: 0; border: 0; box-shadow: none; background: none; }
.studio-drawer-body #customFormTitle { display: none; }
.studio-drawer-body .custom-fields > .bed-field { flex: 1 1 100% !important; }
.studio-drawer-body .custom-fields input:not([type="color"]), .studio-drawer-body .custom-fields select { width: 100%; }
/* A help tile: a big question mark on the right. */
/* Soil Test (soilteststudio.js) */
.studio-drawer-body .fold-hidden{display:block !important}
.studio-drawer-body #stSampleRow{flex-direction:column}
.studio-drawer-body #stSampleRow > div{max-width:none !important;flex:none !important}
.studio-drawer-body #stSampleRow input{width:100%}
.studio-drawer-body #appliedPanel > summary,.studio-drawer-body #prodPanel > summary{display:none}
.studio-drawer-body #prodPanel{margin-top:0 !important}
.studio-drawer-body #sizePanel{padding-bottom:0 !important;margin-bottom:0 !important;border-bottom:0 !important}
.studio-drawer-body #stReportsCard #testName,.studio-drawer-body #stReportsCard #moveSpace{max-width:none !important;flex:1 1 100%}
.studio-drawer-body #stReportsCard #compareBtn{margin-left:0 !important}
.studio-drawer-plain > #addToCalendar{margin-top:14px}
/* Fertigation Calculator: Irrigation Setup's system picker needs an account */
body:not([data-setup="on"]) #setupSystemCard{display:none}
#setupSystemCard + #systemBar{margin-top:14px}
.studio-drawer-body #waterTestSelect,.studio-drawer-body #recipeSetupSelect,.studio-drawer-body #waterType{flex:1 1 100% !important;max-width:none !important;min-width:0 !important}
.studio-tile:has(.studio-tile-q) { position: relative; padding-right: 64px; }
.studio-tile-q { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; font-size: 1.5rem; font-weight: 800; line-height: 1; color: var(--brand); background: color-mix(in srgb, var(--brand) 14%, transparent); }
/* Fertilizer Calculator help window (js/calcstudio.js). */
.studio-drawer-body #calcHowTo { background: none !important; border: 0 !important; padding: 0 !important; }
.studio-drawer-body #calcHowTo > p:first-child { display: none; }
.studio-drawer-body #calcTourHint { margin: 16px 0 0 !important; padding-top: 14px; border-top: 1px solid var(--border-soft); }
/* Garden Planner windows (js/gardenstudio.js): one field per line. */
.studio-drawer-body #gpGardenControls, .studio-drawer-body #gpClimate { display: flex; flex-direction: column; align-items: stretch; gap: 14px; margin: 0; }
.studio-drawer-body #gpGardenControls .bed-field, .studio-drawer-body #gpClimate .bed-field { width: 100%; max-width: none; }
.studio-drawer-body #gpGardenControls select, .studio-drawer-body #gpGardenControls input, .studio-drawer-body #gpClimate input[type="text"], .studio-drawer-body #gpClimate select { width: 100%; }
.studio-drawer-body #gpGardenControls .bed-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.studio-drawer-body #lmRecipeGrid { display: grid; grid-template-columns: 1fr; gap: 22px; margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--border); }
.studio-drawer-body .lm-roster-row { grid-template-columns: 1fr 1fr; }
body.studio #lmWrap { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 12px; }
body.studio #lmWrap > .lm-bar { min-height: 44px; align-items: center; }
body.studio #lmWrap > * { flex: 1 1 100%; min-width: 0; }
body.studio #lmWrap > :is(.lm-controls, .lm-bar) { flex: 0 1 auto; margin: 0; }
body.studio #lmWrap > .lm-controls { display: flex; gap: 12px; align-items: center; }
body.studio #lmWrap > .lm-controls { flex-wrap: wrap; }
body.studio #lmAddType { min-width: 150px; }
body.studio #lmWrap > .lm-controls .field-label { display: none; }
body.studio #lmWrap > .lm-controls .hint:empty { display: none; }
body.studio #lmWrap > .lm-controls .hint { margin: 2px 0 0; font-size: 0.74rem; }
body.studio #lmWrap > .lm-bar { gap: 8px; }
body.studio #lmWrap > .lm-bar .hint { display: none; }
body.studio #lmWrap > .lm-stagewrap { margin-top: 6px; }

/* ── /offgrid landing page ── */
.og { max-width: 980px; }
.og-hero { position: relative; display: grid; grid-template-columns: 1.3fr 1fr; align-items: center; gap: 24px; padding: 48px 40px; margin: 8px 0 18px; border: 1px solid var(--border); border-radius: calc(var(--radius) * 1.5); overflow: hidden;
    background:
        repeating-radial-gradient(circle at 85% 120%, transparent 0 22px, var(--border-soft) 22px 23px),
        linear-gradient(135deg, var(--surface) 0%, var(--surface-2) 100%); }
.og-eyebrow { display: inline-block; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--brand-light); border: 1px solid var(--brand); border-radius: 999px; padding: 4px 12px; margin-bottom: 14px; }
.og-hero h1 { font-size: clamp(2.2rem, 5vw, 3.4rem); line-height: 1.05; margin: 0 0 14px; letter-spacing: -0.02em; }
.og-lede { font-size: 1.12rem; color: var(--text-muted); margin: 0 0 22px; max-width: 34ch; }
.og-cta { display: flex; flex-wrap: wrap; gap: 10px; }
.og-hero-art { width: 100%; max-width: 300px; justify-self: center; color: var(--brand); filter: drop-shadow(0 10px 24px rgba(0,0,0,0.25)); }
.og-strip { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; padding: 0; margin: 0 0 30px; }
.og-strip li { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); font-weight: 650; }
.og-strip svg { color: var(--brand); flex: none; }
.og-sec { margin: 0 0 34px; }
.og-sec > h2, .og-band h2 { font-size: 1.5rem; margin: 0 0 10px; letter-spacing: -0.01em; }
.og-sec > p { font-size: 1.05rem; line-height: 1.6; margin: 0 0 10px; max-width: 70ch; }
.og-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.og-version { padding: 24px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease; }
.og-version:hover { transform: translateY(-3px); border-color: var(--brand); box-shadow: var(--shadow-lift); }
.og-version-ico { width: 52px; height: 52px; display: grid; place-items: center; border-radius: 14px; background: var(--surface-3); color: var(--brand); margin-bottom: 14px; }
.og-version h3 { margin: 0 0 8px; font-size: 1.2rem; }
.og-version p { margin: 0 0 14px; color: var(--text-muted); line-height: 1.55; }
.og-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.og-tag { display: inline-block; font-size: 0.8rem; font-weight: 650; padding: 4px 10px; border-radius: 999px; background: var(--surface-3); color: var(--text); }
.og-chips { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; padding: 0; margin: 14px 0 0; }
.og-chips li { padding: 7px 13px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface-2); font-size: 0.9rem; }
.og-band { margin: 0 0 34px; padding: 36px 32px; border-radius: calc(var(--radius) * 1.5); text-align: center; background: linear-gradient(135deg, var(--brand-deep), var(--brand-dark)); color: #fff; }
.og-band p { font-size: 1.15rem; margin: 0 auto; max-width: 52ch; opacity: 0.95; }
@media (max-width: 760px) {
    .og-hero { grid-template-columns: 1fr; padding: 32px 22px; text-align: left; }
    .og-hero-art { max-width: 180px; grid-row: 1; justify-self: start; }
    .og-strip, .og-grid2 { grid-template-columns: 1fr; }
    .og-band { padding: 28px 20px; }
}
@media (prefers-reduced-motion: reduce) { .og-version { transition: none; } .og-version:hover { transform: none; } }
