/* ==========================================================================
   Dynamite Athletics storefront - design system
   --------------------------------------------------------------------------
   Everything visual is driven by the custom properties in :root. Change those
   (or use the brand lab at ?dev=1) and the whole site re-skins, including the
   SVG bike illustrations, which read their colours from the same variables.

   Contents
     1.  Tokens
     2.  Reset and base
     3.  Typography
     4.  Layout primitives
     5.  Buttons
     6.  Badges, chips and pills
     7.  Header, navigation and mega menu
     8.  Hero
     9.  Cards
     10. Shop: facets and results
     11. Product detail
     12. Basket drawer
     13. Forms
     14. Content blocks (services, steps, FAQ, legal)
     15. Footer
     16. Prototype notice, breadcrumbs, toasts
     17. Brand lab (dev only)
     18. Responsive
     19. Accessibility and motion
   ========================================================================== */

/* 1. Tokens ---------------------------------------------------------------- *
 * The palette lives here and the whole site is skinned from it - including the
 * SVG bike illustrations, which read these same variables.
 *
 * Three themes ship. "light" is the default; "graphite" is a softer, greyer
 * dark for anyone who wants the mood without the near-black; "dark" is the
 * original noir. Pick one with data-theme on the <html> element. brand.js sets
 * the default, the head script in each page reapplies a saved choice before
 * first paint, and the brand lab (?dev=1) swaps them live.
 *
 * There are two accent families, and they have different jobs:
 *
 *   GOLD   (--accent)      actions and brand marks. Primary buttons, the
 *                          wordmark tagline, the trust strip, prices-adjacent
 *                          emphasis. It is the colour you click or that says
 *                          "this is us".
 *   PURPLE (--secondary)   structure and interaction. Section labels, links,
 *                          anything selected, tabular headings, band tints.
 *                          It is the colour that describes the page.
 *
 * Colours used as TEXT are deliberately separate from colours used as FILLS.
 * Gold is lovely as a button fill with near-black text on it, but gold text on
 * a pale background is unreadable. So --accent is the fill and --accent-text is
 * the readable version of the same hue for each theme. The purple pair follows
 * the same rule: --secondary is the border or wash purple, which has to stay
 * visible against a dark theme's surface, and --secondary-fill is the purple
 * safe to put white text on.
 * ------------------------------------------------------------------------- */

/* Theme-independent tokens. */
:root {
  /* Type. No web fonts: nothing to load, nothing to block rendering. */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Noto Sans", sans-serif;
  /* Headings can be given their own stack in the back office. It falls back to
     the body stack, which is what the shipped design uses. */
  --font-heading: var(--font);
  --font-num: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;
  --type-scale: 1;

  /* Space and shape. */
  --gap: 20px;
  --gap-lg: 32px;
  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 22px;
  --wrap: 1240px;
  --header-h: 104px;

  --transition: 160ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* ---- "light": warm off-white, the default ---- */
:root,
[data-theme="light"] {
  color-scheme: light;

  --bg: #f6f4ef;
  --surface: #ffffff;
  --surface-2: #f0ede6;
  --surface-3: #e5e1d6;
  /* The black header, mega menu and footer bands. The page is white, but the
     frame around it keeps the brand's black, gold and purple. */
  --chrome-bg: #0a0a0a;
  /* Solid, high-contrast buttons - not just inside the bands. */
  --solid: #0a0a0a;
  --solid-ink: #ffffff;
  --solid-hover: #262626;
  /* Used for the alternating band and other barely-there tints. */
  --tint-overlay: rgba(20, 18, 15, 0.04);

  /* Text, from most prominent to most incidental. All above 4.5:1 on --bg. */
  --ink: #2c2a26;
  --ink-strong: #14120f;
  --ink-soft: #4a4740;
  --ink-faint: #5f5b54;
  --ink-muted: #6b675f;
  --ink-ghost: rgba(20, 18, 15, 0.16);

  --line: rgba(20, 18, 15, 0.1);
  --line-strong: rgba(20, 18, 15, 0.22);

  /* Gold as a fill, and the same gold darkened until it can be read. */
  --accent: #d4af37;
  --accent-bright: #b8912a;
  --accent-text: #7d5f0f;
  --accent-text-hover: #5c4508;
  --accent-ink: #14120f;
  --accent-wash: rgba(212, 175, 55, 0.18);

  /* The colour the wordmark is actually drawn in, kept apart from --accent.
   * The accent is the shop's own choice in Look & feel and can be any colour -
   * the live one is #f0cb14, a brighter yellow than the logo. The menu's own
   * words were asked to be "the same yellow as the one in our logo", so they
   * use this and not the accent: the two stay equal until somebody deliberately
   * recolours the logo, which is the only thing this token describes. */
  --logo-gold: #d4af37;

  --secondary: #6d28d9;
  --secondary-fill: #6d28d9;
  --secondary-ink: #ffffff;
  --secondary-text: #5b21b6;
  --secondary-text-hover: #4c1d95;
  --secondary-wash: rgba(109, 40, 217, 0.1);
  /* The tint for whole alternating bands. Deliberately weaker than
     --secondary-wash: a muted caption sitting on a full-width tinted band has
     far less headroom than a label inside a small pill, and at wash strength
     muted text measured 4.35:1 here. Derived from --secondary so the brand
     lab's accent presets carry the band with them. */
  --band-tint: rgba(109, 40, 217, 0.06);
  --band-tint: color-mix(in srgb, var(--secondary) 6%, transparent);

  --ok-text: #14603a;
  --info-text: #4b5b6b;
  --danger: #c0392b;
  --danger-text: #b3261e;

  --shadow: 0 18px 40px rgba(20, 18, 15, 0.12);
  --shadow-sm: 0 6px 18px rgba(20, 18, 15, 0.08);
}

/* ---- "graphite": dark, but nothing like as black ---- */
[data-theme="graphite"] {
  color-scheme: dark;

  --bg: #191a1e;
  --surface: #212328;
  --surface-2: #2a2d33;
  --surface-3: #343840;
  --chrome-bg: #101114;
  --solid: #3a3f48;
  --solid-ink: #ffffff;
  --solid-hover: #474d57;
  --tint-overlay: rgba(255, 255, 255, 0.035);

  --ink: #e3e4e7;
  --ink-strong: #ffffff;
  --ink-soft: #b9bcc2;
  --ink-faint: #94979e;
  --ink-muted: #8a8e95;
  --ink-ghost: rgba(255, 255, 255, 0.22);

  --line: rgba(255, 255, 255, 0.1);
  --line-strong: rgba(255, 255, 255, 0.2);

  --accent: #d4af37;
  --accent-bright: #ffd700;
  --accent-text: #e0bd52;
  --accent-text-hover: #ffd700;
  --accent-ink: #14120f;
  --accent-wash: rgba(212, 175, 55, 0.14);

  --secondary: #8b5cf6;
  /* Darker than --secondary on purpose: white text needs 4.5:1, and #8b5cf6
     only reaches 4.2:1. The lighter value stays for borders and washes. */
  --secondary-fill: #7c3aed;
  --secondary-ink: #ffffff;
  --secondary-text: #cbb6ff;
  --secondary-text-hover: #ece2ff;
  --secondary-wash: rgba(139, 92, 246, 0.16);
  --band-tint: rgba(139, 92, 246, 0.06);
  --band-tint: color-mix(in srgb, var(--secondary) 6%, transparent);

  --ok-text: #6fdca0;
  --info-text: #a9bccd;
  --danger: #ff8080;
  --danger-text: #ff9c9c;

  --shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
  --shadow-sm: 0 6px 18px rgba(0, 0, 0, 0.35);
}

/* ---- "dark": the original noir ---- */
[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0a0a0a;
  --surface: #151515;
  --surface-2: #1c1c1c;
  --surface-3: #242424;
  --chrome-bg: #0a0a0a;
  --solid: #2a2d33;
  --solid-ink: #ffffff;
  --solid-hover: #383b42;
  --tint-overlay: rgba(255, 255, 255, 0.02);

  --ink: #ededed;
  --ink-strong: #ffffff;
  --ink-soft: #b8b8b8;
  --ink-faint: #8f8f8f;
  /* Lifted from #7a7a7a: on the noir page background muted text was already at
     4.89:1, and any tint over a band ate the remaining headroom. */
  --ink-muted: #858585;
  --ink-ghost: rgba(255, 255, 255, 0.22);

  --line: rgba(255, 255, 255, 0.08);
  --line-strong: rgba(255, 255, 255, 0.18);

  --accent: #d4af37;
  --accent-bright: #ffd700;
  --accent-text: #d4af37;
  --accent-text-hover: #ffd700;
  --accent-ink: #0a0a0a;
  --accent-wash: rgba(212, 175, 55, 0.12);

  --secondary: #7c3aed;
  --secondary-fill: #7c3aed;
  --secondary-ink: #ffffff;
  --secondary-text: #dcd0ff;
  --secondary-text-hover: #f0e9ff;
  --secondary-wash: rgba(124, 58, 237, 0.16);
  --band-tint: rgba(124, 58, 237, 0.06);
  --band-tint: color-mix(in srgb, var(--secondary) 6%, transparent);

  --ok-text: #57d68a;
  --info-text: #9fb3c8;
  --danger: #ff8080;
  --danger-text: #ff8080;

  --shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
  --shadow-sm: 0 6px 18px rgba(0, 0, 0, 0.4);
}

/* 2. Reset and base -------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Anchor links must not hide under the sticky header. */
  scroll-padding-top: calc(var(--header-h) + 16px);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--base-size, 16px);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg {
  max-width: 100%;
}

/* The site is dark-only by design. Keep form controls and scrollbars dark. */
input,
select,
textarea,
button {
  font: inherit;
  color: inherit;
}

body.no-scroll {
  overflow: hidden;
}

/* 3. Typography ------------------------------------------------------------ */
h1,
h2,
h3,
h4 {
  margin: 0 0 0.5em;
  line-height: 1.1;
  color: var(--ink-strong);
  font-family: var(--font-heading);
  font-weight: 800;
  letter-spacing: -0.02em;
}

h1 {
  font-size: calc(clamp(2rem, 5vw, 3.4rem) * var(--type-scale));
}
h2 {
  font-size: calc(clamp(1.5rem, 3.2vw, 2.2rem) * var(--type-scale));
}
h3 {
  font-size: calc(1.2rem * var(--type-scale));
}
h4 {
  font-size: calc(1rem * var(--type-scale));
}

p {
  margin: 0 0 1em;
}

/* Gold says "this acts"; purple says "this is a link". Keeping the two apart
   means neither has to carry both jobs. */
a {
  color: var(--secondary-text);
  text-decoration: none;
}

a:hover {
  color: var(--secondary-text-hover);
}

/* The small uppercase label used throughout the design. These are the purple
   thread that runs down every page. */
.eyebrow {
  display: block;
  margin: 0 0 10px;
  color: var(--secondary-text);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.eyebrow--secondary {
  color: var(--secondary-text);
}

/* For the rarer label that should read gold instead. */
.eyebrow--accent {
  color: var(--accent-text);
}

.lede {
  max-width: 62ch;
  color: var(--ink-soft);
  font-size: 1.06rem;
}

.muted {
  color: var(--ink-faint);
}

.small {
  font-size: 0.85rem;
}

.tiny {
  font-size: 0.76rem;
}

.strong {
  color: var(--ink-strong);
  font-weight: 700;
}

.nowrap {
  white-space: nowrap;
}

.price {
  font-family: var(--font-num);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--ink-strong);
  letter-spacing: -0.02em;
}

.price--lg {
  font-size: 2.2rem;
}

/* 4. Layout primitives ----------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 20px;
}

.section {
  padding: 64px 0;
}

.section--tight {
  padding: 40px 0;
}

/* The band that alternates down the page. Nudged towards purple rather than a
   neutral tint, so the page breathes lavender rather than grey - at
   --band-tint strength, which muted text can still sit on. */
.section--alt {
  background: linear-gradient(180deg, transparent, var(--band-tint) 40%, transparent);
}

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 28px;
}

.section-head p {
  margin: 0;
}

.grid {
  display: grid;
  gap: var(--gap);
}

.grid--2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid--3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* The homepage's best sellers. Four product cards across, two on a tablet and
 * one on a phone. `.grid--4` above drops straight to one column at 900px,
 * which would turn a four-card rail into four screens of scrolling; this keeps
 * a pair on a tablet, where a card is still comfortably over 300px wide. */
.grid--best {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 900px) {
  .grid--best {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 620px) {
  .grid--best {
    grid-template-columns: 1fr;
  }
}
.grid--products {
  grid-template-columns: repeat(auto-fill, minmax(min(276px, 100%), 1fr));
}

.stack > * + * {
  margin-top: 12px;
}

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.row--between {
  justify-content: space-between;
}

.rule {
  height: 1px;
  margin: 32px 0;
  border: 0;
  background: var(--line);
}

/* 5. Buttons --------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 13px 22px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-strong);
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform var(--transition), background var(--transition),
    border-color var(--transition), color var(--transition);
}

.btn:hover {
  transform: translateY(-1px);
}

.btn:active {
  transform: translateY(0);
}

.btn--primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.btn--primary:hover {
  background: var(--accent-bright);
  color: var(--accent-ink);
}

/* Solid black, to pair with the gold primary button and to carry the brand's
   black into the page itself rather than only the header and footer. */
.btn--dark {
  background: var(--solid);
  color: var(--solid-ink);
}

.btn--dark:hover {
  background: var(--solid-hover);
  color: var(--solid-ink);
}

.btn--outline {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--ink-strong);
}

.btn--outline:hover {
  border-color: var(--secondary);
  color: var(--secondary-text);
}

.btn--ghost {
  background: transparent;
  color: var(--ink-soft);
}

.btn--ghost:hover {
  color: var(--accent-text-hover);
}

.btn--block {
  width: 100%;
}

