/* Plant range research — internal, noindex page. Uses the shared design tokens
   so it matches the site without inventing a second visual system.

   ── SPACING ────────────────────────────────────────────────────────────────
   One scale, declared once. Every section on this page had been reaching for
   its own margin — 22px here, 34px there, 26px somewhere else — so nothing
   lined up vertically and the page read as a stack of unrelated widgets rather
   than one document. These four steps are the only vertical values used
   between blocks; anything inside a block uses the component's own padding. */
:root {
  --pr-gap-xs: 8px;    /* between siblings inside a component  */
  --pr-gap-sm: 14px;   /* heading to its content               */
  --pr-gap-md: 22px;   /* between components in a section      */
  --pr-gap-lg: 40px;   /* between top-level sections           */
}

/* ── Shared section shell ────────────────────────────────────────────────────
   Both bottom sections use this, so their headings sit on the same baseline
   rhythm and their rules are the same weight. */
.pr-sec { margin: var(--pr-gap-lg) 0 0; padding-top: var(--pr-gap-md); border-top: 1px solid var(--border); }
.pr-sec-head { margin-bottom: var(--pr-gap-sm); }
.pr-sec-h { font-size: 1.25rem; font-weight: 700; color: var(--text); margin: 0; letter-spacing: -0.01em; }
.pr-sec-sub { font-size: var(--fs-sm); color: var(--muted); margin: 4px 0 0; max-width: 70ch; line-height: 1.6; }
.pr-sec-none { font-size: var(--fs-sm); color: var(--muted); margin: 0; line-height: 1.6; }
.pr-sec-note {
  font-size: var(--fs-xs); color: var(--faint); line-height: 1.65;
  margin: var(--pr-gap-md) 0 0; max-width: 78ch;
}

/* ── Summary strip ───────────────────────────────────────────────────────────
   Three fixed lines per card — number, label, detail — so every card has the
   same silhouette and the row reads as one object rather than five. The old
   two-line card let one long label wrap and only one card grew, which is the
   difference between a designed second line and an accidental one. */
.pr-stats {
  display: grid; grid-template-columns: repeat(5, 1fr); grid-auto-rows: 1fr;
  gap: 10px; margin: var(--pr-gap-md) 0 var(--pr-gap-sm);
}
.pr-stat {
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  padding: 13px 15px 12px; display: flex; flex-direction: column; min-width: 0;
}
.pr-stat-n {
  font-size: 1.5rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; line-height: 1.15; letter-spacing: -0.02em;
}
.pr-stat-l { margin-top: 3px; font-size: var(--fs-xs); color: var(--text2); line-height: 1.35; }
/* Pinned to the bottom so the detail line sits on one baseline across the row
   even when a label above it runs to two lines. */
.pr-stat-d { margin-top: auto; padding-top: 5px; font-size: var(--fs-xs); color: var(--faint); font-variant-numeric: tabular-nums; }

/* ── Controls ────────────────────────────────────────────────────────────────
   ONE panel, three bands: search, then filters, then actions behind a hairline.

   Previously all twelve controls were flex children of a single wrapping row,
   so the line breaks fell wherever the widths happened to land — eight selects
   across two and a bit rows with Export stranded on a third. Wrapping chose the
   layout; nothing about the design did. Here the filters are an explicit grid
   and the actions are a separate band, so the shape is fixed and the controls
   are grouped by what they DO rather than by how wide they are. */
.pr-controls {
  display: flex; flex-direction: column; gap: 10px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 12px; margin-bottom: var(--pr-gap-sm);
}
.pr-search-wrap { position: relative; min-width: 0; display: block; }
.pr-search-ico {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; color: var(--muted); pointer-events: none;
}
.pr-search-ico svg { width: 100%; height: 100%; display: block; }
.pr-search {
  width: 100%; background: var(--field-bg); color: var(--text);
  border: 1px solid var(--field-border); border-radius: var(--field-radius);
  height: 40px; padding: 0 12px 0 36px; font-size: 1rem; font-family: inherit; outline: none;
}
.pr-search:focus { border-color: var(--blue); box-shadow: var(--focus-ring); }

/* Exactly four columns: the eight filters fall into two even rows instead of
   wrapping into a ragged two-and-a-bit. min-width:0 is what lets 1fr mean 1fr —
   a select sized by its longest option otherwise refuses to shrink. */
.pr-filters { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.pr-filters > * { min-width: 0; }
.pr-select {
  width: 100%; height: 38px;
  background: var(--bg2); color: var(--text); border: 1px solid var(--border2);
  /* Right padding clears the native chevron. At 11px the longest option ran
     underneath it — "Difficulty — any" and the arrow occupied the same pixels. */
  border-radius: 8px; padding: 0 28px 0 11px; font-size: var(--fs-sm); font-family: inherit; cursor: pointer;
  text-overflow: ellipsis;
}
/* A set filter is visibly set. Without this the only way to tell the list was
   filtered was to read all eight controls one by one.

   Opt-IN (data-active="1"), not opt-out: keyed the other way, every select would
   render highlighted for the moment before the script runs. */
.pr-select[data-active="1"] { border-color: var(--blue); background: var(--blue-bg); }

/* Actions band. The hairline is doing the grouping work that spacing alone
   could not once the row wrapped. */
.pr-actions {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px 16px; padding-top: 10px; border-top: 1px solid var(--border);
}
.pr-toggles { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; }
.pr-btns { display: flex; align-items: center; gap: 8px; }
.pr-select:focus { border-color: var(--blue); box-shadow: var(--focus-ring); outline: none; }
.pr-reset {
  background: transparent; color: var(--muted); border: 1px solid var(--border2);
  border-radius: 8px; height: 38px; padding: 0 14px; font-size: var(--fs-sm); font-family: inherit; cursor: pointer;
  transition: color var(--transition), border-color var(--transition), opacity var(--transition);
}
.pr-reset:hover:not(:disabled) { color: var(--text); border-color: var(--border-strong); }
/* Nothing to clear, so it says so rather than sitting there looking live. */
.pr-reset:disabled { opacity: .4; cursor: default; }

/* Export sits with the filters because that is what it exports — the current
   filtered set, every page of it, not just the visible twenty. */
.pr-export {
  background: var(--bg2); color: var(--text2); border: 1px solid var(--border2);
  border-radius: 8px; height: 38px; padding: 0 14px; font-size: var(--fs-sm); font-weight: 600;
  font-family: inherit; cursor: pointer; white-space: nowrap;
  transition: color var(--transition), border-color var(--transition), background var(--transition);
}
.pr-export:hover { color: var(--blue); border-color: var(--blue); background: var(--blue-bg); }
.pr-export:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

.pr-count { color: var(--muted); font-size: var(--fs-sm); margin: 0 0 10px; }
.pr-count.is-err { color: var(--danger); font-weight: 600; }

/* ── The compare grid ────────────────────────────────────────────────────────
   ONE fixed price column — the best offer — plus an expander for the rest.

   A column per shop worked at three and broke at five: 5 x 104px alongside the
   name, saving and add controls is wider than the page, and the plant name is
   what gets squeezed. Rather than shrink everything, only the column you
   actually read down stays fixed. The others are a click away, and on most rows
   they are not what you came for — 783 of 1,050 rows have only one shop anyway.

   Fixed widths, so the best price still reads as a column down the page. That
   was the whole point of the previous rework and it is not given up here. */
.pr-head, .pr-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 132px 96px 92px 78px;
  gap: 0 10px;
  align-items: center;
}

