@import url('theme.css?v=247d86a');

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

:root {

  /* Brand green, refined into a small scale for depth without garishness */
  --radius-sm:    10px;
  --radius:       14px;
  --radius-lg:    20px;
  --radius-pill:  999px;

  /* Layered elevation system (neutral, soft) */
  --ease:         cubic-bezier(0.2, 0.6, 0.2, 1);
  --t-fast:       120ms var(--ease);
  --t:            180ms var(--ease);

  /* ── Type scale ───────────────────────────────────────────────────────────
     Was 38 distinct font sizes across 159 declarations, 15 of them inside the
     0.75-0.95rem band all doing the same job: small muted meta text. A 0.4px
     difference is not hierarchy, it is noise, so those collapse to three steps.
     Every font-size in THIS file now uses one of these. The 18 page-level
     <style> blocks still carry about 180 of their own, which is the next
     target. Reach for the nearest token; if a new size feels necessary, the
     scale is probably wrong rather than the case being special. */
  --fs-2xs:  0.68rem;   /* micro badges, uppercase kickers */
  --fs-xs:   0.74rem;   /* chips, pills, timestamps */
  --fs-sm:   0.8rem;    /* secondary and meta text, small buttons */
  --fs-md:   0.86rem;   /* dense body copy inside cards */
  --fs-base: 0.92rem;   /* body */
  --fs-lg:   1.1rem;    /* card and section titles */
  --fs-xl:   1.3rem;    /* panel headings */
  --fs-2xl:  1.6rem;    /* page titles */
  --fs-3xl:  2.2rem;    /* hero */

  /* ── Spacing scale ────────────────────────────────────────────────────────
     A 4px base. Introduced with the card paddings and the button sizes below,
     which are the two places the old values were worst: seven different card
     paddings and no shared button height.

     HONEST STATUS: the wider sweep has NOT been done. style.css still contains
     roughly 370 hardcoded spacing values, and the 18 page-level <style> blocks
     are untouched. These tokens exist so new work has something correct to
     reach for, not because the file is now consistent. Unused steps are kept
     deliberately so the scale is complete rather than gap-toothed; do not read
     their presence as a claim that spacing has been normalised. */
  --sp-1:  0.25rem;
  --sp-2:  0.5rem;
  --sp-3:  0.75rem;
  --sp-4:  1rem;
  --sp-5:  1.25rem;
  --sp-6:  1.5rem;
  --sp-8:  2rem;
  --sp-half: 0.375rem;  /* the 0.3-0.45rem cluster: tight gaps inside a control */

  /* ── Buttons ──────────────────────────────────────────────────────────────
     Two sizes and no more. Every control is either a call to action or an
     action inside a card; a third size has never been needed, and each one
     added makes the other two mean less.

     The 0.6rem vertical is deliberately OFF the 4px scale. A .btn rendered as
     an <a> inherits body's line-height 1.6, so 0.6rem computes to about 45px
     tall and clears the 44px touch target, where var(--sp-2) at 0.5rem drops
     it to 42.5px. The scale serves the design, not the reverse, and a control
     you have to aim at is the wrong place to round for tidiness. */
  --btn-pad:    0.6rem var(--sp-5);
  --btn-pad-sm: var(--sp-half) var(--sp-3);

  /* ── Card padding ─────────────────────────────────────────────────────────
     Two sizes, not the seven this file used to have. A card in a list, and a
     full panel.

     --pad-panel is a named token rather than an inlined value because two
     negative margins are computed from it to bleed a child to the card edge:
     .modal-header, and .plot-soil-line in garden.html. Written out separately
     they drift and the bleed stops lining up. Always
     calc(var(--pad-panel) * -1), never a literal. */
  --pad-card:  var(--sp-4) var(--sp-5);   /* 16/20px: cards in a list */

  /* Fluid, not stepped. This was 24px with a media query dropping it to 16px
     below 700px, which meant the whole page lurched as a window crossed that
     line and a 600px tablet got phone padding it did not need.
     clamp(16px, 8px + 2vw, 24px) rises smoothly instead: 16px at or below
     400px, 24px from 800px up. Both ends are the values that were already
     there, so a phone and a desktop render exactly as before and only the
     widths between them change.
     calc(var(--pad-panel) * -1) still works: clamp() is valid inside calc, so
     the bleeds that depend on it (.modal-header, .plot-soil-line and .bed-band
     in garden.html) track it automatically. */
  --pad-panel: clamp(1rem, 0.5rem + 2vw, 1.5rem);   /* 16px -> 24px */
}

body {
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  /* Soft, barely-there green wash + dot texture so the canvas isn't dead flat. */
  background-color: var(--bg);
  background-image:
    radial-gradient(rgba(64, 145, 108, 0.05) 1px, transparent 1px),
    linear-gradient(180deg, var(--green-pale) 0%, var(--bg) 240px);
  background-size: 22px 22px, 100% 100%;
  background-attachment: fixed;
  color: var(--text);
  line-height: 1.6;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Tighter, more confident headings */
h1, h2, h3 {
  letter-spacing: -0.02em;
  line-height: 1.2;
}

a {
  color: var(--green-dark);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* ── Base polish: focus, motion, scrollbar ── */
:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--radius-sm);
}

/* App-like tactile press on interactive controls */
button, .btn, .qchip, .tag-chip {
  transition: background var(--t-fast), color var(--t-fast),
              border-color var(--t-fast), box-shadow var(--t), transform var(--t-fast);
}
button:active, .btn:active { transform: translateY(0.5px) scale(0.985); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* Slim, modern scrollbars (WebKit) */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: #bcc9bc; }

/* ── Header ── */
header {
  background: linear-gradient(180deg, var(--green-dark) 0%, var(--green-dark) 100%);
  color: #fff;
  /* Keep the bar full-width, but center its contents in the same 1180px band as
     the page so the logo lines up with the content's left edge and the actions
     line up with its right edge (no dead space past the sidebar). */
  padding: 0 max(1rem, calc((100% - 1180px) / 2 + 1.25rem));
  display: flex;
  align-items: center;
  gap: 0.5rem;
  height: 58px;
  box-shadow: inset 0 -1px 0 rgba(255,255,255,0.06), 0 2px 14px rgba(16,40,28,0.16);
  position: sticky;
  top: 0;
  z-index: 200;
}

.logo {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

.logo-img {
  height: 44px;
  width: auto;
  display: block;
}

.logo-text {
  font-size: var(--fs-xl);
  font-weight: 800;
  color: #fff;
  letter-spacing: -0.3px;
}

.logo-text-accent {
  color: var(--green-light);
}

/* REMOVED: .gl-mid / .gl-pro, which rendered "PROMEGA" as a small text kicker
   above the wordmark. No page ever used them, and the name is deliberately off
   the app now: the mark is hosted on a domain Promega does not own, and
   corporate branding on a third-party domain is the signal phishing filters key
   on. Do not reintroduce it without the custom domain to back it up. */

.login-logo {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 1.25rem;
  text-decoration: none;
}

.logo-img-auth {
  height: 72px;
  width: auto;
  display: block;
  margin: 0 auto;
}

.logo-text-auth {
  font-size: var(--fs-2xl);
  font-weight: 800;
  color: var(--green-dark);
  letter-spacing: -0.5px;
  margin-top: 0.4rem;
}

/* ── Header nav groups ── */
.header-left,
.header-right {
  display: flex;
  align-items: center;
  gap: 0.1rem;
  flex-shrink: 0;
}

/* Pin the bell + Logout to the right edge so wide screens don't leave a dead
   gap after them (the search no longer grows to fill it), and give the cluster
   (Feed · 🔔 · Logout) some breathing room instead of cramming it. */
.header-right {
  margin-left: auto;
  gap: 0.55rem;
}

.header-left a,
.header-right a {
  color: rgba(255,255,255,0.82);
  font-size: var(--fs-md);
  font-weight: 500;
  padding: 0.35rem 0.7rem;
  border-radius: 6px;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}

.header-left a:hover,
.header-right a:hover,
.header-left a.active,
.header-right a.active {
  background: rgba(255,255,255,0.14);
  color: #fff;
  text-decoration: none;
}

/* ── YouTube-style header search ── */
.header-search {
  /* Take up to 560px and shrink on small screens, but don't grow to eat the
     whole bar — the actions stay pinned right via .header-right's auto margin. */
  flex: 1 1 560px;
  max-width: 560px;
  margin: 0 0.75rem;
  display: flex;
  align-items: center;
  background: #fff;
  border-radius: 24px;
  padding: 0 0.9rem;
  height: 36px;
  border: 2px solid transparent;
  transition: border-color 0.15s;
  position: relative;
}

.header-search:focus-within {
  border-color: var(--green-mid);
}

.header-search .search-icon {
  font-size: var(--fs-base);
  flex-shrink: 0;
  margin-right: 0.4rem;
  opacity: 0.55;
}

.header-search input {
  flex: 1;
  border: none;
  outline: none;
  font-size: var(--fs-base);
  background: transparent;
  color: var(--text);
  min-width: 0;
}

.header-search input::placeholder {
  color: var(--text-muted);
}

.header-search .search-clear {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  padding: 0.1rem 0.25rem;
  border-radius: 50%;
  line-height: 1;
  transition: background 0.12s;
}

.header-search .search-clear:hover {
  background: var(--green-pale);
}

/* ── Search dropdown ── */
.search-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: -2px;
  right: -2px;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0,0,0,0.13);
  padding: 0.75rem 1rem;
  z-index: 400;
}

.search-dropdown-label {
  font-size: var(--fs-2xs);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 0.6rem;
}

.search-dropdown-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.search-dd-chip {
  background: var(--green-pale);
  border: 1.5px solid var(--green-light);
  border-radius: 20px;
  padding: 0.28rem 0.75rem;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--green-dark);
  cursor: pointer;
  font-family: inherit;
  transition: background 0.12s, border-color 0.12s;
}

.search-dd-chip:hover {
  background: var(--green-light);
  border-color: var(--green-mid);
}

/* keep the old .search-bar-wrap for page-level search (my-library etc.) */
.nav-user-link {
  font-size: var(--fs-md) !important;
  opacity: 0.85;
}

