/* =====================================================================
   app.css — the first stylesheet.
   =====================================================================
   Every value here comes from tokens.css. Nothing declares a raw colour or
   a raw pixel size: the point of the token file is that changing the brand
   green is one edit, and a hard-coded #0F5132 in this file would quietly
   opt out of that.

   Plain CSS, no build step (D-004). JavaScript is progressive enhancement
   only — every form on the site submits and works without it.
   ===================================================================== */

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

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--ink);
  background: var(--surface-sunk);
}

a { color: var(--brand); }
a:hover { color: var(--brand-dark); }

.wrap {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---------------------------------------------------------------- header */

.site-header {
  background: var(--brand);
  color: var(--surface);
  position: sticky;
  top: 0;
  z-index: var(--z-header);
}

/* Every link in the header stays white on hover.
   Without this the global `a:hover { color: var(--brand-dark) }` wins on
   specificity — (0,1,1) beats a bare class (0,1,0) — and the link turns
   #0A3622 on a #0F5132 bar, which is invisible. It did exactly that to the
   brand wordmark. Written against the header rather than the one link, so the
   next white-on-green link added here cannot inherit the same fault. */
.site-header a:hover,
.site-header a:focus-visible { color: var(--surface); }

/* Two rows: identity + search on top, departments beneath. The search box is
   the widest thing in the row because it is the most used control on a
   marketplace — `flex: 1` gives it everything the others do not need. */
.site-header__top {
  min-height: var(--header-height);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding-block: var(--space-2);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--surface);
  text-decoration: none;
  white-space: nowrap;
}

/* A logo does not get underlined or recoloured on hover — it dims very
   slightly, which is enough to say "this is clickable" without the wordmark
   changing colour on its own background. */
.brand:hover { opacity: 0.88; }

/* Height-driven, with width auto, so the mark keeps its proportions whatever
   the header height becomes. */
.brand__mark { height: 2.2rem; width: auto; display: block; }

@media (max-width: 40rem) {
  /* On a phone the name is the thing worth keeping — it is the brand, and the
     mark is already in the browser tab beside it. */
  .brand__mark { height: 1.8rem; }
}

.deliver-to {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  color: var(--surface);
  white-space: nowrap;
}

.deliver-to small { font-size: var(--text-xs); opacity: 0.75; }
.deliver-to strong { font-size: var(--text-sm); }

/* ------------------------------------------------------------- search */

.searchbar {
  flex: 1 1 18rem;
  display: flex;
  align-items: stretch;
  min-width: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface);
}

.searchbar__scope {
  flex: none;
  border: 0;
  padding: 0 var(--space-2);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--ink-soft);
  background: var(--line-soft);
  cursor: pointer;
  max-width: 9rem;
}

.searchbar__input {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: var(--space-3);
  font: inherit;
  color: var(--ink);
}

/* The whole bar gets the focus ring, not the bare input — the three parts
   read as one control, so they should highlight as one. */
.searchbar:focus-within { outline: 3px solid var(--accent-bright); outline-offset: 0; }
.searchbar__input:focus, .searchbar__scope:focus { outline: none; }

.searchbar__button {
  flex: none;
  display: grid;
  place-items: center;
  width: 3rem;
  border: 0;
  color: var(--brand-darker);
  background: var(--accent-bright);
  cursor: pointer;
}

.searchbar__button:hover { background: var(--accent); color: var(--surface); }

/* ---------------------------------------------------------------- nav */

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.site-nav a {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  color: var(--surface);
  text-decoration: none;
  white-space: nowrap;
}

.site-nav a small { font-size: var(--text-xs); opacity: 0.75; }
.site-nav a strong { font-size: var(--text-sm); }
.site-nav a:hover { text-decoration: underline; }

/* ------------------------------------------------------------ basket */

.basket { position: relative; display: flex; align-items: center; gap: var(--space-2); }
.basket__icon { position: relative; display: block; color: var(--surface); }

.basket__count {
  position: absolute;
  top: -6px;
  inset-inline-end: -8px;
  min-width: 1.15rem;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--accent-bright);
  color: var(--brand-darker);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  text-align: center;
  line-height: 1.15rem;
}

/* --------------------------------------------------------- departments */

.departments {
  background: var(--brand-dark);
  font-size: var(--text-sm);
}

.departments__inner {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  /* Scrolls rather than wraps: on a phone a wrapping department bar can grow
     to three rows and push the page content off the first screen. */
  overflow-x: auto;
  scrollbar-width: none;
}

.departments__inner::-webkit-scrollbar { display: none; }

.departments__inner a {
  padding: var(--space-2) var(--space-3);
  color: var(--surface);
  text-decoration: none;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

.departments__inner a:hover { background: rgba(255, 255, 255, 0.12); }

.inline-form { display: inline; margin: 0; }

.link-button {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--surface);
  cursor: pointer;
  text-decoration: underline;
}

/* ----------------------------------------------------------------- cards */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-5);
  box-shadow: var(--shadow-sm);
}

.card--narrow {
  max-width: 30rem;
  margin: var(--space-6) auto;
}

.card--form { max-width: 44rem; margin: var(--space-6) auto; }

h1 { font-size: var(--text-2xl); line-height: var(--leading-tight); margin-top: 0; }
h2 { font-size: var(--text-lg); margin-bottom: var(--space-2); }

.lede { color: var(--ink-soft); margin-top: 0; }
.muted { color: var(--ink-faint); }

/* ----------------------------------------------------------------- forms */

.field { margin-bottom: var(--space-4); }

.field label {
  display: block;
  font-weight: var(--weight-medium);
  margin-bottom: var(--space-1);
}

.field input:not([type="checkbox"]),
.field select,
.field textarea {
  width: 100%;
  padding: var(--space-3);
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.field input:focus, .field select:focus, .field textarea:focus {
  outline: 2px solid var(--brand-bright);
  outline-offset: 1px;
  border-color: var(--brand);
}

/* The wrapper and button are created by app.js, so these rules apply to
   nothing at all when JavaScript is off — which is the intention. */
.password-wrap { position: relative; display: block; }

.password-wrap input { padding-inline-end: 4.5rem; }

.password-toggle {
  position: absolute;
  inset-inline-end: var(--space-2);
  top: 50%;
  transform: translateY(-50%);
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--brand);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.password-toggle:hover { background: var(--brand-tint); }
.password-toggle:focus-visible { outline: 2px solid var(--brand-bright); outline-offset: 1px; }

.field .helptext {
  display: block;
  font-size: var(--text-sm);
  color: var(--ink-faint);
  margin-top: var(--space-1);
}

/* An invalid field is marked by colour *and* by text — colour alone is
   invisible to a red-green colourblind user, and roughly 1 man in 12 in
   Pakistan is. */
.field--error input, .field--error select, .field--error textarea {
  border-color: var(--danger);
}

.field-errors {
  list-style: none;
  margin: var(--space-1) 0 0;
  padding: 0;
  color: var(--danger);
  font-size: var(--text-sm);
}

.form-errors {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--danger-tint);
  border-left: 3px solid var(--danger);
  border-radius: var(--radius-sm);
  list-style: none;
  color: var(--danger);
}

.button {
  display: inline-block;
  padding: var(--space-3) var(--space-5);
  font: inherit;
  font-weight: var(--weight-medium);
  color: var(--surface);
  background: var(--brand);
  border: 1px solid var(--brand);
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-decoration: none;
  transition: background var(--speed) var(--ease);
}