.btn--sm {
  padding: 9px 14px;
  font-size: 0.72rem;
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

/* A link that behaves like a button. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.link-arrow::after {
  content: "\2192";
  transition: transform var(--transition);
}

.link-arrow:hover::after {
  transform: translateX(3px);
}

/* 6. Badges, chips and pills ----------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  border-radius: 5px;
  background: var(--secondary-fill);
  color: var(--secondary-ink);
  font-size: 0.64rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.badge--accent {
  background: var(--accent);
  color: var(--accent-ink);
}

.badge--outline {
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--ink-soft);
}

.badge--legal {
  background: var(--secondary-wash);
  border: 1px solid var(--secondary);
  color: var(--secondary-text);
}

/* Fulfilment state, used on cards and product pages. Green means in stock,
   which is now the ordinary state of the shop; the other colours are the
   exceptions a reader should notice. */
.stock {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.78rem;
  font-weight: 700;
}

.stock::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.stock--in-stock {
  color: var(--ok-text);
}

.stock--pre-order {
  color: var(--secondary-text);
}

.stock--sold-out,
.stock--discontinued {
  color: var(--danger-text);
}

/* Honeypot field on the contact form: invisible to a person, tempting to a
 * bot that fills every input it finds. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-soft);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--transition), color var(--transition),
    background var(--transition);
}

.chip:hover {
  border-color: var(--secondary);
  color: var(--ink-strong);
}

.chip__count {
  color: var(--ink-muted);
  font-size: 0.72rem;
}


.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* 7. Header, navigation and mega menu -------------------------------------- */

/* The header is mounted into a wrapper div. A sticky element cannot escape its
   parent's box, so a wrapper exactly as tall as the header made `position:
   sticky` do nothing: the whole header scrolled away. Taking the wrapper out of
   the layout box is what lets the header pin to the viewport. */
#header-slot {
  display: contents;
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--chrome-bg);
  border-bottom: 1px solid var(--line);
}

/* ------------------------------------------------------------------------
 * The black frame.
 *
 * The page is white, but the header, mega menu and footer stay black so that
 * the brand's black, gold and purple are all still on the page. Rather than
 * restyling every rule inside them, this block redefines the content tokens
 * within that scope - so the header and footer rules below render correctly on
 * black without needing to know anything about it.
 *
 * Two details are deliberate. Accent-as-text goes back to being gold here
 * (--accent rather than the dark bronze used on white), because gold on black
 * measures 8.9:1. And the shadow becomes a true black, since a shadow designed
 * for white paper is invisible against black.
 * ---------------------------------------------------------------------- */
.site-header,
.site-footer {
  --ink: #f2f1ed;
  --ink-strong: #ffffff;
  --ink-soft: rgba(255, 255, 255, 0.76);
  --ink-faint: rgba(255, 255, 255, 0.62);
  --ink-muted: rgba(255, 255, 255, 0.52);
  --ink-ghost: rgba(255, 255, 255, 0.24);

  --line: rgba(255, 255, 255, 0.14);
  --line-strong: rgba(255, 255, 255, 0.26);

  --surface: #16161a;
  --surface-2: #1e1e22;
  --surface-3: #27272c;

  --accent-text: var(--accent);
  --accent-text-hover: var(--accent);
  /* The second declaration wins wherever color-mix is supported, which is every
     current browser; the first is the fallback if it is not. */
  --accent-text-hover: color-mix(in srgb, var(--accent) 72%, #ffffff);
  /* The frame is black, so purple-as-text goes pale here for the same reason
     gold does - a readable-on-white violet would disappear against #0a0a0a. */
  --secondary-text: #d8c9ff;
  --secondary-text-hover: #ffffff;

  --shadow: 0 18px 42px rgba(0, 0, 0, 0.45);
}

/* ------------------------------------------------------------------------
 * The revolving promo bar.
 *
 * One claim at a time, centred, in gold. Only the active item is in the DOM's
 * accessibility tree - the others carry `hidden` - so a screen reader is told
 * the offer that is actually on screen rather than all three at once.
 * ---------------------------------------------------------------------- */
.promo-bar {
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.promo-bar .wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding-top: 5px;
  padding-bottom: 5px;
}

.promo-bar__list {
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}

.promo-bar__item {
  color: var(--accent-text);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.promo-bar__item.is-active {
  animation: promo-in 420ms ease both;
}

@keyframes promo-in {
  from {
    opacity: 0;
    transform: translateY(-3px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .promo-bar__item.is-active {
    animation: none;
  }
}

.promo-bar a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.promo-bar a:hover {
  border-bottom-color: currentColor;
}

/* ------------------------------------------------------------------------
 * Two-row header: the lockup on its own centred row, the navigation below it.
 *
 * The grid is 1fr / auto / 1fr so the logo sits on the true centre of the
 * page whatever the width of the controls either side of it. Centring it with
 * flex and margins only works while the two sides happen to be the same
 * width, which they never are once the search box gets wide.
 * ---------------------------------------------------------------------- */
.header-top {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a 1fr track will not shrink below its content,
     so a wide search box on the right pushed the logo off centre. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 18px;
  min-height: 74px;
  padding-top: 8px;
  padding-bottom: 8px;
}

.header-top .wordmark {
  justify-self: center;
}

.header-top .header-actions {
  justify-self: end;
}

.header-nav {
  border-top: 1px solid var(--line);
}

.header-nav__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  color: var(--ink-strong);
}

/* The full lockup: DYNAMITE with the lit stick for the I, and ATHLETICS under
   it. Drawn art, not type, so it carries its own spacing and cannot be
   re-created with CSS - hence an image and a fixed aspect ratio.

   Sized by height and allowed to find its own width, because 780x224 is the
   artwork's own proportion and forcing a width would squash the wordmark. The
   clamp keeps it level with the nav on a laptop and stops it crowding the
   actions on a narrow screen. */
.wordmark__logo {
  display: block;
  height: clamp(36px, 4.4vw, 54px);
  width: auto;
}

.wordmark--footer .wordmark__logo {
  height: 52px;
}

.wordmark:hover {
  color: var(--ink-strong);
  opacity: 0.92;
}

.nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin: 0;
}

.nav__link {
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  background: none;
  border: 0;
  cursor: pointer;
  transition: color var(--transition), background var(--transition);
}

.nav__link:hover,
.nav__link[aria-expanded="true"] {
  background: var(--surface-2);
  color: var(--ink-strong);
}

/* The tab you are on, in the same gold as the logo and the panel under it. It
 * used to take the accent, which on this shop is a brighter yellow: sitting
 * directly above the menu's own gold, the two read as a near-miss. */
.nav__link[aria-current="page"] {
  color: var(--logo-gold);
}

/* An item with a panel under it. The label is the link (or the button, when
   there is no page to go to) and the caret beside it opens the panel; the grid
   keeps the two on one line and lets the panel take the full width below them
   in the phone drawer. On a laptop the panel is absolutely positioned against
   the header, so this grid only ever holds the trigger. */
.nav__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
}

.nav__item > .mega {
  grid-column: 1 / -1;
}

.nav__caret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 34px;
  margin-left: -8px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink-faint);
  font-size: 0.62rem;
  line-height: 1;
  cursor: pointer;
  transition: color var(--transition), background var(--transition);
}

.nav__caret:hover,
.nav__caret[aria-expanded="true"] {
  background: var(--surface-2);
  color: var(--ink-strong);
}

.nav__caret span {
  transition: transform var(--transition);
}

.nav__caret[aria-expanded="true"] span {
  transform: rotate(180deg);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

.icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-strong);
  cursor: pointer;
  transition: border-color var(--transition), color var(--transition);
}

.icon-btn:hover {
  border-color: var(--accent);
  color: var(--accent-text-hover);
}

.icon-btn__count {
  position: absolute;
  top: -7px;
  right: -7px;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--secondary-fill);
  color: var(--secondary-ink);
  font-size: 0.68rem;
  font-weight: 800;
  line-height: 20px;
  text-align: center;
}

.icon-btn__count[hidden] {
  display: none;
}

/* Mega menu: opens under the header on desktop, inline on mobile. */
.mega {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  display: none;
  padding: 26px 0 30px;
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow);
  /* A department panel can be taller than the screen - the gym menu lists ten
     types with up to seven links under each, which is 708px on a 1024px
     laptop. A panel that runs past the bottom edge is a menu nobody can
     finish reading, so it scrolls inside itself, sized to the room that is
     actually left below the header. */
  max-height: calc(100dvh - var(--header-h) - 6px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.mega.is-open {
  display: block;
}

.mega__grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: 36px;
}

/* The shop panel is three sections side by side, so the bike types, the
   accessories and the upgrades each get their own column instead of one long
   list beside a short one with half the panel empty below it. */
.mega__grid--shop {
  grid-template-columns: 1.5fr 1fr 0.7fr;
}

/* A department panel: one column per group the shop files its categories
   under, so the panel reads the same way as the section strip on the shop
   page. auto-fit rather than a fixed count, because the number of groups is
   the taxonomy's decision and not this stylesheet's. */
.mega__grid--dept {
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 26px 34px;
}

/* What the department is, in the shop's own words, before the link into it. */
.mega__blurb {
  display: block;
  margin-bottom: 8px;
  color: var(--ink-faint);
}

.mega__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 18px 26px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The panel's own words are gold, the way the logo's own words are.
 *
 * They were lilac (--secondary-text), which is the shop's structure colour on
 * white paper - on black it went pale violet and read as a second, competing
 * accent beside the gold tabs above it. They use --logo-gold rather than the
 * accent because the accent is a brighter yellow than the wordmark: read side
 * by side the two did not match. 8.9:1 on the panel's black. The counts beside
 * each name stay grey, so the name is what reads first. */
.mega .eyebrow,
.mega__cat,
.mega__foot a {
  color: var(--logo-gold);
}

.mega__cat {
  font-size: 0.86rem;
  font-weight: 800;
}

.mega__cat:hover,
.mega__cat:focus-visible {
  color: color-mix(in srgb, var(--logo-gold) 74%, #ffffff);
}

/* Departments in the top-level menu: the name, then whether it is open. */
.mega__dept {
  display: block;
}

.mega__cat--soon {
  color: var(--ink-faint);
}

/* Cycling types sit in a single readable column beside the department list. */
.mega__list--tight {
  grid-template-columns: 1fr;
  gap: 7px;
}

.mega__subs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
}

/* The variants under a name - Commuter, Folding, Helmets - are the second
 * level of the same menu, so they are the same gold, smaller and lighter. */
.mega__subs a {
  color: var(--logo-gold);
  font-size: 0.8rem;
  opacity: 0.86;
}

.mega__subs a:hover {
  color: color-mix(in srgb, var(--logo-gold) 74%, #ffffff);
  opacity: 1;
}

.mega__brands {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.mega__foot {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: 0.8rem;
}

.header-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

.header-search input {
  width: 170px;
  padding: 11px 0;
  border: 0;
  background: transparent;
  color: var(--ink-strong);
  outline: none;
}

.header-search input::placeholder {
  color: var(--ink-muted);
}

/* Mobile menu button and drawer. */
.nav-toggle {
  display: none;
}

/* 8. Hero ------------------------------------------------------------------ */
.hero {
  position: relative;
  overflow: hidden;
  padding: 76px 0 64px;
  border-bottom: 1px solid var(--line);
}

.hero::after {
  content: "";
  position: absolute;
  top: -30%;
  right: -12%;
  width: 620px;
  height: 620px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--secondary-wash), transparent 68%);
  pointer-events: none;
}

.hero__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  align-items: center;
  gap: 40px;
}

/* The home page's hero is words only now that the photographs run full width
   above it: two columns would leave an empty one beside the headline. */
.hero__inner--solo {
  grid-template-columns: 1fr;
}

.hero__inner--solo .lede {
  max-width: 62ch;
}

/* With the whole width to play with, the headline can sit on one line instead
   of being squeezed into three by the two-column hero's 16ch cap. */
.hero .hero__inner--solo h1 {
  max-width: none;
}

.hero__inner--solo .hero__facts {
  max-width: 680px;
}

.hero h1 {
  max-width: 16ch;
}

.hero__photo {
  display: block;
  width: 100%;
  height: auto;
  max-height: 520px;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

/* The home page's hero picture is a 3:2 landscape (1200x800). `height: auto`
 * above means it is shown whole at whatever width the column gives it - about
 * 520x347 next to the headline - so there is no crop to position and a rider
 * is never sliced by a frame that is taller than the photograph. */
.hero__photo--landscape {
  object-position: center;
}

/* A photograph put in place of drawn artwork (the hero, the band, the company
 * picture). It fills the slot the drawing used, so the page keeps its shape
 * whatever shape the upload is. */
.eb-swap-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

.hero__actions {
  margin-top: 26px;
}

.hero__art {
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(160deg, var(--surface), var(--bg));
}

/* A photograph in the hero keeps the height the drawing had in mind, so a
   portrait upload does not turn the fold into a wall of picture. */
.hero__art .eb-swap-photo {
  max-height: 520px;
  border-radius: var(--radius-lg);
}

/* The moving photo showcase: edge to edge, no frame, no border, directly under
   the menu and above the headline. Each picture is 1920x750 - the shape of the
   band itself - so the band is the picture: there is no gap at the ends to
   pad, nothing is blurred to hide one, and no wash darkens the edges to join
   the two. The pictures are stacked in a fixed-ratio box, so the page below
   never shifts as they change. The fade is short (420ms) because a long one
   looks like a slow image loading rather than a change of picture. */
.showcase-strip {
  position: relative;
  background: var(--band-tint);
}

.showcase__frame {
  position: relative;
  overflow: hidden;
  width: 100%;
  /* The photograph's own shape, so it fills the band exactly at any width: no
     padding, no blurred surround, no crop. The height is capped only where a
     window is wider than the picture can serve (past about 2000px, where
     1920x750 would otherwise be 800px deep) and the picture is then trimmed top
     and bottom rather than padded at the sides. */
  aspect-ratio: 1920 / 750;
  max-height: 780px;
}

.showcase__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* A touch above centre keeps riders' heads in frame if a very wide monitor
     ever has to trim the band at all; where the band fits the picture exactly
     this does nothing. */
  object-position: center 45%;
  opacity: 0;
  transform: scale(1.03);
  transition: opacity 420ms ease, transform 2600ms linear;
}