/* ── Secondary nav (personal tools) — kept for non-feed pages if needed ── */
.subnav {
  background: var(--surface);
  border-bottom: 1.5px solid var(--border);
  display: flex;
  gap: 0;
  padding: 0 1.5rem;
}

.subnav a {
  padding: 0.65rem 1.1rem;
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 2.5px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}

.subnav a:hover,
.subnav a.active {
  color: var(--green-dark);
  border-bottom-color: var(--green-dark);
  text-decoration: none;
}

/* ── Layout ── */
.container {
  max-width: 860px;
  margin: 0 auto;
  /* Gutters scale with the window for the same reason --pad-panel does: 16px
     at phone width, the original 20px from about 760px up. The vertical 2rem
     is unchanged; it is scroll distance, not something a narrow screen is
     short of. */
  padding: 2rem clamp(1rem, 0.5rem + 1.6vw, 1.25rem);
}

/* ── Filter bar ── */
/* An author display rule beats the UA's [hidden] { display: none }, so any
   flex container toggled with .hidden needs this companion. journal.html
   hides its plot filter this way and it stayed on screen. */
.filter-bar[hidden] { display: none; }

.filter-bar {
  display: flex;
  gap: 0.75rem;
  margin-bottom: 1.75rem;
  flex-wrap: wrap;
  align-items: center;
}

.filter-bar select,
.filter-bar input[type="text"] {
  padding: 0.5rem 0.75rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-base);
  outline: none;
  transition: border-color 0.15s;
  min-width: 160px;
}

.filter-bar select:focus,
.filter-bar input[type="text"]:focus {
  border-color: var(--green-mid);
}

.filter-bar .spacer {
  flex: 1;
}

/* ── Buttons ── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: var(--btn-pad);
  border-radius: var(--radius-pill);
  border: 1.5px solid transparent;
  cursor: pointer;
  font-size: var(--fs-base);
  font-weight: 650;
  letter-spacing: -0.01em;
  text-decoration: none;
}

.btn:hover {
  text-decoration: none;
  transform: translateY(-1px);
}

.btn-primary {
  background: var(--green-dark);
  color: #fff;
  box-shadow: var(--shadow-sm);
}

.btn-primary:hover {
  background: var(--green-dark);
  box-shadow: var(--shadow-md);
}

.btn-outline {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--green-dark);
}

/* Deliberately deeper than --green-pale. The hover fill used to BE --green-pale,
   which is also the fill of every soft-tinted panel in the app, so an outline
   button sitting on one (Visualize on the bed panel) dissolved into its own
   background the moment you pointed at it. The hover state has to be darker than
   any surface the button can land on, not equal to one of them. */
.btn-outline:hover {
  background: color-mix(in srgb, var(--green-light) 70%, var(--surface));
  border-color: var(--green-mid);
}

/* The third tier, below primary and outline. Row-level utilities that must stay
   reachable without competing: Harvest on a plant card, Edit and Delete on a log
   entry. The app had only two tiers, so every one of these had to borrow
   btn-outline and ended up looking as important as Visualize or Add Plant.
   A screen should carry ONE primary; everything else steps down from here. */
.btn-quiet {
  background: none;
  border-color: var(--border);
  color: var(--text-muted);
  font-weight: 600;
}

.btn-quiet:hover {
  background: var(--green-pale);
  border-color: var(--green-light);
  color: var(--green-dark);
}

/* Destructive, but at rest it is just another quiet action. Red belongs on the
   confirmation step, not sitting in the resting state inviting a misclick: a
   single log entry's Delete was the loudest thing on the whole plant card. */
.btn-quiet.btn-danger {
  color: var(--text-muted);
  border-color: var(--border);
  background: none;
}

.btn-quiet.btn-danger:hover {
  background: #fbe9e2;
  border-color: #f0cdbe;
  color: #b5563a;
}

/* The in-card action size. 59 of the app's buttons use it, so it sets the
   weight of every card that has actions on it. Trimmed from 0.4rem/0.85rem at
   0.82rem, which let two word-labelled buttons dominate a compact goal card.
   A secondary action inside a card should sit under the content it acts on,
   not compete with it. Primary calls to action keep the full .btn size. */
.btn-sm {
  padding: var(--btn-pad-sm);
  font-size: var(--fs-sm);
}

/* ── Post card ── */
.post-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.post-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--pad-card);
  box-shadow: var(--shadow);
  display: grid;
  grid-template-columns: 48px 1fr;
  gap: 0 1rem;
  transition: box-shadow var(--t), border-color var(--t), transform var(--t);
}

.post-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
  transform: translateY(-2px);
}

.vote-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding-top: 0.2rem;
}

.upvote-btn {
  background: none;
  border: 1.5px solid var(--border);
  border-radius: 6px;
  width: 36px;
  height: 36px;
  cursor: pointer;
  font-size: var(--fs-base);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.upvote-btn:hover {
  background: var(--green-pale);
  border-color: var(--green-mid);
  color: var(--green-dark);
}

.upvote-btn.voted {
  background: var(--green-pale);
  border-color: var(--green-dark);
  color: var(--green-dark);
}

.vote-count {
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--text-muted);
}

.post-content {}

.post-meta {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 0.4rem;
}

.post-title {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--text);
  margin-bottom: 0.45rem;
  line-height: 1.35;
}

.post-title a {
  color: inherit;
}

.post-title a:hover {
  color: var(--green-dark);
  text-decoration: none;
}

.post-body {
  font-size: var(--fs-base);
  color: var(--text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: 0.75rem;
}

.post-footer {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.read-more {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--green-dark);
  margin-left: auto;
}

/* ── Tag chips ── */
.tag-chip {
  display: inline-block;
  background: var(--green-pale);
  color: var(--green-dark);
  border: 1px solid var(--green-light);
  border-radius: 20px;
  padding: 0.15rem 0.65rem;
  font-size: var(--fs-xs);
  font-weight: 600;
}

/* ── Single post page ── */
.post-full {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-panel);
  box-shadow: var(--shadow);
  margin-bottom: 2rem;
}

.post-full .post-title-full {
  font-size: var(--fs-2xl);
  font-weight: 800;
  line-height: 1.25;
  margin-bottom: 0.75rem;
}

.post-full .post-meta {
  font-size: var(--fs-md);
  margin-bottom: 1rem;
}

.post-full .post-body-full {
  font-size: var(--fs-base);
  line-height: 1.75;
  white-space: pre-wrap;
  margin-bottom: 1.5rem;
}

.post-full .post-tags {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}

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

.upvote-count-label {
  font-weight: 700;
  font-size: var(--fs-base);
  color: var(--text-muted);
}

/* ── Comments ── */
.section-title {
  font-size: var(--fs-lg);
  font-weight: 700;
  margin-bottom: 1rem;
  color: var(--text);
  border-bottom: 2px solid var(--green-pale);
  padding-bottom: 0.5rem;
}

.comment-list {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  margin-bottom: 2rem;
}

.comment-card {
  background: var(--bg);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-card);
}

.comment-meta {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 0.35rem;
}

.comment-body {
  font-size: var(--fs-base);
  line-height: 1.6;
}

.empty-state {
  color: var(--text-muted);
  font-size: var(--fs-base);
  padding: 1rem 0;
}

/* ── Forms ── */
.form-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-panel);
  box-shadow: var(--shadow);
}

.form-group {
  margin-bottom: 1.25rem;
}

.form-group label {
  display: block;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text);
  margin-bottom: 0.4rem;
}

.form-group input[type="text"],
.form-group textarea {
  width: 100%;
  padding: 0.6rem 0.85rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font-size: var(--fs-base);
  font-family: inherit;
  outline: none;
  transition: border-color 0.15s;
  resize: vertical;
}

.form-group input[type="text"]:focus,
.form-group textarea:focus {
  border-color: var(--green-mid);
  background: var(--surface);
  box-shadow: var(--ring);
}

.form-group textarea {
  min-height: 120px;
}

.tag-checkboxes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.tag-checkboxes label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--fs-md);
  font-weight: 500;
  cursor: pointer;
  background: var(--green-pale);
  border: 1.5px solid var(--green-light);
  border-radius: 20px;
  padding: 0.2rem 0.7rem;
  transition: background 0.12s, border-color 0.12s;
}

.tag-checkboxes label:has(input:checked) {
  background: var(--green-mid);
  border-color: var(--green-dark);
  color: #fff;
}

.tag-checkboxes input[type="checkbox"] {
  display: none;
}

.form-hint {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: 0.3rem;
}

/* ── Modal ── */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(12, 26, 19, 0.42);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: 200;
  align-items: center;
  justify-content: center;
}

.modal-overlay.open {
  display: flex;
}

.modal {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  /* No top padding: the sticky .modal-header supplies the top spacing and sits
     flush at the very top so nothing scrolls into view above it. */
  padding: 0 var(--pad-panel) var(--pad-panel);
  width: min(560px, calc(100vw - 2rem));
  max-height: calc(100vh - 4rem);
  overflow-y: auto;
  box-shadow: 0 8px 40px rgba(0,0,0,0.18);
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Stick to the top of the (scrollable) modal so the close "×" is always
     reachable without scrolling back up. Negative margins pull it flush to the
     modal's padded edges so content scrolls underneath, not beside it. */
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--surface);
  /* Span to the modal's side edges (no negative TOP margin now that the modal
     has no top padding) so it sits flush at the top with no gap above it. */
  margin: 0 calc(var(--pad-panel) * -1) var(--sp-5);
  padding: var(--sp-5) var(--pad-panel) var(--sp-4);
  border-bottom: 1px solid var(--border);
}

.modal-header h2 {
  font-size: var(--fs-lg);
  font-weight: 700;
}

.modal-close {
  background: none;
  border: none;
  font-size: var(--fs-xl);
  cursor: pointer;
  color: var(--text-muted);
  line-height: 1;
  padding: 0.1rem;
}

.modal-close:hover {
  color: var(--text);
}

/* ── Page header area ── */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.page-header h1 {
  font-size: var(--fs-2xl);
  font-weight: 800;
}