.button:hover { background: var(--brand-dark); color: var(--surface); }
.button--small { padding: var(--space-2) var(--space-3); font-size: var(--text-sm); }

.button--quiet {
  color: var(--brand);
  background: transparent;
  border-color: var(--line);
}
.button--quiet:hover { background: var(--brand-tint); color: var(--brand-dark); }

.form-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-5);
}

/* -------------------------------------------------------------- messages */

.messages { list-style: none; padding: 0; margin: var(--space-4) 0; }

.message {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--info);
  background: var(--info-tint);
  color: var(--ink);
  margin-bottom: var(--space-2);
}

.message--success { border-color: var(--ok); background: var(--ok-tint); }
.message--warning { border-color: var(--warn); background: var(--warn-tint); }
.message--error   { border-color: var(--danger); background: var(--danger-tint); }

/* --------------------------------------------------------------- status */

.status {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  background: var(--brand-tint);
  color: var(--brand-dark);
}

.status--pending   { background: var(--warn-tint);   color: var(--warn); }
.status--approved  { background: var(--ok-tint);     color: var(--ok); }
.status--rejected  { background: var(--danger-tint); color: var(--danger); }
.status--suspended { background: var(--danger-tint); color: var(--danger); }

.meta-list { margin: 0; }
.meta-list dt { font-size: var(--text-sm); color: var(--ink-faint); margin-top: var(--space-3); }
.meta-list dd { margin: 0; font-weight: var(--weight-medium); }

.otp-destination {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
}

/* ------------------------------------------------------- seller catalogue */

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-block: var(--space-6) var(--space-4);
}

.page-head h1 { margin: 0; }

/* Screen-reader-only. A table column of buttons still needs a header for
   anyone navigating the table by its structure; sighted users do not need
   to read the word "Actions". */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* A table is the wrong shape for a 360px phone. Rather than collapsing it
   into cards — which loses the column comparison that makes a stock list
   useful — it scrolls sideways inside its own box, so the page itself
   never does. */
.table-scroll { overflow-x: auto; }

.listing {
  width: 100%;
  /* Below this the browser starts breaking product names one word per line,
     which is unreadable. `.table-scroll` then does its job and the table
     scrolls sideways instead of being crushed. */
  min-width: 46rem;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.listing td a { text-wrap: balance; }

.listing th, .listing td {
  padding: var(--space-3) var(--space-4);
  text-align: start;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
  /* Not cosmetic. `.visually-hidden` is absolutely positioned, and with no
     positioned ancestor it is laid out against the page rather than the cell.
     Sitting in the last column of a table wider than the screen, it landed
     past the right edge and gave the whole document a horizontal scrollbar —
     the table was already scrolling inside `.table-scroll`, so the page had no
     business scrolling too. This makes the cell its containing block. */
  position: relative;
}

.listing thead th {
  font-size: var(--text-sm);
  color: var(--ink-faint);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

.listing tbody tr:last-child td { border-bottom: 0; }
.listing .num { text-align: end; white-space: nowrap; }
.listing .muted { color: var(--ink-soft); }
.listing .mono { font-family: var(--font-mono); font-size: var(--text-sm); }
.listing .danger { color: var(--danger); font-weight: var(--weight-medium); }

.row-actions { text-align: end; white-space: nowrap; }
.row-actions form { display: inline; margin: 0; }

.product-cell { display: flex; align-items: center; gap: var(--space-3); }

.thumb {
  width: 56px;
  height: 56px;
  flex: none;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface-sunk);
}

.thumb--empty { display: inline-block; }

.empty-state { max-width: 34rem; margin: var(--space-7) auto; text-align: center; }
.empty-state .button { margin-top: var(--space-4); }

/* ------------------------------------------------------------------ tabs */

.tabs {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-5);
}

.tabs__link {
  padding: var(--space-3) var(--space-4);
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.tabs__link:hover { color: var(--brand); }

.tabs__link.is-active {
  color: var(--brand);
  font-weight: var(--weight-medium);
  border-bottom-color: var(--brand);
}

/* --------------------------------------------------------------- notices */

.notice {
  padding: var(--space-4);
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--info);
  background: var(--info-tint);
  margin-bottom: var(--space-5);
}

.notice ul { margin: var(--space-2) 0 0; padding-inline-start: var(--space-5); }
.notice p { margin: var(--space-2) 0 0; }
.notice--ok     { border-color: var(--ok);     background: var(--ok-tint); }
.notice--warn   { border-color: var(--warn);   background: var(--warn-tint); }
.notice--danger { border-color: var(--danger); background: var(--danger-tint); }

/* ---------------------------------------------------------- photo manager */

.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--space-4);
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-5);
}

.photo {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-sunk);
}

/* The main picture is the one that appears on every search result, so it is
   worth marking clearly rather than leaving the seller to guess. */
.photo--primary { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand); }

/* contain, matching the storefront card. It was `cover`, so a photograph
   looked correctly framed in the seller's own manager and letterboxed once
   published — the shopkeeper was shown a different picture from the buyer. */
.photo img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  background: var(--surface);
}

.photo__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  font-size: var(--text-sm);
}

.photo__bar form { margin: 0; }

.link-button--inline { color: var(--brand); }
.link-button--danger { color: var(--danger); }

.publish-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-5);
  padding: var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--brand-tint);
}

.publish-bar p { margin: 0; }

/* Destructive actions are separated and coloured, never sitting beside Save
   where a mis-tap costs a seller their listing. */
.danger-zone { border-color: var(--danger); }
.danger-zone h2 { color: var(--danger); }

.button--danger {
  color: var(--surface);
  background: var(--danger);
  border-color: var(--danger);
}

.button--danger:hover { background: var(--danger-dark); color: var(--surface); }

/* ============================================================== storefront */

.hero {
  margin-block: var(--space-5);
  padding: var(--space-7) var(--space-5);
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-darker) 100%);
  color: var(--surface);
}

.hero__text { max-width: 42rem; }
.hero h1 { font-size: var(--text-3xl); margin: 0 0 var(--space-3); color: var(--surface); }
.hero__lede { margin: 0; font-size: var(--text-lg); opacity: 0.92; }

/* A real banner replaces the gradient entirely — no padding, no background,
   because an uploaded image is the whole design at that point. */
.hero--banner { padding: 0; background: none; overflow: hidden; }
.hero__banner { display: block; }
.hero__banner img { display: block; width: 100%; height: auto; }

.category-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block: var(--space-5);
}

.category-strip__item {
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.category-strip__item:hover,
/* `.is-open` keeps the chip looking selected once the pointer has travelled
   down into the panel it opened — without it the chip drops back to plain the
   moment you reach the thing it revealed. */
.category-strip__item.is-open {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-dark);
}

.band { margin-block: var(--space-7); }

.band__title {
  font-size: var(--text-xl);
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
}

/* ------------------------------------------------------- category tiles */

.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: var(--space-4);
  list-style: none;
  padding: 0;
  margin: 0;
}

.tile {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
}

.tile > a { text-decoration: none; color: inherit; display: block; }