/* Sticky, so you always know which column is which shop 600 rows down. Opaque
   background, or the rows show through as they scroll under it.
   
   The offset is NOT zero: .site-nav is position:fixed and 60px tall, so a header
   stuck at top:0 parks itself underneath it and is never seen. Kept as a named
   value here because there is no global nav-height token to reference. */
.pr-head {
  /* .site-nav is height:60px at top:var(--banner-h,0) — so clear both, or the
     header parks under the nav (and under the promo banner when one is up). */
  position: sticky; top: calc(var(--banner-h, 0px) + 60px); z-index: 3;
  background: var(--bg); padding: 0 14px 8px;
  font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--faint);
  border-bottom: 1px solid var(--border);
}
.pr-head-best { text-align: center; }
.pr-head-more { text-align: center; }
.pr-head-save { text-align: right; }

.pr-list { display: flex; flex-direction: column; gap: 5px; padding-top: 6px; }

.pr-row {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid transparent; border-radius: 9px; padding: 9px 14px;
}
.pr-row:hover { border-color: var(--border-strong); }
/* A row where one shop is genuinely cheaper gets an edge — the eye finds the
   actionable rows without every row shouting. */
.pr-row.is-win { border-left-color: var(--green); }
.pr-row.is-picked, .pr-row.is-picked:hover { border-color: var(--blue); background: var(--blue-bg); }