/* ── Feedback messages ── */
.error-message {
  background: var(--danger-tint);
  border: 1.5px solid #f5c2c7;
  color: #842029;
  border-radius: var(--radius);
  padding: 0.85rem 1.1rem;
  font-size: var(--fs-base);
}

.success-message {
  background: var(--green-pale);
  border: 1.5px solid var(--green-light);
  color: var(--green-dark);
  border-radius: var(--radius);
  padding: 0.85rem 1.1rem;
  font-size: var(--fs-base);
}

.loading {
  color: var(--text-muted);
  font-size: var(--fs-base);
  padding: 1.5rem 0;
}

/* ── Spinner ──
   Shared busy indicator (see setBusy() in app.js). Sized in em so it matches
   whatever text it sits beside; .spinner-lg for a standalone loading panel. */
.spinner {
  display: inline-block;
  width: 1em;
  height: 1em;
  border: 2px solid var(--green-light);
  border-top-color: var(--green-mid);
  border-radius: 50%;
  vertical-align: -0.15em;
  flex: none;
  animation: spinner-spin 720ms linear infinite;
}

.spinner-lg {
  width: 1.9rem;
  height: 1.9rem;
  border-width: 3px;
  vertical-align: -0.35rem;
}

.busy-label { margin-left: 0.45rem; }

[aria-busy="true"] { cursor: progress; }

@keyframes spinner-spin {
  to { transform: rotate(360deg); }
}

/* Reduced motion: the global rule near the top of this file collapses every
   animation to 0.01ms, which would leave the ring spinning imperceptibly fast
   rather than honestly stopping. Halt it on purpose and keep the two-tone ring
   on screen, so "busy" still reads as a mark instead of silently vanishing. */
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none !important;
    border-color: var(--green-light);
    border-top-color: var(--green-mid);
  }
}

/* ── Back link ── */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--green-dark);
  margin-bottom: 1.25rem;
}

.back-link:hover {
  text-decoration: none;
  color: var(--green-mid);
}

/* ── Saved Items / Library ── */
.library-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-card);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.library-card-title {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--text);
}

.library-card-meta {
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.notes-display {
  font-size: var(--fs-md);
  color: var(--text-muted);
  font-style: italic;
  cursor: pointer;
  border-radius: 4px;
  padding: 0.2rem 0.4rem;
  transition: background 0.12s;
}

.notes-display:hover {
  background: var(--green-pale);
}

.notes-input {
  width: 100%;
  padding: 0.4rem 0.6rem;
  border: 1.5px solid var(--green-mid);
  border-radius: var(--radius);
  font-size: var(--fs-md);
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
  resize: vertical;
  outline: none;
  min-height: 60px;
}

.library-card-footer {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* ── Garden Planner ── */
.garden-layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 1.5rem;
  align-items: start;
}

.plot-sidebar {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  box-shadow: var(--shadow);
  position: sticky;
  top: 76px;
}

.plot-sidebar h3 {
  font-size: var(--fs-base);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin-bottom: 0.75rem;
}

.plot-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.55rem 0.65rem;
  border-radius: 7px;
  cursor: pointer;
  font-size: var(--fs-base);
  font-weight: 500;
  transition: background 0.12s;
  gap: 0.4rem;
}

.plot-list-item:hover {
  background: var(--green-pale);
}

.plot-list-item.active {
  background: var(--green-pale);
  color: var(--green-dark);
  font-weight: 700;
}

.plot-detail {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-panel);
  box-shadow: var(--shadow);
}

.plot-detail-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 1rem;
  gap: 1rem;
  flex-wrap: wrap;
}

.plot-detail-header h2 {
  font-size: var(--fs-xl);
  font-weight: 800;
}

/* ── Plant table ── */
.plant-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-md);
}

.plant-table th {
  text-align: left;
  font-weight: 700;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.4rem 0.6rem;
  border-bottom: 2px solid var(--border);
}

.plant-table td {
  padding: 0.6rem 0.6rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

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

.plant-table tr:hover td {
  background: var(--green-pale);
}

/* ── Status badges ── */
.badge {
  display: inline-block;
  padding: 0.18rem 0.6rem;
  border-radius: 20px;
  font-size: var(--fs-xs);
  font-weight: 700;
}

.badge-planned    { background: var(--info-tint); color: #495057; }
.badge-growing    { background: var(--border); color: var(--green-dark); }
.badge-harvested  { background: #cce5ff; color: #004085; }
.badge-failed     { background: var(--danger-tint); color: #842029; }
.badge-in_progress { background: #fff3cd; color: #856404; }
.badge-done       { background: var(--border); color: var(--green-dark); }

/* ── Journal timeline ── */
.timeline {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.journal-entry-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-card);
  box-shadow: var(--shadow);
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: 0 1rem;
}

.date-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding-top: 0.1rem;
}

.date-badge .day {
  font-size: var(--fs-2xl);
  font-weight: 800;
  line-height: 1;
  color: var(--green-dark);
}

.date-badge .month {
  font-size: var(--fs-2xs);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--text-muted);
  letter-spacing: 0.06em;
}

.date-badge .year {
  font-size: var(--fs-2xs);
  color: var(--text-muted);
}

.entry-content {}

.entry-title {
  font-size: var(--fs-lg);
  font-weight: 700;
  margin-bottom: 0.3rem;
}

.entry-body {
  font-size: var(--fs-base);
  color: var(--text-muted);
  line-height: 1.65;
  margin-bottom: 0.5rem;
  white-space: pre-wrap;
}

.entry-photo {
  max-width: 100%;
  max-height: 260px;
  object-fit: cover;
  border-radius: var(--radius);
  margin-bottom: 0.5rem;
  display: block;
}

.entry-meta {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.entry-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

/* ── Goals kanban ── */
.kanban {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
  align-items: start;
}

.kanban-col {
  background: var(--bg);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-card);
}

.kanban-col-header {
  font-size: var(--fs-md);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  margin-bottom: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.kanban-col-header .col-count {
  background: var(--border);
  color: var(--text-muted);
  border-radius: 20px;
  padding: 0.05rem 0.5rem;
  font-size: var(--fs-sm);
}

/* ───────────────────────────────────────────────────────────────────────────
   Shared card design language
   One anatomy across goals, plants, tracked things and entries so every card
   reads the same way: an icon that says what it is, a title, category/status
   pills, a body, then a footer with the actions. Cards set --accent (their
   category colour) and everything tints from it.
   ─────────────────────────────────────────────────────────────────────────── */
.gl-card-head { display: flex; align-items: flex-start; gap: 0.7rem; }
.gl-ic { flex: none; width: 38px; height: 38px; border-radius: 10px; display: flex; align-items: center;
  justify-content: center; font-size: var(--fs-lg);
  background: color-mix(in srgb, var(--accent, #40916c) 14%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent, #40916c) 26%, var(--surface)); }
.gl-head-main { flex: 1; min-width: 0; }
.gl-title { font-size: var(--fs-base); font-weight: 800; line-height: 1.25; color: var(--text); word-break: break-word; }
.gl-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.35rem; }
.gl-pill { font-size: var(--fs-2xs); font-weight: 700; padding: 2px 9px; border-radius: 999px;
  background: var(--green-pale); border: 1px solid var(--green-light); color: var(--green-dark); white-space: nowrap; }
.gl-pill.is-planned     { background: var(--info-tint); border-color: #dfe4e8; color: #5a6672; }
.gl-pill.is-in_progress { background: var(--warn-tint); border-color: var(--warn); color: #8a6516; }
.gl-pill.is-done        { background: var(--border); border-color: var(--green-light); color: var(--green-dark); }
.gl-pill.is-archived    { background: var(--surface-2); border-color: #ded5c9; color: #7a6c58; }
.gl-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap;
  margin-top: 0.85rem; padding-top: 0.7rem; border-top: 1px solid var(--border); }
.gl-foot-meta { font-size: var(--fs-sm); color: var(--text-muted); display: inline-flex; align-items: center; gap: 0.3rem; }
.gl-actions { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; margin-left: auto; }

.goal-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-card);
  margin-bottom: 0.75rem;
  box-shadow: var(--shadow);
  cursor: pointer;
  transition: box-shadow 0.15s, border-color 0.15s;
}

.goal-card:hover {
  box-shadow: 0 4px 14px rgba(45, 106, 79, 0.12);
  border-color: var(--green-mid);
}

.goal-card-title {
  font-size: var(--fs-base);
  font-weight: 700;
  margin-bottom: 0.35rem;
}

.goal-card-desc {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 0.5rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.goal-card-footer {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.goal-target-date {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-left: auto;
}

.goal-card-actions {
  display: flex;
  gap: 0.4rem;
  margin-top: 0.5rem;
}

/* ── Form inputs not yet covered ── */
.form-group input[type="date"],
.form-group input[type="number"],
.form-group select {
  width: 100%;
  padding: 0.6rem 0.85rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font-size: var(--fs-base);
  font-family: inherit;
  outline: none;
  transition: border-color 0.15s;
}

.form-group input[type="date"]:focus,
.form-group input[type="number"]:focus,
.form-group select:focus {
  border-color: var(--green-mid);
  background: var(--surface);
}

.btn-danger {
  background: var(--danger-tint);
  border: 1.5px solid #f5c2c7;
  color: #842029;
}

.btn-danger:hover {
  background: #f5c2c7;
}

/* ── Notification Bell ── */
.notif-bell-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.notif-bell {
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--fs-lg);
  color: var(--green-light);
  display: inline-flex;
  align-items: center;
  position: relative;
  padding: 0.2rem;
  line-height: 1;
}

.notif-bell:hover {
  color: #fff;
}

.notif-badge {
  position: absolute;
  top: -5px;
  right: -7px;
  background: #e63946;
  color: #fff;
  font-size: var(--fs-2xs);
  font-weight: 700;
  border-radius: 20px;
  padding: 0.1rem 0.32rem;
  min-width: 16px;
  text-align: center;
  line-height: 1.4;
}

.notif-dropdown {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  width: 360px;
  max-width: calc(100vw - 24px);
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 6px 24px rgba(0,0,0,0.14);
  z-index: 300;
  overflow: hidden;
}

.notif-dropdown-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem;
  border-bottom: 1.5px solid var(--border);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--text);
}

.notif-mark-all {
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--fs-sm);
  color: var(--green-dark);
  font-weight: 600;
  padding: 0;
}

.notif-mark-all:hover {
  text-decoration: underline;
}

.notif-dropdown-footer {
  display: block;
  text-align: center;
  padding: 0.7rem 1rem;
  border-top: 1.5px solid var(--border);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--green-dark);
  text-decoration: none;
}

.notif-dropdown-footer:hover {
  text-decoration: underline;
  background: var(--surface-alt, var(--surface));
}

.notif-list {
  max-height: 340px;
  overflow-y: auto;
}

.notif-item {
  display: grid;
  grid-template-columns: 10px 1fr;
  column-gap: 0.6rem;
  row-gap: 0.15rem;
  align-items: start;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--border);
  /* Unread: white surface + a soft left accent so it stands out but stays readable. */
  background: var(--surface);
  border-left: 3px solid var(--green-dark);
  color: var(--text);
  transition: background 0.12s;
  text-decoration: none;
}

.notif-item:last-child {
  border-bottom: none;
}

.notif-item:hover {
  background: var(--green-pale);
  text-decoration: none;
}

/* Unread indicator dot (green for unread, hidden once read). */
.notif-dot {
  grid-row: 1 / span 2;
  width: 8px;
  height: 8px;
  margin-top: 0.35rem;
  border-radius: 50%;
  background: var(--green-dark);
}

.notif-item.notif-read {
  background: var(--surface);
  border-left-color: transparent;
  color: var(--text-muted);
}

.notif-item.notif-read .notif-dot {
  background: transparent;
}

.notif-item.notif-read .notif-msg {
  font-weight: 400;
  color: var(--text-muted);
}

.notif-item.notif-read:hover {
  background: var(--bg);
}

.notif-msg {
  grid-column: 2;
  font-size: var(--fs-md);
  line-height: 1.4;
  font-weight: 600;                 /* unread reads a touch stronger */
  color: var(--text);
  /* Keep items tidy: show up to 3 lines, reveal the rest via the hover tooltip. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.notif-time {
  grid-column: 2;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.notif-empty {
  padding: 1rem;
  font-size: var(--fs-md);
  color: var(--text-muted);
  text-align: center;
}

/* "See more" reveals the next page of notifications inside the dropdown. */
.notif-see-more {
  display: block;
  width: 100%;
  background: none;
  border: none;
  border-top: 1px solid var(--border);
  padding: 0.6rem 1rem;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--green-dark);
  text-align: center;
  cursor: pointer;
}
.notif-see-more:hover { background: var(--green-pale); }

/* ── Profile Page ── */
.profile-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-panel);
  box-shadow: var(--shadow);
  margin-bottom: 2rem;
}