.tile__frame {
  display: grid;
  place-items: center;
  /* SQUARE, and NOTHING is cropped to fit it (D-064).

     It was 4/3, which threw away up to half the height of every upload —
     category artwork almost never is landscape; the first two here were
     720x796 and 574x800.

     D-063 first fixed that by cropping the upload to square on the way in.
     The user's answer was that nothing should be cut: the gloves were not
     fully visible, and an administrator who chose a picture chose all of it.

     So the frame is square because that is the shape artwork usually is, and
     the image below uses `object-fit: contain` — the whole picture, letterboxed
     when it is not square, scaled UP when it is small. `store_category_image`
     now only resizes down and re-encodes; it does not crop, trim or pad. */
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-sm);
  /* White, matching `.card-product__frame` and for the same reason: pictures
     are letterboxed by `contain` and artwork is usually cut out on white, so a
     tinted frame draws a visible white rectangle inside a green one. The
     lettered fallback supplies its own tint. */
  background: var(--surface);
  margin-bottom: var(--space-3);
}

/* CONTAIN, not cover (D-064).

   `cover` fills the square and crops whatever does not fit. Both of the first
   two real uploads were portrait — 720x796 and 574x800 — so it cut the top and
   bottom off every one, and the user reported exactly that: the gloves were
   not fully visible.

   `contain` shows the whole picture whatever shape it is, scaling a small one
   UP to fill the box and letterboxing the rest. Nothing is ever lost, which is
   the rule the storefront's product cards have followed since they were built:
   an administrator who chose a picture chose all of it.

   It is also why the pipeline no longer crops. Doing it in one place means the
   stored file stays the original composition and the frame does the fitting. */
.tile__frame img { width: 100%; height: 100%; object-fit: contain; }


/* Stands in until an administrator uploads category artwork. A single large
   letter reads as deliberate; a broken-image icon does not. */
.tile__letter {
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  color: var(--brand);
  opacity: 0.55;
}

.tile__name { font-weight: var(--weight-medium); font-size: var(--text-lg); }

.tile__children { list-style: none; padding: 0; margin: var(--space-2) 0 0; }

.tile__children a {
  font-size: var(--text-sm);
  color: var(--ink-soft);
  text-decoration: none;
  line-height: 1.7;
}

.tile__children a:hover { color: var(--brand); text-decoration: underline; }

/* ------------------------------------------------------------- rails */

/* ---------------------------------------------------- banner carousel */

.hero-carousel { position: relative; margin-block: var(--space-5); }

.hero-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  list-style: none;
  padding: 0;
  margin: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  border-radius: var(--radius-lg);
  scrollbar-width: none;
}

.hero-track::-webkit-scrollbar { display: none; }

.hero-slide { scroll-snap-align: start; }
.hero-slide img { display: block; width: 100%; height: auto; }

/* Built by app.js — with the script off the strip still swipes, it simply has
   no dots, which is the correct thing to lose. */
.hero-dots {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.hero-dot {
  width: 10px;
  height: 10px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--line);
  cursor: pointer;
}

.hero-dot[aria-current="true"] { background: var(--brand); }
.hero-dot:focus-visible { outline: 2px solid var(--brand-bright); outline-offset: 2px; }

/* -------------------------------------------------------- brand strip */

.brand-strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(8rem, 10rem);
  gap: var(--space-4);
  list-style: none;
  padding: 0 0 var(--space-2);
  margin: 0;
  overflow-x: auto;
}

.brand-strip a { text-decoration: none; color: inherit; display: block; text-align: center; }

.brand-strip__frame {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  background: var(--brand-tint);
  overflow: hidden;
  margin-bottom: var(--space-2);
}

.brand-strip__frame img { width: 100%; height: 100%; object-fit: contain; }

.brand-strip__letter {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  color: var(--brand);
  opacity: 0.55;
}

.brand-strip__name { font-size: var(--text-sm); font-weight: var(--weight-medium); }

/* ------------------------------------------------------------- rails */

.rail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
}

.rail-head .band__title { flex: 1; }

.rail-more {
  flex: none;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--brand);
  white-space: nowrap;
}

.rail-controls { display: flex; gap: var(--space-2); flex: none; }

.rail-arrow {
  width: 2rem;
  height: 2rem;
  font-size: var(--text-xl);
  line-height: 1;
  color: var(--brand);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.rail-arrow:hover { background: var(--brand-tint); border-color: var(--brand); }

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(13rem, 1fr);
  gap: var(--space-4);
  list-style: none;
  padding: 0 0 var(--space-2);
  margin: 0;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  /* Cards stop at a sensible width instead of stretching when three products
     sit in a rail built for ten. */
  grid-auto-columns: minmax(13rem, 15rem);
}

.rail > .card-product { scroll-snap-align: start; }
.rail:focus-visible { outline: 2px solid var(--brand-bright); outline-offset: 4px; }

/* ------------------------------------------------------- pagination */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
}

.pagination__status { font-size: var(--text-sm); color: var(--ink-faint); }

/* auto-fill, not auto-fit: with two products in the catalogue, auto-fit would
   stretch them to half the screen each. This keeps a card card-sized and lets
   the row stay short, which is honest about how much is listed. */
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: var(--space-4);
  list-style: none;
  padding: 0;
  margin: 0;
}

.card-product {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: box-shadow var(--speed) var(--ease), border-color var(--speed) var(--ease);
}

.card-product:hover { border-color: var(--brand); box-shadow: var(--shadow); }

.card-product__link { text-decoration: none; color: inherit; }

.card-product__frame {
  aspect-ratio: 1 / 1;
  /* White, not the sunk grey. Photographs are letterboxed by `contain`, and
     sellers shoot on white, so a grey frame drew a visible white rectangle
     inside a grey one and made a row of cards look misaligned. */
  background: var(--surface);
  display: block;
}

.card-product__frame img {
  width: 100%;
  height: 100%;
  /* contain, not cover: sellers photograph a boxing glove on whatever surface
     they have, and cropping to a square cuts the product itself in half.
     Letterboxing shows the whole item. */
  object-fit: contain;
}

.card-product__noimage { display: block; width: 100%; height: 100%; }

.card-product__name {
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  margin: var(--space-3) var(--space-4) 0;
  line-height: var(--leading-tight);
}

.card-product__price {
  margin: var(--space-2) var(--space-4) 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  color: var(--brand);
}

.card-product__seller {
  margin: var(--space-1) var(--space-4) 0;
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

.card-product__flags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: var(--space-3) var(--space-4) var(--space-4);
  min-height: 1.5rem;
}

.chip {
  display: inline-block;
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  background: var(--line-soft);
  color: var(--ink-soft);
}

.chip--cod { background: var(--cod-tint); color: var(--cod); }
.chip--in  { background: var(--ok-tint); color: var(--ok); }
.chip--out { background: var(--danger-tint); color: var(--danger); }
.chip--verified { background: var(--brand-tint); color: var(--brand-dark); }

/* --------------------------------------------------------- breadcrumb */

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-block: var(--space-5) var(--space-4);
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

.breadcrumb a { color: var(--ink-soft); text-decoration: none; }
.breadcrumb a:hover { color: var(--brand); text-decoration: underline; }

/* ------------------------------------------------------- product page */

/* Three columns on a wide screen — photograph, description, buy box — which is
   how every marketplace lays this out and the only way the extra width is
   worth having. Two columns would simply make the image enormous.
   `relative` so the magnifier panel can be positioned against the whole area. */
.product {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}