/* ── Identity ───────────────────────────────────────────────────────────── */
.pr-id { min-width: 0; }
.pr-n {
  display: block; font-size: 0.9375rem; font-weight: 600; color: var(--text);
  line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* One quiet line of facts instead of four equal-weight chips. Only what we
   actually know: an absent value is omitted, not rendered as "Not known". */
/* One line, always. Wrapping is what made row heights vary from 65px to 113px
   once a fourth shop narrowed this column. */
.pr-meta {
  display: flex; flex-wrap: nowrap; align-items: center; gap: 7px;
  margin-top: 2px; font-size: var(--fs-xs); color: var(--muted);
  min-width: 0; overflow: hidden;
}
.pr-meta > * { white-space: nowrap; }
.pr-meta > .pr-prof { overflow: hidden; text-overflow: ellipsis; }
.pr-dot { width: 4px; height: 4px; border-radius: 50%; background: var(--faint); flex: none; }
.pr-form { color: var(--text2); }
.pr-diff { text-transform: capitalize; }
.pr-place { text-transform: capitalize; color: var(--text2); }
.pr-unk  { color: var(--faint); font-style: italic; }
.pr-co2.is-none { color: var(--green); }
.pr-co2.is-pref { color: var(--amber); }
.pr-co2.is-req  { color: var(--danger); }
.pr-prof { color: var(--blue); text-decoration: none; }
.pr-prof:hover { text-decoration: underline; }

/* ── Price cells ────────────────────────────────────────────────────────── */
.pr-cell {
  display: block; text-align: center; border-radius: 7px; padding: 5px 4px;
  text-decoration: none; color: inherit; min-width: 0;
}
a.pr-cell:hover { background: var(--bg2); }
a.pr-cell:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.pr-p {
  display: block; font-size: 0.9375rem; font-weight: 700; line-height: 1.2;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.pr-s {
  display: block; margin-top: 1px; font-size: 0.625rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--faint);
}
.pr-cell.is-in .pr-s  { color: var(--green); }
.pr-cell.is-out .pr-s { color: var(--danger); }
/* Struck through, because a price you cannot buy is not a price. */
.pr-cell.is-out .pr-p { color: var(--muted); text-decoration: line-through; text-decoration-thickness: 1px; }
.pr-cell.is-low .pr-p { color: var(--green); }
.pr-cell.is-none { color: var(--faint); font-size: 0.9375rem; }
/* Which shop the best price is at — the row's most useful fact after the price
   itself, so it sits under the number rather than behind the expander. */
.pr-best-shop {
  display: block; margin-top: 1px; font-size: 0.625rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
}
.pr-cell.is-low .pr-best-shop { color: var(--green); }
.pr-cell.is-out .pr-best-shop { color: var(--danger); }

/* ── Wide mode: one column per shop ──────────────────────────────────────────
   Opt-in. The default view is better for deciding what to buy; this one is
   better for seeing the market, and which you want depends on why you came.

   Only ONE header is ever displayed — both are in the DOM because the shop
   columns are rendered by PHP from the same list the filters use.

   --pr-shop-cols is set by the JS from the shop list, so a sixth shop needs no
   CSS change. The fallback of 5 only applies if the script has not run. */
.pr-head--wide { display: none; }
.pr-grid[data-view="wide"] .pr-head--best { display: none; }
.pr-grid[data-view="wide"] .pr-head--wide { display: grid; }
.pr-grid[data-view="wide"] .pr-head,
.pr-row--wide {
  grid-template-columns: minmax(0, 1fr) repeat(var(--pr-shop-cols, 5), minmax(0, 94px));
}
/* The rows carry a 1px border plus a 3px left accent; the header carries
   neither, so its content box starts 3px further left and its columns drifted
   out of line with the prices underneath by up to 3px. Mirror the insets with
   transparent borders rather than hand-tuning the padding — this stays correct
   if either padding value changes. */
.pr-grid[data-view="wide"] .pr-head--wide {
  border-left: 3px solid transparent;
  border-right: 1px solid transparent;
}
.pr-head-shop { text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Tighter than the default row: with no meta line there is nothing to make room
   for, and 1,050 rows of the same five prices are read by scanning down. */
.pr-row--wide { padding: 7px 14px; }

/* Fixed height, NOT auto. The "out" label is a second line, so without this a
   row containing one out-of-stock price stands 8px taller than its neighbours
   and the list loses the even rhythm that makes 1,050 rows scannable. Every
   cell reserves the space whether it uses it or not. */
.pr-w {
  display: flex; flex-direction: column; justify-content: center;
  min-height: 34px; text-align: center; border-radius: 7px; padding: 3px 4px;
  text-decoration: none; color: inherit; min-width: 0;
  font-variant-numeric: tabular-nums;
}
a.pr-w:hover { background: var(--bg2); }
a.pr-w:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.pr-w .pr-p { font-size: 0.875rem; }
.pr-w.is-out .pr-p { color: var(--muted); text-decoration: line-through; text-decoration-thickness: 1px; }
.pr-w.is-low .pr-p { color: var(--green); }
/* The stock line. Same .pr-s class and the same colours as the default view, so
   green-means-in-stock reads identically whichever layout you are in.

   Overridden smaller and with line-height pinned to 1: inheriting the body's
   1.6 made this second line 16px tall rather than 9px, which pushed the cell
   past the reserved 34px and put the uneven row heights straight back. */
.pr-w .pr-s { margin-top: 2px; font-size: 0.5625rem; line-height: 1; letter-spacing: .05em; }
.pr-w.is-in  .pr-s { color: var(--green); }
.pr-w.is-out .pr-s { color: var(--danger); }
/* A shop that does not list the plant. Faint, so "no listing" never competes
   with a real price for attention. */
.pr-w.is-none { color: var(--faint); font-size: 0.875rem; padding: 4px; }

/* ── Expander ────────────────────────────────────────────────────────────── */
.pr-more {
  display: block; width: 100%; cursor: pointer;
  background: transparent; color: var(--blue);
  border: 1px solid var(--border2); border-radius: 7px;
  padding: 5px 6px; font-size: var(--fs-xs); font-family: inherit; white-space: nowrap;
  transition: border-color var(--transition), background var(--transition);
}
.pr-more:hover { border-color: var(--blue); background: var(--blue-bg); }
.pr-more:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.pr-more-none { display: block; text-align: center; font-size: var(--fs-xs); color: var(--faint); }

/* The panel spans the whole row and is closed until asked for. Every shop is
   listed, including the ones that do not stock it — "not listed" is an answer. */
.pr-detail {
  grid-column: 1 / -1; display: none; flex-wrap: wrap; gap: 6px;
  margin-top: 9px; padding-top: 9px; border-top: 1px dashed var(--border2);
}
.pr-row.is-open .pr-detail { display: flex; }
.pr-d {
  display: inline-flex; align-items: baseline; gap: 6px; text-decoration: none;
  background: var(--bg2); border: 1px solid var(--border2); border-radius: 7px;
  padding: 5px 10px; font-size: var(--fs-sm); color: var(--text);
  font-variant-numeric: tabular-nums;
}
.pr-d:hover { border-color: var(--blue); }
.pr-d b { font-size: 0.625rem; font-weight: 700; letter-spacing: .04em;
          text-transform: uppercase; color: var(--muted); }
.pr-d-s { font-size: 0.625rem; font-weight: 700; text-transform: uppercase; color: var(--faint); }
.pr-d.is-in  .pr-d-s { color: var(--green); }
.pr-d.is-out .pr-d-s { color: var(--danger); }
.pr-d.is-out { opacity: .72; }
.pr-d.is-low { border-color: var(--green); background: var(--green-faint); }
.pr-d--none { color: var(--faint); border-style: dashed; }

/* ── Verdict ────────────────────────────────────────────────────────────── */
.pr-best {
  text-align: right; font-size: var(--fs-xs); font-weight: 700;
  color: var(--muted); font-variant-numeric: tabular-nums; min-width: 0;
}
.pr-best.is-win { color: var(--green); }
.pr-best small {
  display: block; font-size: 0.625rem; font-weight: 600; color: var(--faint);
  text-transform: uppercase; letter-spacing: .04em;
}

/* ── Add / quantity ──────────────────────────────────────────────────────
   Quiet until used. A row nobody can supply gets a dash, not a button that
   would add a line you cannot buy. */
.pr-add { text-align: right; min-width: 0; }
.pr-add-btn, .pr-qty-b {
  width: 28px; height: 28px; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg2); color: var(--muted);
  border: 1px solid var(--border2); border-radius: 7px;
  font-size: 1rem; font-family: inherit; line-height: 1;
  transition: color var(--transition), border-color var(--transition), background var(--transition);
}
.pr-add-btn:hover, .pr-qty-b:hover { color: var(--blue); border-color: var(--blue); background: var(--blue-bg); }
.pr-add-btn:focus-visible, .pr-qty-b:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.pr-qty { display: inline-flex; align-items: center; gap: 2px; }
.pr-qty-n {
  min-width: 22px; text-align: center; font-size: var(--fs-sm); font-weight: 700;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.pr-add-na { color: var(--faint); }

/* Show-bundles toggle. */
.pr-check {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font-size: var(--fs-sm); color: var(--muted); padding: 9px 4px;
}
.pr-check input { accent-color: var(--blue); width: 15px; height: 15px; cursor: pointer; }

/* ── Basket bar ──────────────────────────────────────────────────────────── */
.pr-basket {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 16px; z-index: 40;
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill); padding: 10px 12px 10px 18px; box-shadow: var(--shadow-lg);
  max-width: calc(100vw - 24px);
}
.pr-basket[hidden] { display: none; }
.pr-basket-n { font-size: var(--fs-sm); font-weight: 600; color: var(--text); white-space: nowrap; }
.pr-basket-clear {
  background: none; border: 0; color: var(--muted); font-family: inherit;
  font-size: var(--fs-sm); cursor: pointer; padding: 10px 4px; text-decoration: underline;
}
.pr-basket-clear:hover { color: var(--text); }
.pr-basket-go {
  background: var(--blue); color: var(--bg); border: 0; border-radius: var(--radius-pill);
  padding: 11px 20px; min-height: 44px; font-family: inherit; font-size: var(--fs-sm);
  font-weight: 700; cursor: pointer; white-space: nowrap;
}
.pr-basket-go:hover { filter: brightness(1.1); }
.pr-basket-go:focus-visible, .pr-basket-clear:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
/* Keep the fixed bar from covering the pager at the end of the list. */
body.pr-basket-open { padding-bottom: 84px; }

/* ── Finalise sheet ──────────────────────────────────────────────────────── */
.pr-sheet { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: 20px; }
.pr-sheet[hidden] { display: none; }
.pr-sheet-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.6); }
.pr-sheet-panel {
  position: relative; z-index: 1; width: 100%; max-width: 520px;
  max-height: min(84dvh, 720px); display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: 20px;
}
.pr-sheet-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.pr-sheet-h { font-size: 1.125rem; font-weight: 700; color: var(--text); margin: 0; }
.pr-sheet-x {
  background: none; border: 0; color: var(--muted); font-size: 1.5rem; line-height: 1;
  cursor: pointer; padding: 4px 8px; margin: -4px -8px 0 0; font-family: inherit;
}
.pr-sheet-x:hover { color: var(--text); }
.pr-sheet-x:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.pr-sheet-lede { font-size: var(--fs-sm); color: var(--muted); margin: 6px 0 14px; line-height: 1.5; }
/* The list is the only scrolling part, so the heading and totals stay put.
   
   min-height:0 IS LOAD-BEARING. A flex child defaults to min-height:auto, which
   means it refuses to shrink below its content — so overflow-y:auto never
   engaged, the list grew past the panel, and the totals block rendered on top of
   the last visible line. flex:1 1 auto gives it the leftover space; min-height:0
   lets it actually take less than its content. */