.profile-header {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
}

.profile-avatar {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: var(--green-pale);
  border: 2.5px solid var(--green-mid);
  object-fit: cover;
  font-size: var(--fs-3xl);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--green-dark);
  flex-shrink: 0;
}

.profile-meta {
  flex: 1;
}

.profile-username {
  font-size: var(--fs-xl);
  font-weight: 800;
  color: var(--text);
  margin-bottom: 0.2rem;
}

.profile-detail {
  font-size: var(--fs-md);
  color: var(--text-muted);
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 0.3rem;
}

.profile-cred {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: var(--green-pale);
  color: var(--green-dark);
  border: 1px solid var(--green-light);
  border-radius: 20px;
  padding: 0.15rem 0.6rem;
  font-size: var(--fs-sm);
  font-weight: 700;
}

/* The top bar collapse.

   Two conditions, deliberately, because there are two different ways to not
   fit. Narrow is the obvious one. The other is a phone held sideways: at 844px
   it clears the 700px breakpoint and gets the full nav back, and because the
   touch rules below grow every control to 44px the bar's content goes from
   793px to 903px and overflows a 844px screen. The thing that causes it is the
   coarse pointer, so that is what the second query asks about. An 850px
   DESKTOP window keeps its nav: 29px controls, 793px of content, fits fine.

   950px, not 903px, so the two phones that land at 844 and 932 are both inside
   it with room to spare, while an iPad at 1024 keeps the full bar it can hold.

   Hiding these costs nothing: the drawer the hamburger opens already has the
   same destinations ("My Homestead" is its Home, "Community Feed" its Feed).
   Log out was the one thing only the top bar had, so it is a drawer item now
   too (see app.js). The bell stays, because a notification count you cannot
   see is the one thing here worth the pixels. */
@media (max-width: 700px), (max-width: 950px) and (pointer: coarse) {
  header .header-left { display: none; }
  header .header-right a { display: none; }
  header .header-right { flex-shrink: 0; }
  /* The search field absorbs the freed space, so it has to be allowed to
     shrink. min-width:0 is the part people forget: a flex item's automatic
     minimum is its content size, so without it an <input> refuses to go below
     its default ~170px and pushes the overflow straight back. */
  .header-search { flex: 1 1 auto; min-width: 0; }
  .header-search input { min-width: 0; width: 100%; }
}

/* ── Responsive ── */
@media (max-width: 700px) {
  /* A `1fr` track has an automatic minimum of its content's min-content width,
     so it does not shrink just because the container did: the single column
     resolved to 394px inside a 350px container. The Garden page nests
     .plot-detail (24px of padding each side) inside .bed-band (another 24px),
     which is 96px of horizontal padding wrapped around a card that already has
     its own. minmax(0, 1fr) lets the track shrink; the tighter --pad-panel
     below gives the content somewhere to shrink TO. */
  .garden-layout {
    grid-template-columns: minmax(0, 1fr);
  }
  .plot-sidebar {
    position: static;
  }
  .kanban {
    grid-template-columns: 1fr;
  }
  .journal-entry-card {
    grid-template-columns: 56px 1fr;
  }
}

/* REMOVED: the phone override of --pad-panel and .container padding.
   Both are clamp()ed at their definitions now, so they ramp between the same
   two values instead of jumping at 700px. Setting them again here would pin
   everything below 700px back to the low end and undo that. */

/* Tap targets.

   Every interactive control on the Garden page measured between 27px and 34px
   tall, against the 44px that both Apple's HIG and WCAG 2.5.8 ask for. That is
   a nuisance on a sofa and a real problem standing in a bed holding a phone in
   one hand, which is the case this is for.

   min-height rather than padding, so a button grows to 44px without its label
   moving off-centre, and nothing that already clears 44px changes at all.
   Pointer query, not width: a 44px target is about fingers, not screen size, so
   this applies to a touch laptop and leaves a small desktop window alone. */
@media (pointer: coarse) {
  .btn,
  .btn-sm,
  .pmenu-btn,
  .nav-drawer-link,
  .season-seg button,
  .pmenu-pop button {
    min-height: 44px;
  }
  /* Icon-only controls need the width too: a 34x44 target is still a miss. */
  .pmenu-btn,
  .nav-hamburger,
  .notif-bell,
  .nav-drawer-close,
  .search-clear {
    min-width: 44px;
    min-height: 44px;
  }
  /* These are centred glyphs, so they need to stay centred as they grow. */
  .nav-hamburger,
  .notif-bell,
  .nav-drawer-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  /* Inputs get the same floor, which also stops iOS zooming the page when one
     is focused (it does that below 16px, and the zoom does not undo itself). */
  input[type="text"], input[type="email"], input[type="password"],
  input[type="number"], input[type="date"], select, textarea {
    min-height: 44px;
    font-size: max(16px, var(--fs-base));
  }
  /* The floor above never actually reached a field inside a .form-group, which
     is most of the forms in the app: `.form-group input[type="text"]` sets
     font-size at a higher specificity, so it won on points no matter which
     media query the other rule sat in. Repeating the floor at matching
     specificity is what makes it apply. min-height was never affected, because
     the .form-group rules do not set one. */
  .form-group input[type="text"], .form-group input[type="email"],
  .form-group input[type="password"], .form-group input[type="number"],
  .form-group input[type="date"], .form-group select, .form-group textarea {
    font-size: max(16px, var(--fs-base));
  }
}

/* ── Responsive ── */
@media (max-width: 600px) {
  header {
    padding: 0 0.5rem;
    gap: 0.25rem;
  }

  .logo-text {
    display: none;
  }

  .header-left a,
  .header-right a {
    padding: 0.3rem 0.45rem;
    font-size: var(--fs-sm);
  }

  .header-search {
    margin: 0 0.25rem;
  }

  .post-card {
    grid-template-columns: 40px 1fr;
    gap: 0 0.75rem;
    padding: var(--sp-3) var(--sp-4);
  }

  .post-full {
    padding: var(--sp-4);
  }

  .form-card {
    padding: var(--sp-4);
  }

  .filter-bar {
    gap: 0.5rem;
  }

  .filter-bar select,
  .filter-bar input[type="text"] {
    min-width: 0;
    flex: 1;
  }
}

/* ── Post Composer Card ── */
.composer-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 1.25rem;
  box-shadow: var(--shadow);
  overflow: hidden;
}

.composer-trigger {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.9rem 1.25rem 0.6rem;
  cursor: pointer;
}

.composer-trigger-input {
  flex: 1;
  background: var(--bg);
  border: 1.5px solid var(--border);
  border-radius: 20px;
  padding: 0.55rem 1rem;
  font-size: var(--fs-base);
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
  transition: border-color 0.15s, color 0.15s;
}

.composer-trigger:hover .composer-trigger-input {
  border-color: var(--green-mid);
  color: var(--text);
}

/* quick-tags strip — visible when composer is collapsed */
/* Collapsed-state helper line under the composer trigger (replaces the old
   duplicate category chips — the real picker lives in the open form). */
.composer-hint {
  padding: 0 1.25rem 0.9rem;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  line-height: 1.4;
}