@media (min-width: 48rem) {
  .product { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

@media (min-width: 78rem) {
  .product {
    /* The gallery is held to roughly a third: past that the photograph grows
       without telling the buyer anything more, and the magnifier is what the
       detail is for. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr) minmax(17rem, 21rem);
  }
  .product__gallery { grid-column: 1; grid-row: 1; }
  .product__detail  { grid-column: 2; grid-row: 1; }
  .product__buybox  { grid-column: 3; grid-row: 1; }
}

/* Thumbnail rail beside the main image. `position: relative` is load-bearing:
   the magnifier panel is positioned against this box. */
.product__gallery {
  position: relative;
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
}

/* One column when there is nothing to put in the rail. */
.product__gallery:not(:has(.gallery__thumbs)) { grid-template-columns: minmax(0, 1fr); }

.gallery__thumbs {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.gallery__thumbs img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: block;
}

.gallery__thumbs a:hover img,
.gallery__thumbs a:focus-visible img { border-color: var(--brand); }
.gallery__thumbs a.is-current img { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand); }

.gallery__main {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}

.gallery__main img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
}

/* --------------------------------------------------------- magnifier */

/* The lens follows the cursor over the main image and shows which part is
   being magnified. Both this and the panel are created by app.js, so neither
   exists when the script does not run. */
.zoom-lens {
  position: absolute;
  pointer-events: none;
  border: 1px solid var(--brand);
  background: rgba(255, 255, 255, 0.28);
  z-index: 2;
}

/* Overlays the detail column, exactly as the reference does — the magnified
   view needs the room, and the buy box is still there the moment the cursor
   leaves the photograph.

   Positioned against `.product`, not against the gallery, so it can run all
   the way to the right edge of the page. Sized against the gallery it stopped
   short of the margin and wasted the space it was there to use. `left`,
   `width` and `height` are set by app.js from the real boxes. */
.zoom-panel {
  position: absolute;
  inset-block-start: 0;
  background-color: var(--surface);
  background-repeat: no-repeat;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  z-index: var(--z-header);
  pointer-events: none;
}

.gallery__main.is-zooming { cursor: crosshair; }

.product__detail h1 { font-size: var(--text-2xl); margin-bottom: var(--space-2); }

.product__seller { margin: 0 0 var(--space-4); color: var(--ink-soft); }
.product__seller .muted { color: var(--ink-faint); }

.product__price {
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  color: var(--brand);
  margin: 0 0 var(--space-3);
}

.product__flags { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0 0 var(--space-5); }

.variant-list { list-style: none; padding: 0; margin: 0 0 var(--space-5); }

.variant-list li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line-soft);
}

.variant-list__price { font-weight: var(--weight-medium); }

/* Stated plainly rather than dressed up as a disabled button. Ordering is
   Phase 6; a greyed-out "Add to cart" reads as a fault in the site. */
.product__pending {
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--warn);
  background: var(--warn-tint);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-5);
}

.product__description { color: var(--ink-soft); }

/* -------------------------------------------------------- buy box */

/* Sticks while the description scrolls past it, so the way to buy is never
   off-screen on a long listing. */
@media (min-width: 78rem) {
  .product__buybox { position: sticky; top: calc(var(--header-height) + var(--space-4)); }
}

.buybox {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: var(--space-5);
}

/* Stacked rather than side by side: the buy box column is narrow, and a
   quantity box next to a full-width button squeezes both. */
@media (min-width: 78rem) {
  .buybox__row { flex-direction: column; align-items: stretch; }
  .field--qty { width: 100%; margin-bottom: var(--space-3); }
}

.buybox__row { display: flex; align-items: flex-end; gap: var(--space-3); }
.buybox .field { margin-bottom: var(--space-3); }
.field--qty { width: 6rem; flex: none; margin-bottom: 0; }

.button--buy {
  flex: 1;
  background: var(--accent-bright);
  border-color: var(--accent-bright);
  color: var(--brand-darker);
  font-weight: var(--weight-bold);
}

.button--buy:hover { background: var(--accent); border-color: var(--accent); color: var(--surface); }

.buybox__note {
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

/* ------------------------------------------------------------- cart */

.cart {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20rem;
  gap: var(--space-6);
  align-items: start;
}

.cart-line {
  display: grid;
  grid-template-columns: 6rem minmax(0, 1fr) auto;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line);
}

.cart-line__image img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.cart-line__name {
  font-weight: var(--weight-medium);
  color: var(--ink);
  text-decoration: none;
}

.cart-line__name:hover { color: var(--brand); text-decoration: underline; }
.cart-line__detail p { margin: var(--space-1) 0 0; font-size: var(--text-sm); }

.cart-line__repriced {
  color: var(--warn);
  font-weight: var(--weight-medium);
}

.cart-line__actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-3);
  flex-wrap: wrap;
}

.cart-line__actions form { margin: 0; }
.qty-form { display: flex; align-items: center; gap: var(--space-2); }
.qty-form input { width: 4.5rem; padding: var(--space-2); font: inherit;
                  border: 1px solid var(--line); border-radius: var(--radius-sm); }

.cart-line__total { font-weight: var(--weight-bold); white-space: nowrap; margin: 0; }

.cart__summary { position: sticky; top: calc(var(--header-height) + var(--space-4)); }
.cart__summary h2 { margin-top: 0; }

.summary-list { display: grid; grid-template-columns: 1fr auto; gap: var(--space-2); margin: 0 0 var(--space-4); }
.summary-list dt { color: var(--ink-soft); }
.summary-list dd { margin: 0; text-align: end; font-weight: var(--weight-medium); }

@media (max-width: 48rem) {
  .cart { grid-template-columns: 1fr; }
  .cart__summary { position: static; }
  .cart-line { grid-template-columns: 4.5rem minmax(0, 1fr); }
  .cart-line__total { grid-column: 2; }
}

/* ------------------------------------------------------------- pitch */

.band--pitch { margin-block: var(--space-8) var(--space-6); }

.pitch {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-4);
  list-style: none;
  padding: 0;
  margin: 0;
}

.pitch li {
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.pitch strong { display: block; color: var(--brand-dark); margin-bottom: var(--space-1); }
.pitch span { color: var(--ink-soft); font-size: var(--text-sm); }

@media (max-width: 48rem) {
  .hero { padding: var(--space-5) var(--space-4); }
  .hero h1 { font-size: var(--text-2xl); }
}

/* ---------------------------------------------------------------- footer */

.site-footer {
  margin-top: var(--space-8);
  background: var(--brand-dark);
  color: var(--surface);
  font-size: var(--text-sm);
}

.site-footer__columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--space-6);
  padding-block: var(--space-6);
}

.footer-col h2 {
  font-size: var(--text-base);
  margin: 0 0 var(--space-3);
  color: var(--surface);
}

.footer-col ul { list-style: none; padding: 0; margin: 0; }
.footer-col li { margin-bottom: var(--space-2); opacity: 0.85; }
.footer-col a { color: var(--surface); text-decoration: none; }
.footer-col a:hover { color: var(--surface); text-decoration: underline; }

.site-footer__base {
  background: var(--brand-darker);
  padding-block: var(--space-4);
}

.site-footer__base p { margin: 0; opacity: 0.8; }

/* ---------------------------------------------------------------- mobile */

@media (max-width: 40rem) {
  .card--narrow, .card--form { margin-inline: 0; }
  .form-actions { flex-direction: column; align-items: stretch; }
  .form-actions .button { text-align: center; }
}