/* The slow drift while a picture is on screen. It is felt rather than watched. */
.showcase__photo.is-active {
  opacity: 1;
  transform: scale(1);
}

/* Nobody who has asked for less motion gets the drift or the fade. The script
 * also leaves those readers on the first photograph rather than rotating. This
 * sits before the wide-screen rules so it cannot undo their centring. */
@media (prefers-reduced-motion: reduce) {
  .showcase__photo,
  .showcase__photo.is-active {
    transform: none;
    transition: none;
  }
}

/* A phone window is a third as wide as the photograph, so at the photograph's
   own shape the band would be a letterbox 150px deep. Cropped to 3:2 it keeps
   its height and the rider - who is centred in all four - stays in frame, with
   no blurred surround and no border to fill the difference. */
@media (max-width: 719px) {
  .showcase__frame {
    aspect-ratio: 3 / 2;
    max-height: 60vh;
  }
}

/* The dots sit on the picture, so the strip stays one clean band with no row of
   chrome underneath it. The pill keeps them legible over any photograph, and
   the buttons stay 30x30 so a thumb can find one. */
.showcase__dots {
  position: absolute;
  left: 50%;
  bottom: 16px;
  z-index: 3;
  display: flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(20, 18, 15, 0.45);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transform: translateX(-50%);
}

.showcase__dot {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  cursor: pointer;
}

.showcase__dot::before {
  content: "";
  width: 24px;
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.65);
  transition: background var(--transition), width var(--transition);
}

.showcase__dot:hover::before {
  background: #fff;
}

.showcase__dot.is-active::before {
  width: 34px;
  background: var(--accent);
}

.showcase__dot:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.hero__facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
  margin-top: 30px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
}

.fact__value {
  color: var(--accent-text);
  font-family: var(--font-num);
  font-size: 1.5rem;
  font-weight: 700;
}

.fact__label {
  color: var(--ink-faint);
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* 9. Cards ----------------------------------------------------------------- */
.card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  transition: transform var(--transition), border-color var(--transition),
    box-shadow var(--transition);
}

.card:hover {
  border-color: var(--secondary);
  transform: translateY(-3px);
  box-shadow: var(--shadow-sm);
}

.card__media {
  position: relative;
  background: linear-gradient(170deg, var(--surface-2), var(--bg));
  border-bottom: 1px solid var(--line);
}

.card__badges {
  position: absolute;
  top: 12px;
  left: 12px;
  right: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: space-between;
  pointer-events: none;
}

.card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 8px;
  padding: 18px;
}

/* The maker's name is a brand mark, not a structural label, so it stays gold
   while the section labels above each block are purple. */
.card__brand {
  color: var(--accent-text);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.card__title {
  margin: 0;
  font-size: 1.12rem;
  line-height: 1.25;
}

.card__title a {
  color: var(--ink-strong);
}

.card__title a:hover {
  color: var(--secondary-text);
}

.card__meta {
  color: var(--ink-faint);
  font-size: 0.82rem;
}

.card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: auto;
  padding-top: 16px;
}

.bike-art {
  display: block;
  width: 100%;
  height: auto;
}

/* Small category and brand tiles. */
.tile {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  transition: border-color var(--transition), transform var(--transition),
    background var(--transition);
}

.tile:hover {
  border-color: var(--secondary);
  background: var(--surface-2);
  color: var(--ink);
  transform: translateY(-2px);
}

.tile__name {
  color: var(--ink-strong);
  font-size: 1.02rem;
  font-weight: 800;
}

.tile__count {
  color: var(--ink-muted);
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.tile--brand {
  align-items: center;
  justify-content: center;
  min-height: 96px;
  text-align: center;
}

/* 10. Shop: facets and results --------------------------------------------- */

/* ------------------------------------------------------------------------
 * Brand profile.
 *
 * Shown at the top of a single-brand listing: the brand's own panel on the
 * left, the story and the facts on the right. The dark tile is deliberate - it
 * is the same black-and-gold the header uses, so a brand page reads as part of
 * the shop rather than as a page on the manufacturer's site.
 * ---------------------------------------------------------------------- */
.brand-profile {
  display: grid;
  grid-template-columns: minmax(210px, 300px) 1fr;
  gap: 32px;
  align-items: start;
  margin: 0 0 30px;
  padding: 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.brand-profile__id {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px 20px;
  border-radius: var(--radius-sm);
  background: #0b0a0d;
  border: 1px solid rgba(255, 255, 255, 0.14);
}

/* The manufacturer's mark, or the name set in our type when we do not hold
   their file (see the LOGOS note at the top of brands.js). */
.brand-profile__mark {
  display: flex;
  align-items: center;
  min-height: 46px;
  color: var(--accent);
  font-size: clamp(1.5rem, 3.2vw, 2.1rem);
  font-weight: 900;
  letter-spacing: 0.04em;
  line-height: 1;
}

/* The makers' own artwork, in the shop's gold rather than five brand palettes.
   The filter is built from the panel's black upwards: brightness(0) throws the
   artwork's own colour away and leaves a shape, invert + sepia + saturate
   warm that shape, and the hue rotation lands it on the accent's gold. Because
   it starts by discarding colour, it draws white artwork (EMU, GymGear) and
   dark artwork (Mark2, Dallingridge) identically - which is what makes one
   rule work for all five. */
.brand-profile__logo {
  display: block;
  width: auto;
  max-width: 100%;
  height: 46px;
  object-fit: contain;
  filter: brightness(0) invert(0.74) sepia(0.45) saturate(7) hue-rotate(2deg)
    brightness(0.88) contrast(0.92);
}

.brand-profile__legal {
  color: rgba(255, 255, 255, 0.62);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  line-height: 1.4;
}

.brand-profile__body {
  min-width: 0;
}

.brand-profile__body .lede {
  margin-top: 0;
}

.brand-profile__facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 24px;
  margin: 20px 0 22px;
  padding: 18px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.brand-profile__facts dt {
  color: var(--ink-muted);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 3px;
}

.brand-profile__facts dd {
  margin: 0;
  font-weight: 600;
}

.brand-profile__points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 14px;
}

.brand-profile__points li {
  display: grid;
  gap: 3px;
}

.brand-profile__source {
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.shop-layout {
  display: grid;
  grid-template-columns: 290px minmax(0, 1fr);
  align-items: start;
  gap: 34px;
}

.filters {
  position: sticky;
  top: calc(var(--header-h) + 18px);
  max-height: calc(100vh - var(--header-h) - 40px);
  overflow-y: auto;
  padding-right: 6px;
}

.facet {
  border-bottom: 1px solid var(--line);
  padding: 16px 0;
}

.facet:first-of-type {
  padding-top: 0;
}

.facet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--secondary-text);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}

/* ---------------------------------------------------------- shop sections
 * The row of subheadings under the shop title: Shop all, Bikes, Accessories,
 * Upgrades. It wraps rather than scrolling sideways: on a 390px phone the
 * fourth chip sat off-screen with no sign there was more, and a section nobody
 * can see is a section nobody taps. The count sits inside the chip, so an
 * empty one is obvious before it is tapped. */
.group-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 22px;
  padding-bottom: 4px;
}

.group-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  padding: 9px 15px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-soft);
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition),
    color var(--transition);
}

.group-chip:hover {
  border-color: var(--accent);
  color: var(--ink);
}

.group-chip.is-active {
  border-color: var(--accent);
  background: var(--accent);
  color: #15120c;
}

.group-chip__count {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--band-tint);
  color: var(--secondary-text);
  font-size: 0.72rem;
  font-weight: 700;
}

.group-chip.is-active .group-chip__count {
  background: rgba(21, 18, 12, 0.18);
  color: #15120c;
}

/* The three sections of the Shop all view: a heading, how many there are, and
 * the grid. The rule above a section is what makes it read as a subheading
 * rather than a label floating between cards. */
.shop-groups {
  display: block;
}

.shop-group {
  margin-top: 30px;
  padding-top: 26px;
  border-top: 1px solid var(--line);
}

.shop-group:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.shop-group__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 18px;
}

.shop-group__title {
  margin: 0;
  font-size: 1.15rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.shop-group__count {
  color: var(--ink-faint);
  font-size: 0.82rem;
  font-weight: 600;
}

/* The heading over a block of categories inside the facet. */
.facet__subhead {
  margin: 14px 0 4px;
  color: var(--secondary-text);
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transition: opacity var(--transition);
}

.facet__subhead:first-child {
  margin-top: 0;
}

.facet__subhead.is-dimmed {
  opacity: 0.45;
}

.facet__head::after {
  content: "\2212";
  color: var(--ink-muted);
  font-size: 1rem;
}

.facet[data-collapsed="true"] .facet__head::after {
  content: "+";
}

.facet[data-collapsed="true"] .facet__body {
  display: none;
}

.facet__body {
  margin-top: 12px;
}

.facet__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 260px;
  overflow-y: auto;
}

.check {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: 0.88rem;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.check:hover {
  background: var(--surface-2);
  color: var(--ink-strong);
}

.check input {
  width: 17px;
  height: 17px;
  accent-color: var(--accent);
  flex: none;
}

.check__count {
  margin-left: auto;
  color: var(--ink-muted);
  font-size: 0.74rem;
}

.check--sub {
  padding-left: 22px;
  font-size: 0.84rem;
}

.range-field {
  display: block;
  margin-bottom: 14px;
}

.range-field__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.range-field__value {
  color: var(--accent-text);
  font-family: var(--font-num);
  font-weight: 700;
}

input[type="range"] {
  width: 100%;
  height: 22px;
  background: transparent;
  accent-color: var(--accent);
  cursor: pointer;
}

.results-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 20px;
}

.select {
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-strong);
  font-size: 0.85rem;
}

.active-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
}

/* An applied filter, with its own remove button. Purple to match the chip that
   is currently selected - they are two views of the same state. */
.filter-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  border: 1px solid var(--secondary);
  border-radius: 999px;
  background: var(--secondary-wash);
  color: var(--secondary-text);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
}

.filter-pill::after {
  content: "\00d7";
  font-size: 1rem;
  line-height: 1;
}

.filters-toggle {
  display: none;
}

/* The drawer's own header. Hidden on a desktop window, where the panel is
 * always on screen and there is nothing to close. */
.filters__top {
  display: none;
}

.empty-state {
  padding: 56px 24px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  text-align: center;
}

/* Pages of the site that match a search. Above the product grid, on a tinted
 * ground, so a link to Help reads as a different kind of answer from a bike. */
.page-matches {
  margin: 0 0 26px;
  padding: 18px 20px 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}

.page-matches__title {
  margin: 0 0 12px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.page-matches__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.page-match {
  display: block;
  height: 100%;
  padding: 12px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: inherit;
  text-decoration: none;
}

.page-match:hover,
.page-match:focus-visible {
  border-color: var(--primary);
  background: var(--secondary-wash);
}

.page-match__title {
  display: block;
  margin-bottom: 4px;
  color: var(--ink-strong);
  font-weight: 600;
}

.page-match__summary {
  display: block;
  color: var(--ink-faint);
  font-size: 0.85rem;
  line-height: 1.45;
}

.sample-note {
  display: flex;
  gap: 10px;
  padding: 12px 14px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--tint-overlay);
  color: var(--ink-faint);
  font-size: 0.8rem;
}

.sample-note strong {
  color: var(--ink-soft);
}

/* 11. Product detail ------------------------------------------------------- */
.breadcrumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 18px 0;
  color: var(--ink-muted);
  font-size: 0.8rem;
}

.breadcrumbs a {
  color: var(--ink-faint);
}

/* Breadcrumbs are navigation, so they hover purple like every other link. */
.breadcrumbs a:hover {
  color: var(--secondary-text-hover);
}

.breadcrumbs span[aria-hidden] {
  color: var(--line-strong);
}

.product-layout {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  align-items: start;
  gap: 40px;
}

.gallery__main {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(165deg, var(--surface-2), var(--bg));
}

/* The manufacturer's photograph. `contain` rather than `cover`: cropping a
   bike shot cuts the wheels off, and a bike missing its wheels does not sell. */
.gallery__photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: contain;
  padding: 14px;
  border-radius: var(--radius-lg);
}

/* On a card the photo sits on the same tinted panel the illustration used, and
   the tile keeps its own proportions so a row of cards stays even. */
.card__photo {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  padding: 10px;
}

/* The thumbnail strip under the main photograph. Each thumbnail swaps the
   picture above rather than opening a separate view, so they are sized like
   photographs and not like the three-word tiles they replaced. Fixed width:
   two shots should not stretch into two enormous tiles. */
.gallery__thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}

.thumb {
  flex: 0 0 auto;
  width: 88px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.thumb img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
}

.thumb:hover {
  border-color: var(--line-strong);
}

.thumb[aria-pressed="true"] {
  border-color: var(--secondary);
  box-shadow: inset 0 0 0 1px var(--secondary);
}

.buybox {
  position: sticky;
  top: calc(var(--header-h) + 18px);
  padding: 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.buybox__title {
  font-size: 1.9rem;
  line-height: 1.1;
}

.buybox__price-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 14px 0;
}

.size-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.size {
  min-width: 62px;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-size: 0.84rem;
  font-weight: 700;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}

.size:hover {
  border-color: var(--secondary);
}

/* The chosen frame size. Purple, like every other selection. */
.size[aria-pressed="true"] {
  border-color: var(--secondary);
  background: var(--secondary-wash);
  color: var(--secondary-text);
}

.size[data-stock="pre-order"] {
  color: var(--ink-faint);
}

.size__stock {
  display: block;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* The description and the specification, directly under the buy box. The
   description is deliberately short - one summary line, the key points, and
   the long copy folded away - so it reads as a paragraph, not a column. */
.product-info {
  max-width: 72ch;
}

.product-info .trust-list {
  margin-top: 18px;
}

.product-specs {
  margin-top: 44px;
}

/* Deliberately smaller than a section h2: these label the facts, they are not
   competing with the product name above them. */
.product-info__title {
  margin: 0 0 14px;
  font-size: calc(1.3rem * var(--type-scale));
}

.product-info__note {
  margin: 14px 0 0;
}

/* Product information: the parts, each with what it does for the rider, then
   the numbers in the table. Two columns on a wide screen keep it scannable
   without turning the page into an essay. */
.product-notes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 26px 44px;
  margin: 0;
}