.pr-sheet-list {
  list-style: none; margin: 0; padding: 0 2px 0 0;
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 6px;
}
/* Never scrolls, never overlaps, and is opaque so a line scrolling under it is
   hidden rather than showing through. */
.pr-sheet-foot {
  flex: 0 0 auto; background: var(--surface);
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border);
}
/* ── The costing list ─────────────────────────────────────────────────────
   A line per plant with a quantity stepper and what it costs at the cheapest
   shop that actually has it. */
.pr-line {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center; gap: 12px;
  background: var(--bg2); border: 1px solid var(--border2);
  border-radius: 9px; padding: 8px 12px;
}
.pr-line-main { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.pr-line-name { font-size: 0.875rem; font-weight: 600; color: var(--text); }
.pr-line-sub  { font-size: var(--fs-xs); color: var(--muted); }
.pr-line-cost {
  font-size: 0.9375rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; min-width: 58px; text-align: right;
}
.pr-line-qty { display: inline-flex; align-items: center; gap: 2px; }
.pr-line-empty { color: var(--faint); padding: 14px 4px; list-style: none; }

/* ── Totals ──────────────────────────────────────────────────────────────
   Every shop is listed, and the lines a shop cannot supply are counted rather
   than dropped — a total that silently skips four plants is not a total.

   Neither figure is a recommendation. Splitting an order four ways to save a
   pound is rarely worth four lots of postage, and only the reader knows that,
   so the split shows exactly which shops it needs and lets them judge.

   One line per shop: five two-line blocks filled the panel and pushed the list
   down to a couple of visible rows. */
.pr-totals { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }

.pr-total {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline; gap: 2px 10px;
  border-radius: 8px; padding: 5px 10px;
}
.pr-total-l { font-size: var(--fs-sm); color: var(--text2); min-width: 0; }
.pr-total-v {
  font-size: 0.9375rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; text-align: right;
}
.pr-total-note { font-size: var(--fs-xs); color: var(--faint); }
.pr-total.is-na .pr-total-v { color: var(--faint); font-weight: 600; }

/* The split is the headline, so it gets the box; the per-shop lines below are
   quiet comparisons against it. */
.pr-total--split {
  border: 1px solid var(--green); background: var(--green-faint);
  padding: 10px 12px; margin-bottom: 4px;
}
.pr-total--split .pr-total-l { font-size: 0.9375rem; font-weight: 700; color: var(--text); }
.pr-total--split .pr-total-v { font-size: 1.125rem; color: var(--green); }
/* Which shops the split actually needs — the cost of that £24.96 is four
   separate orders, and that has to be visible next to the number. */
.pr-split-shops { grid-column: 1 / -1; font-size: var(--fs-xs); color: var(--muted); margin-top: 2px; }
.pr-split-shops b { color: var(--text2); font-weight: 600; }

/* Lock the page behind the sheet. overflow-y only — body sets overflow-x:clip. */
body.pr-sheet-open { overflow-y: hidden; }

/* ── Paging ──────────────────────────────────────────────────────────────── */
.pr-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin: 16px 0 4px; }
/* Must out-specify the display:flex above — the UA [hidden] rule loses to it. */
.pr-pager[hidden] { display: none; }
.pr-pager-btn {
  background: var(--bg2); color: var(--text); border: 1px solid var(--border2);
  border-radius: 8px; padding: 12px 18px; font-size: var(--fs-sm); font-weight: 600;
  font-family: inherit; cursor: pointer; white-space: nowrap; min-width: 104px;
  transition: color var(--transition), border-color var(--transition), opacity var(--transition);
}
.pr-pager-btn:hover:not(:disabled) { color: var(--blue); border-color: var(--blue); }
.pr-pager-btn:disabled { opacity: .45; cursor: not-allowed; }
.pr-pager-btn:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.pr-pager-status { font-size: var(--fs-sm); color: var(--muted); font-variant-numeric: tabular-nums; }