/* =====================================================================
   Product page — the marketplace layout
   =====================================================================
   Added with the specification tab (D-031). Everything below either
   displays data that had nowhere to go before, or replaces a control
   that hid what the buyer came to compare. Nothing here needs
   JavaScript: the tiles are radios, the expanders are <details>, and
   the section jumper is a list of anchors.
   ===================================================================== */

/* ------------------------------------------------------ section jumper */

/* Sticks below the site header, so `top` is the header's own height. A
   plain `top: 0` puts it *behind* the header, where it is invisible and
   still eats the click. */
.pagenav {
  position: sticky;
  top: var(--header-height);
  z-index: calc(var(--z-header) - 1);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-5);
  padding: var(--space-2) 0;
}

.pagenav__links {
  display: flex;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-x: auto;
  /* A phone shows four of five links; scrolling the bar is expected, a
     wrapped second row of it is not. */
  scrollbar-width: none;
}
.pagenav__links::-webkit-scrollbar { display: none; }

.pagenav__links a {
  display: block;
  padding: var(--space-2) 0;
  white-space: nowrap;
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  font-size: var(--text-sm);
}

.pagenav__links a:hover { color: var(--brand); }

/* Set by app.js as the page scrolls. Without the script the bar is still a
   working table of contents, just without the highlight. */
.pagenav__links a.is-current {
  color: var(--brand);
  border-bottom-color: var(--brand);
  font-weight: var(--weight-medium);
}

/* Hidden until the bar has actually stuck — before that the product name is
   still on screen a few centimetres above, and repeating it is noise.
   app.js adds `is-stuck`. */
.pagenav__product {
  display: none;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.pagenav__product img {
  width: 2rem;
  height: 2rem;
  object-fit: contain;
  flex: none;
}

.pagenav__product span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 22rem;
}

@media (min-width: 62rem) {
  .pagenav.is-stuck .pagenav__product { display: flex; }
}

/* ------------------------------------------------------ gallery hint */

.gallery__hint {
  grid-column: 2;
  margin: var(--space-2) 0 0;
  text-align: center;
  font-size: var(--text-xs);
  color: var(--ink-faint);
}

/* One column when there is no thumbnail rail to sit beside. */
.product__gallery:not(:has(.gallery__thumbs)) .gallery__hint { grid-column: 1; }

/* ------------------------------------------------------ title block */

.product__subtitle {
  margin: 0 0 var(--space-3);
  color: var(--ink-soft);
  font-size: var(--text-lg);
  line-height: var(--leading-normal);
}

.product__seller a { color: var(--brand); text-decoration: none; }
.product__seller a:hover { text-decoration: underline; }

.product__rating {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

/* Five stars drawn once and clipped to the score, rather than five elements
   whose widths have to be worked out per star. `--rating` is 0-5. */
.stars {
  --rating: 0;
  /* Held in a custom property because it has to be written twice, once for
     the prefixed property Safari still needs. */
  --star-mask: repeat-x left center / 1.1em 1em
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2l2.9 6.3 6.9.8-5.1 4.7 1.4 6.8L12 17.3 5.9 20.6l1.4-6.8L2.2 9.1l6.9-.8z'/%3E%3C/svg%3E");
  display: inline-block;
  width: 5.5em;
  height: 1em;
  background:
    linear-gradient(90deg, var(--accent-bright) calc(var(--rating) / 5 * 100%),
                           var(--line) calc(var(--rating) / 5 * 100%));
  -webkit-mask: var(--star-mask);
  mask: var(--star-mask);
}

/* ------------------------------------------------------ version tiles */

.styles {
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-5);
}

.styles__legend {
  padding: 0;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--ink-soft);
}

.styles__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Tiles wrap to as many as fit rather than a fixed count - a product with
     two sizes should not leave two empty columns. */
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--space-2);
}

/* The radio is the state, so it stays in the accessibility tree and keeps its
   keyboard behaviour - it is moved out of sight, not removed. `absolute`
   inside a `relative` label, so it cannot escape the tile and widen the page,
   which is the mistake `.visually-hidden` made inside wide tables. */
.style {
  display: block;
  position: relative;
  cursor: pointer;
}

.style input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  margin: 0;
}

.style__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  height: 100%;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  transition: border-color var(--speed) var(--ease);
}

.style:hover .style__body { border-color: var(--brand-bright); }

.style input:checked + .style__body {
  border-color: var(--brand);
  box-shadow: 0 0 0 1px var(--brand);
  background: var(--brand-tint-soft);
}

/* Focus has to be drawn on the body, because the input it belongs to is the
   invisible one. Without this a keyboard user tabs through the tiles with no
   idea where they are. */
.style input:focus-visible + .style__body {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.style__name { font-weight: var(--weight-medium); font-size: var(--text-sm); }
.style__price { font-weight: var(--weight-bold); }
.style__was {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  text-decoration: line-through;
}

/* ------------------------------------------------------ specifications */

.spec-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
}

.spec-table th,
.spec-table td {
  text-align: start;
  vertical-align: top;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line-soft);
}

/* Zebra striping is what makes a long two-column table readable across the
   row - the eye loses the line otherwise. */
.spec-table tr:nth-child(odd) { background: var(--surface-sunk); }

.spec-table th {
  width: 40%;
  font-weight: var(--weight-medium);
  color: var(--ink-soft);
}

/* `.spec-table a` and the global `a:hover` are both (0,1,1), and this rule is
   later in the file — so it wins on hover too and the colour never changes.
   The underline is the hover feedback instead, which is the right answer here
   anyway: these sit inside a striped table where a colour shift reads as a
   rendering fault. */
.spec-table a { color: var(--brand); text-decoration: none; }
.spec-table a:hover { text-decoration: underline; }

.spec-more { margin-bottom: var(--space-5); }

.spec-more > summary {
  cursor: pointer;
  color: var(--brand);
  font-size: var(--text-sm);
  padding: var(--space-2) 0;
  list-style: none;
}

.spec-more > summary::-webkit-details-marker { display: none; }
.spec-more > summary::before { content: "\25B8  "; }
.spec-more[open] > summary::before { content: "\25BE  "; }
.spec-more > summary:hover { text-decoration: underline; }

/* ------------------------------------------------------ about this item */

.about-list {
  margin: 0 0 var(--space-5);
  padding-inline-start: var(--space-5);
  color: var(--ink-soft);
  line-height: var(--leading-normal);
}

.about-list li { margin-bottom: var(--space-2); }

/* ------------------------------------------------------ buy box additions */

.buybox__price {
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  color: var(--brand);
  margin: 0 0 var(--space-1);
}

.buybox__was { margin: 0 0 var(--space-3); font-size: var(--text-sm); color: var(--ink-faint); }