.product-notes__item dt {
  margin-bottom: 5px;
  color: var(--ink-strong);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.product-notes__item dd {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.94rem;
}

.product-specs__subhead {
  margin: 40px 0 10px;
  color: var(--ink-faint);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.spec-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.spec-table th,
.spec-table td {
  padding: 11px 0;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}

.spec-table th {
  width: 42%;
  color: var(--ink-faint);
  font-weight: 600;
}

.spec-table td {
  color: var(--ink);
}

.legal-panel {
  padding: 18px;
  border: 1px solid var(--secondary);
  border-radius: var(--radius);
  background: var(--secondary-wash);
}

.legal-panel h3 {
  font-size: 0.86rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.legal-panel ul {
  margin: 0;
  padding-left: 18px;
  font-size: 0.88rem;
  color: var(--secondary-text);
}

.trust-list {
  display: grid;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.88rem;
}

.trust-list li {
  display: flex;
  gap: 10px;
  color: var(--ink-soft);
}

.trust-list li::before {
  content: "\2713";
  color: var(--secondary-text);
  font-weight: 800;
}

/* 12. Basket drawer -------------------------------------------------------- */
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  width: min(420px, 100vw);
  height: 100%;
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow);
  transform: translateX(102%);
  transition: transform 220ms cubic-bezier(0.2, 0.7, 0.3, 1);
  visibility: hidden;
}

.drawer.is-open {
  transform: translateX(0);
  visibility: visible;
}

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 20px;
  border-bottom: 1px solid var(--line);
}

.drawer__head h2 {
  margin: 0;
  font-size: 1rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: 18px 20px;
}

.drawer__foot {
  padding: 18px 20px;
  border-top: 1px solid var(--line);
  background: var(--bg);
}

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(0, 0, 0, 0.65);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition), visibility var(--transition);
}

.backdrop.is-open {
  opacity: 1;
  visibility: visible;
}

.basket-line {
  display: grid;
  grid-template-columns: 76px minmax(0, 1fr);
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}

.basket-line__media {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  overflow: hidden;
}

/* An order for a bike that is no longer in the catalogue has no picture, and
   the name must not be squeezed into the thumbnail column as a result. Used by
   the order status page (pages/order.js). */
.basket-line--plain {
  grid-template-columns: minmax(0, 1fr);
}

/* Same footprint as the drawing it replaces, so a basket row does not change
   height depending on whether the bike has a photograph. */
.basket-line__photo {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: contain;
}

.basket-line__name {
  color: var(--ink-strong);
  font-size: 0.92rem;
  font-weight: 700;
}

.basket-line__meta {
  color: var(--ink-muted);
  font-size: 0.78rem;
}

.basket-line__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
}

.qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.qty button {
  width: 30px;
  height: 30px;
  border: 0;
  background: var(--surface-2);
  color: var(--ink-strong);
  cursor: pointer;
}

.qty button:hover {
  background: var(--surface-3);
  color: var(--accent-text-hover);
}

.qty span {
  min-width: 32px;
  text-align: center;
  font-family: var(--font-num);
  font-size: 0.9rem;
}

.totals {
  display: grid;
  gap: 8px;
  margin-bottom: 14px;
  font-size: 0.9rem;
}

.totals__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  color: var(--ink-soft);
}

.totals__row--grand {
  padding-top: 10px;
  border-top: 1px solid var(--line);
  color: var(--ink-strong);
  font-size: 1.1rem;
  font-weight: 800;
}

/* 13. Forms ---------------------------------------------------------------- */
.field {
  display: block;
  margin-bottom: 16px;
}

.field__label {
  display: block;
  margin-bottom: 6px;
  color: var(--ink-soft);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.input,
.textarea,
.select--full {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-strong);
  outline: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.input:focus,
.textarea:focus,
.select--full:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

.textarea {
  min-height: 120px;
  resize: vertical;
}

.field__hint {
  margin-top: 6px;
  color: var(--ink-muted);
  font-size: 0.76rem;
}

.field__error {
  margin-top: 6px;
  color: var(--danger-text);
  font-size: 0.78rem;
  font-weight: 600;
}

.input[aria-invalid="true"],
.textarea[aria-invalid="true"] {
  border-color: var(--danger);
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 18px;
}

.radio-card {
  display: flex;
  gap: 12px;
  padding: 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}

.radio-card:hover {
  border-color: var(--accent);
}

.radio-card:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-wash);
}

.radio-card input {
  margin-top: 4px;
  accent-color: var(--accent);
}

.checkbox-line {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.checkbox-line input {
  margin-top: 4px;
  accent-color: var(--accent);
}

.form-note {
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-faint);
  font-size: 0.82rem;
}

/* Checkout: the form beside the order summary. Kept in the stylesheet rather
   than set inline by the page script, so the responsive rule below can
   actually override it - an inline grid-template-columns cannot be undone by a
   media query, which is what used to push the summary off the side of a phone. */
.checkout-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
  gap: 40px;
  align-items: start;
  padding-bottom: 64px;
}

/* 14. Content blocks ------------------------------------------------------- */
/* min(220px, 100%) rather than a bare 220px: a grid track will not shrink
   below its minimum, so a fixed minimum makes the page overflow on a very
   narrow screen instead of just getting cramped. */
.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: 22px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.step {
  padding-top: 18px;
  border-top: 2px solid var(--line-strong);
}

.step::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  display: block;
  margin-bottom: 8px;
  color: var(--secondary-text);
  font-family: var(--font-num);
  font-size: 1.1rem;
  font-weight: 700;
}

.faq {
  border-top: 1px solid var(--line);
}

.faq details {
  border-bottom: 1px solid var(--line);
}

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 0;
  color: var(--ink-strong);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: "+";
  color: var(--secondary-text);
  font-size: 1.3rem;
  font-weight: 400;
}

.faq details[open] summary::after {
  content: "\2212";
}

.faq__body {
  padding: 0 0 20px;
  max-width: 78ch;
  color: var(--ink-soft);
  font-size: 0.94rem;
}

.faq__body ul {
  padding-left: 20px;
}

.prose {
  max-width: 72ch;
}

.prose h2 {
  margin-top: 1.6em;
}

.prose h3 {
  margin-top: 1.3em;
}

.prose ul,
.prose ol {
  padding-left: 20px;
  color: var(--ink-soft);
}

.prose li {
  margin-bottom: 0.5em;
}

.prose .callout {
  padding: 16px 18px;
  border-left: 3px solid var(--secondary);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--surface-2);
  color: var(--ink-soft);
  font-size: 0.9rem;
}

.anchor-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 32px;
}

.service-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

/* Founders on the about page. The headshot is a fixed 4:5 box so the two cards
   line up whatever is dropped into them later, and an empty one is visibly
   empty - a dashed frame and the note "Headshot to follow" - instead of a
   grey block that reads like a failed image. */
.team-member__media {
  margin-bottom: 4px;
}

.team-member__photo {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: center 25%;
  border-radius: calc(var(--radius) - 4px);
  background: var(--surface-2);
}

.team-member__photo--empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px dashed var(--line);
  background: var(--surface-2);
}

.team-member__initials {
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--secondary-text);
}

.team-member__note {
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.service-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 11px;
  background: var(--secondary-wash);
  color: var(--secondary-text);
  font-size: 1.2rem;
}

/* A service card that is a link: the whole card is the target, so it lifts and
   takes the accent border the other clickable tiles use. */
.service-card--link {
  color: var(--ink);
  transition: border-color var(--transition), transform var(--transition),
    background var(--transition);
}

.service-card--link:hover {
  border-color: var(--secondary);
  background: var(--surface-2);
  color: var(--ink);
  transform: translateY(-2px);
}

.service-card--link:focus-visible {
  outline: 2px solid var(--secondary-text);
  outline-offset: 2px;
}

.service-card--link h4 {
  color: var(--ink-strong);
}

.service-card--link .tile__go {
  margin-top: auto;
}

.plan {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 24px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.plan--featured {
  border-color: var(--accent);
  background: linear-gradient(180deg, var(--accent-wash), var(--surface) 55%);
}

.plan__price {
  font-family: var(--font-num);
  font-size: 1.9rem;
  font-weight: 700;
  color: var(--ink-strong);
}

.plan ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 8px;
  font-size: 0.88rem;
  color: var(--ink-soft);
}

.plan ul li::before {
  content: "\2713";
  margin-right: 9px;
  color: var(--accent-text);
}

/* A whole band tinted with the brand purple. Gold is reserved for the button
   inside it, so the two colours do not compete. */
.cta-band {
  padding: 44px;
  border: 1px solid var(--secondary);
  border-radius: var(--radius-lg);
  background: linear-gradient(120deg, var(--secondary-wash), var(--surface) 62%);
}

/* 15. Footer --------------------------------------------------------------- */
.site-footer {
  margin-top: 64px;
  padding: 52px 0 26px;
  border-top: 1px solid var(--line);
  background: var(--chrome-bg);
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr repeat(3, 1fr);
  gap: 34px;
}

.footer-col h4 {
  margin-bottom: 14px;
  color: var(--secondary-text);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.footer-col ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
}

.footer-col a,
.footer-col li {
  color: var(--ink-faint);
  font-size: 0.86rem;
}

.footer-col a:hover {
  color: var(--accent-text-hover);
}

/* The three small blocks that sit under a footer column's main list: payment
 * marks, support hours and the social links. They share a heading style with
 * the column itself, and each is spaced off the list above it rather than
 * inheriting that list's own gap. */
.footer-pay,
.footer-hours,
.footer-social {
  margin-top: 22px;
}

.footer-pay__note {
  margin: 10px 0 0;
  max-width: 32ch;
  color: var(--ink-muted);
  font-size: 0.74rem;
  line-height: 1.5;
}

/* Payment marks: the networks' own artwork on a light chip. The chip is what
 * makes one rule work for all five - Visa's blue, Mastercard's red and yellow
 * and Apple Pay's black are all drawn for a pale background, and the footer is
 * black in every theme. Klarna's badge is already pink in the artwork, so it
 * fills its chip instead of sitting on a white one inside a pink one. */
ul.pay-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.pay-badges li {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  min-width: 56px;
  padding: 0 10px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 6px;
  background: #fff;
}

.pay-badges img {
  display: block;
  width: auto;
  height: 19px;
  max-width: 100%;
  object-fit: contain;
}

.pay-badges .pay-badge--fill {
  padding: 0;
  border-color: transparent;
  background: #ffb3c7;
  overflow: hidden;
}

.pay-badges .pay-badge--fill img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Social links as marks. The glyph is the network's own drawing used as a CSS
 * mask, so it paints in currentColor: the icon follows the footer's ink and
 * turns near-black on the gold pill, which an <img> could not do. */
.footer-social ul {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--ink-soft);
  transition: color var(--transition), background var(--transition),
    border-color var(--transition);
}

.social-link:hover,
.social-link:focus-visible {
  border-color: var(--accent);
  background: var(--accent);
  color: #15120c;
}

.social-link__glyph {
  width: 17px;
  height: 17px;
  background: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat;
  mask: var(--glyph) center / contain no-repeat;
}

/* Root-absolute, not relative: the Worker serves this stylesheet from
   /assets/<build>/css/, so "../img/..." would ask for a picture inside the
   build folder that does not exist. Images are not renamed per build, so the
   unversioned path is the one that is always there. */
.social-link--instagram {
  --glyph: url("/assets/img/social/instagram.svg");
}

.social-link--facebook {
  --glyph: url("/assets/img/social/facebook.svg");
}

.social-link--x {
  --glyph: url("/assets/img/social/x.svg");
}

.footer-legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 40px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: 0.78rem;
}

.footer-legal p {
  margin: 0;
}

.footer-vat {
  font-family: var(--font-num);
}

/* 16. Prototype notice, toasts --------------------------------------------- */
.prototype-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 20px;
  background: var(--secondary-fill);
  color: var(--secondary-ink);
  font-size: 0.78rem;
}

.prototype-bar strong {
  flex: none;
  padding: 2px 8px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.28);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.prototype-bar button {
  margin-left: auto;
  border: 0;
  background: none;
  color: var(--secondary-ink);
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
}

.prototype-bar[hidden] {
  display: none;
}

.toasts {
  position: fixed;
  left: 50%;
  bottom: 24px;
  z-index: 120;
  display: grid;
  gap: 10px;
  transform: translateX(-50%);
  width: min(420px, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  padding: 13px 16px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  box-shadow: var(--shadow);
  color: var(--ink-strong);
  font-size: 0.88rem;
  font-weight: 600;
}

.toast--error {
  border-color: var(--danger);
}

/* 17. Brand lab (dev only) ------------------------------------------------- */
.brandlab {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 130;
  width: min(330px, calc(100vw - 32px));
  padding: 18px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface-2);
  box-shadow: var(--shadow);
  font-size: 0.85rem;
}