@media (max-width: 900px) {
  .pr-stats { grid-template-columns: repeat(3, 1fr); }
  /* Four columns of select at tablet width leaves each too narrow to read its
     own longest option. Two keeps them legible and still even. */
  .pr-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Wide mode scrolls sideways rather than shrinking five price columns past
     legibility. The matrix is the point; a squeezed matrix is not worth having.
     Only this grid scrolls — the page body never does. */
  .pr-grid[data-view="wide"] { overflow-x: auto; }
  /* Computed from the shop count, not a fixed number. At five shops 580px was
     right; the sixth silently squeezed the name column instead of scrolling,
     because the hard-coded minimum no longer matched the columns inside it.
     --pr-shop-cols is unitless, so it multiplies cleanly here and this stays
     correct for a seventh. */
  .pr-grid[data-view="wide"] .pr-head,
  .pr-grid[data-view="wide"] .pr-list {
    min-width: calc(190px + (var(--pr-shop-cols, 5) * 88px));
  }
  /* overflow-x:auto makes this the scroll container, so a sticky header would
     stick to the grid rather than the viewport. Say so rather than leave it
     looking broken. */
  .pr-grid[data-view="wide"] .pr-head { position: static; }
  /* 190px, not 130. The grid already scrolls sideways here, so squeezing the
     plant name buys nothing — and at 130px the name box came out at 91px once
     the form tag took its share, clipping all twenty rows including ones as
     short as "Crinum thaianum". The name is what identifies the row; the extra
     60px of scroll is a far cheaper price than an unreadable first column. */
  .pr-grid[data-view="wide"] .pr-head,
  .pr-row--wide {
    grid-template-columns: minmax(190px, 1fr) repeat(var(--pr-shop-cols, 5), minmax(0, 80px));
  }
}
@media (max-width: 640px) {
  .pr-stats { grid-template-columns: repeat(2, 1fr); }
  /* Five cards into two columns leaves one orphan on the last row. Spanning it
     makes that deliberate instead of leftover. */
  .pr-stat:last-child { grid-column: 1 / -1; }
  /* A grid item defaults to min-width:auto, so a select sized by its longest
     option ("Coverage — any") refuses to shrink and the 1fr columns overflow the
     viewport. Because body sets overflow-x:clip that overflow does not scroll —
     it is silently CLIPPED, leaving the right-hand filters unreachable rather
     than merely off-screen. min-width:0 is what lets 1fr mean 1fr. */
  /* 44px minimum tap target. Padding alone leaves these at 36-42px once the
     iOS 16px font rule applies, so pin the height rather than guessing. */
  .pr-select, .pr-reset, .pr-export { height: 44px; min-height: 44px; }
  .pr-check { min-height: 44px; }
  /* Toggles stack; the two buttons share a row and fill it, so neither is left
     stranded on a line of its own. */
  .pr-actions { flex-direction: column; align-items: stretch; }
  .pr-toggles { flex-direction: column; align-items: flex-start; gap: 0; }
  .pr-btns > * { flex: 1 1 0; }
  .pr-fresh-list { grid-template-columns: 1fr; }

  /* ── The grid restacks on a phone ──────────────────────────────────────
     Name on its own line, then the shop cells across in one row so the
     comparison survives — putting them one above the other would destroy the
     only reason the columns exist. The header is dropped and each cell labels
     its own shop instead. */
  .pr-head { display: none; }
  .pr-row {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px; padding: 11px 12px;
  }
  .pr-id { grid-column: 1 / -1; }
  .pr-n { white-space: normal; }
  .pr-cell { padding: 6px 10px; border: 1px solid var(--border2); border-radius: 8px; text-align: left; }
  .pr-cell.is-none { border-style: dashed; }
  .pr-more { width: auto; min-height: 44px; padding: 8px 14px; }
  /* The detail chips are links, so they need a real tap target too. */
  .pr-d { min-height: 44px; align-items: center; padding: 8px 12px; }
  .pr-best { grid-column: 1 / -1; text-align: left; }
  .pr-best small { display: inline; margin-left: 6px; }
  /* Steppers get a full-width row of their own on a phone. */
  .pr-add { grid-column: 1 / -1; text-align: left; }
  .pr-add-btn, .pr-qty-b { width: 44px; height: 44px; }
  /* Pre-existing controls that were under target once measured on a phone. */
  .pr-basket-clear, .pr-basket-go { min-height: 44px; }
  .pr-sheet-x { width: 44px; height: 44px; }

  .pr-basket { left: 12px; right: 12px; transform: none; max-width: none; justify-content: space-between; }
  .pr-sheet { padding: 0; align-items: flex-end; }
  /* Bottom sheet on a phone: full width, square off the bottom corners. */
  .pr-sheet-panel { max-width: none; max-height: 88dvh; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }

  /* Full-width tap targets; the status sits between them and stays legible. */
  .pr-pager { gap: 10px; }
  .pr-pager-btn { flex: 1 1 0; min-width: 0; padding: 12px 10px; min-height: 44px; }

  /* ── Wide mode opts OUT of the phone restack ───────────────────────────
     The rules above turn a row into a stacked card and drop the header, which
     is right for the default view and wrong for this one — a matrix that has
     been stacked is no longer a matrix. Wide mode keeps its shape and scrolls
     instead, so the header must stay: without it the price columns are five
     unlabelled numbers. */
  /* Same padding AND the same gap as the rows — a header on a different gap
     puts its labels over the wrong prices as the columns march right. */
  .pr-grid[data-view="wide"] .pr-head--wide { display: grid; padding: 0 12px 8px; gap: 0 8px; }
  /* Specificity, not just source order: the .pr-row restack rule above matches
     with the same weight, and being later in the file it wins — which wrapped
     six grid items onto three lines and made a 50px row 150px tall. */
  .pr-grid[data-view="wide"] .pr-row--wide {
    grid-template-columns: minmax(190px, 1fr) repeat(var(--pr-shop-cols, 5), minmax(0, 80px));
  }
  .pr-row--wide { padding: 8px 12px; gap: 0 8px; }
  .pr-row--wide .pr-id { grid-column: auto; }
  .pr-row--wide .pr-n { white-space: nowrap; }
  /* 44px tap target: these are links out to a shop, same as everywhere else. */
  .pr-w { min-height: 44px; display: flex; flex-direction: column; justify-content: center; }
}