.buybox__delivery {
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.buybox__facts {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: var(--space-3) 0 0;
  border-top: 1px solid var(--line-soft);
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.buybox__facts li { margin-bottom: var(--space-1); }

/* ------------------------------------------------- product information */

.info-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

@media (min-width: 62rem) {
  .info-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.info-block {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--space-4);
}

.info-block > summary {
  cursor: pointer;
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.info-block > summary::-webkit-details-marker { display: none; }
.info-block > summary::after { content: "\25BE"; color: var(--ink-faint); }
.info-block[open] > summary::after { content: "\25B4"; }
.info-block[open] > summary { margin-bottom: var(--space-3); }

.info-block .spec-table { margin-bottom: 0; }

/* ------------------------------------------------------ warranty terms */

.terms-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}

@media (min-width: 48rem) {
  .terms-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.terms-grid h3 {
  font-size: var(--text-base);
  margin: 0 0 var(--space-2);
}

.terms-grid p {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* =====================================================================
   Seller panel (D-032)
   =====================================================================
   The chrome for `seller_base.html`. Deliberately quieter than the shop
   front: this is somewhere a shopkeeper works for an hour, not a page
   that has to sell them something in three seconds. Same green, less of
   it — one bar instead of two, and no colour below the menu.
   ===================================================================== */

.panel-header {
  background: var(--brand-darker);
  color: var(--surface);
}

.panel-header__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--header-height);
  padding-block: var(--space-2);
}

/* --------------------------------------------------------- identity */

.panel-brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--surface);
  text-decoration: none;
  min-width: 0;
}

.panel-brand__mark { flex: none; display: block; }

.panel-brand__text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
}

/* The shop's name leads and the platform's name is the small line under it —
   in this panel the seller is in their own shop, and the page should say so
   first. Truncated rather than wrapped: a long business name must not push
   the menu onto a second row. */
.panel-brand__text strong {
  font-size: var(--text-lg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.panel-brand__text small {
  font-size: var(--text-xs);
  opacity: 0.75;
}

/* ------------------------------------------------------------- nav */

.panel-nav {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex: none;
}

.panel-nav a,
.panel-nav .link-button {
  color: var(--surface);
  text-decoration: none;
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* The global `a:hover { color: var(--brand-dark) }` is (0,1,1) and would turn
   these links near-black against a near-black bar — the exact fault that made
   the shop-front logo vanish on hover. Scoped to the whole header at (0,1,2),
   so anything added here later is covered too. */
.panel-header a:hover,
.panel-header .link-button:hover {
  color: var(--surface);
  text-decoration: underline;
}

/* ------------------------------------------------------------ menu */

.panel-menu {
  background: var(--brand-dark);
}

.panel-menu__inner {
  display: flex;
  gap: var(--space-5);
  overflow-x: auto;
  scrollbar-width: none;
}
.panel-menu__inner::-webkit-scrollbar { display: none; }

.panel-menu a {
  display: block;
  padding: var(--space-3) 0;
  color: var(--surface);
  text-decoration: none;
  font-size: var(--text-sm);
  white-space: nowrap;
  border-bottom: 3px solid transparent;
}

.panel-menu a.is-current {
  border-bottom-color: var(--accent-bright);
  font-weight: var(--weight-medium);
}

/* ---------------------------------------------------------- footer */

/* One line, not three columns. The shop front's footer columns exist to send a
   shopper elsewhere; there is nowhere else a seller should be sent from the
   bottom of their own workspace. */
.panel-footer {
  margin-top: var(--space-8);
  border-top: 1px solid var(--line);
  padding-block: var(--space-4);
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.panel-footer p { margin: 0; }

/* The panel has no wide hero, so its content column is narrower than the shop
   front's 1800px — a product form stretched that wide is unreadable. */
.seller-panel main.wrap { max-width: 76rem; }

@media (max-width: 40rem) {
  .panel-header__top { flex-wrap: wrap; }
  .panel-brand__text strong { font-size: var(--text-base); }
}

/* ------------------------------------------------- password reset tips */

/* Advice under the new-password form (D-033). Set apart from the form rather
   than beside the fields: they are guidance, and a checklist next to an input
   reads as a set of rules the field is about to reject you for. */
.password-tips {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line-soft);
  color: var(--ink-soft);
  font-size: var(--text-sm);
}

.password-tips h2 {
  font-size: var(--text-base);
  margin: 0 0 var(--space-2);
  color: var(--ink);
}

.password-tips ul {
  margin: 0;
  padding-inline-start: var(--space-5);
}

.password-tips li { margin-bottom: var(--space-1); }

/* =====================================================================
   Checkout
   =====================================================================
   Two columns on a wide screen, summary sticky beside the review — the
   same shape as the product page's buy box, because it answers the same
   question: what am I about to be charged.
   ===================================================================== */

.checkout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
}

@media (min-width: 62rem) {
  .checkout { grid-template-columns: minmax(0, 1fr) 22rem; }
  .checkout__summary { position: sticky; top: calc(var(--header-height) + var(--space-4)); }
}

.checkout__main > .card { margin-bottom: var(--space-5); }

/* ------------------------------------------------------ addresses */

.address-list {
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

/* The radio stays in the accessibility tree and keeps its keyboard
   behaviour — moved out of sight, not removed. `absolute` inside a
   `relative` label so it cannot escape and widen the page. */
.address {
  display: block;
  position: relative;
  cursor: pointer;
}

.address input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  margin: 0;
}

.address__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: var(--text-sm);
}

.address:hover .address__body { border-color: var(--brand-bright); }

.address input:checked + .address__body {
  border-color: var(--brand);
  box-shadow: 0 0 0 1px var(--brand);
  background: var(--brand-tint-soft);
}

.address input:focus-visible + .address__body {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.address__body .chip { align-self: flex-start; }

.address-add > summary {
  cursor: pointer;
  color: var(--brand);
  font-size: var(--text-sm);
  padding: var(--space-2) 0;
  list-style: none;
}

.address-add > summary::-webkit-details-marker { display: none; }
.address-add > summary::before { content: "\25B8  "; }
.address-add[open] > summary::before { content: "\25BE  "; }
.address-add > summary:hover { text-decoration: underline; }

/* ---------------------------------------------------------- lines */

.seller-group { margin-bottom: var(--space-5); }
.seller-group:last-child { margin-bottom: 0; }

.seller-group h3 {
  font-size: var(--text-base);
  margin: 0 0 var(--space-2);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line-soft);
}

.checkout-lines { list-style: none; margin: 0; padding: 0; }

.checkout-lines li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: var(--space-3);
  align-items: baseline;
  padding: var(--space-2) 0;
  font-size: var(--text-sm);
}

.checkout-lines__name { display: flex; flex-direction: column; }
.checkout-lines__total { font-weight: var(--weight-medium); text-align: end; }

/* -------------------------------------------------------- summary */

/* `.summary-list` itself is already defined above as a two-column grid, which
   is the better layout — this only adds the emphasised last row. Redefining
   the parent here would have replaced that grid with a float and quietly
   broken the cart page, which uses the same list. */
.summary-list__total {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  color: var(--ink);
  border-top: 1px solid var(--line-soft);
  margin-top: var(--space-2);
}

.cod-ok {
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--cod-tint);
  color: var(--ink-soft);
  font-size: var(--text-sm);
}

.checkout__summary .button--buy { width: 100%; }

/* A disabled buy button is legitimate here, unlike on the product page: the
   reason is stated directly above it, so it reads as "not yet" rather than as
   a fault. */
.button--buy:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ------------------------------------------------- after the order */

.next-steps {
  margin: 0 0 var(--space-5);
  padding-inline-start: var(--space-5);
  line-height: var(--leading-normal);
}

.next-steps li { margin-bottom: var(--space-2); }

.order-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }

.order-list__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line-soft);
}

.order-list__head .muted { margin-inline-start: var(--space-2); font-size: var(--text-sm); }