/* Selected-photo thumbnail + its remove button in the composer. */
.composer-photo-thumb {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.4rem;
  margin-bottom: 0.5rem;
}
.composer-photo-thumb img {
  max-height: 120px;
  border-radius: 8px;
  display: block;
}
.composer-photo-remove {
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.2rem 0.6rem;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: #b5563a;
  cursor: pointer;
}
.composer-photo-remove:hover { background: #fbe9e2; border-color: #f0cdbe; }

/* Inline "required" feedback for the composer, shown instead of an alert(). */
.field-hint-err {
  margin-top: 0.3rem;
  font-size: var(--fs-sm);
  color: #c0392b;
}

.composer-field input.field-invalid,
.composer-field textarea.field-invalid {
  border-color: #e0736a;
  background: #fdf3f2;
}

.composer-tags-label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 0.4rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.composer-body {
  display: none;
  padding: 0 1.25rem 1rem;
  border-top: 1.5px solid var(--border);
  padding-top: 1rem;
}

.composer-body.open { display: block; }

.composer-field {
  margin-bottom: 0.75rem;
}

.composer-field input[type="text"],
.composer-field textarea,
.composer-field select {
  width: 100%;
  box-sizing: border-box;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  padding: 0.55rem 0.875rem;
  font-size: var(--fs-base);
  font-family: inherit;
  background: var(--bg);
  color: var(--text);
  outline: none;
  transition: border-color 0.15s;
}

.composer-field input:focus,
.composer-field textarea:focus,
.composer-field select:focus {
  border-color: var(--green-mid);
}

.composer-field textarea { resize: vertical; min-height: 80px; }

.composer-photo-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.composer-photo-label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: var(--green-pale);
  color: var(--green-dark);
  border: 1.5px solid var(--green-light);
  border-radius: 8px;
  padding: 0.45rem 0.875rem;
  font-size: var(--fs-base);
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s;
  white-space: nowrap;
}

.composer-photo-label:hover { background: var(--green-light); }
.composer-photo-label input { display: none; }

.composer-photo-preview {
  font-size: var(--fs-md);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}

.composer-photo-preview img {
  max-height: 120px;
  border-radius: 6px;
  display: block;
  margin-top: 0.4rem;
}

.composer-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.75rem;
}

.composer-tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: var(--bg);
  border: 1.5px solid var(--border);
  border-radius: 20px;
  padding: 0.3rem 0.7rem;
  font-size: var(--fs-sm);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
  user-select: none;
}

.composer-tag-chip.selected {
  background: var(--green-pale);
  border-color: var(--green-mid);
  color: var(--green-dark);
  font-weight: 600;
}

.composer-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

/* ── Pin button ── */
.feed-action-btn.pinned { color: #f59e0b; }
.feed-action-btn.pin-btn:hover { color: #f59e0b; }

/* ── @mention autocomplete dropdown ── */
.mention-dropdown {
  position: absolute;
  z-index: 9999;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.13);
  overflow: hidden;
  max-height: 220px;
  overflow-y: auto;
}

.mention-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  font-size: var(--fs-base);
  transition: background 0.1s;
}

.mention-item:hover,
.mention-item.active { background: var(--green-pale); }

.mention-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.mention-initials {
  background: var(--green-pale);
  border: 1.5px solid var(--green-light);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--green-dark);
}

/* ── inline @mention highlight ── */
.mention {
  color: var(--green-dark);
  font-weight: 600;
  background: var(--green-pale);
  border-radius: 4px;
  padding: 0 3px;
}

/* ── Threaded replies & comment input improvements ── */
.feed-reply {
  margin-left: 2.25rem;
  margin-top: 0.5rem;
}

.feed-replies {
  margin-top: 0.25rem;
}

.comment-meta-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.2rem;
  padding-left: 0.25rem;
}

.comment-time {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.comment-reply-btn {
  background: none;
  border: none;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0;
  font-family: inherit;
  transition: color 0.15s;
}

.comment-reply-btn:hover { color: var(--green-dark); }

.inline-reply-row {
  margin-top: 0.5rem;
  margin-bottom: 0.25rem;
}

/* Improved comment input bar */
/* The header is sticky, 58px tall and z-index 200, and nothing in this sheet
   declared a scroll margin. So anything the browser scrolls to by itself lands
   flush against the viewport top and sits underneath it: tap a comment box on
   a phone and the keyboard-driven scroll hides the very box you are typing in.
   Declaring the margin on the scroll targets is the whole fix. */
input, textarea, select, .comment-input-bar, :target {
  scroll-margin-top: calc(58px + 0.75rem);
}

.comment-input-bar {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.75rem 1.25rem;
  border-top: 1.5px solid var(--border);
  background: var(--bg);
}

/* Visible to a screen reader, not on screen. The 📷 control is an emoji with
   no accessible name of its own. */
.gl-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* ── Attaching a photo to a comment or reply ── */
.gl-cphoto { flex-shrink: 0; display: inline-flex; align-items: center; }
/* Both of these set an explicit `display`, which beats the user-agent's
   [hidden] { display: none } on specificity. Without this pair the button and
   the thumbnail are shown at the same time and the row is 44px wider than it
   should be, which is what pushed Post off a narrow screen. */
.gl-cphoto-btn[hidden], .gl-cphoto-chip[hidden] { display: none; }
.gl-cphoto-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 1.5px solid var(--border); border-radius: 50%;
  background: var(--bg); cursor: pointer; font-size: 1rem; line-height: 1;
  transition: border-color 0.15s, background 0.15s;
}
.gl-cphoto-btn:hover { border-color: var(--green-mid); background: var(--green-pale); }
.gl-cphoto-btn[aria-busy="true"] { opacity: 0.55; cursor: progress; }
/* The attached thumbnail stands in for the button once a photo is on, so the
   row does not grow a second control the member has to interpret. */
.gl-cphoto-chip { position: relative; display: inline-block; }
.gl-cphoto-chip img {
  width: 36px; height: 36px; object-fit: cover;
  border-radius: 8px; border: 1.5px solid var(--border); display: block;
}
.gl-cphoto-x {
  position: absolute; top: -6px; right: -6px;
  width: 18px; height: 18px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  background: var(--text); color: #fff;
  font-size: 0.8rem; line-height: 1; cursor: pointer;
}
/* A photo someone attached to a comment, in the thread. Capped so a portrait
   phone photo cannot push the rest of the thread off the screen. */
.comment-photo {
  display: block; margin-top: 0.5rem;
  max-width: 100%; max-height: 320px;
  border-radius: 10px; object-fit: cover;
}
@media (pointer: coarse) {
  .gl-cphoto-btn { width: 44px; height: 44px; }
  .gl-cphoto-chip img { width: 44px; height: 44px; }
}

/* ── Hyperlinks in post/comment bodies ── */
.body-link {
  color: var(--green-dark);
  text-decoration: underline;
  word-break: break-all;
}
.body-link:hover { color: var(--green-mid); }

/* ── YouTube preview card ── */
.yt-preview {
  display: block;
  position: relative;
  max-width: 360px;
  margin-top: 0.75rem;
  border-radius: 10px;
  overflow: hidden;
  text-decoration: none;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
  flex-shrink: 0;
}

.yt-thumb {
  width: 100%;
  display: block;
  aspect-ratio: 16/9;
  object-fit: cover;
}

.yt-play-overlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-xl);
  padding-left: 3px; /* optical center for ▶ */
  transition: background 0.15s;
}

.yt-preview:hover .yt-play-overlay {
  background: rgba(220, 38, 38, 0.9);
}

/* ── Feed cards (shared by index.html and post.html) ── */
.feed-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 1.25rem;
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: border-color 0.4s, box-shadow 0.2s, transform 0.2s;
}
.feed-card:hover {
  box-shadow: 0 8px 24px rgba(16, 40, 28, 0.13);
  transform: translateY(-2px);
}
.post-highlight {
  border-color: var(--green-mid) !important;
  box-shadow: 0 0 0 3px rgba(74,163,85,0.25) !important;
}
.feed-card-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 1.25rem 0.5rem;
}
.feed-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--green-pale);
  border: 2px solid var(--green-light);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  color: var(--green-dark);
  font-size: var(--fs-base);
  flex-shrink: 0;
}
/* Colorful initial avatars (when there's no profile photo): white initials on
   a per-name color, set inline. */
.avatar-initials {
  color: #fff !important;
  border-color: rgba(255, 255, 255, 0.55) !important;
}
.feed-author-block { flex: 1; min-width: 0; }
.feed-author { font-weight: 700; font-size: var(--fs-base); color: var(--text); }
.feed-meta   { font-size: var(--fs-sm); color: var(--text-muted); }
.feed-card-body { padding: 0.5rem 1.25rem 0.75rem; }
.feed-title {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--text);
  margin-bottom: 0.4rem;
}
.feed-body {
  font-size: var(--fs-base);
  color: var(--text);
  line-height: 1.65;
  white-space: pre-wrap;
  word-break: break-word;
}
.feed-photo {
  width: 100%;
  height: auto;        /* show the whole photo at its natural aspect ratio (no cropping) */
  display: block;
  border-radius: 6px;
  margin: 0.75rem 0;
}
.feed-tags { margin-top: 0.75rem; display: flex; flex-wrap: wrap; gap: 0.4rem; }