/* ── Data freshness ──────────────────────────────────────────────────────────
   Replaces the manual refresh buttons. Quiet when current, loud when not —
   an indicator that only ever reads "updated" is worse than none, because the
   page looks healthy while the data rots. */
/* Collapsed to a single line. Five cards each saying "updated 13 min ago" spent
   200px restating one fact and wrapped 4+1, which was the untidiest thing on the
   page. <details> keeps the per-shop breakdown one click away — native, so it is
   keyboard-operable and needs no script. */
.pr-fresh {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 10px; margin: 0 0 var(--pr-gap-sm);
}
.pr-fresh-sum {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 14px; min-height: 40px; cursor: pointer; list-style: none;
  font-size: var(--fs-sm);
}
.pr-fresh-sum::-webkit-details-marker { display: none; }
.pr-fresh-sum:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; border-radius: 10px; }
.pr-fresh-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); flex: none; }
.pr-fresh.is-warn .pr-fresh-dot { background: var(--amber); }
.pr-fresh-lead { color: var(--text); font-weight: 600; }
.pr-fresh.is-warn .pr-fresh-lead { color: var(--amber); }
.pr-fresh-meta { color: var(--muted); font-size: var(--fs-xs); }
/* Pushed right, and it is the only affordance in the row — so the row reads as
   "here is the state, and here is where the detail is". */
.pr-fresh-more { margin-left: auto; color: var(--blue); font-size: var(--fs-xs); white-space: nowrap; }
.pr-fresh[open] .pr-fresh-more::after { content: ' \2013 hide'; }
.pr-fresh-list {
  /* auto-FIT, not auto-fill: fill reserved a sixth empty track and pushed the
     fifth shop onto a line of its own — the same 4+1 raggedness this section was
     collapsed to avoid, merely hidden behind a disclosure. fit collapses the
     empty track so five shops sit in five even columns. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px; padding: 12px 14px 14px; border-top: 1px solid var(--border);
}
/* A COLUMN, not a wrapping row. As a row the age sat beside a short shop name
   ("Cloud Aqua updated 22 min ago") and under a long one, so the four panels
   never lined up. Stacking makes the layout independent of name length. */
.pr-fresh-shop {
  min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  background: var(--bg2); border: 1px solid var(--border);
  border-left-width: 3px; border-radius: 8px; padding: 8px 11px;
}
.pr-fresh-name { font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.pr-fresh-age  { font-size: var(--fs-xs); color: var(--muted); }
.pr-fresh-err  { font-size: var(--fs-xs); font-weight: 700; color: var(--danger); }
.pr-fresh-shop.is-stale .pr-fresh-age,
.pr-fresh-shop.is-never .pr-fresh-age { color: var(--amber); font-weight: 600; }
.pr-fresh-shop.is-stale { border-color: var(--amber-faint-border); }
.pr-fresh-shop.is-never { border-color: var(--red-faint-border); }

/* ── What changed today ──────────────────────────────────────────────────────
   One list per KIND of change with both shops merged into it, rather than a
   panel per shop subdivided again into columns. The old shape gave every plant
   name a column too narrow to hold it; here a row gets the full page width. */
.pr-today { margin: var(--pr-gap-lg) 0 0; padding-top: var(--pr-gap-md); border-top: 1px solid var(--border); }
.pr-today-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px 16px; margin-bottom: var(--pr-gap-sm); }
.pr-today-h { font-size: 1.25rem; font-weight: 700; color: var(--text); margin: 0; }
.pr-today-none { color: var(--muted); font-size: 0.9375rem; margin: 0 0 16px; max-width: 70ch; line-height: 1.6; }

/* Headline counts, so the shape of the day reads before any list does. */
.pr-today-pills { display: flex; flex-wrap: wrap; gap: 7px; }
.pr-pill {
  font-size: var(--fs-xs); font-weight: 700; padding: 3px 10px; border-radius: 20px;
  border: 1px solid var(--border2); background: var(--bg3); color: var(--muted); white-space: nowrap;
}
.pr-pill--out { background: var(--red-faint);   color: var(--danger); border-color: var(--red-faint-border); }
.pr-pill--in  { background: var(--green-faint); color: var(--green);  border-color: var(--green-faint-border); }
.pr-pill--new { background: var(--blue-bg);     color: var(--blue);   border-color: var(--blue-dark); }

.pr-changes { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px 26px; }
.pr-change-group { min-width: 0; }
.pr-change-h {
  display: flex; align-items: center; gap: 8px; margin: 0 0 9px;
  font-size: var(--fs-sm); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted);
}
.pr-change-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: currentColor; }
.pr-change-h--out { color: var(--danger); }
.pr-change-h--in  { color: var(--green); }
.pr-change-h--new { color: var(--blue); }
.pr-change-n {
  background: var(--bg3); color: var(--text2); border-radius: 20px;
  padding: 1px 8px; font-size: var(--fs-xs); letter-spacing: 0; font-variant-numeric: tabular-nums;
}

.pr-change-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }

/* Same rhythm as the compare grid: a FIXED shop column so the plant names line
   up down the list. With variable-width chips ("Aqua Ess." against "Aq.
   Gardens") every name started at a different x and the two groups read as four
   ragged columns. One line per change, no wrapping. */