.brandlab h3 {
  margin: 0 0 4px;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.brandlab__row {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

.brandlab__swatches {
  display: flex;
  gap: 6px;
  margin-top: 12px;
}

.brandlab__swatches span {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  border: 1px solid var(--line-strong);
}

.brandlab select {
  width: 100%;
  margin-top: 10px;
}

.brandlab code {
  font-family: var(--font-num);
  font-size: 0.92em;
  color: inherit;
}

.brandlab kbd {
  padding: 1px 5px;
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  background: var(--surface);
  font-family: var(--font-num);
  font-size: 0.9em;
}

/* Live contrast readout, so a colour choice is never a guess. */
.brandlab__checks {
  display: grid;
  gap: 3px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.brandlab__check {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.74rem;
  color: var(--ink-muted);
}

.brandlab__check .is-ok,
.brandlab__check .is-warn {
  font-family: var(--font-num);
  font-weight: 700;
}

.brandlab__check .is-ok {
  color: var(--ok-text);
}

.brandlab__check .is-warn {
  color: var(--danger-text);
}

/* 18. Responsive ----------------------------------------------------------- */
@media (max-width: 620px) {
  /* There is room to give the name its old weight back once the nav and the
     search box have collapsed into the drawer. */
  /* The lockup is wider than the old two-line text on a phone, and would push
     into the buttons at the other end of the header. */
  .wordmark__logo {
    height: 34px;
  }
}

/* 320px is as narrow as a phone gets - a small Android, an iPhone SE. The
   header's centred lockup needs about 390px (logo, two buttons, the gaps
   between them and the page padding); below that the buttons were pushed off
   the right-hand edge and the whole page scrolled sideways under the finger.
   Left-aligned with the buttons at the far end costs the centred logo and
   nothing else. */
@media (max-width: 340px) {
  .header-top {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .header-top__spacer {
    display: none;
  }

  .header-actions {
    margin-left: auto;
    gap: 6px;
  }

  .icon-btn {
    width: 38px;
    height: 38px;
  }
}

/* The second search box, the one in the menu row. It is the same form as the
   header's, so a visitor on a narrow laptop, a tablet or a phone still has a
   search - the header box is the one that goes when space runs out, and before
   this the shop simply had no search at all below 1200px. Hidden while the
   header box is showing, because two search fields on one screen is a puzzle
   rather than a feature. */
.nav-search {
  display: none;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  padding: 6px 0 6px 8px;
}


.nav-search input {
  width: 210px;
  min-height: 38px;
  padding: 8px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-strong);
}

.nav-search input::placeholder {
  color: var(--ink-muted);
}

@media (max-width: 1200px) {
  /* A two-word wordmark takes about 200px that a seven-letter one did not, so
     the search box goes before the navigation does. The nav itself tightens
     up rather than spilling off the right-hand edge - and the menu row takes
     the search over, so there is still one on screen. */
  .header-search {
    display: none;
  }
  .nav-search {
    display: flex;
  }
  .nav {
    gap: 0;
    margin-left: 6px;
  }
  .nav__link {
    padding: 10px 9px;
  }
}

@media (max-width: 1080px) {
  .hero__inner {
    grid-template-columns: 1fr;
  }

  /* One column, so the frame hugs the photograph instead of stretching around
     it and leaving a card of empty white on either side. */
  .hero__art--landscape {
    max-width: 620px;
    margin: 0 auto;
  }

  .hero__photo--landscape {
    max-width: 600px;
    margin: 0 auto;
  }
  .shop-layout {
    grid-template-columns: 240px minmax(0, 1fr);
    gap: 24px;
  }
  .product-layout,
  .checkout-layout {
    grid-template-columns: 1fr;
    gap: 28px;
  }
  .buybox {
    position: static;
  }
  .footer-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 28px;
  }
  .header-search input {
    width: 130px;
  }
}

@media (max-width: 900px) {
  :root {
    --header-h: 96px;
  }

  /* Navigation collapses into a single drawer on small screens. */
  .nav-toggle {
    display: inline-flex;
  }

  .nav,
  .header-search {
    display: none;
  }

  /* At this width the menu is a drawer rather than a row, so the search goes
     to the top of it - the first thing in the panel, where a phone user looks
     - and takes the full width instead of a 210px field. */
  .nav-search {
    display: flex;
    flex: 0 0 auto;
    /* The drawer is a column, so this puts the search above the seven menu
     * rows instead of below the fold. */
    order: -1;
    margin: 0 0 10px;
    padding: 0;
  }

  .nav-search input {
    width: 100%;
    flex: 1 1 auto;
  }

  /* The nav row exists only to hold the centred menu on a wide screen. On a
     phone the menu lives in the drawer, so the row goes to zero height - but
     it cannot be display:none, because the drawer panel is a child of it. */
  .header-nav {
    border-top: 0;
  }

  .header-nav__inner {
    min-height: 0;
  }

  .nav.is-open {
    display: flex;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    max-height: calc(100vh - var(--header-h));
    overflow-y: auto;
    margin: 0;
    padding: 14px 20px 26px;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow);
  }

  .nav.is-open .nav__link {
    padding: 14px 4px;
    font-size: 0.95rem;
    border-bottom: 1px solid var(--line);
    text-align: left;
  }

  /* The rule goes under the whole row, caret included, rather than stopping
     at the label and leaving a gap beside it. */
  .nav.is-open .nav__item {
    border-bottom: 1px solid var(--line);
  }

  .nav.is-open .nav__item > .nav__link {
    border-bottom: 0;
  }

  .nav.is-open .nav__caret {
    width: 40px;
    height: 48px;
    margin-left: 0;
    font-size: 0.75rem;
  }

  .mega,
  .mega.is-open {
    position: static;
    display: none;
    padding: 14px 0 4px;
    border: 0;
    box-shadow: none;
    background: transparent;
    /* In the drawer the panel is a block in the page rather than a layer over
       it, so it must not scroll inside itself: the drawer scrolls as one. */
    max-height: none;
    overflow: visible;
  }

  .mega.is-open {
    display: block;
  }

  .mega__grid {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  /* The drawer already has its own 20px inset; a wrap inside the panel would
     add a second one and squeeze the categories into a narrow column. */
  .mega .wrap {
    max-width: none;
    padding: 0;
  }

  /* The panel is a directory, not a page. The variant links and the popular
     searches are one tap away on the shop itself, and inside the drawer they
     pushed About Us, Warranty and the rest two screens down. */
  .mega__subs,
  .mega__foot {
    display: none;
  }

  .shop-layout {
    grid-template-columns: 1fr;
  }

  /* The section strip. On a phone these four buttons are the only way to move
   * between Bikes, Accessories and Upgrades - the mega menu that does the same
   * job on a laptop is folded into the hamburger - and the Shop all list runs
   * to thousands of pixels. So the strip keeps to one row, scrolling sideways,
   * and stays pinned under the header: the sections are reachable from
   * anywhere in the list, not only from the top of it.
   *
   * The negative margin bleeds it to the edges of the screen so the last chip
   * can scroll clear of the padding instead of stopping short of it. */
  .group-bar {
    position: sticky;
    top: var(--header-h);
    z-index: 40;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    margin: 0 -20px 18px;
    padding: 10px 20px;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    scrollbar-width: none;
  }

  .group-bar::-webkit-scrollbar {
    height: 0;
  }

  /* With the drawer open the strip would sit on top of the drawer's own
   * header, which carries the only Close button within reach. One sticky bar
   * at a time: the drawer's. */
  body.filters-open .group-bar {
    position: static;
  }

  .brand-profile {
    grid-template-columns: 1fr;
    gap: 20px;
    padding: 20px;
  }

  .filters {
    position: static;
    display: none;
    max-height: none;
    /* The desktop sidebar scrolls on its own; on a phone the page is the
     * scroll container, and `visible` is also what lets the drawer's sticky
     * header stick to the viewport rather than to this box. */
    overflow: visible;
    padding: 20px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
  }

  .filters.is-open {
    display: block;
  }

  /* A way out at the top of the panel: the toggle that opened it sits below
   * the whole list, a screen and a half away. It sticks, so the way out is
   * still there after scrolling to the bottom of the categories. */
  .filters__top {
    position: sticky;
    /* Under the site header, which is itself sticky: pinned at 0 the way out
     * sits behind the black bar and cannot be hit. */
    top: var(--header-h);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
  }

  /* One scroll for the whole drawer. The 260px window the facet list uses on
   * a desktop sidebar turns fourteen categories into a scroll-inside-a-scroll
   * on a phone, and the outer one is the one a thumb expects. */
  .filters .facet__list {
    max-height: none;
    overflow: visible;
  }

  .filters__title {
    color: var(--secondary-text);
    font-size: 0.74rem;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }

  .filters-toggle {
    display: inline-flex;
  }

  .form-grid,
  .grid--2,
  .grid--3,
  .grid--4,
  .product-notes {
    grid-template-columns: 1fr;
  }

  .hero {
    padding: 44px 0 36px;
  }

  .cta-band {
    padding: 28px;
  }
}

@media (max-width: 620px) {
  .grid--products {
    grid-template-columns: 1fr;
  }
  .hero__facts {
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .footer-grid {
    grid-template-columns: 1fr;
  }
  .section {
    padding: 44px 0;
  }
  .promo-bar__item {
    font-size: 0.62rem;
    letter-spacing: 0.06em;
  }

  /* Two columns of facts on a phone gives each one about 140px, which turns a
     warranty sentence into a ladder of four-word lines. */
  .brand-profile__facts {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .header-actions .btn span.label {
    display: none;
  }
}

/* 19. Accessibility and motion --------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 12px;
  top: -60px;
  z-index: 200;
  padding: 10px 16px;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 800;
  border-radius: var(--radius-sm);
  transition: top var(--transition);
}

.skip-link:focus {
  top: 12px;
}

/* Applied for a single frame by the brand lab (assets/js/devpanel.js) while it
   rewrites the palette variables. Re-skinning the site should land instantly
   rather than animating hundreds of elements at once - and without this, a
   transition whose duration itself comes from a custom property (see .btn)
   can hold the previous colour after the variables change. */
.no-transitions,
.no-transitions *,
.no-transitions *::before,
.no-transitions *::after {
  transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* 20. Home: departments, wide picture band and the company block ----------- */

/* Department cards carry a picture: the cycling half a life shot, the gym half
   a product cut-out. It runs down the left of the card rather than across the
   top, because the gym's best-known product is a 460x613 rig: contained in a
   tall column it reads as a product shot, and cropped into a wide band it
   would lose its top and its feet. */
.tile--dept .tile__media {
  display: block;
  flex: 0 0 40%;
  overflow: hidden;
  margin: 0;
  border-radius: calc(var(--radius) - 4px);
  background: var(--surface-2);
}

.tile--dept .tile__photo--dept {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 200px;
  object-fit: cover;
}

.tile--dept .tile__photo--cutout {
  object-fit: contain;
  padding: 12px 8px;
}

.tile--dept .tile__body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}


/* A labelled half of "shop by type". The rule above the second heading is
   what makes the section read as two departments rather than one long list. */
.type-group + .type-group {
  margin-top: 38px;
  padding-top: 30px;
  border-top: 1px solid var(--line);
}

.type-group__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 18px;
}

.type-group__head h3 {
  margin: 0;
  font-size: calc(1.32rem * var(--type-scale));
}

/* Department tiles. All one size on purpose: the departments are given equal
   weight, so nothing implies a favourite. */
.tile--dept {
  flex-direction: row;
  align-items: stretch;
  gap: 18px;
  padding: 16px;
  min-height: 0;
}

/* One column on a phone: two photos side by side in a 40% column would leave
   about six characters of room for the words. This sits after the rule above
   rather than with the other department-card rules at the top of the section,
   because a media query does not win on specificity - only on being later. */
@media (max-width: 719px) {
  .tile--dept {
    flex-direction: column;
  }

  .tile--dept .tile__media {
    flex: 0 0 auto;
  }

  .tile--dept .tile__photo--dept {
    height: 220px;
    min-height: 0;
  }
}

.tile__go {
  margin-top: auto;
  color: var(--accent-text);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

/* The one-line description under a department's name, so a tile says what is
   behind it rather than only what it is called. Body colour, not the purple
   secondary: that one is for links and eyebrows, and a paragraph of it reads
   like a warning. */
.tile__note {
  color: var(--ink-soft);
  font-size: 0.9rem;
  line-height: 1.45;
}

.tile--soon {
  border-style: dashed;
  background: transparent;
}

.tile--soon .tile__go {
  color: var(--secondary-text);
}

/* Picture tiles. The photograph is the thing to click, so it fills the top of
   the card, holds a steady 4:3 shape, and lifts a little under the pointer. */
.tile--photo {
  gap: 8px;
  padding: 12px;
  overflow: hidden;
}

.tile--photo .tile__media {
  overflow: hidden;
  border-radius: calc(var(--radius) - 4px);
  background: var(--surface-2);
}

.tile--photo .tile__photo {
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform 500ms ease;
}

.tile--photo:hover .tile__photo {
  transform: scale(1.04);
}

.tile--photo .tile__name {
  font-size: 1.14rem;
}

/* One arrow treatment for every card that is one big link. */
.tile--photo .tile__go::after,
.service-card--link .tile__go::after {
  content: "\2192";
  display: inline-block;
  margin-left: 6px;
  transition: transform var(--transition);
}

.tile--photo:hover .tile__go::after,
.service-card--link:hover .tile__go::after {
  transform: translateX(3px);
}

/* Brand tiles: the photograph is the evidence that we actually stock the
   brand, so it sits above the name rather than beside it. */
.tile--brand {
  gap: 8px;
  padding: 14px 18px 18px;
}

.tile__media {
  display: block;
  width: 100%;
}

.tile__photo {
  display: block;
  width: 100%;
  height: 148px;
  object-fit: contain;
}

/* The full-width band. The photograph fills the section and a left-to-right
   wash of the page background keeps the copy readable against it in all three
   themes without needing a colour that only works in one of them. The wash
   holds at full strength under the paragraph and only then fades: a photograph
   has far more detail behind the text than the drawing it replaced. */
.band {
  position: relative;
  overflow: hidden;
  min-height: 440px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.band__art-slot {
  position: absolute;
  inset: 0;
}

.band__art-slot svg,
.band__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* The wash covers the left of this band, so the half of the picture anybody
   actually sees is the right-hand one. The towpath photograph has the rider on
   the left and the moored boats on the right, which meant the band read as a
   picture of boats with a bike hidden behind the text. Mirrored, the bike is in
   the visible half and the boats fall behind the wash where the words are.
   Nothing in frame is text or a logo, so a mirror costs nothing but framing. */
/* Biased a little lower than the middle, so the wheels and the frame are in the
   band rather than only the rider's shoulders. Scoped to the shipped file, so a
   photograph uploaded in the back office keeps its own framing and is never
   mirrored - one with a sign or a name on it would read backwards. */
.band__photo[src$="towpath-ride.jpg"] {
  transform: scaleX(-1);
  object-position: center 62%;
}

/* The wash protects the copy, so it is measured against the copy rather than
   in one fixed width. The paragraph is a fixed 34rem column inside the centred
   wrap, so on a big monitor it starts further right: a flat 620px wash left the
   ends of the lines - and on a wide screen most of the paragraph - sitting on
   the photograph, which is what made it hard to read. The wrap is at most
   1240px, which is 50% of the viewport less 620px, so the first stop is the
   wider of 620px and 50%: that always clears the column. The second stop sets
   how quickly the picture takes over past it. */
.band__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    var(--bg) 0,
    var(--bg) max(620px, 50%),
    transparent max(920px, calc(50% + 360px))
  );
}

.band__inner {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  min-height: 440px;
  padding-top: 56px;
  padding-bottom: 56px;
}

.band__copy {
  max-width: 34rem;
}

.band__copy h2 {
  margin-bottom: 10px;
}

.band__copy p {
  margin: 0;
  color: var(--ink);
}

/* A second sentence gets its own paragraph and its own breath. */
.band__copy p + p {
  margin-top: 14px;
}

.band__copy .btn {
  margin-top: 18px;
}

/* The company block. */
.company__media {
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(160deg, var(--surface), var(--bg));
}

.company__art,
.company__photo {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}

.company__photo {
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* The company picture is a tall photograph with the bikes standing on the
   pavement at the bottom of the frame. A centred 4:3 window would cut their
   wheels, so this one is framed from the bottom instead. Named by file, so a
   photograph uploaded in the back office keeps its own framing. */
.company__photo[src$="shop-front-portrait.jpg"] {
  object-position: center bottom;
}

.company__facts {
  display: grid;
  gap: 10px 22px;
  margin: 22px 0;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
}

.company__facts dt {
  color: var(--secondary-text);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.company__facts dd {
  margin: 2px 0 0;
  color: var(--ink-strong);
  font-weight: 700;
}

.company__lists {
  gap: 34px 44px;
  margin-top: 40px;
}

.company__block h3 {
  margin-bottom: 14px;
  font-size: 1.12rem;
}

.company__points {
  display: grid;
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.company__points li {
  display: grid;
  gap: 4px;
  padding-left: 18px;
  border-left: 3px solid var(--accent);
}

.company__points strong {
  color: var(--ink-strong);
}

/* Outstanding details. Deliberately not styled to look like a warning banner -
   it is a build note for whoever finishes the site, not a customer message. */
.pending-note {
  margin-top: 40px;
  padding: 20px 22px;
  border: 1px dashed var(--secondary);
  border-radius: var(--radius);
  background: var(--secondary-wash);
  color: var(--ink);
}

.pending-note > strong {
  display: block;
  margin-bottom: 8px;
  color: var(--ink-strong);
}

.pending-note ul {
  margin: 0;
  padding-left: 20px;
}

/* 20. Partner programme and portal ----------------------------------------
 * The programme page uses the shop's own cards and fields. The portal adds
 * two things the storefront does not need: a row of figures and a small data
 * table. Both are built from the same tokens, so they follow every theme.
 * ------------------------------------------------------------------------- */

/* The drawn panel in the programme page's hero.
 *
 * It replaced a stock photograph of four riders who had nothing to do with the
 * shop. This is drawn instead: the two rates and the one balance, in the brand's
 * black frame - the same black the header and footer use - so the fold carries
 * the shop's own colours rather than borrowed faces, and there is nothing that
 * needs a caption to be honest about.
 *
 * Like .site-header, it redefines the content tokens inside its own scope. The
 * frame is black in every theme, so white-on-white would otherwise make this
 * disappear on the pale default; and gold goes back to being gold-as-text here,
 * because that is the readable version of it against #0a0a0a.
 */
.partner-board {
  --ink: #f2f1ed;
  --ink-strong: #ffffff;
  --ink-muted: rgba(255, 255, 255, 0.56);
  --line: rgba(255, 255, 255, 0.14);
  --accent-text: var(--accent);

  display: grid;
  gap: 18px;
  height: 100%;
  align-content: space-between;
  padding: 26px 26px 22px;
  border-radius: calc(var(--radius-lg) - 6px);
  background: var(--chrome-bg);
  color: var(--ink);
}

.partner-board__eyebrow {
  margin: 0;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.partner-board__tracks {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.partner-board__track {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 14px;
  padding: 14px 0;
  border-top: 1px solid var(--line);
}

.partner-board__track:first-child {
  border-top: 0;
  padding-top: 4px;
}

.partner-board__label {
  font-weight: 600;
  color: var(--ink-strong);
}

.partner-board__basis {
  display: block;
  margin-top: 3px;
  font-size: 0.8rem;
  color: var(--ink-muted);
}

.partner-board__rate {
  font-family: var(--font-num);
  font-size: 1.9rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent-text);
  white-space: nowrap;
}

.partner-board__foot {
  margin: 0;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.partner-board__foot strong {
  color: var(--ink);
  font-weight: 600;
}

.partner-apply {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px;
}

.partner-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
}

.partner-stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 16px 18px;
}

.partner-stat__label {
  display: block;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.partner-stat__value {
  display: block;
  margin-top: 6px;
  font-family: var(--font-num);
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--ink-strong);
}

.partner-stat__hint {
  display: block;
  margin-top: 4px;
  font-size: 0.78rem;
  color: var(--ink-muted);
}

.partner-table-wrap {
  overflow-x: auto;
}

.partner-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.partner-table th,
.partner-table td {
  padding: 10px 8px;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}

.partner-table th {
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  font-weight: 600;
}

.partner-table td.num {
  text-align: right;
  font-family: var(--font-num);
  white-space: nowrap;
}

.partner-table tbody tr:last-child td {
  border-bottom: 0;
}

.partner-tag {
  display: inline-block;
  padding: 2px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--ink-muted);
  white-space: nowrap;
}

.partner-tag--good {
  color: var(--accent-text);
  border-color: var(--accent-text);
}

.partner-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 18px;
}

.partner-heading h1,
.partner-heading h2 {
  margin: 0;
}

/* The tier ladder. Three steps, with the one the partner is on lifted out of
 * the row; the others stay quiet so the current rate is what they read first. */
.partner-ladder {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin: 16px 0;
}

.partner-ladder__step {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  background: var(--bg-alt);
}

.partner-ladder__step.is-current {
  border-color: var(--accent-text);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--accent-text);
}

.partner-ladder__rate {
  display: block;
  font-family: var(--font-num);
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--ink-strong);
}

.partner-ladder__name {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-top: 2px;
}

.partner-ladder__need {
  display: block;
  margin-top: 8px;
  font-size: 0.78rem;
  color: var(--ink-muted);
}

.partner-progress {
  height: 8px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
  margin: 10px 0 8px;
}

.partner-progress span {
  display: block;
  height: 100%;
  background: var(--accent-text);
  border-radius: inherit;
}

.partner-bonus-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
}

.partner-bonus {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 16px 18px;
  background: var(--bg-alt);
}

.partner-bonus__amount {
  display: block;
  font-family: var(--font-num);
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--accent-text);
}