/* ── Feed action bar ── */
.feed-actions {
  display: flex;
  gap: 0;
  border-top: 1.5px solid var(--border);
  border-bottom: 1.5px solid var(--border);
}
.feed-action-btn {
  flex: 1;
  background: none;
  border: none;
  padding: 0.6rem 0.5rem;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  transition: background 0.15s, color 0.15s;
  font-family: inherit;
}
.feed-action-btn:hover  { background: var(--green-pale); color: var(--green-dark); }
.feed-action-btn.voted  { color: var(--green-dark); }
.feed-action-btn.pinned { color: #f59e0b; }
.feed-action-btn.pin-btn:hover { color: #f59e0b; }

/* ── Comments section ── */
.feed-comments { padding: 0.75rem 1.25rem; display: none; }
.feed-comments.open { display: block; }
.feed-comment {
  display: flex;
  gap: 0.6rem;
  margin-bottom: 0.75rem;
  align-items: flex-start;
}
.feed-comment-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--green-pale);
  border: 1.5px solid var(--green-light);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--green-dark);
  flex-shrink: 0;
}
.feed-comment-bubble {
  background: var(--bg);
  border-radius: 12px;
  padding: 0.5rem 0.75rem;
  font-size: var(--fs-md);
  flex: 1;
}
.feed-comment-author {
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--text);
  margin-bottom: 0.15rem;
}
.feed-comment-body { color: var(--text); line-height: 1.5; }
.feed-comment-input {
  flex: 1;
  /* A textarea's min-content width is its `cols` default (20 characters), and
     a flex item will not shrink below that on its own. Without this the row
     overflows on a narrow screen and the Post button next to it ends up off
     the edge or underneath the box, where it cannot be tapped. */
  min-width: 0;
  border: 1.5px solid var(--border);
  border-radius: 20px;
  padding: 0.5rem 1rem;
  font-size: var(--fs-md);
  font-family: inherit;
  background: var(--bg);
  color: var(--text);
  resize: none;
  outline: none;
  transition: border-color 0.15s;
  min-height: 36px;
}
.feed-comment-input:focus { border-color: var(--green-mid); }
.feed-comment-submit {
  flex-shrink: 0;
  background: var(--green-dark);
  color: #fff;
  border: none;
  border-radius: 20px;
  padding: 0.5rem 1rem;
  font-size: var(--fs-md);
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s;
}
.feed-comment-submit:hover { background: var(--green-mid); }
.feed-no-comments {
  font-size: var(--fs-md);
  color: var(--text-muted);
  margin-bottom: 0.75rem;
}

/* ── Post ellipsis menu ── */
.feed-card-header { position: relative; }

.post-menu { margin-left: auto; position: relative; }

.post-menu-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--fs-lg);
  color: var(--text-muted);
  padding: 0.2rem 0.5rem;
  border-radius: 50%;
  line-height: 1;
  transition: background 0.12s, color 0.12s;
  font-family: inherit;
}
.post-menu-btn:hover { background: var(--green-pale); color: var(--green-dark); }

.post-menu-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 140px;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(0,0,0,0.13);
  z-index: 50;
  padding: 0.3rem 0;
  display: flex;
  flex-direction: column;
}
.post-menu-dropdown[hidden] { display: none; }

.post-menu-item {
  background: none;
  border: none;
  text-align: left;
  padding: 0.5rem 0.9rem;
  font-size: var(--fs-md);
  font-family: inherit;
  color: var(--text);
  cursor: pointer;
  transition: background 0.1s;
}
.post-menu-item:hover { background: var(--green-pale); }
.post-menu-item.delete-post-btn:hover { background: #fef3c7; color: #b45309; }

/* ── Edited badge on posts ── */
.edited-tag {
  font-style: italic;
  color: var(--text-muted);
  cursor: help;
  border-bottom: 1px dotted var(--text-muted);
}

.zone-chip {
  display: inline-block;
  background: var(--green-pale, var(--surface-2));
  color: var(--green-dark, #2d5a1b);
  border-radius: 10px;
  padding: 0.05rem 0.45rem;
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
}

.zone-prompt {
  background: #fff7e6;
  border: 1.5px solid var(--warn);
  border-radius: 10px;
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
  font-size: var(--fs-base);
  color: #6b5418;
}

.zone-prompt a { color: var(--green-dark, #2d5a1b); font-weight: 600; }

/* ── Comment ellipsis menu + inline edit ── */
.feed-comment-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.comment-menu { margin-left: 0; }
.comment-menu .post-menu-btn {
  font-size: var(--fs-base);
  padding: 0 0.4rem;
  color: var(--text-muted);
}

.comment-edit-input {
  width: 100%;
  border: 1.5px solid var(--green-mid);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
  font-size: var(--fs-md);
  font-family: inherit;
  background: var(--surface);
  color: var(--text);
  resize: vertical;
  outline: none;
  min-height: 60px;
}

.comment-edit-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.4rem;
  margin-top: 0.4rem;
}

/* Collapsible USDA hardiness-zone map reference, used beside the zone picker on
   the register and profile forms. Kept collapsed by default and the image is
   lazy-loaded so it doesn't add weight to the form until someone asks for it. */
details.zone-map {
  margin-top: 0.5rem;
}
details.zone-map > summary {
  cursor: pointer;
  color: var(--green-dark);
  font-size: var(--fs-sm);
  font-weight: 600;
  list-style: none;
  user-select: none;
  width: fit-content;
}
details.zone-map > summary::-webkit-details-marker { display: none; }
details.zone-map > summary::before { content: "🗺️ "; }
details.zone-map > summary:hover { text-decoration: underline; }
details.zone-map[open] > summary { margin-bottom: 0.5rem; }
details.zone-map img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
details.zone-map .zone-map-caption {
  display: block;
  margin-top: 0.4rem;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  line-height: 1.4;
}

/* First-run state for the Garden Planner when the user has no plots yet. */
.plot-empty {
  text-align: center;
  padding: 3rem 1.5rem;
}
.plot-empty-icon {
  font-size: var(--fs-3xl);
  line-height: 1;
}
.plot-empty h2 {
  margin: 0.6rem 0 0.5rem;
}
.plot-empty p {
  color: var(--text-muted);
  max-width: 430px;
  margin: 0 auto 1.25rem;
  line-height: 1.55;
}

/* "What needs attention" chips at the top of a plot's detail panel. */
.attention-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 1.1rem;
}
.attention-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--fs-sm);
  font-weight: 600;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
}
.attention-chip.harvest {
  background: var(--warn-tint);
  border-color: #f0c97a;
  color: #8a5a00;
}
.attention-chip.plant {
  background: var(--green-pale);
  border-color: var(--green-light);
  color: var(--green-dark);
}
.attention-chip.calm {
  color: var(--text-muted);
  font-weight: 500;
}

/* Brief flash when the feed is opened deep-linked to a specific post
   (e.g. from the profile's "View on the feed" links). */
.feed-card.post-highlight {
  animation: post-highlight-fade 2.4s ease-out;
}
@keyframes post-highlight-fade {
  0%   { box-shadow: 0 0 0 3px var(--green-mid); }
  100% { box-shadow: 0 0 0 3px rgba(82, 183, 136, 0); }
}

/* ── Slide-in navigation drawer (personal sections) ── */
.nav-hamburger {
  background: transparent;
  border: none;
  color: #fff;
  font-size: var(--fs-2xl);
  line-height: 1;
  padding: 0.25rem 0.55rem;
  margin-right: 0.1rem;
  cursor: pointer;
  border-radius: 6px;
  flex-shrink: 0;
}
.nav-hamburger:hover { background: rgba(255,255,255,0.14); }

.nav-drawer-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s ease;
  z-index: 998;
}
.nav-drawer-overlay.open { opacity: 1; visibility: visible; }

.nav-drawer {
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  /* dvh tracks the real viewport as mobile browser chrome shows and hides; the
     100% above stays as the fallback for anything that doesn't know dvh. */
  height: 100dvh;
  width: 270px;
  max-width: 82vw;
  background: var(--surface);
  box-shadow: 2px 0 16px rgba(0,0,0,0.18);
  transform: translateX(-100%);
  transition: transform 0.22s ease;
  z-index: 999;
  display: flex;
  flex-direction: column;
  padding: 0.5rem;
}
.nav-drawer.open { transform: translateX(0); }

.nav-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 0.6rem 0.8rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.5rem;
  flex-shrink: 0;            /* never let the title collapse to buy list space */
}

/* The scrolling part of the drawer. overscroll-behavior keeps a flick at the
   end of the list from scrolling the page behind it — body has overflow:hidden
   while the drawer is open, but that alone does not stop scroll chaining. */
.nav-drawer-body {
  flex: 1 1 auto;
  min-height: 0;             /* without this a flex child refuses to shrink, and
                                overflow-y never engages */
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.nav-drawer-title {
  font-weight: 800;
  color: var(--green-dark);
  font-size: var(--fs-lg);
}
.nav-drawer-close {
  background: transparent;
  border: none;
  font-size: var(--fs-2xl);
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0 0.3rem;
  border-radius: 6px;
}
.nav-drawer-close:hover { background: var(--green-pale); color: var(--text); }

.nav-drawer-link {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  padding: 0.6rem 0.75rem;
  border: none;
  background: none;
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
  font-weight: 600;
  font-size: var(--fs-base);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.nav-drawer-link:hover { background: var(--green-pale); text-decoration: none; }
.nav-drawer-link.active {
  background: var(--green-pale);
  color: var(--green-dark);
  box-shadow: inset 3px 0 0 var(--green-mid);
}
.nav-drawer-link.soon { color: var(--text-muted); cursor: default; }
.nav-drawer-link.soon:hover { background: none; }
.nav-drawer-link .nav-drawer-label { flex: 1; }
.nav-soon {
  font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--text-muted); background: var(--bg); border: 1px solid var(--border);
  border-radius: 999px; padding: 1px 7px;
}
.nav-caret { color: var(--text-muted); font-size: var(--fs-md); }
.nav-drawer-link[aria-expanded="true"] .nav-caret { color: var(--green-dark); }
.nav-drawer-icon { font-size: var(--fs-lg); width: 1.4rem; text-align: center; }

/* Drawer sections */
.nav-section { margin-top: 0.4rem; }
.nav-section + .nav-section { border-top: 1px solid var(--border); padding-top: 0.4rem; margin-top: 0.6rem; }
.nav-section-title {
  font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); padding: 0.3rem 0.75rem 0.35rem;
}