.pr-change-item {
  display: grid; grid-template-columns: 74px minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 7px; padding: 5px 10px;
}
.pr-change-shop {
  font-size: 0.5625rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--muted);
}
.pr-shop-chip--ae { color: var(--amber); }
.pr-shop-chip--ag { color: var(--teal); }
.pr-shop-chip--hz { color: var(--purple); }
.pr-change-name {
  color: var(--text); text-decoration: none; font-size: 0.8125rem; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pr-change-name:hover { color: var(--blue); text-decoration: underline; }
.pr-change-price {
  font-size: var(--fs-xs); color: var(--muted); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Collapsed rather than truncated — 45 back-in-stock is real information, it
   just should not be the first thing on the page. */
.pr-change-more { margin-top: 6px; }
.pr-change-more summary {
  cursor: pointer; font-size: var(--fs-sm); color: var(--blue);
  padding: 6px 2px; min-height: 32px; display: flex; align-items: center;
}
.pr-change-more summary:hover { text-decoration: underline; }
.pr-change-more[open] summary { margin-bottom: 6px; }

.pr-today-foot { display: flex; flex-wrap: wrap; gap: 6px 20px; margin: 20px 0 8px; }
.pr-today-src { font-size: var(--fs-xs); color: var(--muted); }
.pr-today-note { font-size: var(--fs-sm); color: var(--muted); margin: 0; max-width: 78ch; line-height: 1.6; }

@media (max-width: 640px) {
  .pr-changes { grid-template-columns: 1fr; gap: 20px; }
  /* Chip above the name on a phone — three columns in ~360px squeezes the name
     to a couple of words per line. */
  /* Roomier rows on a phone: the name is the row's target, and at desktop
     density it is only 21px tall. */
  .pr-change-item { grid-template-columns: minmax(0, 1fr) auto; padding: 11px 12px; }
  .pr-change-shop { grid-column: 1 / -1; justify-self: start; }
  .pr-change-name { white-space: normal; }
  .pr-change-more summary { min-height: 44px; }
}

/* ── Borrowed placement ──────────────────────────────────────────────────────
   A dotted underline, the long-standing convention for "there is more to know
   here", rather than a colour — colour would rank it against the CO2 and
   difficulty chips beside it, and this is the same kind of fact, just from a
   different source. */
.pr-place.is-shop { border-bottom: 1px dotted var(--border-strong); cursor: help; }

/* ── Price-history badge ─────────────────────────────────────────────────────
   Sits under the best price and only ever appears when there is enough history
   to justify it, so it must not reserve space when absent — hence a separate
   element rather than a third line inside the cell. */
.pr-hist {
  display: block; margin-top: 2px; font-size: 0.5625rem; font-weight: 700;
  line-height: 1; letter-spacing: .05em; text-transform: uppercase; text-align: center;
}
.pr-hist.is-low  { color: var(--green); }
.pr-hist.is-high { color: var(--amber); }
.pr-d--hist { border-style: dashed; color: var(--muted); }

/* ── Hard to find ────────────────────────────────────────────────────────────
   Two columns because the two groups are different findings, not one list split
   for layout. The left column carries names, the right carries an explanation —
   deliberately asymmetric, since listing 200 single-shop plants would bury the
   ten that actually matter. */
.pr-hard-cols {
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: var(--pr-gap-md);
}
.pr-hard-col { min-width: 0; }
.pr-hard-h {
  display: flex; align-items: center; gap: 9px;
  font-size: var(--fs-sm); font-weight: 700; color: var(--text);
  margin: 0 0 10px; padding-bottom: 9px; border-bottom: 1px solid var(--border);
}
.pr-hard-n {
  flex: none; min-width: 26px; padding: 2px 8px; border-radius: 20px;
  background: var(--bg3); border: 1px solid var(--border2);
  font-size: var(--fs-xs); font-weight: 700; color: var(--muted);
  text-align: center; font-variant-numeric: tabular-nums;
}
.pr-hard-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.pr-hard-item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 7px 0; border-bottom: 1px solid var(--border);
}
.pr-hard-item:last-child { border-bottom: 0; }
.pr-hard-name {
  font-size: var(--fs-sm); color: var(--text); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pr-hard-meta { flex: none; font-size: var(--fs-xs); color: var(--faint); font-variant-numeric: tabular-nums; }
.pr-hard-more { font-size: var(--fs-xs); color: var(--faint); margin: 10px 0 0; }
.pr-hard-note { font-size: var(--fs-sm); color: var(--muted); margin: 0; line-height: 1.65; }

@media (max-width: 640px) {
  .pr-hard-cols { grid-template-columns: 1fr; gap: var(--pr-gap-md); }
}

/* ── Form tag (wide mode) ────────────────────────────────────────────────────
   Wide mode has no meta line, so nothing distinguished a tissue-culture cup
   from a potted plant — two things that are never compared with each other and
   do not cost the same. Three or four characters, so it costs almost no width
   and the plant name keeps the column.

   Deliberately monochrome. A colour per form would turn a 20-row screen into a
   swatch chart and compete with the green that means "cheapest", which is the
   one colour on this page that carries a decision. */
.pr-row--wide .pr-id { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.pr-row--wide .pr-n  { flex: 0 1 auto; min-width: 0; }
.pr-tag {
  flex: none; font-size: 0.5625rem; font-weight: 700; line-height: 1;
  letter-spacing: .06em; padding: 3px 5px; border-radius: 4px;
  color: var(--faint); background: var(--bg3); border: 1px solid var(--border2);
  cursor: help; white-space: nowrap;
}
/* One exception: tissue culture is the distinction that actually changes what
   you are buying, so it gets a slightly stronger foreground — still no hue. */
.pr-tag.is-invitro { color: var(--text2); border-color: var(--border-strong); }

/* Each shop's own cadence, so an hourly crawl reading "58 min ago" is visibly
   on schedule rather than looking like the one that is lagging. */
.pr-fresh-cad { font-size: 0.5625rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--faint); margin-top: 2px; }

/* ── The order plan ──────────────────────────────────────────────────────────
   The answer sits at the top of the sheet, at a size you cannot miss, and the
   working goes underneath. Someone opening this has one question — what do I
   order and what will it cost — so that is the first thing on the panel rather
   than the last line of a table. */
.pr-plan { margin: 0 0 12px; }

.pr-plan-head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 16px;
  padding: 14px 16px; border-radius: 12px;
  background: var(--green-faint); border: 1px solid var(--green-faint-border);
}
.pr-plan-figure { display: flex; flex-direction: column; min-width: 0; }
.pr-plan-total {
  font-size: 1.75rem; font-weight: 700; color: var(--text); line-height: 1.05;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
/* "from" is a qualifier on the number, not part of it — a shop with unpublished
   postage must not make the total look precise. */
.pr-plan-total small { font-size: 0.875rem; font-weight: 600; color: var(--muted); letter-spacing: 0; }
.pr-plan-break { margin-top: 4px; font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.pr-plan-orders {
  flex: none; font-size: var(--fs-sm); font-weight: 700; color: var(--green);
  white-space: nowrap; padding-bottom: 2px;
}

.pr-plan-vs { font-size: var(--fs-xs); color: var(--green); margin: 8px 2px 0; line-height: 1.5; }
/* When the clever answer and the obvious answer agree, say so quietly — a
   claim of "saves £0.00" would be worse than saying nothing. */
.pr-plan-vs.is-flat { color: var(--muted); }

.pr-plan-list { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.pr-order {
  background: var(--bg2); border: 1px solid var(--border2);
  border-radius: 10px; padding: 8px 12px;
}
.pr-order-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.pr-order-shop { font-size: var(--fs-sm); font-weight: 700; color: var(--text); }
.pr-order-sum { font-size: var(--fs-sm); font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.pr-order-bot {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin-top: 3px; font-size: var(--fs-xs); color: var(--muted);
}
.pr-order-n { font-variant-numeric: tabular-nums; }
.pr-order-ship.is-free { color: var(--green); font-weight: 600; }
/* Not an error — we simply do not know, and the row says so rather than
   implying zero. */
.pr-order-ship.is-unknown { color: var(--amber); }

.pr-plan-gap {
  margin: 12px 0 0; padding: 9px 12px; border-radius: 9px;
  background: var(--bg3); border: 1px solid var(--border2);
  font-size: var(--fs-xs); color: var(--muted); line-height: 1.6;
}
.pr-plan-empty { font-size: var(--fs-sm); color: var(--muted); padding: 12px 0; line-height: 1.6; }

/* ── Section labels inside the sheet ─────────────────────────────────────────
   The order cards and the plant lines were rendering as identical rounded
   cards, so the panel read as one long undifferentiated stack and you could not
   tell where the plan stopped and its contents began. A small uppercase label
   above a hairline is enough — these are different KINDS of thing, not
   different levels of importance, so the separator does the work rather than a
   change of colour or weight. */
.pr-sec-label {
  display: block; margin: 16px 0 8px; padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--faint);
}
/* The first label in the plan follows the headline, which already separates
   itself — a rule there would be a line under a line. */
.pr-plan .pr-sec-label:first-of-type { border-top: 0; padding-top: 0; margin-top: 14px; }
/* It is an <li> inside the list, so it must not inherit the row chrome. */
.pr-sec-label--li { list-style: none; background: none; border-radius: 0; padding-left: 0; padding-right: 0; }

/* ── How this compares ───────────────────────────────────────────────────────
   Three totals priced the same way, so they are genuinely comparable. The
   difference is shown against the plan rather than left for the reader to
   subtract. */
.pr-cmp { display: flex; flex-direction: column; }
.pr-cmp-row {
  display: flex; align-items: baseline; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-sm);
}
.pr-cmp-row:last-child { border-bottom: 0; }
.pr-cmp-l { flex: 1; color: var(--text2); min-width: 0; }
.pr-cmp-n { flex: none; font-size: var(--fs-xs); color: var(--faint); font-variant-numeric: tabular-nums; }
.pr-cmp-v {
  flex: none; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* The winner is stated, not merely first. */
.pr-cmp-row.is-best .pr-cmp-l { color: var(--text); font-weight: 600; }
.pr-cmp-row.is-best .pr-cmp-v { color: var(--green); }
/* The gap to the plan, so "saves £4.58" is evidenced rather than asserted. */
.pr-cmp-d {
  display: inline-block; margin-left: 7px; font-style: normal; font-weight: 600;
  font-size: var(--fs-xs); color: var(--muted);
}
.pr-cmp-row.is-na .pr-cmp-v { color: var(--faint); font-weight: 400; font-size: var(--fs-xs); }

/* ── The single-shop alternative (legacy footer block, now unused) ────────── */
.pr-alt-h {
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--faint); margin-bottom: 8px;
}
.pr-alt {
  display: flex; align-items: baseline; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-sm);
}
.pr-alt:last-child { border-bottom: 0; }
.pr-alt-shop { font-weight: 600; color: var(--text); }
.pr-alt-note { flex: 1; font-size: var(--fs-xs); color: var(--faint); }
.pr-alt-v { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pr-alt.is-none { color: var(--muted); font-size: var(--fs-sm); border-bottom: 0; }

@media (max-width: 640px) {
  .pr-plan-head { flex-direction: column; align-items: flex-start; gap: 8px; }
  .pr-plan-total { font-size: 1.5rem; }
}

/* The sheet's scrolling region. min-height:0 is mandatory on a flex child that
   scrolls — without it the child refuses to shrink below its content and the
   footer is pushed off the panel instead. */
.pr-sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* The list no longer scrolls on its own; the body it sits in does. */
.pr-sheet-body .pr-sheet-list { flex: 0 0 auto; overflow: visible; min-height: 0; }

/* A quiet second line on a comparison row, for the qualifier that stops a label
   being read as a stronger claim than it is. */
.pr-cmp-sub {
  display: block; margin-top: 2px; font-style: normal;
  font-size: var(--fs-xs); color: var(--faint); line-height: 1.4;
}
.pr-cmp-row { align-items: flex-start; }
.pr-cmp-n, .pr-cmp-v { padding-top: 1px; }