.partner-bonus__product {
  display: block;
  margin-top: 2px;
  font-weight: 700;
  color: var(--ink-strong);
}

.partner-bonus__meta {
  display: block;
  margin-top: 6px;
  font-size: 0.8rem;
  color: var(--ink-muted);
}

@media (max-width: 640px) {
  .partner-ladder {
    grid-template-columns: 1fr;
  }
}

/* The portal's rooms. The tab row is a row of buttons rather than links: it
 * switches panels on one page, so it must not look like site navigation and
 * must not change the address. A room the shop has not opened for this account
 * is not drawn at all, so there is no disabled state to style. */
.partner-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 18px 0 20px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 10px;
}

.partner-tab {
  appearance: none;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: 999px;
  cursor: pointer;
}

.partner-tab:hover {
  color: var(--ink);
  background: var(--surface);
}

.partner-tab.is-active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--background);
}

.partner-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Arrivals by day. Bars rather than a chart library: thirty rectangles and a
 * title each is all this needs, and it stays readable in every theme. */
.partner-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 120px;
  padding: 10px 0 0;
  border-bottom: 1px solid var(--line);
}

.partner-bar {
  flex: 1 1 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  min-width: 3px;
}

.partner-bar > span {
  display: block;
  width: 100%;
  min-height: 2px;
  background: var(--secondary);
  border-radius: 2px 2px 0 0;
  opacity: 0.85;
}

.partner-bar:hover > span {
  opacity: 1;
  background: var(--accent);
}

/* One conversation in the partner's own inbox: a summary line that opens to
 * the messages, exactly like the shop's own mailbox does. */
.partner-mail {
  border-bottom: 1px solid var(--line);
  padding: 10px 0;
}

.partner-mail:last-child {
  border-bottom: 0;
}

.partner-mail summary {
  cursor: pointer;
  font-size: 0.92rem;
}

.partner-mail summary strong {
  font-weight: 600;
}

@media (max-width: 640px) {
  .partner-apply {
    padding: 20px;
  }

  .partner-tabs {
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
  }

  .partner-bars {
    height: 90px;
  }
}

@media (max-width: 760px) {
  /* A photograph cannot sit behind the copy on a phone: the whole image is
     cropped into the same band the paragraph fills, and the paragraph ends up
     on the darkest part of the frame. So the picture goes above the copy at a
     fixed height and the wash is no longer needed. */
  .band {
    min-height: 0;
  }

  .band__art-slot {
    position: relative;
    inset: auto;
    height: 230px;
  }

  .band__scrim {
    display: none;
  }

  .band__inner {
    min-height: 0;
  }

  .band__copy {
    max-width: none;
  }
}

/* ------------------------------------------------ partner portal: the details
 *
 * The supplier directory grew a second half - the trade price, the shop's
 * margin and the partner's share, per product - and the mailbox grew files.
 * Both are dense, so they are drawn as disclosure panels and small chips rather
 * than more stacked sections: the page stays readable at a glance and the
 * arithmetic is one click away when it is wanted.
 */

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.partner-chip {
  display: inline-block;
  padding: 2px 8px;
  margin: 0 2px 2px 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* A brand the shop does not carry yet: still worth recording, just not yet
   earning. Quiet rather than alarming. */
.partner-chip--quiet {
  border-style: dashed;
  color: var(--muted);
}

/* ------------------------------------------------------ the partner stock list
 *
 * The range as a rack of cards, then one product opened. The commission is the
 * loudest thing on a card because it is the only number a partner came here
 * for; the price sits under it, quieter. Nothing on this screen shows the
 * shop's trade price - see the endpoint's comment in server/lib/affiliates.mjs.
 */

.partner-stock__intro {
  margin: 0 0 16px;
  max-width: 68ch;
}

.partner-stock__tools {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin: 0 0 6px;
}

.partner-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.partner-chips .partner-chip {
  background: var(--surface);
  cursor: pointer;
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 600;
  font-family: inherit;
  color: var(--ink-soft);
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.partner-chips .partner-chip:hover {
  border-color: var(--line-strong);
  color: var(--ink-strong);
}

.partner-chips .partner-chip.is-active {
  background: var(--ink-strong);
  border-color: var(--ink-strong);
  color: var(--surface);
}

.partner-chips .partner-chip:focus-visible,
.partner-card:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
}

.partner-stock__sort .input {
  min-width: 13rem;
  padding: 8px 10px;
  font-size: 0.85rem;
}

.partner-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 16px;
  margin: 12px 0 0;
}

.partner-card {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.partner-card:hover {
  border-color: var(--line-strong);
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.08);
}

.partner-card__img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--tint-overlay);
}

.partner-card__img--none {
  display: grid;
  place-items: center;
  padding: 12px;
  text-align: center;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.partner-card__body {
  display: block;
  padding: 12px 14px 14px;
}

.partner-card__brand {
  display: block;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.partner-card__name {
  display: block;
  margin-top: 3px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink-strong);
}

.partner-card__price {
  display: block;
  margin-top: 6px;
  font-family: var(--font-num);
  color: var(--ink-soft);
}

.partner-card__earn {
  display: block;
  margin-top: 8px;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

.partner-card__earn strong {
  font-family: var(--font-num);
  font-size: 1.02rem;
  color: var(--accent-text);
}

.partner-card__earn-cell {
  font-weight: 600;
  color: var(--accent-text);
}

.partner-product__back {
  margin: 0 0 14px;
}

.partner-product {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
}

.partner-product__top {
  display: grid;
  grid-template-columns: minmax(220px, 320px) 1fr;
  gap: 20px;
  align-items: start;
}

.partner-product__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--tint-overlay);
}

.partner-product__brand {
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.partner-product__head h3 {
  margin: 4px 0 0;
  font-size: 1.35rem;
}

.partner-product__price {
  margin: 8px 0 12px;
  font-family: var(--font-num);
  font-size: 1.05rem;
  color: var(--ink-soft);
}

.partner-product__earn {
  display: grid;
  gap: 2px;
  padding: 14px 16px;
  border: 1px solid var(--accent-text);
  border-radius: var(--radius-sm);
  background: var(--tint-overlay);
}

.partner-product__earn-label {
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.partner-product__earn-value {
  font-family: var(--font-num);
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--accent-text);
}

.partner-product__earn-note {
  font-size: 0.78rem;
  color: var(--ink-muted);
}

.partner-product__link {
  margin-top: 14px;
}

.partner-product__link-label {
  display: block;
  margin-bottom: 6px;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.partner-product__link-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.partner-product__link-input {
  flex: 1 1 260px;
  min-width: 0;
}

.partner-product__body {
  margin-top: 18px;
  max-width: 70ch;
}

.partner-product__body p {
  margin: 0 0 10px;
}

.partner-product__list {
  margin: 10px 0 0;
  padding-left: 18px;
}

.partner-product__list li {
  margin-bottom: 4px;
}

.partner-product__subhead {
  margin: 22px 0 8px;
  font-size: 1rem;
}

@media (max-width: 720px) {
  .partner-product {
    padding: 14px;
  }

  .partner-product__top {
    grid-template-columns: 1fr;
  }

  .partner-cards {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  }
}

.partner-table__caption {
  caption-side: top;
  text-align: left;
  padding: 0 0 8px;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted);
}

.partner-disclosure {
  border: 1px solid var(--line);
  border-radius: 10px;
  margin: 0 0 10px;
  background: rgba(0, 0, 0, 0.02);
}

.partner-disclosure > summary {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 12px 14px;
  cursor: pointer;
  font-size: 0.92rem;
}

.partner-disclosure > summary::-webkit-details-marker {
  color: var(--muted);
}

.partner-disclosure[open] > summary {
  border-bottom: 1px solid var(--line);
}

.partner-disclosure__body {
  padding: 12px 14px 14px;
}

.partner-disclosure__body .partner-table th:first-child,
.partner-disclosure__body .partner-table td:first-child {
  padding-left: 0;
}

/* The plain-English note above the numbered terms: a quiet aside rather than a
   clause, so the contract itself still reads as a list. */
.partner-terms__note {
  margin: 0 0 18px;
  padding: 12px 14px;
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--tint-overlay);
  font-size: 0.92rem;
  line-height: 1.55;
}