/* "Build" flyout — slides in to the right of the drawer */
.nav-flyout {
  position: fixed; top: 0; left: min(270px, 82vw); height: 100dvh; width: 270px; max-width: 80vw;
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: 6px 0 18px rgba(0,0,0,0.12);
  transform: translateX(-12px); opacity: 0; visibility: hidden;
  transition: transform 0.18s ease, opacity 0.18s ease, visibility 0.18s ease;
  z-index: 999; overflow-y: auto; padding: 0.6rem;
}
.nav-flyout.open { transform: translateX(0); opacity: 1; visibility: visible; }
.nav-flyout-title {
  font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); padding: 0.6rem 0.75rem 0.5rem;
}
.nav-flyout-link {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.6rem 0.75rem; border-radius: 8px; color: var(--text); text-decoration: none;
}
.nav-flyout-link:hover { background: var(--green-pale); text-decoration: none; }
.nav-flyout-link .nf-text { display: flex; flex-direction: column; line-height: 1.25; }
.nav-flyout-link .nf-l { font-weight: 600; }
.nav-flyout-link .nf-s { font-size: var(--fs-sm); color: var(--text-muted); }

body.nav-drawer-lock { overflow: hidden; }

/* ── Toasts ── */
.gl-toast-host {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 1200;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  pointer-events: none;
}
.gl-toast {
  background: var(--green-dark);
  color: #fff;
  padding: 0.6rem 1.1rem;
  border-radius: 10px;
  font-size: var(--fs-md);
  font-weight: 600;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
  max-width: min(90vw, 420px);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.2s, transform 0.2s;
}
.gl-toast.show { opacity: 1; transform: translateY(0); }
.gl-toast-error { background: #b5563a; }


/* ══ Photo check-up ════════════════════════════════════════════════════
   Moved out of garden.html when the feature gained its own page, so the
   card and the page cannot drift apart. */
/* ── Photo check-up: "What's wrong with this?" ──────────────────────────────
   Six outcomes, and the whole point is that a gardener can tell them apart at
   a glance — so each gets its own SHAPE, not just its own wording:
     .confident  one answer, green spine, with a confidence meter
     .unsure     no answer at all: dashed frame, candidates side by side
     .healthy    short, green, none of the diagnosis furniture
     .retake     a polite request for a better photo (calm blue, not red)
     .quota / .failed   over the daily cap, and an actual failure
   Colours reuse the palette already in this file: .checkin.behind amber for
   "hold on", .stage-pill.failed clay for failure, green for good news. */
.dx { margin-top: 1.4rem; border-top: 1px solid var(--border); padding-top: 1rem; }
.dx-head { font-size: var(--fs-xs); font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--text-muted); }
.dx-intro { font-size: var(--fs-md); color: var(--text-muted); margin: 0.35rem 0 0.7rem; line-height: 1.5; max-width: 560px; }
.dx-pick { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.dx-status { font-size: var(--fs-md); color: var(--text-muted); }
.dx-lock { opacity: 0.55; pointer-events: none; }        /* in flight: one photo at a time */
.dx-thumb img { max-height: 120px; border-radius: 8px; margin-top: 0.5rem; display: block; }
.dx-live { margin-top: 0.75rem; }
.dx-wait { font-size: var(--fs-md); line-height: 1.45; color: var(--text); }
.dx-disc { font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.5; margin: 0.75rem 0 0; max-width: 560px; }

/* Chrome shared by every outcome card. */
.dx-card { border-radius: 12px; padding: 0.9rem 1.05rem; }
.dx-card h4 { margin: 0; font-size: var(--fs-lg); line-height: 1.3; }
.dx-idas { font-size: var(--fs-md); color: var(--text-muted); margin: 0.25rem 0 0; }
.dx-idas i { font-style: italic; }
.dx-sec { margin-top: 0.75rem; }
.dx-sec-h { font-size: var(--fs-xs); font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); }
.dx-sec p { margin: 0.2rem 0 0; font-size: var(--fs-md); line-height: 1.55; }
.dx-do { margin: 0.3rem 0 0; padding-left: 1.15rem; font-size: var(--fs-md); line-height: 1.55; }
.dx-do li { margin-top: 0.22rem; }
/* "Log it" sits inline after the action, quiet until hovered — the advice is
   the content, logging it is an afterthought the gardener reaches for once
   they've actually done the thing. */
.dx-log { background: none; border: 0; padding: 0 0 0 0.15rem; margin-left: 0.3rem;
          font: inherit; font-size: var(--fs-sm); color: var(--green-dark); cursor: pointer;
          opacity: 0.62; white-space: nowrap; }