.order-list__seller {
  display: flex;
  gap: var(--space-3);
  align-items: baseline;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  margin-bottom: var(--space-2);
}

/* The address row holds the radio tile and a "Change" link beside it. The link
   sits outside the <label> — inside, the label's own click handling would
   swallow it and select the radio instead of opening the editor. */
.address-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.address-list li > .address { flex: 1; min-width: 0; }

.address__edit {
  flex: none;
  font-size: var(--text-sm);
  padding-top: var(--space-3);
  white-space: nowrap;
}

/* The location pin button, created by app.js and therefore absent when the
   script does not run. Full width so it does not read as a secondary action
   squeezed beside the save button — it is a separate, optional step. */
.pin-button { width: 100%; margin-bottom: var(--space-2); }

/* =====================================================================
   The buyer / seller fork (D-039)
   ===================================================================== */

.choice { max-width: 52rem; margin: var(--space-7) auto; text-align: center; }

.choice-list {
  list-style: none;
  margin: var(--space-6) 0 var(--space-5);
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}

@media (min-width: 44rem) {
  .choice-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The whole card is the link. A card with a button inside it gives the same
   choice two hit areas, one of which is most of the card and does nothing. */
.choice-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-6) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  color: var(--ink);
  text-decoration: none;
  text-align: center;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}

.choice-card:hover,
.choice-card:focus-visible {
  border-color: var(--brand);
  box-shadow: var(--shadow);
  color: var(--ink);
}

.choice-card__icon { color: var(--brand); }
.choice-card strong { font-size: var(--text-lg); }
.choice-card span { color: var(--ink-soft); font-size: var(--text-sm); }

/* The time cost, which is the question people are actually weighing. */
.choice-card small { color: var(--ink-faint); font-size: var(--text-xs); }

.choice__note {
  color: var(--ink-soft);
  font-size: var(--text-sm);
  margin-bottom: var(--space-4);
}

/* =====================================================================
   The account area (D-040)
   ===================================================================== */

.account-grid {
  list-style: none;
  margin: var(--space-5) 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}

@media (min-width: 40rem) {
  .account-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 70rem) {
  .account-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* The whole card is the link, for the same reason as the signup fork: a card
   with a button inside gives one destination two hit areas, and most of the
   card then does nothing when tapped. */
.account-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: var(--space-4);
  row-gap: var(--space-1);
  height: 100%;
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  color: var(--ink);
  text-decoration: none;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}

.account-card:hover,
.account-card:focus-visible {
  border-color: var(--brand);
  box-shadow: var(--shadow);
  color: var(--ink);
}

.account-card__icon { grid-row: 1 / span 2; color: var(--brand); }
.account-card strong { font-size: var(--text-lg); }
.account-card span { color: var(--ink-soft); font-size: var(--text-sm); }

/* The invitation to open a shop reads as an offer rather than as one more
   setting, so it carries the brand tint the other cards do not. */
.account-card--invite { background: var(--brand-tint-soft); border-color: var(--brand-tint); }

.account-signout { margin-top: var(--space-6); }
.account-signout .link-button { color: var(--brand); }

/* ------------------------------------------------- the address book */

/* The book lists addresses as cards; checkout lists them as radio tiles. Same
   data, different job, so the shared flex row from checkout is turned off. */
.address-list--book { display: grid; gap: var(--space-4); }

.address-list--book li {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.address-actions {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  font-size: var(--text-sm);
}

.address-actions .link-button { color: var(--brand); }


/* ---------------------------------------------------------------------------
   Orders — the seller's queues and an order's history
   ------------------------------------------------------------------------ */

/* A consignment number is read out loud, typed into a courier's website and
   compared against a paper receipt, so it is set in the monospace face for the
   same reason a SKU is: 0 and O, 1 and l. `.listing .mono` already did this
   inside a table; this is the same thing outside one. */
.mono { font-family: var(--font-mono); font-size: var(--text-sm); }

/* The queue headings on the orders page. They carry more weight than an
   ordinary h2 because they are the page's structure — a shopkeeper scans for
   "Pack and send", not for an order number.

   Its own class rather than `.seller-panel main h2`, which would have restyled
   every heading on the catalogue screens as a side effect of building this
   page. */
.queue-head {
  margin-top: var(--space-6);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
}

.order-history { list-style: none; margin: 0; padding: 0; }

.order-history li {
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line);
}

.order-history li:last-child { border-bottom: 0; }

.order-history .muted { margin-inline-start: var(--space-2); font-size: var(--text-sm); }


/* ---------------------------------------------------------------------------
   The site notice — a bar above everything (D-048)
   ------------------------------------------------------------------------ */

/* Above the header rather than under it, because a warning below the thing it
   warns about is read second or not at all. Amber rather than red: the site
   works, it is simply not finished, and a red bar on a working shop reads as an
   outage. */
.site-notice {
  background: var(--warn-tint);
  border-bottom: 2px solid var(--warn);
  color: var(--warn);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.site-notice p {
  margin: 0;
  padding: var(--space-2) var(--space-4);
  text-align: center;
  /* It has to survive a 320px phone without pushing the page sideways, which
     is most of this audience. */
  text-wrap: balance;
}

/* --------------------------------------------------- department menu (D-056)

   The "All" cascade: departments, their groups, their children. Three columns,
   revealed on hover.

   TWO TRAPS LIVE HERE, both already paid for once elsewhere.

   1. `.departments__inner` is `overflow-x: auto`. A panel rendered inside it is
      CLIPPED by that overflow, and the symptom is a panel that opens empty or
      cut off rather than anything that looks like a CSS fault. So the markup
      puts `.dept-menu` as a SIBLING of `.departments`, and this positions it
      against `.site-header`. Do not move it into the bar to "keep it tidy".

   2. `.site-header a:hover` sets `color: var(--surface)` -- white -- because
      the header is green. The panel is a light surface inside that header, so
      every link in it inherits that rule and turns WHITE ON WHITE on hover:
      invisible, exactly like the wordmark bug the user found.

      Counting it out: `.site-header a:hover` is one class, one pseudo-class and
      one element -- (0,2,1). The overrides below add a second class, so
      `.dept-menu .dept-menu__item:hover` is (0,3,1) and wins.

      (HANDOVER §7 records the earlier fix as "(0,1,2)". The digits are
      transposed there; the conclusion it draws is right.)

      And per that same section: do not trust this arithmetic by eye. It is
      confirmed on the running page by reading the computed colour, not here.

   The columns are FIXED-WIDTH on purpose. With `1fr` each, the sheet's height
   and the second column's width would change every time the pointer moved to a
   department with a different number of groups -- the menu would appear to
   twitch under the cursor, and a target the user was aiming at would move.
   ------------------------------------------------------------------------- */

.site-header { position: relative; }

.dept-menu {
  position: absolute;
  inset-inline: 0;
  top: 100%;
  z-index: var(--z-dropdown);
}

.dept-menu__sheet {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
}

.dept-menu__inner {
  display: grid;
  grid-template-columns: 260px 260px 1fr;
  align-items: stretch;
  padding: 0 var(--gutter);
  min-height: 420px;
}

/* Focused mode: the panel was opened from a department in the bar, so it shows
   THAT department only (D-058).

   The first column is hidden because the department bar sitting directly above
   it is that list. Rendering both put the same five names on screen twice, one
   row apart -- which the user called "makhi py makhi marna", and which was the
   result of reusing the "All" panel instead of asking what hovering a single
   department should show.

   "All" is the one trigger that keeps the column, because listing every
   department is what "All" means. */
.dept-menu--focused .dept-menu__col--1 { display: none; }

.dept-menu--focused .dept-menu__inner { grid-template-columns: 280px 1fr; }

/* col--2 is the leftmost column here, so it loses the inset it carries when
   it sits beside the department list. */
.dept-menu--focused .dept-menu__col--2 { padding-left: 0; }

.dept-menu__col {
  padding: var(--space-3) 0;
  /* Each column scrolls on its own. Cricket has 18 children and the viewport
     may be short; without this the sheet grows past the bottom of the screen
     and the last few are unreachable. */
  max-height: min(70vh, 560px);
  overflow-y: auto;
}

.dept-menu__col--1 {
  list-style: none;
  margin: 0;
  padding-inline: 0;
  border-right: 1px solid var(--line);
}

.dept-menu__col--2 { border-right: 1px solid var(--line); }

.dept-menu__col--2,
.dept-menu__col--3 { padding-left: var(--space-4); }

.dept-menu__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* The third column has room to breathe: Cricket's 18 children read better in
   two tracks than as one long ladder the eye has to scan. */
.dept-menu__col--3 .dept-menu__list {
  columns: 2;
  column-gap: var(--space-5);
}

.dept-menu__col--3 li { break-inside: avoid; }

.dept-menu__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  text-decoration: none;
}