/* The programme terms. Numbered, spaced, and read as prose rather than as a
   wall of small print - the acceptance box points here, so it has to be usable. */
.partner-terms {
  margin: 0 0 18px;
  padding-left: 1.3rem;
  display: grid;
  gap: 10px;
}

.partner-terms li {
  font-size: 0.95rem;
  line-height: 1.55;
}

.partner-terms li::marker {
  color: var(--accent);
  font-weight: 600;
}

.partner-fieldset {
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}

.partner-fieldset > legend {
  padding: 0;
  margin: 0 0 12px;
  font-size: 1.05rem;
  font-weight: 600;
}

/* One message inside a conversation. Incoming and outgoing read differently,
   because "what did I say" is the question people actually open an inbox with. */
/* ---------------------------------------------------------------- zmail
   The partner's inbox, laid out the way Zoho lays one out: folders down the
   left, the list in the middle, the message on the right. Three columns on a
   desktop; on a phone it is one column, and choosing a message shows it in
   place of the list (with Back to get out). */
.zmail {
  display: grid;
  grid-template-columns: 186px minmax(230px, 320px) minmax(0, 1fr);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
  min-height: 460px;
}

/* The narrowed-inbox notice, under the search box in the list column: it
   describes what that column is showing, so it travels with its head and keeps
   the same side padding as everything else in the column. */
.zmail__scope {
  margin: 0;
  padding: 0 12px 10px;
  line-height: 1.45;
}

.zmail__rail {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 12px;
  background: var(--surface-2);
  border-right: 1px solid var(--line);
}

.zmail__compose {
  width: 100%;
}

.zmail__folders {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.zmail__folder {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: inherit;
  font: inherit;
  font-size: 0.9rem;
  text-align: left;
  cursor: pointer;
}

.zmail__folder:hover {
  background: var(--surface-3);
}

.zmail__folder.is-active {
  background: var(--surface);
  box-shadow: inset 2px 0 0 var(--accent);
  font-weight: 600;
}

.zmail__count {
  font-size: 0.75rem;
  color: var(--muted);
}

.zmail__badge {
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  text-align: center;
}

.zmail__refresh {
  margin-top: auto;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: inherit;
  font: inherit;
  font-size: 0.82rem;
  cursor: pointer;
}

.zmail__list {
  border-right: 1px solid var(--line);
  overflow-y: auto;
  max-height: 620px;
}

/* The head and the search box travel together and stay at the top of the
   list while the rows scroll under them. */
.zmail__list-top {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.zmail__list-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 14px 8px;
  background: var(--surface);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.zmail__search {
  display: block;
  padding: 0 12px 10px;
}

.zmail__search .input {
  width: 100%;
  padding: 7px 10px;
  font-size: 0.83rem;
}

.zmail__row-wrap {
  position: relative;
}

.zmail__row {
  display: block;
  width: 100%;
  /* Room on the right for the star, so a subject never runs under it. */
  padding: 11px 40px 11px 14px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  position: relative;
}

.zmail__row:hover {
  background: var(--tint-overlay);
}

.zmail__row.is-active {
  background: var(--tint-overlay);
  box-shadow: inset 3px 0 0 var(--accent);
}

.zmail__row-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

/* The flag on a row. Gold when it is on, quiet when it is not, and always the
   same size so the rows do not shuffle as they are flagged. */
.zmail__star {
  position: absolute;
  right: 8px;
  bottom: 8px;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
}

.zmail__star:hover,
.zmail__star:focus-visible {
  background: var(--surface-3);
  color: var(--accent);
}

.zmail__star.is-on,
.zmail__flag.is-on {
  color: var(--accent);
}

.zmail__who {
  font-size: 0.86rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zmail__row.is-unread .zmail__who,
.zmail__row.is-unread .zmail__subject {
  font-weight: 700;
}

.zmail__row.is-unread .zmail__who::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--accent);
  vertical-align: middle;
}

.zmail__when {
  flex: none;
  font-size: 0.72rem;
  color: var(--muted);
}

.zmail__subject,
.zmail__snippet {
  display: block;
  margin-top: 2px;
  font-size: 0.83rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zmail__snippet {
  color: var(--muted);
}

.zmail__clip {
  display: inline-block;
  margin-top: 4px;
  padding: 1px 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.68rem;
  color: var(--muted);
}

.zmail__pane {
  padding: 16px 18px;
  overflow-y: auto;
  max-height: 620px;
}

.zmail__pane-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}

.zmail__pane-title {
  flex: 1 1 200px;
  min-width: 0;
}

.zmail__pane-title h3 {
  margin: 0;
  font-size: 1.05rem;
  overflow-wrap: anywhere;
}

.zmail__pane-title p {
  margin: 2px 0 0;
}

.zmail__back {
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  padding: 2px 0;
  display: none;
}

.zmail__message {
  margin: 0 0 14px;
  padding: 10px 12px;
  border-left: 3px solid var(--line);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--tint-overlay);
}

.zmail__message[data-out] {
  border-left-color: var(--accent);
}

.zmail__message-head {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: baseline;
  margin: 0 0 6px;
  font-size: 0.76rem;
  color: var(--muted);
}

.zmail__message-head strong {
  color: inherit;
  font-size: 0.82rem;
}

.zmail__message-body {
  font-size: 0.92rem;
  line-height: 1.55;
  overflow-wrap: anywhere;
}

.zmail__placeholder {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  padding: 24px 4px;
}

.zmail__none {
  margin: 0;
  padding: 18px 14px;
  font-size: 0.85rem;
  color: var(--muted);
}

.zmail__form .field + .field {
  margin-top: 12px;
}

/* One column on a phone. The list and the reading pane take turns, and the
   Back button is the way out of a message. */
@media (max-width: 900px) {
  .zmail {
    grid-template-columns: 1fr;
    min-height: 0;
  }

  .zmail__rail {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding: 10px 12px;
  }

  .zmail__compose {
    width: auto;
  }

  .zmail__folders {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 4px;
  }

  .zmail__folder.is-active {
    box-shadow: inset 0 -2px 0 var(--accent);
  }

  .zmail__refresh {
    margin-left: auto;
  }

  .zmail__list,
  .zmail__pane {
    max-height: none;
    border-right: 0;
  }

  .zmail.is-reading .zmail__list {
    display: none;
  }

  .zmail:not(.is-reading) .zmail__pane {
    display: none;
  }

  .zmail__back {
    display: inline-flex;
  }
}

.partner-message {
  margin: 0 0 14px;
  padding: 10px 12px;
  border-left: 3px solid var(--line);
  border-radius: 0 8px 8px 0;
  background: rgba(0, 0, 0, 0.015);
}

.partner-message[data-out] {
  border-left-color: var(--accent);
}

.partner-attachments {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.partner-attachments li {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-width: 160px;
  font-size: 0.8rem;
  overflow-wrap: anywhere;
}

.partner-attachment__picture {
  display: block;
  line-height: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}

.partner-attachment__picture img {
  display: block;
  width: 160px;
  height: 120px;
  object-fit: cover;
}

/* The signature the Worker will put under the message, shown in the composer as
   the recipient will read it - on white paper, because that is what an email is.
   A partner's address signs as them, so this is the place to see that it does. */
.partner-signature {
  margin: 4px 0 14px;
  padding: 12px 14px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
}

.partner-signature__label {
  margin: 0 0 10px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.partner-signature__label span {
  margin-left: 8px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0.8;
}

.partner-signature__body {
  overflow-x: auto;
}

.partner-signature__body img {
  max-width: 100%;
  height: auto;
}

.partner-filelist {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 14px;
}

.partner-file {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 0.8rem;
  background: var(--paper, #fff);
}

.partner-file__remove {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 0 0 2px;
  color: var(--muted);
}

.partner-file__remove:hover,
.partner-file__remove:focus-visible {
  color: var(--accent);
}

/* Anything the partner can open or press must show where the keyboard is. */
.partner-apply a:focus-visible,
.partner-apply button:focus-visible,
.partner-apply summary:focus-visible,
.partner-apply input:focus-visible,
.partner-apply select:focus-visible,
.partner-apply textarea:focus-visible,
.partner-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.partner-apply input[type='file'] {
  padding: 8px;
  font-size: 0.85rem;
}

@media (max-width: 640px) {
  .partner-disclosure > summary {
    flex-direction: column;
    gap: 4px;
  }

  .partner-attachment__picture img {
    width: 120px;
    height: 90px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .partner-apply * {
    scroll-behavior: auto !important;
  }
}
/* ---------------------------------------------------------- partner portal
 *
 * The portal is a working tool, not a shop page, so it gets its own skin rather
 * than the shop's marketing rhythm: the brand's black frame at the top carrying
 * the partner's name, code, rate and status; cream paper below it; white panels
 * for the work; and gold only where something is money or is selected.
 *
 * Everything here is scoped to `body[data-page="affiliate"]` on purpose. The
 * programme page at /affiliates borrows the same `.partner-apply` and
 * `.partner-table` classes for its application form, and restyling those
 * globally would change a page that was not part of this.
 */

body[data-page="affiliate"] .portal-wrap {
  max-width: 1140px;
  padding: 26px 0 76px;
}

/* The frame. Light ink is hard-coded rather than taken from the content
 * tokens: the band is near-black in every theme, so the page's own ink colours
 * would disappear on it. Same reasoning as .partner-board. */
body[data-page="affiliate"] .portal-head {
  --head-ink: #f5f3ee;
  --head-muted: rgba(245, 243, 238, 0.62);
  --head-line: rgba(255, 255, 255, 0.14);

  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  padding: 30px 32px 28px;
  border-radius: var(--radius-lg);
  color: var(--head-ink);
  background:
    radial-gradient(120% 150% at 100% 0%, rgba(212, 175, 55, 0.18), transparent 55%),
    var(--chrome-bg);
}

body[data-page="affiliate"] .portal-head__eyebrow {
  display: block;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}

body[data-page="affiliate"] .portal-head h1 {
  margin: 8px 0 0;
  font-size: clamp(1.5rem, 1rem + 1.6vw, 2.15rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: #ffffff;
}

body[data-page="affiliate"] .portal-head__meta {
  margin: 10px 0 0;
  font-size: 0.88rem;
  color: var(--head-muted);
  overflow-wrap: anywhere;
}

body[data-page="affiliate"] .portal-head__meta strong {
  color: #ffffff;
  font-weight: 600;
}

body[data-page="affiliate"] .portal-head__track {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--head-line);
  max-width: 62ch;
  font-size: 0.84rem;
  color: var(--head-muted);
}

body[data-page="affiliate"] .portal-head__side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 14px;
}

body[data-page="affiliate"] .portal-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 13px;
  border: 1px solid rgba(212, 175, 55, 0.45);
  border-radius: 999px;
  background: rgba(212, 175, 55, 0.14);
  color: var(--accent);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

body[data-page="affiliate"] .portal-status::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.18);
}

body[data-page="affiliate"] .portal-head__actions {
  display: flex;
  gap: 8px;
}

body[data-page="affiliate"] .portal-head__actions .btn {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: #ffffff;
}

body[data-page="affiliate"] .portal-head__actions .btn:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.42);
  color: #ffffff;
}

/* The figures. The one the partner is waiting on carries the gold; the rest
 * stay paper so the balance is what the eye lands on first. */
body[data-page="affiliate"] .partner-stats {
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 14px;
  margin: 18px 0 0;
}