.dx-log:hover { opacity: 1; text-decoration: underline; }
.dx-log[disabled] { opacity: 0.55; cursor: default; text-decoration: none; }
.dx-acts { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.9rem; }
.dx-watch { margin-top: 0.75rem; font-size: var(--fs-sm); line-height: 1.5; color: #8a5a2b; background: var(--warn-tint); border: 1px solid var(--warn); border-radius: 8px; padding: 0.5rem 0.7rem; }

/* 1. Confident — the only card that asserts something, and the only one that
      carries a confidence meter. */
.dx-card.confident { background: var(--surface); border: 1px solid var(--border); border-left: 5px solid var(--green-dark); box-shadow: var(--shadow-sm); }
.dx-conf { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.55rem; }
.dx-conf .mini-bar { margin: 0; width: 110px; flex: none; }
.dx-conf .mini-bar i { background: var(--green-dark); }
.dx-conf-txt { font-size: var(--fs-sm); font-weight: 700; color: var(--green-dark); }

/* 2. Not sure — deliberately NOT a dimmer confident card. No verdict line, a
      dashed unfinished frame, and the candidates laid out side by side with
      what tells them apart, because comparing is the job here. */
.dx-card.unsure { background: var(--surface); border: 2px dashed #e0c98d; }
.dx-badge { display: inline-block; font-size: var(--fs-2xs); font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: #9c6a14; background: #fff7e6; border: 1px solid #f3dca0; border-radius: 999px; padding: 2px 10px; }
.dx-lead { margin: 0.55rem 0 0; font-size: var(--fs-base); font-weight: 600; line-height: 1.45; }
.dx-cands { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 0.6rem; margin-top: 0.8rem; align-items: start; }
.dx-cand { background: var(--surface); border: 1px solid #ecdcb4; border-radius: 10px; padding: 0.7rem 0.8rem; }
.dx-cand-label { font-weight: 700; font-size: var(--fs-base); line-height: 1.3; }
.dx-cand .mini-bar { max-width: none; }
.dx-cand .mini-bar i { background: #d9a53c; }
.dx-cand-pct { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 0.25rem; }
.dx-cand-tell { font-size: var(--fs-sm); line-height: 1.5; margin: 0.5rem 0 0; }
.dx-cand-tell b { display: block; font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: #9c6a14; margin-bottom: 0.15rem; }

/* 3. Healthy — good news, said plainly. */
.dx-card.healthy { background: var(--green-pale); border: 1px solid var(--green-light); display: flex; gap: 0.8rem; align-items: flex-start; }
.dx-card.healthy h4 { color: var(--green-dark); }
.dx-ok-ic { font-size: var(--fs-2xl); line-height: 1; flex: none; }

/* 4. Unusable photo — a request, not an error: calm blue, camera tips. */
.dx-card.retake { background: var(--info-tint); border: 1px solid #cfdcec; }
.dx-card.retake h4 { color: #2c4f77; }
.dx-tips { margin: 0.5rem 0 0; padding-left: 1.15rem; font-size: var(--fs-md); line-height: 1.6; color: #2c4f77; }

/* Over the daily cap, a real failure, and "still working" — three more shapes. */
.dx-card.quota { background: #fff7e6; border: 1px solid #f3dca0; color: #9c6a14; }
.dx-card.failed { background: #fbe9e2; border: 1px solid #f0cdbe; color: #b5563a; }
.dx-card.slow { background: var(--surface-2); border: 1px solid var(--border); color: var(--text); }
.dx-card.quota h4, .dx-card.failed h4, .dx-card.slow h4 { color: inherit; }
.dx-note { font-size: var(--fs-md); line-height: 1.55; margin: 0.35rem 0 0; }
.dx-detail { font-size: var(--fs-sm); line-height: 1.5; margin: 0.4rem 0 0; opacity: 0.85; word-break: break-word; }
.dx-thanks { font-size: var(--fs-sm); line-height: 1.5; margin-top: 0.75rem; color: var(--green-dark); background: var(--green-pale); border: 1px solid var(--green-light); border-radius: 8px; padding: 0.5rem 0.7rem; }


/* The plant card's one-line photo-check summary. The capture flow lives on
   diagnose.html; this is just the answer, where the plant is. */
.dx-mini { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  margin-top: 0.9rem; padding-top: 0.85rem; border-top: 1px dashed var(--border); }
.dx-mini-h { font-size: var(--fs-sm); font-weight: 700; color: var(--green-dark); }
.dx-mini-s { font-size: var(--fs-sm); color: var(--text-muted); flex: 1; min-width: 0; overflow-wrap: anywhere; }
.dx-mini.ok  .dx-mini-s { color: var(--text); }
.dx-mini.bad .dx-mini-s { color: #8a5a2b; }


/* ══ Photo check-up promo ══════════════════════════════════════════════════
   An editorial card, not another white panel. The member's own last check
   photograph is the background, under a scrim heavy enough at the base to
   keep white type legible over any image, and the copy sits on top of it.
   That is what makes it read as a feature rather than a settings row, and it
   is honest: the picture is theirs, not stock.

   With no history yet the image is absent and the gradient carries the card
   on its own, so the layout does not change shape once a photo exists. */
.dxf-card {
  position: relative; overflow: hidden; isolation: isolate;
  /* No margin-bottom: both rails that hold this card are flex columns with
     gap: 1rem, so a margin here adds to the gap and opened a 32px hole
     under the promo where every other card sits 16px apart. */
  border-radius: var(--radius);
  min-height: 208px; display: flex; align-items: flex-end;
  /* The illustration is cropped to the lens and the spotted leaf. The full
     original put the glass at a third of its width, which at 300px in the
     sidebar made it unreadably small. The green stays underneath as the
     fallback and the colour before the image decodes. */
  background:
    url('dx-glass.jpg') no-repeat center 40% / cover,
    linear-gradient(140deg, var(--green-dark), #1d4a38 55%, var(--green-dark));
  box-shadow: 0 8px 22px rgba(20, 60, 40, 0.24);
  color: #fff;
}
/* .dxf-art and .has-shot are gone with the photo overlay they existed for. */
/* The scrim. Light at the top so the photograph is still visible as a
   photograph, near-opaque at the base where the text sits. */
.dxf-card::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(12, 32, 22, 0.10) 0%,
    rgba(12, 32, 22, 0.50) 42%,
    rgba(12, 32, 22, 0.90) 100%);
}
/* The scrim used to be switched off when there was no member photo,
   because the card was then a flat green wash that needed no help.
   It always has artwork now, so the scrim always applies. */
.dxf-body { position: relative; z-index: 2; padding: 1.05rem 1.15rem 1.15rem; width: 100%; }
.dxf-tag {
  display: inline-block; font-size: var(--fs-2xs); font-weight: 800; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--green-dark); background: var(--green-light);
  border-radius: 999px; padding: 2px 9px; margin-bottom: 0.55rem;
}
.dxf-h { font-size: var(--fs-lg); font-weight: 800; line-height: 1.22; letter-spacing: -0.015em;
  text-shadow: 0 1px 8px rgba(0,0,0,0.35); }
.dxf-p { font-size: var(--fs-sm); line-height: 1.5; opacity: 0.92; margin: 0.3rem 0 0.9rem;
  text-shadow: 0 1px 6px rgba(0,0,0,0.3); }
.dxf-cta {
  display: inline-block; font-size: var(--fs-sm); font-weight: 700; text-decoration: none;
  color: var(--green-dark); background: #fff; border-radius: 999px; padding: 0.44rem 1rem;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: transform .12s, box-shadow .12s;
}
.dxf-cta:hover { transform: translateY(-1px); box-shadow: 0 5px 14px rgba(0,0,0,0.28); text-decoration: none; }


/* ══ Ellipsis action menu ═══════════════════════════════════════════════
   Shared by the garden's plant cards and the tracker's item cards. It was
   defined twice with different chrome, so the same control looked like a
   pill on one page and a bare glyph on the other. */
/* No border. It used to carry .btn.btn-outline, which put a pill outline
   around a glyph and made it read as a peer of "Harvest" and "Visualize".
   So a row of real actions ended up with an odd, unevenly-weighted extra.
   A menu affordance should recede until reached for. Hit area is kept via
   padding rather than a visible box. */
.pmenu-btn {
  background: none;
  border: 0;
  padding: 0.3rem 0.5rem;
  margin: 0;
  font: inherit;
  font-size: var(--fs-lg);
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: 8px;
  transition: background .12s, color .12s;
}
.pmenu-btn:hover { background: var(--green-pale); color: var(--green-dark); }
.pmenu-btn[aria-expanded="true"] { background: var(--green-pale); color: var(--green-dark); }
.pmenu-btn:focus-visible { outline: 2px solid var(--green-mid); outline-offset: 2px; }


/* ── Waiting line ────────────────────────────────────────────────────────
   nvSay() and nvWaiting() in app.js build these on any page with a long
   wait: the variety verdict on Garden, the photo check on Photo Check-up.
   They used to be defined only in garden.html, so the same markup rendered
   unstyled everywhere else. */
.nv-spin { display: inline-block; width: 12px; height: 12px; margin-right: 0.4rem; vertical-align: -2px;
  border: 2px solid var(--green-light); border-top-color: var(--green-dark); border-radius: 50%;
  animation: nvSpin 0.8s linear infinite; }
@keyframes nvSpin { to { transform: rotate(360deg); } }
.nv-prog { display: block; margin: 0.35rem 0 0.15rem; max-width: 200px; }
.nv-prog i { transition: width 0.9s linear; }
.nv-left { display: block; font-size: var(--fs-2xs); color: var(--text-muted); }
@media (prefers-reduced-motion: reduce) {
  /* No spin; the determinate bar under it still carries the progress. */
  .nv-spin { animation: none; }
  .nv-prog i { transition: none; }
}

/* The photo check's confirm step. Softer than an answer card: it is a
   question, not a verdict, so it should not look like a result. */
.dx-card.confirm { border-left: 3px solid var(--green-mid); background: var(--surface-2, var(--surface)); }
.dx-card.confirm h4 { margin: 0 0 0.35rem; font-size: var(--fs-md); }
.dx-confirm-why { margin: 0 0 0.2rem; font-size: var(--fs-sm); color: var(--text-muted); }

/* ── Password rule hints ──
   Live checklist under a password field. Shown only once typing starts, so an
   empty form is not a wall of red before anyone has done anything wrong. */
.pw-hints {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.8rem;
  margin-top: 0.45rem;
}
.pw-hints:empty { display: none; }
.pw-hint {
  font-size: var(--fs-sm);
  line-height: 1.35;
  display: inline-flex;
  align-items: baseline;
  gap: 0.28rem;
}
.pw-hint.ok  { color: var(--green-mid); }
/* Not red: an unmet rule on a half-typed password is not an error yet, and
   colouring it like one makes normal typing feel like failure. */
.pw-hint.bad { color: var(--text-muted); }


/* ── Subject artwork on cards ──────────────────────────────────────────────
   Used by the Tracker's subject tiles and My Homestead's project tiles. Both
   are white cards with dark text, so the photograph is NOT a full-bleed
   background: it is washed into the right-hand side behind a surface-coloured
   gradient, leaving the text column on the left solid and legible. Each image
   was composed with its subject in the right two-thirds and a deliberately
   plain left third, which is what makes this work without a dark scrim.

   The attribute selector is load-bearing. Both .subj-tile and .htile set
   `background:` as a shorthand in their own page's inline <style>, which
   resets background-image to none and would otherwise win on source order.
   .subj-tile[data-subject] outranks .subj-tile on specificity, so this holds
   wherever it sits.

   ?v= on every filename because the deploy only stamps app.js and style.css;
   an image replaced without bumping this is served stale forever. Bump the
   number when you change a picture.

   A missing file degrades to exactly today's appearance: the gradient still
   paints the surface colour and the card is simply plain. */
.subj-tile[data-subject],
.htile[data-subject] {
  background-image:
    linear-gradient(100deg,
      var(--art-wash) 0%,
      var(--art-wash) 50%,
      color-mix(in srgb, var(--art-wash) 40%, transparent) 72%,
      transparent 94%),
    var(--subject-art, none);
  background-repeat: no-repeat, no-repeat;
  background-position: center, center right;
  background-size: cover, cover;
}

.subj-tile[data-subject="gardening"],          .htile[data-subject="gardening"]          { --subject-art: url('subjects/gardening.webp?v=1'); }
.subj-tile[data-subject="composting"],         .htile[data-subject="composting"]         { --subject-art: url('subjects/composting.webp?v=1'); }
.subj-tile[data-subject="beekeeping"],         .htile[data-subject="beekeeping"]         { --subject-art: url('subjects/beekeeping.webp?v=1'); }
.subj-tile[data-subject="chickens-poultry"],   .htile[data-subject="chickens-poultry"]   { --subject-art: url('subjects/chickens-poultry.webp?v=1'); }
.subj-tile[data-subject="solar"],              .htile[data-subject="solar"]              { --subject-art: url('subjects/solar.webp?v=1'); }
.subj-tile[data-subject="green-energy"],       .htile[data-subject="green-energy"]       { --subject-art: url('subjects/green-energy.webp?v=1'); }
.subj-tile[data-subject="water-conservation"], .htile[data-subject="water-conservation"] { --subject-art: url('subjects/water-conservation.webp?v=1'); }
.subj-tile[data-subject="food-preservation"],  .htile[data-subject="food-preservation"]  { --subject-art: url('subjects/food-preservation.webp?v=1'); }
.subj-tile[data-subject="diy-building"],       .htile[data-subject="diy-building"]       { --subject-art: url('subjects/diy-building.webp?v=1'); }
.subj-tile[data-subject="sustainability"],     .htile[data-subject="sustainability"]     { --subject-art: url('subjects/sustainability.webp?v=1'); }
.subj-tile[data-subject="homesteading"],       .htile[data-subject="homesteading"]       { --subject-art: url('subjects/homesteading.webp?v=1'); }

/* The subject tiles and the My Homestead project tiles carry a photograph, so
   the emoji chip that used to identify them is gone: two things saying "this
   is beekeeping" is one too many, and the chip was a bright square sitting on
   warm paper over an image.

   That hands the job to the title, which takes a serif. It is a system stack
   with no web font and so no network request, no build step and nothing to
   self-host, on a site that already has enough to explain about itself. On
   warm paper beside a photograph it reads like a field guide rather than a
   dashboard, which is nearer what this app is.

   Dropping the chip also bought back about 56px of width, which is what makes
   the fully-exposed photograph workable: the long labels that used to wrap to
   two lines and push their subtitle into the picture now fit on one. */
.subj-tile[data-subject] .subj-name,
.htile[data-subject] .htile-nm {
  font-family: ui-serif, Georgia, 'Iowan Old Style', 'Palatino Linotype', 'Book Antiqua', serif;
  /* Kept large enough that the long names still break onto two lines. The
     wrapping is wanted: with the text anchored top-left the second line grows
     down into the corner the photograph never uses, and a two-line name reads
     as a title rather than a row label. Dropping this to 1rem fitted them all
     on one line and lost that. */
  font-size: 1.12rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.15;
}
/* The text gets a column of its own rather than running as far as it likes.
   Without a width it behaved differently on every card: a one-line name put
   the subtitle high, a two-line name pushed it down, so a row of tiles had no
   shared rhythm, and on the wrapping cards the subtitle ran out onto the
   photograph.

   72% is the figure that only breaks the names that genuinely are long
   (Chickens & Poultry, Water Conservation, Food Preservation). Narrower and
   Green Energy starts breaking too, which is wrapping for its own sake.

   With the column fixed, the block can be centred again - every tile now puts
   its title and count at the same height - and the wash is held mostly opaque
   out to the column's edge so no text ever sits on bare photograph. */
.subj-tile[data-subject] {
  align-items: center;
  padding: 0.85rem 1rem 0.9rem 1.15rem;
}
.subj-tile[data-subject] .subj-meta { max-width: 72%; }
.subj-tile[data-subject] .subj-count { white-space: nowrap; }