/* (0,3,1) -- beats `.site-header a:hover` at (0,2,1). See trap 2 above. */
.dept-menu .dept-menu__item { color: var(--ink); }

.dept-menu .dept-menu__item:hover,
.dept-menu .dept-menu__item:focus-visible,
.dept-menu .dept-menu__item.is-open {
  background: var(--brand-tint);
  color: var(--brand-dark);
}

.dept-menu__arrow { color: var(--ink-faint); font-size: var(--text-base); }

.dept-menu .dept-menu__item.is-open .dept-menu__arrow,
.dept-menu .dept-menu__item:hover .dept-menu__arrow { color: var(--brand-dark); }

.dept-menu__more { margin-top: var(--space-2); }

.dept-menu .dept-menu__more a {
  display: inline-block;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--brand);
  text-decoration: none;
}

.dept-menu .dept-menu__more a:hover { text-decoration: underline; }

.dept-menu__all {
  grid-column: 1 / -1;
  margin: 0;
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line);
}

.dept-menu .dept-menu__all a {
  color: var(--brand);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
}

.dept-menu .dept-menu__all a:hover { text-decoration: underline; }

/* Narrow screens get one column at a time rather than three squeezed ones.
   The cascade still works: tapping a department replaces the list. */
@media (max-width: 860px) {
  .dept-menu__inner { grid-template-columns: 1fr; min-height: 0; }
  .dept-menu__col--1 { border-right: 0; border-bottom: 1px solid var(--line); }
  .dept-menu__col--2 { border-right: 0; padding-left: 0; }
  .dept-menu__col--3 { padding-left: 0; }
  .dept-menu__col--3 .dept-menu__list { columns: 1; }
}

/* The page the panel degrades to. Plain document, not a panel. */

.dept-page {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  margin-top: var(--space-4);
}

.dept-page__block h2 {
  margin: 0 0 var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 2px solid var(--brand);
  font-size: var(--text-base);
}

.dept-page__block h2 a { color: var(--ink); text-decoration: none; }
.dept-page__block h2 a:hover { color: var(--brand); }

.dept-page__groups,
.dept-page__leaves { list-style: none; margin: 0; padding: 0; }

.dept-page__groups > li + li { margin-top: var(--space-3); }

.dept-page__group {
  font-weight: var(--weight-medium);
  color: var(--ink);
  text-decoration: none;
}

.dept-page__group:hover { color: var(--brand); }

.dept-page__leaves {
  margin-top: var(--space-1);
  padding-left: var(--space-3);
  border-left: 1px solid var(--line);
}

.dept-page__leaves a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.dept-page__leaves a:hover { color: var(--brand); text-decoration: underline; }

.dept-page__empty { color: var(--ink-faint); font-size: var(--text-sm); margin: 0; }

/* ------------------------------------------------- sub-category strip (D-059)

   Hovering a chip on a category page reveals its own children beneath the
   strip, so /category/gloves/ can reach Welding Gloves in one movement instead
   of loading Work Gloves and reading a second strip.

   ONE panel under the whole strip, not one per chip. A popover anchored to
   each chip needs positioning, and the right-hand chip would push its panel
   off the screen; a full-width panel cannot overflow and needs no measuring.

   No `position: absolute` here on purpose either. The panel takes part in
   normal flow, so opening it moves the product grid down rather than covering
   it -- on a page whose whole job is showing products, a hover that hides them
   is worse than one that shifts them. It is also why the strip is not inside
   any scrolling container: see the .departments trap in the header menu.
   ------------------------------------------------------------------------- */

.substrip {
  /* FLUSH WITH THE STRIP, and this is not cosmetic.

     `.category-strip` has `margin-block: var(--space-5)`. Anything less than a
     full cancel here leaves a strip of bare page between the chip and the panel
     it opened -- and that gap is outside both, so a pointer travelling down
     through it is "outside the menu" and closes it before arriving. The user
     hit exactly that: they could not reach Wool Gloves from Winter Gloves.

     -space-5 collapses against the strip's bottom margin to zero. The panel's
     own padding does the visual separating instead, so there is no dead pixel
     between the two at any zoom level.

     The close delay in app.js is the belt to this pair of braces: sub-pixel
     rounding and diagonal movement can still land a frame outside both boxes. */
  margin: calc(var(--space-5) * -1) 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.substrip__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
}

.substrip__list a {
  display: inline-block;
  padding: var(--space-1) 0;
  font-size: var(--text-sm);
  color: var(--ink);
  text-decoration: none;
}

.substrip__list a:hover,
.substrip__list a:focus-visible { color: var(--brand); text-decoration: underline; }

.substrip__all {
  margin: var(--space-2) 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--line);
}

.substrip__all a {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--brand);
  text-decoration: none;
}

.substrip__all a:hover { text-decoration: underline; }

/* Evidence photographs on a return or dispute (D-078). Square thumbnails that
   wrap; each opens the full picture through the permission-checked view. */
.evidence-photos {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.evidence-photos img {
  width: 120px;
  height: 120px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line);
  display: block;
}

/* Reviews (D-079): the star breakdown on a product page, and the shop's reply
   set in from the review it answers. */
.star-breakdown { border-collapse: collapse; margin: var(--space-3) 0 var(--space-4); }
.star-breakdown th { text-align: left; font-weight: normal; padding-right: var(--space-3); white-space: nowrap; }
.star-breakdown td { padding: var(--space-1) var(--space-2); }
.star-breakdown__bar {
  display: block;
  width: 12rem;
  max-width: 40vw;
  height: 0.6rem;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
.star-breakdown__bar span { display: block; height: 100%; background: var(--accent-bright); }
.review-reply {
  margin: var(--space-2) 0 0 var(--space-4);
  padding-left: var(--space-3);
  border-left: 3px solid var(--line);
}
.card-product__rating { margin: 0; font-size: var(--text-sm); }
.card-product__rating .stars { font-size: 0.85em; vertical-align: -0.1em; }