body[data-page="affiliate"] .partner-stat {
  padding: 18px 20px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

body[data-page="affiliate"] .partner-stat__label {
  font-size: 0.66rem;
  letter-spacing: 0.12em;
}

body[data-page="affiliate"] .partner-stat__value {
  margin-top: 8px;
  font-size: 1.55rem;
  letter-spacing: -0.02em;
}

body[data-page="affiliate"] .partner-stat--primary {
  border-color: rgba(212, 175, 55, 0.55);
  background:
    linear-gradient(180deg, rgba(212, 175, 55, 0.16), rgba(212, 175, 55, 0.04)),
    var(--surface);
}

body[data-page="affiliate"] .partner-stat--primary .partner-stat__label {
  color: var(--accent-text);
}

/* The rooms. An underlined row rather than pills: the portal is one page with
 * sections, and underlined tabs say that without competing with the panel
 * headings underneath. */
body[data-page="affiliate"] .partner-tabs {
  gap: 0;
  margin: 26px 0 0;
  padding-bottom: 0;
  border-bottom: 1px solid var(--line-strong);
}

body[data-page="affiliate"] .partner-tab {
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  margin-right: 26px;
  padding: 10px 0;
  background: transparent;
  color: var(--ink-muted);
  font-size: 0.95rem;
}

body[data-page="affiliate"] .partner-tab:hover {
  background: transparent;
  color: var(--ink-strong);
}

body[data-page="affiliate"] .partner-tab.is-active {
  background: transparent;
  border-color: transparent;
  border-bottom-color: var(--accent);
  color: var(--ink-strong);
  font-weight: 700;
}

/* The panels the work sits in. */
body[data-page="affiliate"] .partner-apply {
  margin: 18px 0 0;
  padding: 24px 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

body[data-page="affiliate"] .partner-apply:first-child {
  margin-top: 0;
}

body[data-page="affiliate"] .partner-heading {
  align-items: flex-start;
  padding-bottom: 14px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line);
}

body[data-page="affiliate"] .partner-heading h2 {
  font-size: 1.12rem;
  letter-spacing: -0.01em;
  color: var(--ink-strong);
}

body[data-page="affiliate"] .partner-heading .small.muted {
  max-width: 44ch;
  text-align: right;
}

/* Sub-headings inside a commission panel. The commission terms are long enough
 * to need them, and a heading is what somebody scans for the rate they care
 * about rather than reading three paragraphs to find it. */
body[data-page="affiliate"] .portal-subhead {
  margin: 22px 0 8px;
  font-size: 1rem;
  letter-spacing: -0.01em;
  color: var(--ink-strong);
}

body[data-page="affiliate"] .portal-subhead:first-of-type {
  margin-top: 2px;
}

body[data-page="affiliate"] .partner-table {
  font-size: 0.9rem;
}

body[data-page="affiliate"] .partner-table th {
  padding: 4px 10px 9px;
  border-bottom: 1px solid var(--line-strong);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
}

body[data-page="affiliate"] .partner-table td {
  padding: 12px 10px;
}

body[data-page="affiliate"] .partner-table th:first-child,
body[data-page="affiliate"] .partner-table td:first-child {
  padding-left: 0;
}

body[data-page="affiliate"] .partner-table th:last-child,
body[data-page="affiliate"] .partner-table td:last-child {
  padding-right: 0;
}

body[data-page="affiliate"] .partner-table tbody tr:hover td {
  background: var(--surface-2);
}

body[data-page="affiliate"] .partner-tag {
  border-color: transparent;
  background: var(--surface-2);
  color: var(--ink-muted);
}

body[data-page="affiliate"] .partner-tag--good {
  border-color: rgba(212, 175, 55, 0.4);
  background: rgba(212, 175, 55, 0.2);
  color: var(--accent-text);
}

/* Sign in: the frame carries the rates, the form does the work, so the first
 * screen of the programme looks like the programme. */
body[data-page="affiliate"] .portal-signin {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  max-width: 900px;
  margin: 12px auto 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
}

body[data-page="affiliate"] .portal-signin__side {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 34px 32px;
  background:
    radial-gradient(120% 120% at 0% 0%, rgba(212, 175, 55, 0.2), transparent 60%),
    var(--chrome-bg);
  color: #f5f3ee;
}

body[data-page="affiliate"] .portal-signin__pitch {
  margin: 0;
  font-size: 1.15rem;
  line-height: 1.4;
  letter-spacing: -0.01em;
  color: #ffffff;
}

body[data-page="affiliate"] .portal-signin__rates {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 14px;
}

body[data-page="affiliate"] .portal-signin__rates li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: 12px;
  padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}

body[data-page="affiliate"] .portal-signin__rates strong {
  font-family: var(--font-num);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--accent);
}

body[data-page="affiliate"] .portal-signin__rates span {
  font-size: 0.85rem;
  color: rgba(245, 243, 238, 0.66);
}

body[data-page="affiliate"] .portal-signin__foot {
  margin: 6px 0 0;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(245, 243, 238, 0.5);
}

body[data-page="affiliate"] .portal-signin__form {
  padding: 36px 36px 32px;
}

body[data-page="affiliate"] .portal-signin__form h1 {
  margin: 0 0 6px;
  font-size: 1.6rem;
  letter-spacing: -0.02em;
}

@media (max-width: 860px) {
  body[data-page="affiliate"] .portal-signin {
    grid-template-columns: 1fr;
  }

  body[data-page="affiliate"] .portal-signin__side {
    padding: 26px 24px;
  }

  body[data-page="affiliate"] .portal-signin__form {
    padding: 26px 24px 28px;
  }
}

@media (max-width: 720px) {
  body[data-page="affiliate"] .portal-wrap {
    padding: 20px 0 56px;
  }

  body[data-page="affiliate"] .portal-head {
    padding: 24px 22px;
  }

  body[data-page="affiliate"] .portal-head__side {
    align-items: flex-start;
    width: 100%;
  }

  body[data-page="affiliate"] .partner-heading {
    flex-direction: column;
    gap: 6px;
  }

  body[data-page="affiliate"] .partner-heading .small.muted {
    max-width: none;
    text-align: left;
  }

  body[data-page="affiliate"] .partner-apply {
    padding: 20px 18px;
  }

  body[data-page="affiliate"] .partner-tab {
    margin-right: 18px;
  }
}

/* --------------------------------------- partner portal: dashboard layout
 *
 * The overview used to be one long column of identical white panels, so the
 * page read as a list rather than a dashboard. It is now two columns: the work
 * (rate, orders, payouts) on the left, and the things a partner does between
 * visits - share a link, check a bonus, change a password - in a narrower
 * column that sticks while the left one scrolls.
 *
 * The brand carries the meaning: purple for structure (group labels, the
 * supplier rate bars, links), gold for money and for whatever is selected.
 * ------------------------------------------------------------------------- */

body[data-page="affiliate"] .portal-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr);
  gap: 18px;
  align-items: start;
  margin-top: 22px;
}

body[data-page="affiliate"] .portal-main,
body[data-page="affiliate"] .portal-side {
  display: grid;
  gap: 14px;
  min-width: 0;
  align-content: start;
}

body[data-page="affiliate"] .portal-side {
  position: sticky;
  top: 18px;
}

body[data-page="affiliate"] .portal-main > .partner-apply,
body[data-page="affiliate"] .portal-side > .partner-apply {
  margin: 0;
}

/* A section label, not a heading: it divides the page into "your earnings"
 * and "share and grow" without competing with the panel titles underneath. */
body[data-page="affiliate"] .portal-group-label {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 0 0;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--secondary-text);
}

body[data-page="affiliate"] .portal-group-label::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
}

body[data-page="affiliate"] .portal-side .partner-heading h2 {
  font-size: 1rem;
}

body[data-page="affiliate"] .portal-panel__note {
  margin: 12px 0 0;
}

body[data-page="affiliate"] .portal-note-form {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

/* The rate, as one large figure rather than a sentence with a number in it. */
body[data-page="affiliate"] .portal-rate {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 20px;
  align-items: start;
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background:
    linear-gradient(135deg, var(--accent-wash), transparent 68%),
    var(--surface-2);
}

body[data-page="affiliate"] .portal-rate__value {
  font-family: var(--font-num);
  font-size: clamp(2.1rem, 1.5rem + 2vw, 2.9rem);
  line-height: 0.92;
  letter-spacing: -0.04em;
  color: var(--accent-text);
}

body[data-page="affiliate"] .portal-rate__copy {
  display: grid;
  gap: 6px;
  min-width: 0;
}

body[data-page="affiliate"] .portal-rate__copy p {
  margin: 0;
}

body[data-page="affiliate"] .portal-rate__headline {
  font-weight: 600;
  color: var(--ink-strong);
}

body[data-page="affiliate"] .portal-rate__foot {
  font-size: 0.82rem;
  color: var(--ink-muted);
}

/* Rate history is reference, not news, so it folds away by default. */
body[data-page="affiliate"] .portal-disclosure {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

body[data-page="affiliate"] .portal-disclosure > summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--secondary-text);
  list-style: none;
}

body[data-page="affiliate"] .portal-disclosure > summary::-webkit-details-marker {
  display: none;
}

body[data-page="affiliate"] .portal-disclosure > summary::before {
  content: "+";
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--secondary-wash);
  color: var(--secondary-text);
  font-size: 0.85rem;
  line-height: 1;
}

body[data-page="affiliate"] .portal-disclosure[open] > summary::before {
  content: "–";
}

body[data-page="affiliate"] .portal-disclosure[open] > summary {
  margin-bottom: 12px;
}

/* The two supplier rates side by side, so 10% and 3% are compared rather than
 * read about. */
body[data-page="affiliate"] .portal-split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
}

body[data-page="affiliate"] .portal-split__cell {
  display: grid;
  gap: 3px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--secondary);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

body[data-page="affiliate"] .portal-split__value {
  font-family: var(--font-num);
  font-size: 1.8rem;
  letter-spacing: -0.03em;
  color: var(--ink-strong);
}

body[data-page="affiliate"] .portal-split__when {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--secondary-text);
}

body[data-page="affiliate"] .portal-split__note {
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--ink-muted);
}

/* A supplier's rate steps down exactly once, so the rates are laid out in the
 * order they apply rather than as two figures to compare - months 1 to 24 at
 * the introductory rate, then everything after it at the ongoing one. */
body[data-page="affiliate"] .portal-timeline {
  display: grid;
  gap: 12px;
  margin: 0 0 18px;
  padding: 0;
  list-style: none;
}

body[data-page="affiliate"] .portal-timeline__step {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  grid-template-areas:
    "rate when"
    "rate note";
  column-gap: 16px;
  row-gap: 2px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

body[data-page="affiliate"] .portal-timeline__step--now {
  border-left-color: var(--accent);
}

body[data-page="affiliate"] .portal-timeline__rate {
  grid-area: rate;
  align-self: center;
  font-family: var(--font-num);
  font-size: 1.65rem;
  letter-spacing: -0.03em;
  color: var(--ink-strong);
}

body[data-page="affiliate"] .portal-timeline__when {
  grid-area: when;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--secondary-text);
}

body[data-page="affiliate"] .portal-timeline__note {
  grid-area: note;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--ink-muted);
}

@media (max-width: 520px) {
  body[data-page="affiliate"] .portal-timeline__step {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "when"
      "rate"
      "note";
    row-gap: 4px;
  }
  body[data-page="affiliate"] .portal-timeline__rate {
    font-size: 1.5rem;
  }
}

/* The link is the one thing a partner comes back to copy, so it gets the
 * brand's black card and the site's only gold button in this column. */
body[data-page="affiliate"] .portal-card-dark {
  display: grid;
  gap: 10px;
  padding: 20px 20px 18px;
  border-radius: var(--radius);
  color: #f5f3ee;
  background:
    radial-gradient(130% 130% at 100% 0%, rgba(212, 175, 55, 0.24), transparent 58%),
    var(--chrome-bg);
}

body[data-page="affiliate"] .portal-card-dark__label {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}

body[data-page="affiliate"] .portal-card-dark__code {
  font-family: var(--font-num);
  font-size: 1.3rem;
  letter-spacing: 0.06em;
  color: #ffffff;
}

body[data-page="affiliate"] .portal-card-dark .input {
  width: 100%;
  border-color: rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.1);
  color: #ffffff;
  font-family: var(--font-num);
  font-size: 0.8rem;
}

body[data-page="affiliate"] .portal-card-dark__field {
  display: block;
}

body[data-page="affiliate"] .portal-card-dark .btn--primary {
  justify-self: start;
}

body[data-page="affiliate"] .portal-card-dark__hint {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: rgba(245, 243, 238, 0.66);
}

/* The tabs become a segmented control: one row, clear selected state. */
body[data-page="affiliate"] .partner-tabs {
  display: flex;
  width: fit-content;
  max-width: 100%;
  gap: 4px;
  margin: 22px 0 0;
  padding: 5px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-2);
  overflow-x: auto;
  scrollbar-width: none;
}

body[data-page="affiliate"] .partner-tabs::-webkit-scrollbar {
  display: none;
}

body[data-page="affiliate"] .partner-tab {
  margin: 0;
  padding: 8px 16px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-soft);
  font-size: 0.88rem;
  white-space: nowrap;
}

body[data-page="affiliate"] .partner-tab:hover {
  background: var(--surface);
  color: var(--ink-strong);
}

body[data-page="affiliate"] .partner-tab.is-active {
  border: 0;
  background: var(--solid);
  color: var(--solid-ink);
  font-weight: 600;
}

body[data-page="affiliate"] .partner-stat__value {
  font-family: var(--font-num);
  font-feature-settings: "tnum" 1;
}

@media (max-width: 980px) {
  body[data-page="affiliate"] .portal-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  body[data-page="affiliate"] .portal-side {
    position: static;
  }
}

@media (max-width: 560px) {
  body[data-page="affiliate"] .portal-rate {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }

  body[data-page="affiliate"] .partner-tab {
    padding: 8px 13px;
    font-size: 0.84rem;
  }
}

/* A grid item will not shrink below its content unless it is told to, and a
 * wide table inside a panel counts as content. Without this the column stays
 * as wide as the widest table and a phone scrolls sideways. */
body[data-page="affiliate"] .portal-main > *,
body[data-page="affiliate"] .portal-side > * {
  min-width: 0;
}

/* ------------------------------------------- partner portal: mail folders
 *
 * Inbox, Sent and All mail are the same segmented control as the room tabs
 * above them, sitting on the heading of the list so the two never look like
 * two different kinds of control. The count is the number in that folder.
 * ------------------------------------------------------------------------- */

body[data-page="affiliate"] .partner-mail-head {
  align-items: center;
  margin: 20px 0 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

body[data-page="affiliate"] .partner-mail-head h3 {
  margin: 0;
  font-size: 0.98rem;
  letter-spacing: -0.01em;
  color: var(--ink-strong);
}

body[data-page="affiliate"] .partner-mail-head__tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

body[data-page="affiliate"] .partner-mail-tabs {
  margin: 0;
  padding: 4px;
}

body[data-page="affiliate"] .partner-mail-tabs .partner-tab {
  padding: 6px 13px;
  font-size: 0.82rem;
}

body[data-page="affiliate"] .partner-mail-tab__count {
  font-family: var(--font-num);
  font-size: 0.74rem;
  opacity: 0.7;
}

@media (max-width: 560px) {
  body[data-page="affiliate"] .partner-mail-head__tools {
    margin-left: 0;
    width: 100%;
    justify-content: space-between;
  }
}
