/* ==========================================================================
   tp.css — TeamPrinted unified design system.

   The ONE stylesheet for the new front-end (home, /collection/, collection
   detail, and the product page next). Deliberately small: no framework, no
   utility explosion, no JS dependencies. Every page that loads this loads
   nothing else.

   Derived from new_build/*.dc.html. Those mockups were authored desktop-first
   with inline styles and attribute-selector media queries
   (`[style*="repeat(4,"] { ... }`); that does not survive contact with real
   templates, so the same design is expressed here as named classes with
   ordinary breakpoints.

   SCOPING — every component selector is written `.tp .tp-thing`, and the shell
   puts class="tp" on <body>. This is not decoration: store.html's local <style>
   block and teamprinted-unified.css already define their own `.tp-card`,
   `.tp-grid`, `.tp-media`, `.tp-footer` and `.tp-eyebrow`, meaning different
   things. Scoping lets this file be dropped onto a legacy page during migration
   without the two systems fighting. Specificity is uniform (0,2,0) throughout,
   so source order still decides between rules here — keep it that way when
   adding selectors, or modifiers like `.tp-card--dark` stop winning.

   Structure:
     1. Tokens
     2. Reset + base type
     3. Layout (shell, section, grid)
     4. Chrome (stripe, announce bar, header, nav, footer)
     5. Components (button, card, product card, product row, swatch, badge,
        tag, panel, filters, breadcrumbs, faq, field)
     6. Blocks (hero, trust bar, steps, quote form)
     7. Responsive
   ========================================================================== */

/* ── 1. Tokens ──────────────────────────────────────────────────────────── */

:root {
  /* Ink
  . The navy is the text colour AND the dark surface — hero, footer,
     B2B block — which is what makes the palette feel like one thing. */
  --tp-ink: #10243c;
  --tp-ink-deep: #0b1a2e;
  /* Club pages run a separate, near-black ink: the club owns the chrome
     (header, nav, badges, banner), TeamPrinted owns the commerce. */
  --tp-ink-club: #111418;
  --tp-club-yellow: #f7c600;   /* club chrome + club-surface CTAs */
  --tp-club-green: #0e6c3f;
  --tp-photo: #faf7ef;         /* club-page image ground */
  --tp-muted: #5f6c7b;

  /* The four process colours, straight off the brand stripe. */
  --tp-cyan: #00a0dc;
  --tp-cyan-600: #0090c8;
  --tp-cyan-700: #007ea8;
  --tp-cyan-50: #e7f6fd;
  --tp-cyan-25: #f4fbfe;
  --tp-magenta: #ec008c;
  --tp-yellow: #f6e500;

  /* CTA. Deliberately NOT the cyan: cyan is the accent/link colour and reads
     too light at button size. Every call to action on the site is this blue,
     so a visitor learns one shape for "this is the thing to press". */
  --tp-cta: #003f8f;
  --tp-cta-hover: #00357a;
  --tp-cta-active: #002b64;
  --tp-cta-50: #eaf0fa;

  /* Neutrals. Three greys is the whole scale — resist adding a fourth. */
  --tp-white: #ffffff;
  --tp-page: #f7f8f9;
  /* The sticky header's two rows: 76px bar + ~52px nav strip. Anything that
     sticks below it offsets by this, or it slides underneath. */
  --tp-header-h: 128px;
  --tp-wash: #f4f6f8;
  --tp-line: #e6eaef;
  --tp-line-soft: #eef1f4;
  --tp-line-strong: #cdd4dc;

  /* Semantic. Green appears exactly once, on the wholesale badge. */
  --tp-good-bg: #e8f5ee;
  --tp-good-ink: #0f6b45;
  /* The only red in the file, and it earns its place on the basket: a rejected
     VAT number and a sold-out express tier both have to read as "this did not
     work", and muted grey reads as "this is a footnote". */
  --tp-bad-ink: #b42318;

  --tp-font-head: "Space Grotesk", system-ui, -apple-system, sans-serif;
  --tp-font-body: "Mulish", system-ui, -apple-system, sans-serif;
  /* Mulish is the house body face — the live home_v2, store.html,
     product_detail, club_directory, about-us and the base footer all pair it
     with Space Grotesk. Unifying means joining that pairing, not inventing a
     third one. It is also the better foil: a rounded humanist against a
     geometric grotesque gives real textural contrast, where a second grotesque
     (Inter, Instrument Sans) just reads as the headings shrunk down. */

  /* Type scale. Nine whole-pixel steps, roughly 1.25×, and NOTHING outside it —
     the first cut of this file grew twenty ad-hoc sizes (10.5, 12.5, 13.5,
     14.5, 15.5, 16.5…), which is what made the page read as fussy and small.
     If a new size seems necessary, the answer is almost always the neighbouring
     step plus a weight or colour change. */
  --tp-t-xs: 12px;     /* shipping + micro-label — uppercase and tracked ONLY */
  --tp-t-sm: 13px;     /* metadata. The floor: no running text is smaller. */
  --tp-t-base: 15px;   /* secondary body */
  --tp-t-md: 16px;     /* body */
  --tp-t-lg: 18px;     /* card + product-grid titles */
  --tp-t-lede: 19px;   /* hero and section intro body */
  --tp-t-xl: 21px;     /* card price, h4 */
  --tp-t-2xl: 26px;    /* sub-section */
  --tp-t-3xl: 34px;    /* section heading */
  --tp-t-4xl: 44px;    /* page title */
  --tp-t-banner: 44px; /* club-store banner display */
  /* Club + PDP layer. Off the marketplace ladder on purpose — the handoff spec
     sets these per screen, and the club chrome is its own type system. */
  --tp-t-club-name: 25px;  /* club name in the club header */
  --tp-t-club-nav: 15px;   /* club nav, uppercase */
  --tp-t-pdp-name: 34px;   /* product name on the PDP */
  --tp-t-pdp-price: 28px;  /* PDP price */
  --tp-t-acc: 18px;        /* accordion summary */
  --tp-t-5xl: 54px;    /* hero display */

  --tp-r-lg: 16px;
  --tp-r: 14px;
  --tp-r-sm: 9px;
  --tp-r-xs: 7px;
  --tp-r-pill: 999px;

  /* Card lift. Two layers: a tight one for the edge, a wide soft one for the
     lift. Both are the same near-black at low alpha rather than grey, so the
     shadow reads as depth on white instead of as a dirty border. Kept shallow
     — this is a flat design with a lift, not a material one. */
  --tp-shadow-card: 0 1px 2px rgba(16, 24, 40, .04), 0 4px 12px rgba(16, 24, 40, .06);
  --tp-shadow-card-hover: 0 2px 4px rgba(16, 24, 40, .06), 0 10px 24px rgba(16, 24, 40, .10);

  --tp-shell: 1280px;   /* marketing pages */
  --tp-shell-wide: 1440px;  /* catalogue pages, which need the extra column */
  --tp-gutter: 40px;
}

/* ── 2. Reset + base type ───────────────────────────────────────────────── */

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

.tp {
  margin: 0;
  background: var(--tp-white);
  color: var(--tp-ink);
  font-family: var(--tp-font-body);
  font-size: var(--tp-t-md);
  line-height: 1.55;
  /* NOT -webkit-font-smoothing: antialiased. On macOS that swaps subpixel for
     greyscale rendering, which thins every glyph — it is why the same Space
     Grotesk 700 looked lighter here than on the live concept store, which
     leaves smoothing at the browser default. */
  text-rendering: optimizeLegibility;
}

/* The grey ground the buy cards sit on. Tried white with flat columns and the
   page lost its figure: a white card needs something to be lifted off. */
.tp--page { background: var(--tp-page); }

.tp h1, .tp h2, .tp h3, .tp h4, .tp h5 {
  font-family: var(--tp-font-head);
  margin: 0;
  letter-spacing: 0;   /* Below 20px Space Grotesk needs no tracking at all;
                          negative tracking there just crowds it. The display
                          steps below tighten as they grow. Tracking is a
                          function of size, not of being a heading. */
  line-height: 1.14;
  font-weight: 700;
}

.tp p { margin: 0 0 16px; }
.tp a { color: var(--tp-cyan-700); text-decoration: none; }
.tp a:hover { color: #005f80; }
.tp img { display: block; max-width: 100%; }
.tp ul, .tp ol { margin: 0; padding: 0; list-style: none; }
.tp input, .tp button, .tp select, .tp textarea { font-family: inherit; }
.tp :focus-visible { outline: 2px solid var(--tp-cyan); outline-offset: 2px; }
.tp ::selection { background: #cdeefb; }

/* Type scale. Named rather than tag-bound so a section heading can be an h2
   or an h3 for document outline reasons without changing size. */
.tp .tp-display { font-size: var(--tp-t-5xl); font-weight: 700; letter-spacing: -0.035em; line-height: 1.0; text-wrap: balance; }
.tp .tp-h1 { font-size: var(--tp-t-4xl); font-weight: 700; letter-spacing: -0.03em; line-height: 1.06; }
.tp .tp-h2 { font-size: var(--tp-t-3xl); font-weight: 700; letter-spacing: -0.02em; line-height: 1.12; }
.tp .tp-h3 { font-size: var(--tp-t-2xl); font-weight: 700; letter-spacing: -0.02em; }
.tp .tp-h4 { font-size: var(--tp-t-xl); font-weight: 700; }   /* below 20px: no tracking */
.tp .tp-h5 { font-family: var(--tp-font-head); font-size: var(--tp-t-lg); font-weight: 700; }
/* The bottom rung: a label inside a control group — the printable's name over
   its own size grid — not a heading in the page's outline. */
.tp .tp-h6 { font-family: var(--tp-font-head); font-size: var(--tp-t-md); font-weight: 700; }

.tp .tp-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: var(--tp-t-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--tp-muted);
}
.tp .tp-eyebrow::before {
  content: "";
  width: 22px;
  height: 3px;
  background: var(--tp-yellow);
  flex: none;
}
.tp .tp-eyebrow--plain::before { display: none; }
.tp .tp-eyebrow--magenta { color: var(--tp-magenta); }
.tp .tp-eyebrow--magenta::before { background: var(--tp-magenta); }
.tp .tp-eyebrow--on-dark { color: rgba(255, 255, 255, 0.82); }

.tp .tp-lede { font-size: var(--tp-t-lede); line-height: 1.5; color: var(--tp-muted); }
.tp .tp-muted { color: var(--tp-muted); }
.tp .tp-small { font-size: var(--tp-t-sm); }
.tp .tp-nowrap { white-space: nowrap; }
/* Colour alone never carries the meaning — every .tp-good/.tp-bad in the markup
   sits on a word ("Free", "Saved", the rejection reason) that says it too. */
.tp .tp-good { color: var(--tp-good-ink); }
.tp .tp-bad { color: var(--tp-bad-ink); }

/* Visually hidden, still read aloud — colour swatch links need a name. */
.tp .tp-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── 3. Layout ──────────────────────────────────────────────────────────── */

.tp .tp-shell {
  max-width: var(--tp-shell);
  margin: 0 auto;
  padding-left: var(--tp-gutter);
  padding-right: var(--tp-gutter);
}
.tp .tp-shell--wide { max-width: var(--tp-shell-wide); }

.tp .tp-section { padding-top: 80px; }
.tp .tp-section--tight { padding-top: 32px; }
.tp .tp-section--mid { padding-top: 60px; }
.tp .tp-section--last { padding-bottom: 88px; }

/* Section head: title, optional meta, right-aligned "see all" link. */
.tp .tp-section-head {
  display: flex;
  align-items: flex-end;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.tp .tp-section-head__meta { font-size: var(--tp-t-sm); color: var(--tp-muted); margin-bottom: 8px; }
.tp .tp-section-head__link {
  margin-left: auto;
  font-size: var(--tp-t-md);
  font-weight: 600;
  margin-bottom: 6px;
  white-space: nowrap;
}
.tp .tp-section-intro { max-width: 62ch; margin: 0 0 30px; color: var(--tp-muted); }

.tp .tp-grid { display: grid; gap: 22px; }
.tp .tp-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tp .tp-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tp .tp-grid--3 > * { min-width: 0; }
.tp .tp-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tp .tp-grid--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.tp .tp-grid__wide { grid-column: span 2; }

.tp .tp-stack { display: flex; flex-direction: column; gap: 20px; }

/* Catalogue layout: sticky filter rail beside the results. */
.tp .tp-catalogue {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
  padding-top: 24px;
  padding-bottom: 64px;
}
/* The club header is no longer sticky, so there is nothing to clear — the old
   168px offset just parked the rail a third of the way down the viewport. */
.tp .tp-catalogue--club .tp-catalogue__aside { top: 24px; }
.tp .tp-catalogue__aside {
  position: sticky;
  top: 110px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* ── 4. Chrome ──────────────────────────────────────────────────────────── */

/* The four-colour rule at the top of every page. */
.tp .tp-stripe { height: 4px; display: flex; }
.tp .tp-stripe > span { flex: 1; }
.tp .tp-stripe > span:nth-child(1) { background: var(--tp-cyan); }
.tp .tp-stripe > span:nth-child(2) { background: var(--tp-magenta); }
.tp .tp-stripe > span:nth-child(3) { background: var(--tp-yellow); }
.tp .tp-stripe > span:nth-child(4) { background: var(--tp-ink); }

.tp .tp-announce { background: var(--tp-ink); color: var(--tp-white); }
.tp .tp-announce__inner {
  display: flex;
  align-items: center;
  gap: 28px;
  padding-top: 9px;
  padding-bottom: 9px;
  font-size: var(--tp-t-sm);
}
.tp .tp-announce__note { display: flex; align-items: center; gap: 8px; }
.tp .tp-announce__note::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-yellow);
  flex: none;
}
.tp .tp-announce__links {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 24px;
  color: rgba(255, 255, 255, 0.78);
}
.tp .tp-announce__links a { color: inherit; }
.tp .tp-announce__links a:hover { color: var(--tp-white); }
.tp .tp-announce__links a.is-strong { color: var(--tp-white); font-weight: 600; }

.tp .tp-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--tp-white);
  border-bottom: 1px solid var(--tp-line);
}
.tp .tp-header__bar {
  display: flex;
  align-items: center;
  gap: 28px;
  padding-top: 18px;
  padding-bottom: 18px;
  flex-wrap: wrap;
}
.tp .tp-header__logo { flex: none; }
.tp .tp-header__logo img { height: 40px; width: auto; }
.tp .tp-header__actions { display: flex; align-items: center; gap: 26px; flex: none; margin-left: auto; }

.tp .tp-search { flex: 1 1 300px; min-width: 260px; max-width: 460px; position: relative; }
.tp .tp-search svg { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); }
.tp .tp-search input {
  width: 100%;
  height: 46px;
  padding: 0 16px 0 44px;
  font-size: var(--tp-t-md);
  color: var(--tp-ink);
  background: var(--tp-white);
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r-pill);
}
.tp .tp-search input:hover { border-color: #5f6c7b; }
.tp .tp-search input:focus { border-color: var(--tp-cyan); outline: none; }

/* Header contact block: bold line over a quiet caption. */
.tp .tp-contact { display: flex; flex-direction: column; align-items: center; gap: 2px; color: var(--tp-ink); }
.tp .tp-contact:hover { color: var(--tp-cyan-700); }
.tp .tp-contact__main {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--tp-font-head);
  font-weight: 600;
  font-size: var(--tp-t-md);
  white-space: nowrap;
}
.tp .tp-contact__sub { font-size: var(--tp-t-xs); color: var(--tp-muted); white-space: nowrap; }

.tp .tp-cart { color: var(--tp-ink); display: flex; align-items: center; gap: 8px; position: relative; }
.tp .tp-cart:hover { color: var(--tp-cyan-700); }
.tp .tp-cart__count {
  background: var(--tp-magenta);
  color: var(--tp-white);
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-xs);
  min-width: 20px;
  height: 20px;
  border-radius: var(--tp-r-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px;
}
.tp .tp-cart__count:empty { display: none; }

.tp .tp-nav-bar { border-top: 1px solid var(--tp-line-soft); }
.tp .tp-nav-bar__inner { display: flex; align-items: center; gap: 28px; }
/* Bold, not medium — at nav size a 500 weight reads as washed out next to the
   40px logo. */
.tp .tp-nav { display: flex; align-items: center; gap: 28px; font-size: var(--tp-t-md); font-weight: 700; }
.tp .tp-nav a {
  color: var(--tp-ink);
  padding: 14px 0;
  display: block;
  border-bottom: 2px solid transparent;
}
.tp .tp-nav a:hover { color: var(--tp-cyan-700); border-bottom-color: var(--tp-cyan); }
.tp .tp-nav a.is-active { border-bottom-color: var(--tp-cyan); }
.tp .tp-nav-bar__cta { margin-left: auto; }

/* The menu disclosure. Desktop: summary hidden, panel is the horizontal nav.
   Mobile: summary is the hamburger, panel drops below the header. No script. */
.tp .tp-menu { display: none; }   /* mobile only — see the 860px block */
.tp .tp-menu__toggle::-webkit-details-marker { display: none; }

/* Club rail — the row of crests under the header. */
.tp .tp-rail { display: flex; align-items: center; gap: 32px; }
.tp .tp-rail__label {
  font-size: var(--tp-t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--tp-muted);
  flex: none;
}
.tp .tp-rail__items {
  display: flex;
  align-items: center;
  gap: 30px;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: thin;
}
.tp .tp-rail__item {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--tp-ink);
  font-size: var(--tp-t-md);
  font-weight: 500;
  white-space: nowrap;
}
.tp .tp-rail__item:hover { color: var(--tp-cyan-700); }
.tp .tp-rail__item img {
  width: 40px;
  height: 40px;
  padding: 3px;
  border-radius: var(--tp-r-xs);
  object-fit: contain;
  background: var(--tp-white);
  flex: none;
}

.tp .tp-footer { background: var(--tp-ink); color: rgba(255, 255, 255, 0.78); margin-top: 88px; }
.tp .tp-footer a { color: rgba(255, 255, 255, 0.78); }
.tp .tp-footer a:hover { color: var(--tp-white); }
.tp .tp-footer__cols {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, 1fr));
  gap: 48px;
  padding-top: 56px;
  padding-bottom: 28px;
}
.tp .tp-footer__logo { height: 22px; width: auto; filter: brightness(0) invert(1); margin-bottom: 16px; }
.tp .tp-footer__phone {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-xl);
  margin-bottom: 6px;
}
.tp .tp-footer__phone a { color: var(--tp-white); }
.tp .tp-footer__title {
  font-family: var(--tp-font-body);   /* uppercase — never the display face */
  font-weight: 600;
  font-size: var(--tp-t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  margin-bottom: 16px;
}
.tp .tp-footer__links { display: flex; flex-direction: column; gap: 10px; font-size: var(--tp-t-sm); }
.tp .tp-footer__base {
  padding-top: 24px;
  padding-bottom: 44px;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}
.tp .tp-footer__pay { display: flex; gap: 8px; flex-wrap: wrap; font-size: var(--tp-t-xs); color: rgba(255, 255, 255, 0.7); }
.tp .tp-footer__pay span {
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 5px;
  padding: 5px 10px;
}
.tp .tp-footer__copy { margin-left: auto; font-size: var(--tp-t-xs); color: rgba(255, 255, 255, 0.6); }

/* ── 5. Components ──────────────────────────────────────────────────────── */

.tp .tp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-md);
  line-height: 1.2;
  padding: 14px 26px;
  border-radius: var(--tp-r-sm);
  border: 1px solid transparent;
  cursor: pointer;
  text-align: center;
  white-space: nowrap;
}
.tp .tp-btn--primary { background: var(--tp-cta); color: var(--tp-white); }
.tp .tp-btn--primary:hover { background: var(--tp-cta-hover); color: var(--tp-white); }
.tp .tp-btn--primary:active { background: var(--tp-cta-active); }
.tp .tp-btn--outline { border-color: var(--tp-line-strong); background: var(--tp-white); color: var(--tp-ink); }
.tp .tp-btn--outline:hover { border-color: var(--tp-cta); color: var(--tp-cta); background: var(--tp-cta-50); }
/* On the navy hero, where an outline needs to read against dark. */
.tp .tp-btn--on-ink {
  background: var(--tp-white);
  color: var(--tp-ink);
}
.tp .tp-btn--on-ink:hover { background: var(--tp-cyan-50); color: var(--tp-ink); }
.tp .tp-btn--on-ink:active { background: #cdeefb; }
/* The club's own accent when the page supplies one, our cobalt CTA when it
   doesn't — never a third colour. --club-accent-text ships with the accent so a
   pale club colour still gets readable text. */
.tp .tp-btn--club {
  background: var(--club-accent, var(--tp-cta));
  color: var(--club-accent-text, var(--tp-white));
}
.tp .tp-btn--club:hover { filter: brightness(0.94); }
.tp .tp-btn--on-dark { border-color: rgba(255, 255, 255, 0.28); color: var(--tp-white); }
.tp .tp-btn--on-dark:hover { background: rgba(255, 255, 255, 0.09); color: var(--tp-white); }
.tp .tp-btn--pill { border-radius: var(--tp-r-pill); padding: 10px 22px; font-size: var(--tp-t-md); }
.tp .tp-btn--sm { padding: 11px 18px; font-size: var(--tp-t-sm); }
.tp .tp-btn--block { width: 100%; }

/* The one boxed surface. Everything else is whitespace. */
.tp .tp-card {
  display: block;
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  overflow: hidden;
  box-shadow: var(--tp-shadow-card);
  transition: box-shadow .16s ease, border-color .16s ease;
}
a.tp-card:hover, .tp-card--hover:hover {
  border-color: var(--tp-line-strong);
  box-shadow: var(--tp-shadow-card-hover);
}
/* The flat variants opt out: a shadow under a dashed placeholder or a tinted
   panel reads as a bug, because neither is a card sitting on the page. */
.tp .tp-card--dashed, .tp .tp-card--quiet { box-shadow: none; }
.tp .tp-card--dark { background: var(--tp-ink); border-color: var(--tp-ink); color: var(--tp-white); }
.tp .tp-card--dashed { border-style: dashed; border-color: var(--tp-line-strong); background: var(--tp-page); }
.tp .tp-card--dashed:hover { border-color: var(--tp-cyan); background: var(--tp-cyan-25); }
.tp .tp-card--quiet { background: var(--tp-page); border-color: var(--tp-line-soft); }
.tp .tp-card__body { display: block; padding: 18px 20px; }

/* Panel: a wash or navy slab that isn't a card — hero, B2B block, newsletter. */
.tp .tp-panel { border-radius: var(--tp-r-lg); padding: 44px 48px; }
.tp .tp-panel--wash { background: var(--tp-wash); }
.tp .tp-panel--ink { background: var(--tp-ink); color: var(--tp-white); }
/* :not(.tp-quote-card *) — the white lead card sits INSIDE the ink panel, and
   without this its heading and its "a person replies in a day" line inherit
   white-on-white and vanish. That copy is what the form is asking for. */
.tp .tp-panel--ink h1:not(.tp-quote-card *),
.tp .tp-panel--ink h2:not(.tp-quote-card *),
.tp .tp-panel--ink h3:not(.tp-quote-card *) { color: var(--tp-white); }
.tp .tp-panel--ink p:not(.tp-quote-card *) { color: rgba(255, 255, 255, 0.82); }

/* Media box. All product/club imagery sits in one of these so aspect ratio
   and the placeholder wash are defined once. */
.tp .tp-media { display: block; position: relative; background: var(--tp-white); overflow: hidden; }
.tp .tp-media img { width: 100%; height: 100%; object-fit: contain; }
.tp .tp-media--pad img { padding: 8%; }
.tp .tp-media--cover img { object-fit: cover; }
.tp .tp-media--4x5 { aspect-ratio: 4 / 5; }
.tp .tp-media--1x1 { aspect-ratio: 1 / 1; }
.tp .tp-media--16x11 { aspect-ratio: 16 / 11; }
.tp .tp-media--fill { position: absolute; inset: 0; }
.tp .tp-media__empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--tp-line-strong);
}

/* Product card — the vertical one, used in every grid. */
.tp .tp-product { display: flex; flex-direction: column; }
.tp .tp-product__body {
  padding: 16px 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}
.tp .tp-product__store {
  font-size: var(--tp-t-xs);
  color: var(--tp-muted);
  margin-bottom: 2px;
}
/* The name carries the card, so it is the heaviest thing on it; the price sits
   back at 400 rather than competing. */
/* Whose shirt this is — cross-store grids only (shop pages, home bestsellers).
   Quiet by design: it identifies the card, it does not compete with the name. */
.tp .tp-product__store {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
  line-height: 1.2;
}
.tp .tp-product__store img { width: 16px; height: 16px; object-fit: contain; flex: none; }
.tp .tp-product__store span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tp .tp-product__title { font-size: var(--tp-t-lede); font-weight: 700; line-height: 1.25; }
.tp .tp-product__title a { color: var(--tp-ink); }
.tp .tp-product__title a:hover { color: var(--tp-cyan-700); }
.tp .tp-product__price { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 9px; margin-top: 2px; }
/* 400 — a real weight now that base.html requests Space Grotesk 400. 300 would
   still be faux-light: the family's own axis starts at 300 but Google only
   serves what we ask for, and 400 is as light as this reads well at card size. */
.tp .tp-product__price b { font-family: var(--tp-font-head); font-weight: 400; font-size: var(--tp-t-lg); letter-spacing: -0.015em; }
.tp .tp-product__price s { font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-product__price small { font-size: var(--tp-t-sm); color: var(--tp-muted); }
/* Which price this is. A struck figure on its own only says "reduced" — the
   label says the reduction is the club's agreed rate, not a sale that ends.
   Small and set as a chip: it annotates the two figures, it isn't a third one. */
.tp .tp-product__tier {
  flex: none;
  align-self: center;
  font-size: 9.5px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--tp-cyan-700);
  background: var(--tp-cyan-50);
  border-radius: 3px;
  padding: 4px 6px;
  white-space: nowrap;
}
/* How many garments the one price covers. Without it a set is a card with a
   price and no way to tell what the price is for. */
.tp .tp-product__setn { margin-top: 6px; font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-product__foot {
  margin-top: auto;
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.tp .tp-product__note { font-size: var(--tp-t-xs); color: var(--tp-muted); text-align: center; }

/* Card badge. Top-left, opposite the club crest, so the two never meet however
   narrow the card gets. max-width keeps a long custom_label from running under
   the crest on a two-up mobile grid — it ellipsises instead. */
.tp .tp-product__badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  max-width: calc(100% - 60px);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 5px 11px;
  border-radius: var(--tp-r-pill);
}
.tp .tp-product__badge--new { background: var(--tp-ink); color: var(--tp-white); }
.tp .tp-product__badge--pers { background: var(--tp-cyan-700); color: var(--tp-white); }
/* A club-authored label is the club speaking — club colour, set per store. */
.tp .tp-product__badge--custom {
  background: var(--tp-club-accent, var(--tp-ink));
  color: var(--tp-club-accent-text, var(--tp-white));
}

/* Product row — the horizontal one, used on the collection page. */
.tp .tp-row {
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  padding: 22px;
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 28px;
}
.tp .tp-row:hover { border-color: var(--tp-line-strong); }
.tp .tp-row__media { border-radius: 10px; }
.tp .tp-row__body { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.tp .tp-row__head { display: flex; align-items: flex-start; gap: 24px; flex-wrap: wrap; }
.tp .tp-row__title { flex: 1; min-width: 240px; }
.tp .tp-row__title h2, .tp .tp-row__title h3 { font-size: var(--tp-t-2xl); font-weight: 700; margin-bottom: 8px; }
.tp .tp-row__title a { color: var(--tp-ink); }
.tp .tp-row__title a:hover { color: var(--tp-cyan-700); }
.tp .tp-row__price { text-align: right; flex: none; }
.tp .tp-row__price b { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-2xl); white-space: nowrap; }
.tp .tp-row__badge { margin-top: 8px; }
.tp .tp-row__cta { margin-top: auto; }

/* Swatches. Rounded squares on rows/filters, circles on grid cards. */
.tp .tp-swatches { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; }
.tp .tp-swatches__label { font-size: var(--tp-t-sm); font-weight: 600; color: var(--tp-muted); margin-bottom: 9px; }
.tp .tp-swatch {
  width: 30px;
  height: 30px;
  border-radius: var(--tp-r-xs);
  border: 1px solid rgba(0, 0, 0, 0.12);
  flex: none;
  display: block;
}
.tp .tp-swatch--light { border-color: #dfe4ea; }
.tp .tp-swatch--sm { width: 16px; height: 16px; border-radius: var(--tp-r-pill); }
.tp .tp-swatch--lg { width: 34px; height: 34px; border-radius: 8px; }
.tp .tp-swatch--more {
  width: auto;
  min-width: 30px;
  padding: 0 8px;
  font-size: var(--tp-t-xs);
  font-weight: 600;
  color: var(--tp-muted);
  background: var(--tp-wash);
  border-color: var(--tp-line);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.tp .tp-swatch--more:hover { border-color: var(--tp-cyan); color: var(--tp-cyan-700); }
.tp .tp-swatch.is-active { outline: 2px solid var(--tp-cyan); outline-offset: 2px; }
.tp .tp-swatches__note { font-size: var(--tp-t-xs); color: var(--tp-muted); margin-left: 2px; }

/* Badges: the corner flag on a card image. */
.tp .tp-badge {
  position: absolute;
  top: 12px;
  left: 12px;
  font-family: var(--tp-font-body);   /* uppercase — never the display face */
  font-weight: 700;
  font-size: var(--tp-t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 9px;
  border-radius: 5px;
  pointer-events: none;
  color: var(--tp-white);
  background: var(--tp-magenta);
}
.tp .tp-badge--ink { background: var(--tp-ink); }

/* Tags: inline labels in flow. */
.tp .tp-tag {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--tp-t-xs);
  font-weight: 600;
  padding: 5px 11px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-cyan-50);
  color: var(--tp-cyan-700);
  white-space: nowrap;
}
.tp .tp-tag--good { background: var(--tp-good-bg); color: var(--tp-good-ink); border-radius: 6px; }
/* Club tag beside a section title: solid club colour, sized to sit against a
   34px heading rather than disappear next to it. */
.tp .tp-tag--club {
  background: var(--tp-ink-club);
  color: var(--tp-white);
  font-size: var(--tp-t-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 7px 15px;
}
.tp .tp-tag--count {
  background: var(--tp-wash);
  color: var(--tp-muted);
  font-size: var(--tp-t-xs);
  padding: 2px 9px;
}
.tp .tp-tag--count-on { background: var(--tp-cyan); color: var(--tp-white); font-weight: 700; }

/* Sidebar filter block. */
.tp .tp-filter {
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  padding: 22px 20px;
}
.tp .tp-filter__head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.tp .tp-filter__head h2 { font-size: var(--tp-t-lg); font-weight: 700; }
.tp .tp-filter__list { display: flex; flex-direction: column; }
.tp .tp-filter__item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
  color: var(--tp-ink);
  font-size: var(--tp-t-md);
}
.tp .tp-filter__item:hover { color: var(--tp-cyan-700); }
.tp .tp-filter__item.is-active { color: var(--tp-cyan-700); font-weight: 600; }
.tp .tp-filter__item .tp-tag { margin-left: auto; }
.tp .tp-chip {
  font-size: var(--tp-t-sm);
  font-weight: 600;
  padding: 7px 13px;
  border: 1px solid var(--tp-line-strong);
  border-radius: 8px;
  cursor: pointer;
  color: var(--tp-ink);
  display: inline-block;
}
.tp .tp-chip:hover { border-color: var(--tp-cyan); color: var(--tp-cyan-700); }
.tp .tp-chip.is-active { border-color: var(--tp-cyan); background: var(--tp-cyan-50); color: var(--tp-cyan-700); }
.tp .tp-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 22px; }
/* Club finder search. Capped rather than full-width: a single search field
   spanning a 1280px page reads as a page-wide form, not as one input. */
.tp .tp-finder { max-width: 520px; margin: 4px 0 18px; }

.tp .tp-crumbs {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
  margin-bottom: 10px;
}
.tp .tp-crumbs a { color: var(--tp-muted); }
.tp .tp-crumbs a:hover { color: var(--tp-cyan-700); }
.tp .tp-crumbs__here { color: var(--tp-ink); font-weight: 600; }

/* FAQ — rules only, no card. */
/* Answers people actually read before buying — so they get card weight and real
   type, not hairline rows of grey text. The first item keeps no top rule: the
   section heading is already the boundary. */
.tp .tp-faq { display: flex; flex-direction: column; gap: 12px; }
.tp .tp-faq__item {
  padding: 22px 26px;
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
}
.tp .tp-faq__item:last-child { border-bottom: 1px solid var(--tp-line); }
.tp .tp-faq__q {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-xl);
  letter-spacing: -0.015em;
  margin-bottom: 8px;
}
.tp .tp-faq__a {
  margin: 0;
  font-size: var(--tp-t-md);
  line-height: 1.6;
  color: var(--tp-body, var(--tp-muted));
  max-width: 68ch;
}
.tp .tp-faq__a a { font-weight: 700; color: var(--tp-cyan-700); }

@media (max-width: 860px) {
  .tp .tp-faq__item { padding: 18px 18px; }
  .tp .tp-faq__q { font-size: var(--tp-t-lg); }
}

.tp .tp-field { display: block; }
.tp .tp-field > label,
.tp label.tp-field > span {
  display: block;
  font-size: var(--tp-t-xs);
  font-weight: 600;
  color: var(--tp-muted);
  margin-bottom: 6px;
}
.tp .tp-input {
  width: 100%;
  height: 44px;
  padding: 0 14px;
  font-size: var(--tp-t-md);
  color: var(--tp-ink);
  background: var(--tp-wash);
  border: 1px solid var(--tp-line);
  border-radius: 8px;
}
.tp .tp-input:focus { border-color: var(--tp-cyan); background: var(--tp-white); outline: none; }
.tp .tp-input--plain { background: var(--tp-white); border-color: var(--tp-line-strong); height: 48px; }

.tp .tp-select {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--tp-line-strong);
  background: var(--tp-white);
  border-radius: var(--tp-r-sm);
  padding: 11px 16px;
  font-size: var(--tp-t-md);
  font-weight: 500;
  color: var(--tp-ink);
  cursor: pointer;
}
.tp .tp-select:hover { border-color: var(--tp-cyan); }

/* ── 6. Blocks ──────────────────────────────────────────────────────────── */

/* Hero: navy copy panel beside a full-bleed photograph. */
.tp .tp-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg);
  overflow: hidden;
}
.tp .tp-hero__copy {
  background: var(--tp-ink);
  color: var(--tp-white);
  padding: 56px 52px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.tp .tp-hero__copy h1 { color: var(--tp-white); margin: 22px 0 20px; }
.tp .tp-hero__copy p { color: rgba(255, 255, 255, 0.82); max-width: 40ch; margin-bottom: 30px; }
.tp .tp-hero__actions { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 28px; }
.tp .tp-hero__media {
  position: relative;
  background: var(--tp-wash);
  min-height: 520px;
  display: grid;
  place-items: center;
  overflow: hidden;
}
/* Contain, not cover. The club artwork carries its own headline; cropping it to
   fill the panel slices the words — which is exactly what happened to
   "…ZOWANE / gadżety / WICE". Opt into cover only for a true bleed photo. */
.tp .tp-hero__media > img { width: 100%; height: 100%; object-fit: contain; }
.tp .tp-hero__media--bleed > img { object-fit: cover; }

/* The typographic hero stack — three display lines, the middle one in club
   yellow. This is the mode that works before any photography exists, which is
   the state most new club stores are actually in on launch day. */
.tp .tp-hero__stack { display: flex; flex-direction: column; margin: 22px 0 20px; }
.tp .tp-hero__stack--club { color: var(--tp-yellow); }

/* Tick list, on dark. */
.tp .tp-ticks { display: flex; flex-direction: column; gap: 9px; font-size: var(--tp-t-sm); color: rgba(255, 255, 255, 0.74); }
.tp .tp-ticks--grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 28px; }
.tp .tp-ticks li { display: flex; gap: 10px; align-items: flex-start; }
.tp .tp-ticks svg { flex: none; margin-top: 4px; }

/* Trust bar: four hairline-separated cells. The 1px gap over a line-coloured
   background is what draws the dividers — cheaper than per-cell borders. */
.tp .tp-trust {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--tp-line);
  border: 1px solid var(--tp-line);
  border-radius: 12px;
  overflow: hidden;
}
.tp .tp-trust__cell {
  background: var(--tp-white);
  padding: 30px 28px;
  display: flex;
  gap: 16px;
  align-items: flex-start;
}
/* Icons scale with the cell — a 20px glyph beside 18px type reads as a bullet,
   not as proof. */
.tp .tp-trust__cell svg { flex: none; margin-top: 1px; width: 26px; height: 26px; }
.tp .tp-trust__title {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-lg);
  line-height: 1.3;
  margin-bottom: 3px;
}
.tp .tp-trust__sub { font-size: var(--tp-t-base); color: var(--tp-muted); line-height: 1.45; }

/* Numbered steps. */
.tp .tp-steps { display: flex; flex-direction: column; gap: 18px; }
.tp .tp-steps li { display: flex; gap: 16px; }
.tp .tp-steps__n {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-cyan-50);
  color: var(--tp-cyan-700);
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-sm);
  display: flex;
  align-items: center;
  justify-content: center;
}
.tp .tp-steps__title { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-md); }
.tp .tp-steps__sub { font-size: var(--tp-t-sm); color: var(--tp-muted); }

/* Split: copy beside a form/photo. */
.tp .tp-split { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 56px; align-items: center; }
.tp .tp-split--top { align-items: start; }
.tp .tp-split--wide-gap { gap: 64px; }
.tp .tp-split--narrow-left { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); }

/* Club store tile with the caption sitting over the photo. */
.tp .tp-club-tile { position: relative; display: flex; flex-direction: column; min-height: 330px; }
.tp .tp-club-tile__caption {
  position: relative;
  margin-top: auto;
  padding: 26px 28px;
  background: rgba(16, 36, 60, 0.93);
  color: var(--tp-white);
}
.tp .tp-club-tile__caption h3 { color: var(--tp-white); font-size: var(--tp-t-2xl); margin-bottom: 6px; }
.tp .tp-club-tile__caption p { margin: 0; font-size: var(--tp-t-sm); color: rgba(255, 255, 255, 0.78); }
.tp .tp-live {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--tp-t-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.8);
  margin-bottom: 9px;
}
.tp .tp-live::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-yellow);
}

.tp .tp-club-card { padding: 24px; display: flex; flex-direction: column; gap: 16px; }
.tp .tp-club-card__crest {
  width: 56px;
  height: 56px;
  border-radius: 8px;
  object-fit: contain;
  background: var(--tp-wash);
}
.tp .tp-club-card__name { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-lg); margin-bottom: 3px; }
.tp .tp-club-card__meta { font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-club-card--row { flex-direction: row; align-items: center; padding: 22px; }
.tp .tp-club-card--row .tp-club-card__crest { width: 44px; height: 44px; flex: none; opacity: 0.85; }

/* Club crest grid. Every club identical: the crest is the identity, so sizing
   one club's tile larger than another reads as a ranking we don't intend.
   Auto-fill rather than a fixed column count — the row stays dense whether the
   platform has five clubs or fifty. */
.tp .tp-crests {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 16px;
}
.tp .tp-crest {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  text-align: center;
  padding: 24px 16px;
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  color: var(--tp-ink);
}
.tp a.tp-crest:hover { border-color: var(--tp-line-strong); color: var(--tp-ink); }
.tp .tp-crest img {
  width: 84px;
  height: 84px;
  max-width: 84px;   /* explicit: a stale or unloaded stylesheet must never let
                        a 2000px crest render at its intrinsic size */
  object-fit: contain;
  flex: none;
}
.tp .tp-crest__name {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-sm);
  line-height: 1.25;
  text-wrap: balance;
}
/* Opening soon: present and named, deliberately not a link. Muted rather than
   hidden — a supporter whose club is on the way should see it coming. */
.tp .tp-crest__meta { font-size: var(--tp-t-xs); color: var(--tp-muted); }
.tp .tp-crest--soon { background: var(--tp-page); border-color: var(--tp-line-soft); }
.tp .tp-crest--soon img { opacity: 0.5; filter: grayscale(1); }
.tp .tp-crest--soon .tp-crest__name { color: var(--tp-muted); }
.tp .tp-crest__label {
  font-size: var(--tp-t-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tp-muted);
  background: var(--tp-line-soft);
  border-radius: var(--tp-r-pill);
  padding: 3px 10px;
}
.tp .tp-crest--add {
  border-style: dashed;
  border-color: var(--tp-line-strong);
  background: var(--tp-page);
  justify-content: center;
}
.tp .tp-crest--add:hover { border-color: var(--tp-cta); background: var(--tp-cta-50); }
.tp .tp-crest--add .tp-crest__name { color: var(--tp-cta); }

.tp .tp-plus {
  width: 34px;
  height: 34px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-cyan-50);
  color: var(--tp-cyan-700);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--tp-t-lg);
  font-weight: 600;
  line-height: 1;
}

/* Licence strip. */
.tp .tp-licence {
  background: var(--tp-wash);
  border-radius: var(--tp-r);
  padding: 28px 32px;
  display: flex;
  align-items: center;
  gap: 26px;
  flex-wrap: wrap;
}
.tp .tp-licence__crest { width: 52px; height: 52px; border-radius: 8px; object-fit: contain; flex: none; }

/* Lead card. Lifted off the ink panel so it reads as the one thing to do here,
   and explicitly re-coloured — it is a light card on a dark field, so it cannot
   inherit anything. */
.tp .tp-quote-card {
  background: var(--tp-white);
  color: var(--tp-ink);
  border-radius: var(--tp-r-lg);
  padding: 32px;
  box-shadow: 0 2px 6px rgba(16, 24, 40, 0.06), 0 24px 48px rgba(16, 24, 40, 0.18);
}
.tp .tp-quote-card__title { color: var(--tp-ink); margin-bottom: 8px; }
.tp .tp-quote-card__lede {
  color: var(--tp-muted);
  font-size: var(--tp-t-md);
  line-height: 1.5;
  margin: 0 0 22px;
}
.tp .tp-quote-card__form { display: flex; flex-direction: column; gap: 14px; }

/* Assurances read as a checklist, not as small print. */
.tp .tp-quote-card__assure {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin: 2px 0 0;
  padding: 0;
}
.tp .tp-quote-card__assure li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}
.tp .tp-quote-card__assure svg { flex: none; margin-top: 3px; color: var(--tp-good-ink, #1F7A4C); }

/* The phone is the second conversion path, so it is a real line of type. */
.tp .tp-quote-card__alt {
  border-top: 1px solid var(--tp-line);
  margin-top: 6px;
  padding-top: 16px;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}
.tp .tp-quote-card__alt a {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-lg);
  letter-spacing: -0.01em;
  color: var(--tp-ink);
  margin-left: 4px;
}
.tp .tp-quote-card__alt a:hover { color: var(--tp-cyan-700); }
.tp .tp-quote-card__hours { display: block; margin-top: 2px; }

.tp .tp-empty {
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  padding: 56px 24px;
  text-align: center;
}

/* Pagination. Prev / count / Next, with the count holding the middle even when
   one of the two buttons is missing — otherwise the row jumps sideways between
   the first page and the second. */
.tp .tp-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 34px;
  padding-top: 24px;
  border-top: 1px solid var(--tp-line);
}
.tp .tp-pager__count { margin: 0 auto; font-size: var(--tp-t-sm); }

/* ── Club chrome ─────────────────────────────────────────────────────────
   The club owns the header, nav and banner; TeamPrinted owns the commerce
   below them. Near-black rather than the marketplace navy, so a club page
   reads as the club's own shop and not as a TeamPrinted section. */
/* Not sticky: the crest overhangs the header box, and sticking would drag that
   overhang down over the page content. */
.tp .tp-club-header { position: relative; background: var(--tp-ink-club); }

/* The crest spans BOTH bars. Absolute inside the utility bar's container, so it
   starts at the page grid rather than the viewport edge, and overhangs into the
   nav — that overlap is what reads the two bars as one piece of club chrome. */
.tp .tp-club-header__crest {
  position: absolute;
  left: var(--tp-gutter);
  top: 9px;
  z-index: 4;
  width: 104px;
  height: 104px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.tp .tp-club-header__crest img { width: 100%; height: 100%; object-fit: contain; }
.tp .tp-club-header__crest-txt {
  color: var(--tp-white);
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-2xl);
}

/* Utility bar: black full-bleed, contents on the page grid. The inner indents
   past the crest rather than wrapping around it. */
.tp .tp-club-header__ubar { background: var(--tp-ink-club); color: var(--tp-white); }
.tp .tp-club-header__ubar-inner {
  position: relative;
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  padding-top: 12px;
  padding-bottom: 12px;
  padding-left: calc(var(--tp-gutter) + 126px);   /* 104px crest + 22px gap */
  min-height: 78px;
}
.tp .tp-club-header__name {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 7px;
  min-width: 0;
  color: var(--tp-white);
  padding-right: 22px;
}
.tp .tp-club-header__name:hover { color: var(--tp-white); }
.tp .tp-club-header__club {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-club-name);
  letter-spacing: 0.02em;
  line-height: 1.1;
  /* Truncate rather than wrap — long club names otherwise push the badge onto
     a third line and grow the bar. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 46vw;
}
.tp .tp-club-header__badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  background: var(--club-accent, var(--tp-club-yellow));
  color: var(--tp-ink-club);
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: 6px;
  padding: 4px 10px;
}
/* The TeamPrinted mark sits in its own cell, divided from the club's half by a
   rule that runs the full height of the row — the two brands are adjacent, not
   nested. This is the ownership split made visible, and the "store run by"
   label is what stops the wordmark implying the club is the merchant. */
.tp .tp-club-header__tp {
  margin-left: auto;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #9BA3AB;
  padding-left: 26px;
  border-left: 1px solid rgba(255, 255, 255, 0.16);
}
.tp .tp-club-header__tp img { height: 30px; width: auto; max-width: 158px; }

/* Was an inline padding-top on the section, which no media query could beat —
   which is why the mobile gap survived every attempt to close it. */
.tp .tp-store-top { padding-top: 24px; }
.tp .tp-pdp-top { padding-top: 20px; }
/* Black utility bar over a club-coloured nav band — the live concept store's
   split, driven by the same store.nav_color. Falls back to ink when a club has
   set no colour, so the bar is never an unstyled strip. */
/* Nav band in the club's own colour, contents on the page grid like the ubar. */
.tp .tp-club-nav {
  position: relative;
  background: var(--club, var(--tp-ink-club));
  color: var(--tp-white);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10);
}
.tp .tp-club-nav__inner {
  display: flex;
  align-items: center;
  gap: 28px;
  padding-left: calc(var(--tp-gutter) + 126px);
}
.tp .tp-club-nav__links { display: flex; align-items: center; gap: 28px; overflow-x: auto; scrollbar-width: none; }
.tp .tp-club-nav__links::-webkit-scrollbar { display: none; }
.tp .tp-club-nav__links a {
  flex: none;
  white-space: nowrap;
  padding: 19px 0;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-club-nav);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--tp-white);
  opacity: 0.92;
  border-bottom: 3px solid transparent;
}
.tp .tp-club-nav__links a:hover { opacity: 1; }
.tp .tp-club-nav__links a.is-active {
  color: var(--club-accent, var(--tp-club-yellow));
  opacity: 1;
  border-bottom-color: var(--club-accent, var(--tp-club-yellow));
}
.tp .tp-cart--club { margin-left: auto; color: var(--tp-white); }
.tp .tp-cart--club:hover { color: var(--club-accent, var(--tp-club-yellow)); }
.tp .tp-cart--club .tp-cart__count {
  background: var(--club-accent, var(--tp-club-yellow));
  color: var(--tp-ink-club);
}

/* Club banner. Typographic by design: it holds up before any photography
   exists, which is the state every new club store launches in. */
.tp .tp-club-banner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  background: var(--tp-ink-club);
  border-radius: var(--tp-r-lg);
  overflow: hidden;
  min-height: 340px;
}
.tp .tp-club-banner__copy { position: relative; overflow: hidden; padding: 40px 44px; display: flex; flex-direction: column; justify-content: center; }
.tp .tp-club-banner__chip {
  align-self: flex-start;
  background: var(--tp-club-yellow);
  color: var(--tp-ink-club);
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: 6px;
  padding: 5px 11px;
}
.tp .tp-club-banner__meta { color: rgba(255, 255, 255, 0.7); font-size: var(--tp-t-sm); }
.tp .tp-club-banner__stack {
  position: relative;
  display: flex;
  flex-direction: column;
  margin: 18px 0 14px;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-banner);
  line-height: 0.98;
  letter-spacing: -0.035em;
  color: var(--tp-white);
}
.tp .tp-club-banner__stack em { font-style: normal; color: var(--tp-club-yellow); }
.tp .tp-club-banner__copy p { position: relative; color: rgba(255, 255, 255, 0.8); max-width: 44ch; }
.tp .tp-club-banner__actions { position: relative; display: flex; gap: 12px; flex-wrap: wrap; margin-top: 8px; }
/* No background of its own — the media panel inherits the banner's field so the
   hero reads as one block. It used to sit on --tp-photo (cream), which split the
   banner down the middle and, worse, framed every crest: club logos are supplied
   on a solid dark tile, so on cream they rendered as a black square stuck onto a
   pale panel rather than as the club's mark. */
.tp .tp-club-banner__media { position: relative; display: grid; place-items: center; overflow: hidden; }
.tp .tp-club-banner__media > img { width: 100%; height: 100%; object-fit: contain; }

/* Product detail. The row/span/align combination below is load-bearing: the
   buy column spans both rows and is sticky, so without `grid-template-rows:
   max-content 1fr` AND `align-self: start` on both spanning children, the tall
   buy column inflates row 1 and drops ~200px of phantom whitespace between the
   thumbnails and the accordions. */
.tp .tp-pdp {
  display: grid;
  /* 7/5, as on the original PDP — the near-even split made the gallery look
     cramped and the buy column overweight. */
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  grid-template-rows: max-content 1fr;
  gap: 44px;
  align-items: start;
  padding-top: 12px;
  padding-bottom: 76px;
}
.tp .tp-pdp__gallery { grid-column: 1; grid-row: 1; }
.tp .tp-pdp__buy {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: start;
  position: sticky;
  top: 24px;   /* the club header no longer sticks, so nothing to clear */
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.tp .tp-pdp__details { grid-column: 1; grid-row: 2; align-self: start; }

.tp .tp-pdp__chip {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
  background: var(--tp-ink-club);
  color: var(--tp-white);
  font-size: var(--tp-t-sm);
  border-radius: 8px;
  padding: 5px 11px;
}
.tp .tp-pdp__thumbs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 14px; }
.tp .tp-pdp__thumb { border: 1px solid var(--tp-line); border-radius: 8px; }
.tp .tp-pdp__thumb.is-active { border: 2px solid var(--tp-ink-club); }
.tp .tp-pdp__licence-line { display: flex; align-items: center; gap: 10px; font-size: var(--tp-t-sm); font-weight: 700; }
.tp .tp-pdp__licence-line img { width: 28px; height: 32px; object-fit: contain; }
.tp .tp-pdp__title { display: flex; align-items: flex-start; gap: 24px; flex-wrap: wrap; }
.tp .tp-pdp__title h1 { flex: 1; min-width: 240px; }
/* Price sits on its own line under the name, not right-aligned beside it. The
   struck retail price runs alongside what they actually pay rather than above
   it — stacked, the "was" reads as the price until the eye catches the rule
   through it. "Edit design" is pushed to the far end because it leaves the
   page, and must not sit in the reading path between the two numbers. */
.tp .tp-pdp__priceline {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  /* Cancels most of the buy column's 18px flex gap. The price belongs to the
     name above it, not to the spec strip below — spaced like a sibling of both
     it read as a free-floating third thing. */
  margin-top: -10px;
}
.tp .tp-pdp__priceline b {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-pdp-price);
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.tp .tp-pdp__was {
  font-size: var(--tp-t-md);
  color: var(--tp-muted);
  text-decoration: line-through;
  white-space: nowrap;
}
.tp .tp-pdp__edit {
  margin-left: auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--tp-t-sm);
  font-weight: 700;
  color: var(--tp-cyan-700);
  white-space: nowrap;
}
.tp .tp-pdp__edit:hover { color: var(--tp-ink); }
.tp .tp-licence-card { padding: 26px 30px; }
.tp .tp-licence-card__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--tp-line);
}
.tp .tp-licence-card__head img { width: 40px; height: 46px; object-fit: contain; flex: none; }
.tp .tp-licence-card__eyebrow {
  font-family: var(--tp-font-body);
  font-weight: 700;
  font-size: var(--tp-t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tp-muted);
  margin-bottom: 3px;
}
.tp .tp-licence-card__title { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-lg); }
.tp .tp-pdp__support {
  display: flex;
  align-items: center;
  gap: 11px;
  background: var(--tp-cyan-50);
  color: var(--tp-cyan-700);
  border-radius: var(--tp-r-sm);
  padding: 13px 16px;
  margin: 18px 0;
  font-size: var(--tp-t-base);
  font-weight: 600;
}
.tp .tp-pdp__support svg { flex: none; }
.tp .tp-pdp__support span { color: var(--tp-ink); }
.tp .tp-ticks--ink { color: var(--tp-ink); font-size: var(--tp-t-sm); }

/* Category section heading — a heavy ink bar, the name, its count, and the
   "see all" pushed right over a hairline. Same device as the live store. */
.tp .tp-catsection + .tp-catsection { margin-top: 44px; }
.tp .tp-catsection__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 14px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--tp-line);
}
.tp .tp-catsection__title {
  position: relative;
  padding-left: 22px;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-4xl);
  letter-spacing: -0.03em;
  line-height: 1.05;
}
.tp .tp-catsection__title::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 8px;
  height: 0.88em;
  border-radius: 4px;
  background: var(--tp-ink);
}
/* Count rides with the title as a superscript, not as another word in the row. */
.tp .tp-catsection__count {
  align-self: flex-start;
  margin-top: 6px;
  font-size: var(--tp-t-sm);
  font-weight: 700;
  color: var(--tp-muted);
}
.tp .tp-catsection__all {
  margin-left: auto;
  flex: none;
  font-weight: 700;
  font-size: var(--tp-t-md);
  color: var(--tp-ink);
  white-space: nowrap;
}
.tp .tp-catsection__all:hover { color: var(--tp-cyan-700); }

/* Licence card. Pads itself rather than wrapping in .tp-card__body, because the
   support band has to sit inside the padding instead of running edge to edge. */
.tp .tp-licence-card { padding: 18px 20px; }
.tp .tp-licence-card__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.tp .tp-licence-card__head img { width: 40px; height: 46px; object-fit: contain; flex: none; }
.tp .tp-licence-card__eyebrow {
  font-size: var(--tp-t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tp-muted);
  margin-bottom: 2px;
}
.tp .tp-licence-card__title {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-acc);
  line-height: 1.25;
}
/* Card footer: the phone number, then the hours, then who the merchant is.
   Reaching a person is something we sell, so the number is set at reading size
   in the accent colour — not as small print under the card. */
.tp .tp-licence-card__foot {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--tp-line);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}
.tp .tp-licence-card__calllabel {
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tp-muted);
  margin-bottom: 2px;
}
.tp .tp-licence-card__phone {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--tp-font-head);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--tp-ink);
}
.tp .tp-licence-card__phone:hover { color: var(--tp-cyan-700); }
.tp .tp-licence-card__hours { font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-licence-card__runby {
  margin-top: 8px;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}

/* Native disclosure — no script, collapsed by default on every breakpoint. */
.tp .tp-acc { border-top: 1px solid var(--tp-line); }
.tp .tp-acc:last-of-type { border-bottom: 1px solid var(--tp-line); }
.tp .tp-acc > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-acc);
}
.tp .tp-acc > summary::-webkit-details-marker { display: none; }
.tp .tp-acc > summary::after {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--tp-muted);
  border-bottom: 2px solid var(--tp-muted);
  transform: rotate(45deg);
  transition: transform 160ms ease;
  flex: none;
  margin-bottom: 4px;
}
.tp .tp-acc[open] > summary::after { transform: rotate(-135deg); margin-bottom: 0; }
.tp .tp-acc__body { padding-bottom: 18px; color: var(--tp-muted); font-size: var(--tp-t-md); }
.tp .tp-acc__body p:last-child { margin-bottom: 0; }

/* Size chart. The markup is whatever was pasted into Printable.size_chart_html,
   so this styles a generic <table> defensively and lets it scroll rather than
   widen the page — a 9-column measurement table does not fit a phone. */
.tp .tp-sizechart { overflow-x: auto; margin: 0 0 14px; -webkit-overflow-scrolling: touch; }
.tp .tp-sizechart table { border-collapse: collapse; width: 100%; min-width: 380px; font-size: var(--tp-t-sm); }
.tp .tp-sizechart th,
.tp .tp-sizechart td { border: 1px solid var(--tp-line); padding: 7px 10px; text-align: center; white-space: nowrap; }
.tp .tp-sizechart th { font-weight: 700; color: var(--tp-ink); background: var(--tp-page); }
.tp .tp-sizechart img { max-width: 100%; height: auto; display: block; }

/* Abbreviated club-page footer. */
.tp .tp-store-footer {
  background: var(--tp-page);
  border-top: 1px solid var(--tp-line);
  margin-top: 64px;
  padding: 40px 0 48px;
  text-align: center;
}
.tp .tp-store-footer__links { display: flex; justify-content: center; gap: 28px; flex-wrap: wrap; margin-bottom: 18px; }
.tp .tp-store-footer__links a { color: var(--tp-ink); font-weight: 600; font-size: var(--tp-t-md); }
.tp .tp-store-footer__links a:hover { color: var(--tp-cta); }
.tp .tp-store-footer__powered {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--tp-muted);
  margin-bottom: 12px;
}
.tp .tp-store-footer__powered img { height: 16px; width: auto; }
.tp .tp-store-footer__legal { font-size: var(--tp-t-sm); color: var(--tp-muted); }

/* Filters. Desktop renders the rail directly; the disclosure is mobile-only.
   They cannot be the same element — see _store_filters.html. */
.tp .tp-filters__rail { display: flex; flex-direction: column; gap: 20px; }
.tp .tp-filters { display: none; }
.tp .tp-filters__toggle::-webkit-details-marker { display: none; }
.tp .tp-filters__panel { display: flex; flex-direction: column; gap: 20px; }

/* The buy form, as a card. Flat on white was tried and the column lost its
   figure — the card is what says "this is the part you act on". The
   configurator's option panel takes the same treatment so the two steps of the
   same flow don't change shape halfway. The 5px two-segment bar is the two-step
   flow made visible: step 1 filled in club yellow, step 2 waiting. */
.tp .tp-personalise,
.tp .tp-cfg__panel {
  padding: 0;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 12px 28px rgba(16, 24, 40, 0.09);
}
.tp .tp-personalise__bar { display: flex; height: 5px; }
.tp .tp-personalise__bar > span { flex: 1; background: var(--tp-line); }
.tp .tp-personalise__bar > span:first-child { background: var(--tp-club-yellow); }
.tp .tp-personalise__body,
.tp .tp-cfg__panel > .tp-cfg__panelbody { padding: 30px 34px; }
.tp .tp-personalise__step {
  font-family: var(--tp-font-body);   /* uppercase — never the display face */
  font-weight: 700;
  font-size: var(--tp-t-xs);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--tp-muted);
  margin-bottom: 10px;
}
.tp .tp-personalise__title {
  font-family: var(--tp-font-head);
  font-size: var(--tp-t-2xl);
  font-weight: 700;
  margin-bottom: 6px;
}
.tp .tp-personalise__lede { font-size: var(--tp-t-base); color: var(--tp-muted); margin-bottom: 22px; }
.tp .tp-personalise__form { display: flex; flex-direction: column; gap: 18px; }
.tp .tp-personalise .tp-field > label {
  display: flex;
  align-items: baseline;
  gap: 9px;
  font-size: var(--tp-t-lg);
  font-weight: 700;
  color: var(--tp-ink);
  margin-bottom: 8px;
}
/* Scoped to the hint, not every span — this rule was also shrinking the "1." */
.tp .tp-personalise .tp-field > label .tp-field__hint { font-size: var(--tp-t-sm); font-weight: 400; color: var(--tp-muted); }
/* Also used as a <button> in the v2 wizard, so it has to reset the UA chrome —
   otherwise it renders as a bordered grey box instead of a text link. */
.tp .tp-personalise__skip {
  font-size: var(--tp-t-base);
  display: block;
  width: 100%;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--tp-ink);
  text-align: center;
  margin-top: 16px;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.tp .tp-input--lg { height: 56px; font-size: var(--tp-t-lg); border-color: var(--tp-line-strong); background: var(--tp-white); }
.tp .tp-personalise .tp-field > label { font-size: var(--tp-t-xl); font-weight: 700; color: var(--tp-ink); margin-bottom: 10px; }
.tp .tp-personalise .tp-field > label .tp-muted { font-size: var(--tp-t-md); font-weight: 400; }
/* The step number is part of the instruction — same size and weight as the
   label. Only the character limit is a muted aside. */
.tp .tp-personalise .tp-field > label .tp-field__num { font-size: inherit; font-weight: inherit; color: inherit; }
.tp .tp-personalise .tp-field > label .tp-field__hint { font-size: var(--tp-t-md); font-weight: 400; color: var(--tp-muted); }
.tp .tp-field > label { font-size: var(--tp-t-md); font-weight: 600; color: var(--tp-ink); }

/* Gallery dots. Overlaid on the image on a translucent pill, not stacked
   beneath it — a strip underneath costs a band the mobile fold cannot spare. */
/* The gallery frame carries the card look so .main-image-container inside it
   stays a plain positioning context — the design overlays are percentages of
   that element, so an extra padded box between them shifts every placement. */
.tp .tp-pdp__frame {
  position: relative;
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  overflow: hidden;
}
.tp .tp-pdp__dots {
  display: none;
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  gap: 10px;
  padding: 8px 14px;
  border-radius: var(--tp-r-pill);
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(4px);
}
.tp .tp-pdp__dots span { width: 8px; height: 8px; border-radius: var(--tp-r-pill); background: var(--tp-line-strong); }
.tp .tp-pdp__dots span.is-active { background: var(--tp-ink-club); }

/* A taller primary — the personalise CTA is the one action on the screen. */
.tp .tp-btn--lg { padding: 17px 28px; font-size: var(--tp-t-lg); }

/* ── 7. Responsive ──────────────────────────────────────────────────────── */

@media (max-width: 1180px) {
  /* One column, and BOTH spanning children reset — otherwise the buy column
     keeps its row span and the accordions land beside it. */
  .tp .tp-pdp { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto; gap: 32px; }
  .tp .tp-pdp__gallery,
  .tp .tp-pdp__buy,
  .tp .tp-pdp__details { grid-column: 1; grid-row: auto; position: static; }
}

@media (max-width: 1120px) {
  .tp .tp-grid--5 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .tp .tp-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tp .tp-hero { grid-template-columns: minmax(0, 1fr); }
  .tp .tp-hero__media { min-height: 320px; }
  .tp .tp-split, .tp .tp-split--narrow-left { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .tp .tp-catalogue { grid-template-columns: minmax(0, 1fr); }
  .tp .tp-catalogue__aside { position: static; }

  /* Mobile: trade the full 40px shell gutter for a narrow one. The cards get
     most of the width back without sitting flush against the screen edge. */
  .tp .tp-catalogue { padding-left: 12px; padding-right: 12px; }
  .tp .tp-footer__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; }
}

@media (max-width: 1000px) {
  /* Club header type steps down before the nav starts clipping. */
  .tp .tp-club-header__name { font-size: var(--tp-t-2xl); max-width: 100%; }
  .tp .tp-club-header__brand > img { width: 44px; height: 50px; }
}

@media (max-width: 860px) {
  :root { --tp-gutter: 20px; }

  /* Club banner: one column, photo above the type — the artwork sets the
     scene, the words carry the offer. The two-column grid had no mobile rule
     at all, so it kept both columns at half width and the display stack broke
     one word per line. */
  .tp .tp-club-banner { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .tp .tp-club-banner__media { order: -1; aspect-ratio: 16 / 10; }
  .tp .tp-club-banner__copy { padding: 26px 22px 30px; }
  .tp .tp-club-banner__stack { font-size: 32px; }
  .tp .tp-club-banner__actions .tp-btn { flex: 1 1 100%; }
  .tp .tp-store-top { padding-top: 12px; }
  /* Breadcrumbs are hidden on mobile, so this padding was pure empty space
     between the header and the product image. */
  .tp .tp-pdp-top { padding-top: 0; }
  /* 76px of tail padding under the accordions is a desktop measure; on a phone
     it reads as the page having ended twice. */
  .tp .tp-pdp { padding-top: 0; padding-bottom: 24px; gap: 24px; }
  .tp .tp-pdp__details { margin-top: 0; }

  /* Club header on mobile: smaller crest, and the name and badge step right
     down — at desktop sizes the badge wrapped onto two lines and the name ate
     the bar. Indent follows the crest: 84px + 22px gap. */
  .tp .tp-club-header__crest { width: 84px; height: 84px; top: 8px; }
  .tp .tp-club-header__ubar-inner {
    padding-left: calc(var(--tp-gutter) + 106px);
    padding-top: 9px;
    padding-bottom: 9px;
    min-height: 62px;
  }
  .tp .tp-club-header__name { gap: 0; }
  .tp .tp-club-header__club { font-size: var(--tp-t-lg); letter-spacing: 0.02em; }
  .tp .tp-club-header__badge {
    font-size: 7px;
    padding: 3px 6px;
    gap: 3px;
    margin-top: 4px;
    white-space: nowrap;
  }
  .tp .tp-club-header__badge svg { width: 8px; height: 8px; }
  .tp .tp-club-header__tp { padding-left: 14px; }
  .tp .tp-club-header__tp img { height: 28px; }
  .tp .tp-club-nav__inner { padding-left: calc(var(--tp-gutter) + 106px); }

  /* Product name: 26px so a long one stays two lines beside the price. */
  .tp .tp-pdp__title h1 {
    font-size: 26px;
    line-height: 1.08;
    letter-spacing: -0.025em;
    min-width: 0;
  }
  .tp .tp-club-nav__links a { font-size: var(--tp-t-sm); padding: 12px 0; }

  /* Filters collapse. The rail rendered its full height above the products,
     so a phone visitor scrolled past every filter to reach one item. */
  .tp .tp-filters__rail { display: none; }
  .tp .tp-filters { display: block; }
  .tp .tp-filters__toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 12px 16px;
    border: 1px solid var(--tp-line-strong);
    border-radius: var(--tp-r-sm);
    background: var(--tp-white);
    font-weight: 600;
    cursor: pointer;
    list-style: none;
  }
  .tp .tp-filters__toggle::-webkit-details-marker { display: none; }
  .tp .tp-filters[open] > .tp-filters__toggle { margin-bottom: 16px; }
  .tp .tp-filters:not([open]) .tp-filters__panel { display: none; }

  /* Gallery: full-bleed, no inner padding, dots instead of thumbnails. */
  /* Full-bleed, square corners, no frame. The gallery is the product on mobile,
     so it runs to both edges instead of sitting in a bordered card. */
  .tp .tp-pdp__frame {
    border: 0;
    border-radius: 0;
    margin: 0 calc(var(--tp-gutter) * -1);
  }
  .tp .tp-pdp__frame .main-image-container { border-radius: 0; }
  .tp .tp-pdp__thumbs { padding-inline: 0; }
  .tp .tp-pdp__thumbs { display: none; }
  .tp .tp-pdp__dots { display: flex; }
  .tp .tp-media--pad img { padding: 0; }

  /* PDP: name, then price on the line below it. */
  .tp .tp-crumbs--desktop { display: none; }
  .tp .tp-pdp__title { flex-direction: row; align-items: baseline; gap: 14px; flex-wrap: nowrap; }
  .tp .tp-pdp__title h1 { font-size: 26px; line-height: 1.08; letter-spacing: -0.025em; min-width: 0; }
  .tp .tp-pdp__priceline { margin-top: 8px; gap: 10px; }
  .tp .tp-pdp__priceline b { font-size: 26px; }
  .tp .tp-pdp__priceline .tp-small { display: none; }   /* "incl. VAT" — desktop only */
  .tp .tp-personalise__body,
  .tp .tp-cfg__panel > .tp-cfg__panelbody { padding: 22px 18px; }
  .tp .tp-grid--3, .tp .tp-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tp .tp-grid__wide { grid-column: span 2; }
  .tp .tp-section { padding-top: 56px; }
  /* Step down one rung each, not two — collapsing display and h1 onto the same
     size is what flattened the hierarchy at this breakpoint. */
  .tp .tp-display { font-size: var(--tp-t-4xl); }
  .tp .tp-h1 { font-size: var(--tp-t-3xl); }
  .tp .tp-h2 { font-size: var(--tp-t-2xl); }
  .tp .tp-h3 { font-size: var(--tp-t-xl); }
  .tp .tp-hero__copy { padding: 30px 22px; }
  .tp .tp-hero__media { min-height: 260px; }
  .tp .tp-panel { padding: 30px 22px; }
  .tp .tp-trust { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tp .tp-row { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .tp .tp-row__media { max-width: 260px; }

  /* One column turns the price block into its own right-aligned row, which left
     a tall gap between the name and the swatches. Sit the badge beside the
     price instead and let the title use the full width.

     Head stacks rather than wraps. Side by side, the price is `flex: none` with
     a nowrap 26px number, so it took its full width out of a phone and left the
     name whatever remained — "Zina hoodie - ROSARIO PRO" came out four lines
     deep in a 130px column. Column-direction gives the name the whole width and
     drops the price under it. */
  .tp .tp-row__body { gap: 14px; }
  .tp .tp-row__head { flex-direction: column; align-items: stretch; gap: 10px; }
  .tp .tp-row__title { flex: none; min-width: 0; }
  .tp .tp-row__price { text-align: left; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  .tp .tp-row__price b { font-size: 22px; }
  .tp .tp-row__badge { margin-top: 0; }
  .tp .tp-ticks--grid { grid-template-columns: minmax(0, 1fr); }
  .tp .tp-crests { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
  .tp .tp-crest { padding: 16px 10px; }
  .tp .tp-crest img { width: 60px; height: 60px; }

  /* ── Mobile chrome ──────────────────────────────────────────────────────
     The desktop header is three stacked rows of flex that wrap badly at phone
     width: the nav shrank its own links until "Club stores" broke onto two
     lines and "Help" clipped to "H", and the whole thing ate 540px before any
     content. Rebuilt here as: one compact row (logo + actions), search below,
     then a single horizontally-scrolling nav with the CTA pinned beside it. */
  .tp .tp-announce__inner { padding-top: 7px; padding-bottom: 7px; justify-content: center; }
  .tp .tp-announce__links { display: none; }   /* the same links live in the footer,
                                                  and the phone is in the header */
  .tp .tp-header__bar { gap: 12px 16px; padding-top: 12px; padding-bottom: 12px; }
  .tp .tp-header__logo img { height: 32px; }
  .tp .tp-header__actions { margin-left: auto; gap: 18px; }
  .tp .tp-contact__sub { display: none; }
  .tp .tp-contact__main { font-size: var(--tp-t-sm); gap: 6px; }
  .tp .tp-search { order: 3; flex: 1 1 100%; min-width: 0; max-width: none; }
  .tp .tp-search input { height: 42px; }
  /* The account catalogue drops search on mobile: that buyer came for a list
     someone prepared, not to search the marketplace, and the full-width field
     was pushing the products off the first screen. */
  .tp .tp-search--desk { display: none; }

  /* Shopify-pattern mobile menu: a hamburger on the left of the utility row,
     the links in a full-width drop panel. The old horizontal row could not be
     made to work — five links plus a pill CTA at 360px will always either wrap
     mid-word or clip, and a scrolling nav hides items with no affordance. */
  .tp .tp-nav-bar__inner { gap: 12px; position: relative; }
  .tp .tp-nav-bar__inner > .tp-nav { display: none; }
  .tp .tp-menu { display: block; flex: 0 0 auto; }
  .tp .tp-menu__toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 12px 0;
    font-size: var(--tp-t-md);
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    user-select: none;
  }
  .tp .tp-menu[open] > .tp-menu__toggle { color: var(--tp-cta); }
  .tp .tp-menu__panel {
    position: absolute;
    left: calc(var(--tp-gutter) * -1);
    right: calc(var(--tp-gutter) * -1);
    top: 100%;
    z-index: 45;
    background: var(--tp-white);
    border-top: 1px solid var(--tp-line);
    border-bottom: 1px solid var(--tp-line);
    box-shadow: 0 12px 24px rgba(16, 36, 60, 0.08);
    padding: 8px var(--tp-gutter) 16px;
  }
  .tp .tp-menu__panel .tp-nav { flex-direction: column; align-items: stretch; gap: 0; }
  .tp .tp-menu__panel .tp-nav a {
    padding: 14px 0;
    border-bottom: 1px solid var(--tp-line-soft);
    border-bottom-color: var(--tp-line-soft);
  }
  .tp .tp-menu__panel .tp-nav a:last-child { border-bottom: 0; }
  .tp .tp-nav-bar__cta { flex: none; margin-left: auto; padding: 9px 16px; font-size: var(--tp-t-sm); }

  .tp .tp-rail { gap: 16px; }
  .tp .tp-rail__label { display: none; }
  .tp .tp-hero__actions .tp-btn { flex: 1 1 100%; }
}

@media (max-width: 560px) {
  .tp .tp-grid--5, .tp .tp-grid--4, .tp .tp-grid--3, .tp .tp-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Two-up mobile listing, after eobuwie: no card chrome at all. The grid goes
     full-bleed and its 1px gap over a line-coloured background draws the only
     rules on the screen, so the photo runs to the edge and the product — not a
     box around it — is what you see. */
  /* A real gap, not the 1px hairline — with the gutter back, hairlines read as
     a grey field between tiles rather than as rules. */
  .tp .tp-grid--3, .tp .tp-grid--4, .tp .tp-grid--2 { gap: 10px; }
  .tp .tp-product {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: var(--tp-white);
  }
  .tp .tp-product .tp-media { border-radius: 0; }

  /* Badge square and pinned to the foot of the photo, the way eobuwie stacks
     its flags — a floating pill reads as an overlay, this reads as a label. */
  .tp .tp-product__badge {
    top: auto;
    bottom: 0;
    left: 0;
    max-width: 100%;
    border-radius: 0;
    padding: 5px 10px;
    font-size: 10px;
  }

  /* Title, then the delivery line, then price — tight and left-aligned. */
  .tp .tp-product__body { padding: 9px 11px 14px; gap: 3px; }
  .tp .tp-product__title { font-size: var(--tp-t-sm); line-height: 1.35; }
  .tp .tp-product__price { margin-top: 2px; gap: 6px; }
  .tp .tp-product__price b { font-size: var(--tp-t-md); }
  .tp .tp-product__foot { padding-top: 6px; gap: 6px; }
  .tp .tp-product__note { font-size: 11px; text-align: left; }
  .tp .tp-club-banner__stack { font-size: 27px; }
  .tp .tp-club-header__badge { font-size: 10px; padding: 3px 8px; }
  .tp .tp-footer__cols { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .tp .tp-display { font-size: var(--tp-t-2xl); }
  .tp .tp-h1 { font-size: var(--tp-t-2xl); }
  .tp .tp-club-tile { min-height: 260px; }
  .tp .tp-footer__copy { margin-left: 0; }
  .tp .tp-trust { grid-template-columns: minmax(0, 1fr); }
  /* Phone and account collapse to their icons; the labels are the only thing
     that was forcing the action row wider than the logo. */
  .tp .tp-contact__label { display: none; }
  .tp .tp-header__actions { gap: 20px; }
  .tp .tp-crests { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Print/reduced-motion hygiene: nothing here animates, so there is no
   prefers-reduced-motion block to write. That is the point. */
/* ── 8. PDP application ──────────────────────────────────────────────────
   Markup bound to the Vue app in teamprinted/includes/_pdp_app_v2.html.
   The main-image-container / zoom-wrapper / design-overlay names are the app's,
   not ours: it queries the first on resize to recompute image bounds, and the
   overlays are positioned absolutely against it. Renaming them breaks the
   design compositing, so they keep the original names and geometry. */

[v-cloak] { display: none; }

.tp .main-image-container {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--tp-photo);
  cursor: zoom-in;
}
.tp .main-image-container.zoomed { cursor: zoom-out; }
.tp .main-image-container .zoom-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.15s ease-out;
  transform-origin: center center;
}
.tp .main-image-container.zoomed .zoom-wrapper { transform: scale(2); }
.tp .main-image-container img { width: 100%; height: 100%; object-fit: contain; }
.tp .design-overlay { position: absolute; pointer-events: none; }
/* Same job on the thumbnail strip. Without absolute positioning the overlay
   lands in flow under the garment and .tp-media's overflow:hidden eats it, so
   the strip shows blank garments beside a personalised main image. The inline
   left/top/width/height from the placement geometry supply the box. */
.tp .thumb-design-overlay { position: absolute; pointer-events: none; object-fit: contain; }

.tp .mockup-loading-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.8);
}
.tp .mockup-spinner,
.tp .tp-fullload__spinner {
  width: 40px;
  height: 40px;
  border: 3px solid var(--tp-line);
  border-top-color: var(--tp-ink);
  border-radius: 50%;
  animation: tp-spin 0.8s linear infinite;
}
@keyframes tp-spin { to { transform: rotate(360deg); } }

/* Full-screen loader: the render and the add-to-cart redirect both block. */
.tp-fullload {
  position: fixed;
  inset: 0;
  z-index: 1080;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: rgba(255, 255, 255, 0.92);
}
.tp-fullload p { font-weight: 700; color: var(--tp-ink); margin: 0; }

/* Step bar: one segment per step, filled up to the current one. */
.tp .tp-personalise__bar > span.is-on { background: var(--tp-club-yellow); }

.tp .tp-fielderror {
  background: var(--tp-bad-bg, #FEF2F2);
  color: var(--tp-bad-ink, #B42318);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: var(--tp-t-sm);
  font-weight: 600;
  margin-bottom: 14px;
}

.tp .tp-optgroup + .tp-optgroup { margin-top: 22px; }
/* The options step. .tp-optgroup only spaces itself against another optgroup,
   so outside the guided flow the first one landed on the name field above it. */
.tp .tp-optsteps { margin-top: 26px; }
.tp .tp-optgroup__title {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-lg);
  margin-bottom: 10px;
}

/* Which cut — men's, women's, kids'. Tabs rather than a select: there are two
   or three of them, they carry different prices, and a closed select hides the
   fact that a choice exists at all. Same tile language as the sizes below, so
   the column reads as one set of choices. */
.tp .tp-styletabs { display: flex; flex-wrap: wrap; gap: 10px; }
.tp .tp-styletab {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  min-width: 0;
  border: 1px solid var(--tp-line-strong);
  background: var(--tp-white);
  border-radius: var(--tp-r-sm);
  padding: 12px 18px;
  font-family: var(--tp-font-head);
  color: var(--tp-ink);
  text-align: left;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.tp .tp-styletab:hover { border-color: var(--tp-ink); }
.tp .tp-styletab.is-active { background: var(--tp-ink); border-color: var(--tp-ink); color: var(--tp-white); }
.tp .tp-styletab__name { font-weight: 700; font-size: var(--tp-t-md); line-height: 1.2; }
/* Lighter than the name — it answers "how much", but the cut is what is being
   picked. Opacity rather than a grey, so it holds up on the ink fill too. */
.tp .tp-styletab__price { font-family: var(--tp-font-body); font-weight: 600; font-size: var(--tp-t-sm); opacity: 0.72; }

/* Size tiles. Selected is solid ink, because on this page "chosen" has to
   survive a glance. The .tp-styletab above deliberately shares this language —
   one column, one way of saying "chosen". */
.tp .tp-sizes { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.tp .tp-size {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 58px;
  border: 1px solid var(--tp-line-strong);
  background: var(--tp-white);
  border-radius: var(--tp-r-sm);
  padding: 18px 6px;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-lg);
  line-height: 1.1;
  text-align: center;
  color: var(--tp-ink);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.tp .tp-size:hover { border-color: var(--tp-ink); }
.tp .tp-size.is-active { background: var(--tp-ink); border-color: var(--tp-ink); color: var(--tp-white); }
.tp .tp-size small { display: block; font-size: var(--tp-t-xs); font-weight: 600; opacity: 0.7; }
/* Struck through rather than hidden — a missing size reads as a broken page. */
.tp .tp-size.is-out { opacity: 0.4; text-decoration: line-through; cursor: not-allowed; }

.tp .tp-swatchrow { display: flex; flex-wrap: wrap; gap: 10px; }
.tp .tp-swatchdot {
  width: 38px;
  height: 38px;
  border-radius: var(--tp-r-pill);
  border: 1px solid var(--tp-line-strong);
  cursor: pointer;
  padding: 0;
}
/* Ring outside the swatch, so it reads on white and on black alike. */
.tp .tp-swatchdot.is-active {
  box-shadow: 0 0 0 2px var(--tp-white), 0 0 0 4px var(--tp-ink);
}
.tp .tp-swatchdot--more {
  background: var(--tp-wash);
  color: var(--tp-ink);
  font-weight: 700;
  font-size: var(--tp-t-sm);
}

.tp .tp-choices { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.tp .tp-choice {
  border: 1px solid var(--tp-line-strong);
  background: var(--tp-white);
  border-radius: var(--tp-r-sm);
  padding: 10px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  font-size: var(--tp-t-xs);
  font-weight: 700;
}
.tp .tp-choice img { width: 100%; aspect-ratio: 1; object-fit: contain; }
.tp .tp-choice.is-active { border-color: var(--tp-ink); box-shadow: inset 0 0 0 1px var(--tp-ink); }

.tp .tp-qty { display: inline-flex; align-items: center; border: 1px solid var(--tp-line-strong); border-radius: var(--tp-r-sm); overflow: hidden; }
.tp .tp-qty button {
  width: 46px;
  height: 46px;
  border: 0;
  background: var(--tp-white);
  font-size: var(--tp-t-xl);
  font-weight: 700;
  cursor: pointer;
  color: var(--tp-ink);
}
.tp .tp-qty button:disabled { opacity: 0.35; cursor: not-allowed; }
.tp .tp-qty input {
  width: 64px;
  height: 46px;
  border: 0;
  border-left: 1px solid var(--tp-line);
  border-right: 1px solid var(--tp-line);
  text-align: center;
  font-weight: 700;
  font-size: var(--tp-t-md);
  -moz-appearance: textfield;
}
.tp .tp-qty input::-webkit-outer-spin-button,
.tp .tp-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.tp .tp-personalise__nav { margin-top: 22px; display: flex; flex-direction: column; gap: 12px; }
.tp .tp-personalise__back {
  border: 0;
  background: none;
  color: var(--tp-muted);
  font-weight: 700;
  font-size: var(--tp-t-sm);
  cursor: pointer;
  padding: 4px;
}
.tp .tp-personalise__back:hover { color: var(--tp-ink); }

/* Single ⇄ team order. A full-width row rather than a third button under the
   CTA: it doesn't buy anything, it changes what the form is asking for, and
   stacked with the buy buttons it read as a competing way to check out. */
.tp .tp-modeswitch {
  width: 100%;
  margin-top: 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 17px 20px;
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r-sm);
  background: var(--tp-white);
  font: inherit;
  font-weight: 700;
  font-size: var(--tp-t-md);
  color: var(--tp-ink);
  text-align: left;
  cursor: pointer;
}
.tp .tp-modeswitch:hover { border-color: var(--tp-cta); background: var(--tp-cta-50); color: var(--tp-cta); }
.tp .tp-modeswitch svg { flex: none; }
.tp .tp-modeswitch__go { margin-left: auto; color: var(--tp-muted); }

/* Store tools. Boxed after all — a rule and a label were not enough. These sat
   directly under the buyer's "Request to add to my Catalog" wearing the same
   outline button, and reps aiming for "update the product" were hitting
   "request" instead, which files a request and sends the lead back to the
   graphic queue. So the panel is a ground of its own and the buttons are solid
   ink: heavy and neutral-dark, an internal control rather than a second offer.
   Quiet wash, no dash — it must read as separate, not as the loudest thing in
   the column. */
.tp .tp-staffacts {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 26px;
  padding: 16px;
  border: 1px solid var(--tp-line-strong);
  border-left: 4px solid var(--tp-ink);
  border-radius: var(--tp-r-sm);
  background: var(--tp-wash);
}
.tp .tp-staffacts__label {
  font-family: var(--tp-font-body);
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tp-ink);
}
/* Solid, so weight alone tells them apart at a glance — the buyer's button in
   this column is always light and outlined. */
.tp .tp-staffacts .tp-btn {
  border-color: var(--tp-ink);
  background: var(--tp-ink);
  color: var(--tp-white);
}
.tp .tp-staffacts .tp-btn:hover {
  border-color: var(--tp-ink-deep);
  background: var(--tp-ink-deep);
  color: var(--tp-white);
}

/* "Request to add to my Catalog". Same rule-and-stack shape as the store tools
   above, but this one belongs to the buyer, so it takes no internal label. */
.tp .tp-catreq {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 18px;
  border-top: 1px solid var(--tp-line);
}
.tp .tp-catreq__note { margin: 0; font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-catreq__done {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 0;
  padding: 12px 14px;
  border-radius: var(--tp-r-sm);
  background: var(--tp-good-bg);
  color: var(--tp-good-ink);
  font-size: var(--tp-t-sm);
  font-weight: 600;
}
.tp .tp-catreq__done svg { flex: none; margin-top: 1px; }
.tp .tp-catreq__err {
  margin: 0;
  padding: 10px 14px;
  border-radius: var(--tp-r-sm);
  background: var(--tp-bad-bg, #FEF2F2);
  color: var(--tp-bad-ink, #B42318);
  font-size: var(--tp-t-sm);
  font-weight: 600;
}

/* ── Team order: roster and quantity grid ───────────────────────────────── */

/* The panel header for team mode. "Add person" lives here rather than only at
   the foot of the list: on a twenty-name roster the foot is off-screen. */
.tp .tp-teamhead {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.tp .tp-teamhead h2 { margin: 0; }
.tp .tp-teamhead .tp-btn { margin-left: auto; }

/* One row per person. Head and rows share flex track widths rather than a
   grid-template, because the number of personalisation fields is set by the
   product — a template would have to be written per product. */
.tp .tp-roster {
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-sm);
  overflow: hidden;
}
.tp .tp-roster__head,
.tp .tp-roster__row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
}
.tp .tp-roster__head {
  background: var(--tp-wash);
  border-bottom: 1px solid var(--tp-line);
  font-family: var(--tp-font-body);
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tp-muted);
}
.tp .tp-roster__row + .tp-roster__row { border-top: 1px solid var(--tp-line-soft); }

/* Shared tracks. :first-child and :last-child are (0,3,0) and so beat the
   general `> span` rule below them regardless of source order. */
.tp .tp-roster__head > span,
.tp .tp-roster__row .tp-input { flex: 1 1 0; min-width: 0; }
.tp .tp-roster__head > :first-child,
.tp .tp-roster__n {
  flex: none;
  width: 22px;
  text-align: right;
  font-weight: 700;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}
.tp .tp-roster__head > :nth-last-child(2),
.tp .tp-roster__row .tp-select--plain { flex: 0 0 140px; }
.tp .tp-roster__head > :last-child,
.tp .tp-roster__del { flex: none; width: 40px; }
/* White, like every other field on the page — the wash fill made an empty row
   look disabled next to the one the cursor was in. Set at body size rather than
   the metadata size the roster started at: these are the names that get printed
   on the shirts, and the buyer proofreads them off this list. */
.tp .tp-roster__row .tp-input {
  background: var(--tp-white);
  border-color: var(--tp-line-strong);
  height: 50px;
  font-size: var(--tp-t-lg);
}
.tp .tp-roster__n { font-size: var(--tp-t-md); }

.tp .tp-roster__del {
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--tp-muted);
  cursor: pointer;
}
.tp .tp-roster__del svg { width: 21px; height: 21px; }
.tp .tp-roster__del:hover { background: var(--tp-wash); color: #b3261e; }

.tp .tp-roster__foot {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px;
  border-top: 1px solid var(--tp-line);
  background: var(--tp-wash);
}
/* Dashed, because it adds a row rather than submitting anything — the same
   language the configurator uses for an empty print location. */
.tp .tp-roster__add {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border: 1px dashed var(--tp-line-strong);
  border-radius: var(--tp-r-sm);
  background: var(--tp-white);
  font: inherit;
  font-weight: 700;
  font-size: var(--tp-t-sm);
  color: var(--tp-ink);
  cursor: pointer;
}
.tp .tp-roster__add:hover { border-color: var(--tp-cyan); color: var(--tp-cyan-700); }
.tp .tp-roster__foot .tp-small { margin-left: auto; }

/* .tp-select is an inline-flex shell used on divs; on a real <select> it eats
   the native caret and the platform picker sizing. This is the element form. */
.tp .tp-select--plain {
  display: block;
  width: 100%;
  height: 50px;
  padding: 0 12px;
  border: 1px solid var(--tp-line-strong);
  border-radius: 8px;
  background: var(--tp-white);
  font: inherit;
  font-size: var(--tp-t-md);
  font-weight: 500;
  color: var(--tp-ink);
  cursor: pointer;
}
.tp .tp-select--plain:focus { border-color: var(--tp-cyan); outline: none; }

/* Quantities by size, one wrapping grid per garment. Not a table: size runs
   differ between an adult and a kids printable, so the columns would differ
   between the two grids anyway. */
.tp .tp-bulkgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: 10px;
}
/* The size caption sits above the box, not in it. Sharing the box capped the
   number at 21px to leave the caption room; on its own the field can carry the
   quantity at full sub-section size, which is what the buyer scans when they
   check the run back. The border stays on one element (__field) — a bordered
   input inside a bordered wrapper put two rings around every number. */
.tp .tp-bulkgrid__cell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  cursor: text;
}
.tp .tp-bulkgrid__size {
  font-family: var(--tp-font-body);
  font-size: var(--tp-t-sm);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  color: var(--tp-muted);
}
.tp .tp-bulkgrid__field {
  display: block;
  padding: 10px 8px;
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r-sm);
  background: var(--tp-white);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.tp .tp-bulkgrid__cell:hover .tp-bulkgrid__field { border-color: var(--tp-ink); }
.tp .tp-bulkgrid input {
  width: 100%;
  border: 0;
  background: none;
  padding: 0;
  text-align: center;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-2xl);
  line-height: 1.2;
  color: var(--tp-ink);
  -moz-appearance: textfield;
}
.tp .tp-bulkgrid input:focus { outline: none; }
.tp .tp-bulkgrid input::placeholder { color: var(--tp-line-strong); font-weight: 700; }
.tp .tp-bulkgrid input::-webkit-outer-spin-button,
.tp .tp-bulkgrid input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Focus rings the field, since the field is what the buyer sees as the control. */
.tp .tp-bulkgrid__cell:focus-within .tp-bulkgrid__field { border-color: var(--tp-cyan); box-shadow: 0 0 0 3px var(--tp-cyan-25); }
/* A filled cell is the only thing on the screen saying the order is not empty,
   so it takes ink — and the size label goes with it, or the count floats above
   a caption still styled as "nothing here". */
.tp .tp-bulkgrid__cell.is-set .tp-bulkgrid__field { border-color: var(--tp-ink); box-shadow: inset 0 0 0 1px var(--tp-ink); }
.tp .tp-bulkgrid__cell.is-set .tp-bulkgrid__size { color: var(--tp-ink); }

@media (max-width: 560px) {
  .tp .tp-sizes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .tp .tp-choices { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The roster stops being a table and becomes one stacked block per person:
     number and bin on a strip, then the fields full-width beneath. `order`
     keeps the bin on the strip — without it the 100%-basis inputs wrap first
     and strand the number on a line of its own. */
  .tp .tp-roster__head { display: none; }
  .tp .tp-roster__row { flex-wrap: wrap; padding: 14px 12px; }
  .tp .tp-roster__n { flex: 1 1 auto; order: 0; text-align: left; }
  .tp .tp-roster__del { order: 1; }
  .tp .tp-roster__row .tp-input { flex: 1 1 100%; order: 2; }
  .tp .tp-roster__row .tp-select--plain { flex: 1 1 100%; order: 3; }
  .tp .tp-bulkgrid { grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); }
}

/* ── Help & FAQ page ────────────────────────────────────────────────────────
   Contact rail beside the answers. Its own namespace, not .tp-faq — that is an
   existing component (a flat Q/A list) and reusing the name would restyle every
   page carrying one. */
.tp .tp-faqpage { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 40px; align-items: start; padding-bottom: 72px; }
/* Contact first in the DOM so it stacks ABOVE the answers on a phone — someone
   who came here to reach a person shouldn't scroll past sixteen questions. */
.tp .tp-faqpage__rail { position: sticky; top: calc(var(--tp-header-h) + 16px); }
.tp .tp-faqpage__call {
  background: var(--tp-wash);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  padding: 22px 24px;
}
.tp .tp-faqpage__method {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 0;
  border-top: 1px solid var(--tp-line);
  color: var(--tp-ink);
}
.tp .tp-faqpage__method svg { flex: none; color: var(--tp-cyan-700); }
.tp .tp-faqpage__method:hover { color: var(--tp-cyan-700); }
.tp .tp-faqpage__method > span { display: flex; flex-direction: column; min-width: 0; }
.tp .tp-faqpage__method strong { font-size: var(--tp-t-base); }
.tp .tp-faqpage__method span span { font-size: var(--tp-t-sm); color: var(--tp-muted); overflow-wrap: anywhere; }
.tp .tp-faqpage__cta { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--tp-line); }
.tp .tp-faqpage__cta b { font-family: var(--tp-font-head); font-size: var(--tp-t-lg); }
.tp .tp-faqpage__answers { min-width: 0; }
.tp .tp-faqpage__group + .tp-faqpage__group { margin-top: 40px; }
.tp .tp-faqpage__grouptitle { margin-bottom: 10px; }

@media (max-width: 1000px) {
  .tp .tp-faqpage { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .tp .tp-faqpage__rail { position: static; }
}

/* FAQ left rail: sticky, so the "call us" card travels with the answers instead
   of stranding a heading above a column of white space. */
.tp .tp-faq__rail { position: sticky; top: calc(var(--tp-header-h) + 16px); }
.tp .tp-faq__call {
  background: var(--tp-wash);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}
.tp .tp-faq__calllabel {
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tp-muted);
}
.tp .tp-faq__phone {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-2xl);
  letter-spacing: -0.02em;
  color: var(--tp-ink);
}
.tp .tp-faq__phone:hover { color: var(--tp-cyan-700); }

@media (max-width: 1000px) {
  .tp .tp-faq__rail { position: static; }
}

/* ── Help: FAQ beside the channel card ──────────────────────────────────── */
.tp .tp-helpsplit {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: 40px;
  align-items: start;
}
.tp .tp-helpsplit__title { margin-bottom: 20px; }

/* Channel card. Rows are the whole tap target — a link inside a paragraph is a
   support channel people don't use. */
.tp .tp-help {
  background: var(--tp-page, var(--tp-wash));
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg);
  padding: 28px;
  position: sticky;
  top: 24px;
}
.tp .tp-help__title {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-2xl);
  letter-spacing: -0.02em;
  margin-bottom: 6px;
}
.tp .tp-help__lede { font-size: var(--tp-t-md); color: var(--tp-muted); line-height: 1.5; margin: 0 0 18px; }
.tp .tp-help__row {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  padding: 14px 16px;
  color: var(--tp-ink);
  transition: border-color 120ms ease, transform 120ms ease;
}
.tp .tp-help__row + .tp-help__row { margin-top: 10px; }
.tp .tp-help__row:hover { border-color: var(--tp-line-strong); transform: translateY(-1px); }
.tp .tp-help__icon {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  color: var(--tp-white);
}
/* Channel-native colours: recognised faster than any brand-tinted equivalent. */
.tp .tp-help__icon--wa { background: #25D366; }
.tp .tp-help__icon--mail { background: var(--tp-cyan, #3BA7DD); }
.tp .tp-help__icon--phone { background: var(--tp-ink); }
.tp .tp-help__body { display: flex; flex-direction: column; min-width: 0; }
.tp .tp-help__name { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-lg); }
.tp .tp-help__meta {
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tp .tp-help__arrow { margin-left: auto; flex: none; color: var(--tp-muted); }
.tp .tp-help__row:hover .tp-help__arrow { color: var(--tp-ink); }

/* Licence bar: a footnote, not a section — one line of claim, tightly set. */
.tp .tp-licence__copy { flex: 1; min-width: 260px; }
.tp .tp-licence__copy .tp-h5 { margin-bottom: 3px; }
.tp .tp-licence__copy p { margin: 0; font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-licence__partner { flex: none; text-align: right; }
.tp .tp-licence__partner img { height: 18px; width: auto; display: inline-block; }
.tp .tp-licence__partner span { display: block; margin-top: 5px; font-size: var(--tp-t-sm); color: var(--tp-muted); }

@media (max-width: 1000px) {
  .tp .tp-helpsplit { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .tp .tp-help { position: static; }
}
@media (max-width: 560px) {
  .tp .tp-help { padding: 20px 16px; }
  .tp .tp-licence__partner { text-align: left; }
}

/* ── Wholesale catalogue head (/s/) ─────────────────────────────────────────
   Not the club banner: this is our trade list for a buyer, so it states whose
   catalogue it is, how big it is and that the prices are trade — no fan copy,
   no club colour field. */
.tp .tp-cathead {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  background: var(--tp-wash);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg);
  padding: 26px 30px;
}
.tp .tp-cathead__crest { width: 64px; height: 64px; object-fit: contain; flex: none; }
.tp .tp-cathead__copy { flex: 1; min-width: 260px; }
.tp .tp-cathead__eyebrow {
  display: block;
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tp-muted);
  margin-bottom: 4px;
}
.tp .tp-cathead__title { font-size: var(--tp-t-3xl); margin-bottom: 4px; }
.tp .tp-cathead__meta { margin: 0; font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-cathead__actions {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  text-align: center;
}
.tp .tp-cathead__phone {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-md);
  color: var(--tp-ink);
}
.tp .tp-cathead__phone:hover { color: var(--tp-cyan-700); }

@media (max-width: 860px) {
  .tp .tp-cathead { padding: 20px; gap: 16px; }
  .tp .tp-cathead__title { font-size: var(--tp-t-2xl); }
  .tp .tp-cathead__actions { width: 100%; }
}


/* ── 9. Ordering catalogue (/s/) ────────────────────────────────────────── */


.oc-stripe { display: flex; height: 4px; }
.oc-stripe span { flex: 1; }
.oc-stripe span:nth-child(1) { background: #00a0dc; }
.oc-stripe span:nth-child(2) { background: #e6007e; }
.oc-stripe span:nth-child(3) { background: #ffe600; }
.oc-stripe span:nth-child(4) { background: #1b1b1b; }

/* Account bar. Two identities: ours (who runs it) and theirs (whose catalogue
   it is), split by a rule. The rep is a named person, not a support address —
   on an agreed-price catalogue the relationship IS the product. */
.oc-bar { background: var(--tp-white); border-bottom: 1px solid var(--tp-line); }
.oc-bar__inner { display: flex; align-items: center; gap: 22px; padding: 16px 0; }
.oc-bar__logo { height: 30px; width: auto; flex: none; }
.oc-bar__who { padding-left: 22px; border-left: 1px solid var(--tp-line); min-width: 0; }
/* First thing in the bar now that our logo has gone — the dividing rule split
   it from nothing, and the indent pushed it out of line with the page. */
.oc-bar__who--lead { padding-left: 0; border-left: 0; }
.oc-bar__account { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-lg); line-height: 1.2; }
.oc-bar__meta { font-size: var(--tp-t-sm); color: var(--tp-muted); }
.oc-bar__rep { margin-left: auto; display: flex; align-items: center; gap: 14px; }
.oc-bar__repname { text-align: right; }
.oc-bar__replabel { display: block; font-size: var(--tp-t-xs); font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--tp-muted); }
.oc-bar__repname b { font-family: var(--tp-font-head); font-size: var(--tp-t-lg); }
.oc-bar__avatar { width: 44px; height: 44px; border-radius: 999px; object-fit: cover; flex: none; background: var(--tp-wash); }
.oc-iconbtn {
  width: 44px; height: 44px; border-radius: 12px; flex: none;
  border: 1px solid var(--tp-line); background: var(--tp-white);
  display: grid; place-items: center; color: var(--tp-ink); cursor: pointer;
}
.oc-iconbtn:hover { border-color: var(--tp-line-strong); background: var(--tp-wash); }

/* Hero. Carries the offer reference and who prepared it — this is a document
   with provenance, and the buyer may be forwarding it internally for approval. */
.oc-hero { background: var(--tp-ink); color: var(--tp-white); }
.oc-hero__inner { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; padding: 46px 0 52px; }
/* Crest beside the name, not above it: one lockup, the way a crest is worn.
   The provenance line then sits under both, explaining what the page is. */
.oc-hero__lockup { display: flex; align-items: center; gap: 22px; }
.oc-hero__crest { width: 96px; height: 96px; flex: none; object-fit: contain; }
.oc-hero__eyebrow { display: flex; align-items: center; gap: 12px; font-size: var(--tp-t-xs); font-weight: 700; letter-spacing: .11em; text-transform: uppercase; color: rgba(255,255,255,.76); }
.oc-hero__eyebrow::before { content: ""; width: 26px; height: 3px; background: var(--tp-club-yellow); flex: none; }
.oc-hero h1 { font-size: var(--tp-t-4xl); letter-spacing: -.03em; line-height: 1.04; margin-bottom: 0; }
.oc-hero p { margin: 0; font-size: var(--tp-t-lede); line-height: 1.55; color: rgba(255,255,255,.82); max-width: 62ch; }

/* The single most important line on the page for a school or a small club:
   they assume a trade catalogue means minimums. It doesn't. */
.oc-nomin { background: var(--tp-cyan-50); border-bottom: 1px solid var(--tp-line); }
.oc-nomin__inner { display: flex; align-items: center; gap: 18px; padding: 18px 0; flex-wrap: wrap; }
.oc-nomin svg { color: var(--tp-cyan-700); flex: none; }
.oc-nomin b { font-family: var(--tp-font-head); font-size: var(--tp-t-xl); letter-spacing: -.015em; }
.oc-nomin span { font-size: var(--tp-t-md); color: var(--tp-cyan-700); }

.oc-sec { padding-top: 46px; }
.oc-sec__head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin-bottom: 8px; }
.oc-sec__head h2 { font-size: var(--tp-t-3xl); letter-spacing: -.025em; }
.oc-sec__count { font-size: var(--tp-t-md); color: var(--tp-muted); }
.oc-sec__lede { font-size: var(--tp-t-md); color: var(--tp-muted); margin: 0 0 22px; max-width: 76ch; }
.oc-pill {
  margin-left: auto; display: inline-flex; align-items: center; gap: 7px;
  background: var(--tp-good-bg, #E8F6EE); color: var(--tp-good-ink, #1F7A4C);
  font-size: var(--tp-t-sm); font-weight: 700; padding: 8px 14px; border-radius: 999px;
}

/* ── Quick order row ───────────────────────────────────────────────────────
   One row per product, quantities typed straight into a size grid. A buyer
   reordering PE kit does not want a product page, a gallery or a wizard — the
   fastest correct path is a grid and a total. */
.oc-row {
  /* 260px, not 132px — this is the only look the buyer gets at the garment
     before committing a quantity, and a thumbnail that size can't carry a
     crest. */
  display: grid; grid-template-columns: 260px minmax(0, 1fr) auto; gap: 22px;
  background: var(--tp-white); border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg); padding: 20px; align-items: start;
  box-shadow: var(--tp-shadow-card);
}
.oc-row + .oc-row { margin-top: 14px; }
/* Inline confirmation next to the row's own button — a toast at the corner of a
   long scrolling list is read by nobody typing in the middle of it. */
.oc-row__said { font-size: var(--tp-t-sm); font-weight: 600; color: var(--tp-green, #0a7a45); }
.oc-row__said.is-bad { color: #b31229; }

/* Ordering columns: rows on the left, the order being assembled on the right.
   The rail is sticky because the order is typed down a long page — a summary
   that scrolls away is a summary the buyer stops trusting. */
.oc-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }
/* No grids on the page means nothing can fill the order rail, so it isn't
   rendered — and the column it would have sat in isn't reserved either. */
.oc-layout--solo { grid-template-columns: minmax(0, 1fr); }
.oc-layout__main { min-width: 0; }
/* Clears the sticky site header. At top:16px the panel slid underneath it and
   the buyer lost the first line of their own order. --tp-header-h is the two
   header rows: the 76px bar plus the ~52px nav strip. */
.oc-layout__side { position: sticky; top: calc(var(--tp-header-h) + 16px); }

.oc-order {
  background: var(--tp-white); border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg); padding: 18px;
  box-shadow: var(--tp-shadow-card);
}
.oc-order__head { display: flex; align-items: center; gap: 9px; margin-bottom: 14px; }
.oc-order__head b { font-family: var(--tp-font-head); font-size: var(--tp-t-lg); }
.oc-order__n {
  margin-left: auto; background: var(--tp-navy, #12253f); color: #fff;
  border-radius: 999px; font-size: 11px; font-weight: 700; padding: 3px 10px;
}
.oc-order__list { list-style: none; margin: 0; padding: 0; max-height: 420px; overflow-y: auto; }
.oc-order__line { display: flex; gap: 10px; padding: 12px 0; border-top: 1px solid var(--tp-line); }
.oc-order__line:first-child { border-top: 0; padding-top: 0; }
.oc-order__thumb {
  flex: none; width: 46px; height: 46px; border-radius: 8px; overflow: hidden;
  background: var(--tp-photo); display: flex; align-items: center; justify-content: center;
}
.oc-order__thumb img { width: 100%; height: 100%; object-fit: contain; }
.oc-order__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.oc-order__nm { font-weight: 600; font-size: var(--tp-t-sm); }
/* The size split, deliberately quiet — it is the detail under the line, not
   the line. The buyer approves "24 tees", then checks the sizes. */
.oc-order__sz { font-size: 12px; color: var(--tp-muted); }
.oc-order__amt { font-weight: 700; font-size: var(--tp-t-sm); }
.oc-order__amt em { font-style: normal; font-weight: 400; color: var(--tp-muted); }
.oc-order__del {
  flex: none; align-self: start; background: none; border: 0; cursor: pointer;
  color: var(--tp-muted); padding: 2px; border-radius: 6px;
}
.oc-order__del:hover { color: #b31229; }
.oc-order__sums { margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--tp-line); }
.oc-order__sum { display: flex; justify-content: space-between; gap: 10px; font-size: var(--tp-t-sm); padding: 3px 0; }
.oc-order__sum dt { color: var(--tp-muted); }
.oc-order__sum dd { margin: 0; font-weight: 600; }
.oc-order__sum.is-total { margin-top: 6px; padding-top: 10px; border-top: 1px solid var(--tp-line); }
.oc-order__sum.is-total dt { color: inherit; font-weight: 700; }
.oc-order__sum.is-total dd { font-family: var(--tp-font-head); font-size: var(--tp-t-xl); }
/* Nothing typed yet. Quiet — it is a label for an empty box, not an offer. */
.oc-order--empty { background: var(--tp-wash); border-style: dashed; box-shadow: none; }
.oc-order--empty .oc-order__head { margin-bottom: 6px; color: var(--tp-muted); }
.oc-order--empty .oc-order__note { text-align: left; margin: 0; }
.oc-order__cta { width: 100%; margin-top: 14px; justify-content: center; }
.oc-order__note { margin: 10px 0 0; font-size: 12px; color: var(--tp-muted); text-align: center; }
/* contain, not cover. A hoodie mockup is a wide front-and-back pair; cropping it
   to a square cut the back panel — the half carrying the crest the buyer is
   checking. The tile keeps a minimum height so short rows still line up, but
   nothing is ever cut. */
.oc-row__media {
  position: relative; border-radius: var(--tp-r); overflow: hidden;
  background: var(--tp-photo); min-height: 200px;
  display: flex; align-items: center; justify-content: center;
}
/* The canvas is the same picture as the img, composited for the colour the
   buyer picked. Only one of the two is ever visible, so they share a rule. */
.oc-row__media img,
.oc-row__media canvas { width: 100%; height: auto; display: block; object-fit: contain; }
/* `display: block` above outranks the browser's own [hidden] rule, so without
   this the img and the canvas both take a slot in the flex row — each squeezed
   to half the column, the mockup beside an empty box. The swap toggles
   `hidden`, so `hidden` has to win. */
.oc-row__media img[hidden],
.oc-row__media canvas[hidden] { display: none; }
.oc-row__edit { position: absolute; top: 8px; left: 8px; display: flex; gap: 6px; }
.oc-row__edit button {
  border: 0; border-radius: 7px; padding: 5px 10px; cursor: pointer;
  background: rgba(16,24,40,.82); color: #fff; font-size: 11px; font-weight: 700;
}
.oc-row__title { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-xl); letter-spacing: -.015em; }
.oc-row__spec { font-size: var(--tp-t-sm); color: var(--tp-muted); margin: 3px 0 16px; }

.oc-sizes { display: flex; gap: 10px; flex-wrap: wrap; }
.oc-size { width: 74px; }
.oc-size label { display: block; text-align: center; font-size: var(--tp-t-sm); font-weight: 700; color: var(--tp-muted); margin-bottom: 5px; }
.oc-size input {
  width: 100%; height: 52px; text-align: center;
  border: 1px solid var(--tp-line-strong); border-radius: var(--tp-r-sm);
  font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-lg);
  color: var(--tp-ink); background: var(--tp-white); -moz-appearance: textfield;
}
.oc-size input::-webkit-outer-spin-button,
.oc-size input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.oc-size input:focus { outline: 0; border-color: var(--tp-ink); }
/* A typed quantity is the signal that this row is part of the order. */
.oc-size input.has { border-color: var(--tp-cta); box-shadow: inset 0 0 0 1px var(--tp-cta); }

.oc-row__foot { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--tp-line); }
.oc-row__hist { font-size: var(--tp-t-sm); color: var(--tp-muted); }
.oc-row__opts { margin-left: auto; font-weight: 700; font-size: var(--tp-t-sm); color: var(--tp-cyan-700); }

.oc-price { text-align: right; min-width: 150px; }
.oc-price__now { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-2xl); letter-spacing: -.02em; display: block; }
.oc-price__was { font-size: var(--tp-t-sm); color: var(--tp-muted); text-decoration: line-through; }
.oc-price__save { display: block; margin-top: 3px; font-size: var(--tp-t-sm); font-weight: 700; color: var(--tp-good-ink, #1F7A4C); }

/* ── Configure & order card ────────────────────────────────────────────────
   Anything carrying per-person data cannot be a size grid, so it gets a card
   that states WHAT has to be collected and how far along that collection is.
   The status line is the point: a teacher chasing 24 names needs to see 18. */
.oc-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
.oc-card {
  background: var(--tp-white); border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg); overflow: hidden; display: flex; flex-direction: column;
  box-shadow: var(--tp-shadow-card); transition: box-shadow .16s ease, border-color .16s ease;
}
.oc-card:hover { border-color: var(--tp-line-strong); box-shadow: var(--tp-shadow-card-hover); }
.oc-card__media { position: relative; aspect-ratio: 1; background: var(--tp-photo); }
.oc-card__media img { width: 100%; height: 100%; object-fit: cover; }
.oc-card__badge {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  font-size: 11px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  padding: 6px 11px; border-radius: 7px; color: #fff;
}
.oc-card__badge--name { background: #e6007e; }
.oc-card__badge--art { background: var(--tp-ink); }
.oc-card__body { padding: 18px 20px 20px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.oc-card__title { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-xl); letter-spacing: -.015em; }
.oc-card__price { display: flex; align-items: baseline; gap: 9px; }
.oc-card__price b { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-2xl); letter-spacing: -.02em; }
.oc-card__price small { font-size: var(--tp-t-sm); color: var(--tp-muted); }
.oc-card__desc { font-size: var(--tp-t-sm); color: var(--tp-muted); line-height: 1.5; margin: 0; }
.oc-status { display: inline-flex; align-items: center; gap: 7px; align-self: flex-start; font-size: var(--tp-t-sm); font-weight: 700; padding: 7px 12px; border-radius: 999px; }
.oc-status--done { background: var(--tp-good-bg, #E8F6EE); color: var(--tp-good-ink, #1F7A4C); }
.oc-status--open { background: #FFF7E6; color: #8A6100; }
.oc-progress { margin-top: auto; padding-top: 14px; border-top: 1px solid var(--tp-line); display: flex; justify-content: space-between; gap: 12px; font-size: var(--tp-t-sm); color: var(--tp-muted); }

/* Name collection. The count carries it; the bar is a second reading of the
   same number, so it only appears when there is a real roster to size it
   against. */
.oc-names { margin-top: 2px; }
.oc-names__head { display: flex; gap: 12px; font-size: var(--tp-t-sm); color: var(--tp-muted); margin-bottom: 7px; }
.oc-names__due { margin-left: auto; white-space: nowrap; }
.oc-names__bar { height: 6px; border-radius: 999px; background: var(--tp-line); overflow: hidden; }
.oc-names__bar span { display: block; height: 100%; border-radius: 999px; background: var(--tp-cta); }

/* Order history. Rules, not cards: this is a ledger the buyer scans, and five
   boxed rows would out-weigh the products they are meant to be ordering. */
.oc-hist {
  margin-top: 46px; padding: 22px 24px;
  background: var(--tp-white); border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg);
}
.oc-hist__head { display: flex; align-items: baseline; gap: 16px; margin-bottom: 6px; }
.oc-hist__head .oc-row__opts { margin-left: auto; }
.oc-hist__row {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 12px 0; border-top: 1px solid var(--tp-line-soft);
  font-size: var(--tp-t-base);
}
.oc-hist__ref { font-family: var(--tp-font-head); font-weight: 700; min-width: 120px; }
.oc-hist__what { color: var(--tp-muted); flex: 1; min-width: 150px; }
.oc-hist__state { font-weight: 700; color: var(--tp-good-ink, #1F7A4C); }
.oc-hist__amt { font-family: var(--tp-font-head); font-weight: 700; min-width: 110px; text-align: right; }

/* Catalogue sign-off. Navy, because the page it ends is a document rather than
   a shop — and the buyer forwards it to someone who arrives at the bottom. */
.oc-foot { background: var(--tp-ink); color: rgba(255, 255, 255, 0.78); margin-top: 56px; }
.oc-foot__inner { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; padding: 30px 0; font-size: var(--tp-t-base); }
.oc-foot img { height: 20px; width: auto; filter: brightness(0) invert(1); }
.oc-foot__legal { margin-left: auto; font-size: var(--tp-t-sm); color: rgba(255, 255, 255, 0.6); }

/* Nothing typed yet.

   A titled card with almost nothing in it. The header sits at the top the way
   it does on the filled panel — same panel, same anchor — and everything under
   it is space. The one line of copy floats in that space rather than being
   centred in it, with ~200px of nothing beneath, because a line pinned to the
   middle reads as the card's content where a line with a void under it reads as
   the card being empty.

   Taller than the filled panel's first few lines on purpose: at 420px the rail
   ended well above the first product card and the column looked truncated
   before any scrolling had happened. */
.oc-empty {
  min-height: 520px;
  display: flex;
  flex-direction: column;
  padding: 0;
}
.oc-empty__head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 18px 22px;
  border-bottom: 1px solid var(--tp-line);
  color: var(--tp-muted);
}
.oc-empty__head b { font-family: var(--tp-font-head); font-size: var(--tp-t-lg); color: var(--tp-ink); }
.oc-empty__void {
  flex: 1;
  display: flex;
  justify-content: center;
  /* The 200px below the line is the whole design — it is what makes the card
     read as empty rather than as short. */
  padding: 170px 24px 200px;
}
/* Deliberately lighter than --tp-muted: this is a state, not information, and
   at muted weight it competed with the product copy on the left. */
.oc-empty__line { margin: 0; font-size: var(--tp-t-md); color: #aab4c0; text-align: center; }

/* The colour the size grid is for — see the template. */
.oc-row__colour { display: flex; align-items: center; gap: 8px; margin: 6px 0 2px; font-size: var(--tp-t-sm); font-weight: 600; color: var(--tp-muted); }
.oc-row__dot { width: 15px; height: 15px; border-radius: 4px; border: 1px solid var(--tp-line-strong); flex: none; }
.oc-swatches { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 10px; }
.oc-swatch {
    width: 26px; height: 26px; border-radius: 6px; padding: 0; cursor: pointer;
    border: 1px solid var(--tp-line-strong); background-clip: padding-box;
}
.oc-swatch:hover { border-color: var(--tp-ink); }
.oc-swatch.is-on { border-color: var(--tp-cta); box-shadow: 0 0 0 2px var(--tp-white) inset, 0 0 0 2px var(--tp-cta); }

/* Running total, phones only — see the template. On desktop the order rail is
   on screen the whole time and this was the same number printed twice. */
.oc-bask { display: none; position: sticky; bottom: 0; z-index: 30; background: var(--tp-white); border-top: 1px solid var(--tp-line); box-shadow: 0 -8px 28px rgba(16,24,40,.08); margin-top: 46px; }
.oc-bask__inner { display: flex; align-items: center; gap: 14px; padding: 12px 0; }
/* The whole left side is the handle that opens the order. A total the buyer
   cannot open is a number they have to take on trust. */
.oc-bask__peek {
  display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
  border: 0; background: none; padding: 6px 0; font: inherit; color: var(--tp-ink);
  text-align: left; cursor: pointer;
}
.oc-bask__peek svg { flex: none; color: var(--tp-muted); }
.oc-bask__count { font-size: var(--tp-t-sm); color: var(--tp-muted); white-space: nowrap; }
.oc-bask__total { margin-left: auto; font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-xl); letter-spacing: -.02em; white-space: nowrap; }

/* The order as a bottom sheet. It holds the ONE order panel, moved in and back
   out again — see the template; a copy would go stale on the next keystroke. */
.oc-sheet { position: fixed; inset: 0; z-index: 1080; display: flex; align-items: flex-end; background: rgba(11, 26, 46, .55); }
.oc-sheet[hidden] { display: none; }
.oc-sheet__panel {
  width: 100%; max-height: 86vh; display: flex; flex-direction: column;
  background: var(--tp-white); border-radius: var(--tp-r) var(--tp-r) 0 0;
}
.oc-sheet__head { display: flex; align-items: center; gap: 12px; padding: 16px 18px; border-bottom: 1px solid var(--tp-line); }
.oc-sheet__head b { font-family: var(--tp-font-head); font-size: var(--tp-t-lg); }
.oc-sheet__close {
  margin-left: auto; width: 38px; height: 38px; display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--tp-r-pill); background: transparent; color: var(--tp-muted); cursor: pointer;
}
.oc-sheet__close:hover { background: var(--tp-wash); color: var(--tp-ink); }
.oc-sheet__body { padding: 14px; overflow-y: auto; }
/* Inside the sheet the panel is the sheet — its own card chrome would be a
   second box drawn against the dialog's edge. */
.oc-sheet__body .oc-order { border: 0; box-shadow: none; padding: 0; }

@media (max-width: 1000px) {
  /* One column, and the rail stops sticking — a 340px panel pinned over a
     narrow screen covers the rows the buyer is typing into. */
  .oc-layout { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .oc-layout__side { position: static; }
  .oc-order__list { max-height: none; }
  .oc-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .oc-hero__inner { gap: 14px; padding: 32px 0 36px; }
  .oc-hero__lockup { gap: 16px; }
  .oc-hero__crest { width: 72px; height: 72px; }
}
@media (max-width: 760px) {
  /* Stacked, not side by side: at 84px the image was a postage stamp squeezed
     against the size grid. Full width above the details gives the garment the
     whole column and the quantities a straight left edge to type down. */
  .oc-row { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  /* Natural height here too — the image gets the full column width, so a fixed
     ratio would only add letterboxing above and below it. */
  .oc-row__media { min-height: 0; }
  .oc-price { grid-column: 1; text-align: left; min-width: 0; margin-top: 4px; }
  .oc-cards { grid-template-columns: minmax(0, 1fr); }
  .oc-bar__rep .oc-bar__repname { display: none; }
  .oc-size { width: 62px; }
}

/* ── 10. Configurator ───────────────────────────────────────────────────────
   The design page: a blank garment, the print locations, and the modal that
   fills one in. It reuses the PDP grid (.tp-pdp) wholesale — same gallery, same
   sticky buy column — so only what the configurator adds lives here. */

/* The stage is a .tp-pdp__frame, so it already has the card look. It only needs
   a square well for the garment and room for the call to action on top of it.
   White, not the grey wash: a tinted well behind a white or pale garment reads
   as a second background rather than as the product's own ground. */
.tp .tp-cfg__stage { background: var(--tp-white); aspect-ratio: 1 / 1; display: block; }
.tp .tp-cfg__stageimg { width: 100%; height: 100%; object-fit: contain; display: block; }
/* Centred on the garment, not below it: on a blank shirt there is nothing else
   to look at, and the whole page exists to get this one click. */
.tp .tp-cfg__add {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 11px;
  background: var(--tp-ink);
  color: var(--tp-white);
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-md);
  padding: 15px 28px;
  border: 0;
  border-radius: var(--tp-r-sm);
  cursor: pointer;
}
.tp .tp-cfg__add:hover { background: #1b3453; }
.tp .tp-cfg__add:active { background: var(--tp-ink-deep); }

.tp .tp-cfg__price { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.tp .tp-cfg__price b {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-2xl);
  letter-spacing: -0.02em;
  white-space: nowrap;
}

/* Three garment facts, rules top and bottom. Each cell is dropped when the
   printable doesn't carry the fact, so the row narrows rather than lying. */
.tp .tp-cfg__specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 16px;
  padding: 20px 0;
  border-top: 1px solid var(--tp-line);
  border-bottom: 1px solid var(--tp-line);
}
.tp .tp-cfg__spec { display: flex; align-items: center; gap: 11px; min-width: 0; }
.tp .tp-cfg__spec svg { flex: none; color: var(--tp-ink); }
.tp .tp-cfg__spectitle { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-base); }
.tp .tp-cfg__specsub { font-size: var(--tp-t-sm); color: var(--tp-muted); }

.tp .tp-cfg__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 6px; }

/* Delivery, in the spec strip's shape. Its own class rather than a reuse of
   .tp-cfg__spec: that strip describes the garment and is asserted to hold
   exactly three cells, and this is a promise about the order. */
.tp .tp-deliv {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 16px;
  margin-top: 20px;
  padding: 18px 0;
  border-top: 1px solid var(--tp-line);
  border-bottom: 1px solid var(--tp-line);
}
/* It now sits last in both buy panels, where the closing rule would double up
   against the card's own edge. */
.tp .tp-deliv:last-child { border-bottom: 0; padding-bottom: 0; }
.tp .tp-deliv__cell { display: flex; align-items: center; gap: 11px; min-width: 0; }
.tp .tp-deliv__cell svg { flex: none; color: var(--tp-ink); }
.tp .tp-deliv__title { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-base); }
.tp .tp-deliv__sub { font-size: var(--tp-t-sm); color: var(--tp-muted); }

/* Swatches here are rounded squares on the same radius as every card and tile
   on the page, not the PDP's dots: on a screen made of soft rectangles a row of
   circles reads as a control from a different system. Bigger, too — colour is a
   real decision on this page, not a summary of one already made. */
.tp .tp-cfg__colors .tp-swatchdot,
.tp .tp-optgroup .tp-swatchrow .tp-swatchdot { width: 44px; height: 44px; border-radius: var(--tp-r-sm); }

/* Print locations. Dashed until filled, solid and green-ticked after — the tile
   IS the progress indicator, so "2 of 4 placed" only restates what it shows.
   The dash is drawn in ink rather than the hairline grey: on a white page a
   pale dashed box reads as a disabled control, and this is the one thing on the
   screen the visitor is meant to click. */
.tp .tp-places { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.tp .tp-place {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 24px 20px;
  background: var(--tp-white);
  border: 2px dashed var(--tp-ink);
  border-radius: var(--tp-r);
  cursor: pointer;
  font: inherit;
  color: var(--tp-ink);
  text-align: center;
}
.tp .tp-place:hover { border-color: var(--tp-cyan); background: var(--tp-cyan-25); }
.tp .tp-place__icon {
  width: 46px;
  height: 46px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-wash);
  color: var(--tp-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.tp .tp-place__name { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-md); }
.tp .tp-place__note {
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
  /* A long template title must not stretch the tile out of the grid. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tp .tp-place.is-set { border-style: solid; border-color: var(--tp-good-ink); background: var(--tp-white); }
.tp .tp-place.is-set .tp-place__icon { background: var(--tp-good-bg); color: var(--tp-good-ink); }

/* Only shown once something is placed: the header price is a "from", and each
   extra location can move it. */
.tp .tp-cfg__total {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 14px 0;
  border-top: 1px solid var(--tp-line);
  font-size: var(--tp-t-md);
}
.tp .tp-cfg__total b {
  margin-left: auto;
  font-family: var(--tp-font-head);
  font-size: var(--tp-t-xl);
  white-space: nowrap;
}

/* 10px clear of whatever it follows — against the print-area tiles the button
   sat flush on their bottom edge and read as part of the last tile. */
.tp .tp-cfg__cta { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }

/* Why the CTA didn't go through. Sits between the button and whatever follows,
   so the answer is where the buyer is already looking. */
.tp .tp-cfg__err {
  margin: 0;
  padding: 10px 14px;
  border-radius: var(--tp-r-sm, 8px);
  background: var(--tp-warn-bg, #FEF0C7);
  color: var(--tp-warn-ink, #B54708);
  font-size: var(--tp-t-sm);
  font-weight: 600;
}

.tp .tp-assure { display: flex; flex-direction: column; }
.tp .tp-assure__row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0;
  border-top: 1px solid var(--tp-line);
  font-size: var(--tp-t-base);
}
.tp .tp-assure__row svg { flex: none; }

.tp .tp-cfg__facts { display: flex; flex-direction: column; font-size: var(--tp-t-base); max-width: 460px; }
.tp .tp-cfg__facts > div { display: flex; gap: 16px; padding: 9px 0; border-top: 1px solid var(--tp-line-soft); }
.tp .tp-cfg__facts span { color: var(--tp-muted); }
.tp .tp-cfg__facts b { margin-left: auto; text-align: right; color: var(--tp-ink); }

/* ── Design modal ───────────────────────────────────────────────────────────
   Plain overlay, no Bootstrap: this shell loads one stylesheet and no component
   framework, and the modal is the only thing on these pages that needed one. */
.tp .tp-modal {
  position: fixed;
  inset: 0;
  z-index: 1070;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(11, 26, 46, 0.55);
}
.tp .tp-modal__panel {
  display: flex;
  flex-direction: column;
  width: min(720px, 100%);
  max-height: calc(100vh - 48px);
  background: var(--tp-white);
  border-radius: var(--tp-r);
  overflow: hidden;
}
.tp .tp-modal__head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--tp-line);
}
.tp .tp-modal__head h2 { min-width: 0; }
.tp .tp-modal__close {
  margin-left: auto;
  flex: none;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--tp-r-pill);
  background: transparent;
  color: var(--tp-muted);
  cursor: pointer;
}
.tp .tp-modal__close:hover { background: var(--tp-wash); color: var(--tp-ink); }
/* The body scrolls, never the panel: the step rail and the Continue button have
   to stay put or a three-step form loses its way out. */
.tp .tp-modal__body { padding: 22px 24px; overflow-y: auto; }
.tp .tp-modal__foot {
  padding: 16px 24px;
  border-top: 1px solid var(--tp-line);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.tp .tp-modal__acts { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.tp .tp-modal__acts .tp-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.tp .tp-modal__staff { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--tp-line); }
.tp .tp-modal__panel--sm { width: min(440px, 100%); }
.tp .tp-modal__summary {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px;
  border-radius: var(--tp-r-sm);
  background: var(--tp-wash);
}
.tp .tp-modal__summary img { width: 60px; height: 60px; object-fit: contain; flex: none; border-radius: 6px; background: var(--tp-white); }

.tp .tp-steprail {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 24px;
  border-bottom: 1px solid var(--tp-line-soft);
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}
.tp .tp-steprail__dot {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: var(--tp-r-pill);
  background: var(--tp-wash);
  color: var(--tp-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-sm);
}
.tp .tp-steprail__dot.is-on { background: var(--tp-ink); color: var(--tp-white); }
.tp .tp-steprail__label { white-space: nowrap; }
.tp .tp-steprail__line { flex: 1; height: 2px; background: var(--tp-line); margin: 0 4px; }
.tp .tp-steprail__line.is-on { background: var(--tp-ink); }

.tp .tp-tmpls { display: flex; flex-direction: column; gap: 12px; }
.tp .tp-tmpl {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 12px;
  background: var(--tp-white);
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: var(--tp-ink);
}
.tp .tp-tmpl:hover { border-color: var(--tp-ink); }
.tp .tp-tmpl.is-active { border-color: var(--tp-ink); box-shadow: inset 0 0 0 1px var(--tp-ink); }
.tp .tp-tmpl__thumb {
  width: 76px;
  height: 76px;
  flex: none;
  border-radius: 8px;
  background: var(--tp-wash);
  overflow: hidden;
}
.tp .tp-tmpl__thumb img { width: 100%; height: 100%; object-fit: contain; }
.tp .tp-tmpl__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.tp .tp-tmpl__price { font-size: var(--tp-t-sm); font-weight: 700; color: var(--tp-ink); }
.tp .tp-tmpl__price--free { color: var(--tp-good-ink); }
.tp .tp-tmpl__tick { width: 20px; flex: none; }

/* Checkbox rows the whole width of which is the hit area — the live page's
   1-pixel tick box was the single most-missed control in the flow. */
.tp .tp-opt {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 14px 16px;
  margin-bottom: 18px;
  background: var(--tp-white);
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r-sm);
  cursor: pointer;
  text-align: left;
  font: inherit;
  font-weight: 600;
  color: var(--tp-ink);
}
.tp .tp-opt:hover { border-color: var(--tp-cyan); }
.tp .tp-opt__check {
  width: 24px;
  height: 24px;
  flex: none;
  border-radius: 6px;
  border: 1px solid var(--tp-line-strong);
  background: var(--tp-white);
  color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}
.tp .tp-opt.is-on { border-color: var(--tp-ink); background: var(--tp-wash); }
.tp .tp-opt.is-on .tp-opt__check { background: var(--tp-ink); border-color: var(--tp-ink); color: var(--tp-white); }
.tp .tp-opt.is-bad { border-color: var(--tp-bad-ink, #B42318); }

.tp .tp-upload {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 34px 20px;
  border: 2px dashed var(--tp-line-strong);
  border-radius: var(--tp-r);
  background: var(--tp-page);
  color: var(--tp-muted);
  cursor: pointer;
  text-align: center;
}
.tp .tp-upload:hover { border-color: var(--tp-cyan); background: var(--tp-cyan-25); }
.tp .tp-upload b { color: var(--tp-ink); }

.tp .tp-logogrid { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 18px; }
.tp .tp-logotile {
  width: 92px;
  height: 92px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r-sm);
  background: var(--tp-white);
  cursor: pointer;
}
.tp .tp-logotile img { max-width: 100%; max-height: 100%; object-fit: contain; }
.tp .tp-logotile:hover { border-color: var(--tp-ink); }
.tp .tp-logotile.is-active { border-color: var(--tp-ink); box-shadow: inset 0 0 0 1px var(--tp-ink); }
.tp .tp-logotile--add {
  flex-direction: column;
  gap: 4px;
  border-style: dashed;
  color: var(--tp-muted);
  font-size: var(--tp-t-xs);
  font-weight: 700;
}

.tp .tp-logocard {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  padding: 14px;
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
}
.tp .tp-logocard__preview {
  width: 84px;
  height: 84px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  overflow: hidden;
}
.tp .tp-logocard__preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.tp .tp-logocard__meta { min-width: 0; }
.tp .tp-logocard__meta b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp .tp-logocard__acts { display: flex; gap: 16px; margin-top: 6px; }
.tp .tp-logocard__acts button {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: var(--tp-t-sm);
  font-weight: 600;
  color: var(--tp-muted);
  cursor: pointer;
}
.tp .tp-logocard__acts button:hover { color: var(--tp-ink); }
.tp .tp-logocard__acts button.is-danger { color: var(--tp-bad-ink, #B42318); }
.tp .tp-logocard__nobg {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--tp-line);
  font-size: var(--tp-t-sm);
  font-weight: 600;
}
.tp .tp-logocard__nobg .tp-switch { margin-left: auto; }

.tp .tp-bgpick { display: flex; gap: 6px; }
.tp .tp-bgpick button {
  width: 26px;
  height: 26px;
  padding: 3px;
  border: 1px solid var(--tp-line);
  border-radius: 6px;
  background: var(--tp-white);
  cursor: pointer;
}
.tp .tp-bgpick button.is-on { border-color: var(--tp-ink); }
.tp .tp-bgpick span { display: block; width: 100%; height: 100%; border-radius: 3px; }

.tp .tp-switch {
  width: 44px;
  height: 24px;
  flex: none;
  padding: 3px;
  border: 0;
  border-radius: var(--tp-r-pill);
  background: var(--tp-line-strong);
  cursor: pointer;
}
.tp .tp-switch span { display: block; width: 18px; height: 18px; border-radius: 50%; background: var(--tp-white); transition: transform 140ms ease; }
.tp .tp-switch.is-on { background: var(--tp-ink); }
.tp .tp-switch.is-on span { transform: translateX(20px); }

.tp .tp-inlineswitch { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; font-size: var(--tp-t-sm); color: var(--tp-muted); cursor: pointer; }
.tp .tp-inlineswitch input { width: 16px; height: 16px; accent-color: var(--tp-ink); }

@media (max-width: 720px) {
  /* Full-bleed sheet: a centred dialog with 24px of margin on a phone leaves the
     template list two cards tall and scrolling inside a scroll. */
  .tp .tp-modal { padding: 0; align-items: stretch; }
  .tp .tp-modal__panel { width: 100%; max-height: 100vh; border-radius: 0; }
  .tp .tp-modal__head,
  .tp .tp-modal__body,
  .tp .tp-modal__foot,
  .tp .tp-steprail { padding-left: 18px; padding-right: 18px; }
  /* Step labels go; the numbered dots still say where you are. */
  .tp .tp-steprail__label { display: none; }
  .tp .tp-cfg__add { padding: 13px 20px; font-size: var(--tp-t-base); }
  .tp .tp-place { padding: 18px 12px; gap: 8px; }

  /* ── Configurator header, condensed ──
     Name and price are one statement, so they sit as one block: the buy
     column's 18px row gap pushed the price far enough from the title that it
     read as a separate section, and 26px of price beside a 26px name gave the
     two equal weight. */
  .tp .tp-pdp__buy > .tp-cfg__price { margin-top: -8px; }
  .tp .tp-cfg__price b { font-size: 22px; }

  /* ── PDP header, condensed ──
     Name, price and the three garment facts were taking the whole screen
     before the buyer reached a control they could touch. Same content, tighter
     setting — spacing and type size coming down a step, with only the spec
     icons dropped (see the strip below). The 860px rules above are the tablet
     setting; a phone gets less. */
  .tp .tp-pdp__buy { gap: 14px; }
  .tp .tp-pdp__title h1 { font-size: 22px; }
  .tp .tp-pdp__priceline { margin-top: -6px; gap: 8px; }
  .tp .tp-pdp__priceline b { font-size: 23px; }
  /* The badge explains the number beside it; at full size it competed with it. */
  .tp .tp-pdp__priceline .tp-tag { padding: 4px 9px; font-size: 11px; }
  .tp .tp-pdp__edit { font-size: 12px; }

  /* The panel below the strip. Its heading is a section marker on a page with
     one section — it does not need display size to be found. */
  .tp .tp-personalise__body,
  .tp .tp-cfg__panel > .tp-cfg__panelbody { padding: 20px 16px; }
  .tp .tp-personalise__title { font-size: 22px; }
  .tp .tp-personalise__lede { font-size: var(--tp-t-sm); margin-bottom: 16px; }
  .tp .tp-personalise .tp-field > label { font-size: var(--tp-t-md); margin-bottom: 7px; }
  .tp .tp-personalise .tp-field > label .tp-muted,
  .tp .tp-personalise .tp-field > label .tp-field__hint { font-size: var(--tp-t-sm); }

  /* Three facts, three columns — always. auto-fit at 150px wrapped the third
     cell onto its own line, which reads as a grid that failed rather than a
     strip of three. Stacked rather than icon-beside-label: that is what buys
     each cell the width to hold its own at a third of a phone.

     The icons go. Beside "Material" a pair of scissors adds nothing the word
     did not already say, and on a phone they cost a whole row of the strip —
     height this screen has to spend on the controls further down. */
  .tp .tp-cfg__specs { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; padding: 11px 0; }
  .tp .tp-cfg__spec { flex-direction: column; align-items: flex-start; gap: 2px; }
  .tp .tp-cfg__spec svg { display: none; }
  .tp .tp-cfg__spectitle { font-size: 13px; line-height: 1.2; }
  .tp .tp-cfg__specsub { font-size: 12px; line-height: 1.3; }
  .tp .tp-logocard { grid-template-columns: auto minmax(0, 1fr); }
  .tp .tp-bgpick { grid-column: 1 / -1; }
}

/* ── 11. Order tracking (/order-status/) ─────────────────────────────────────
   One narrow column. The page has exactly one job — a three-field lookup, then
   a straight answer about where the order is — so it gets no rail, no grid and
   no second call to action. The card is the same white/line/shadow recipe as
   .tp-card; it is a separate class only because it wants the panel's padding
   rather than the card's, and no hover lift (nothing here is clickable).       */

.tp .tp-lookup { max-width: 620px; margin: 0 auto; }
.tp .tp-lookup__card {
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-lg);
  box-shadow: var(--tp-shadow-card);
  padding: 40px 44px;
}
.tp .tp-lookup__head { text-align: center; margin-bottom: 30px; }
.tp .tp-lookup__head p { margin: 12px 0 0; max-width: 42ch; margin-inline: auto; }
.tp .tp-lookup__icon {
  width: 60px;
  height: 60px;
  margin: 0 auto 18px;
  border-radius: 50%;
  background: var(--tp-cyan-50);
  color: var(--tp-cyan-700);
  display: grid;
  place-items: center;
}
.tp .tp-lookup__form { display: flex; flex-direction: column; gap: 18px; }
.tp .tp-lookup__hint {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: 7px 0 0;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}
.tp .tp-lookup__hint svg { flex: none; margin-top: 2px; }
/* "or" rule between the two verification fields — either one identifies the
   buyer, and a plain stack of inputs reads as "fill in both". */
.tp .tp-lookup__or {
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tp-muted);
}
.tp .tp-lookup__or::before,
.tp .tp-lookup__or::after { content: ''; flex: 1; height: 1px; background: var(--tp-line); }
.tp .tp-lookup__back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 26px;
  font-size: var(--tp-t-sm);
  font-weight: 600;
  color: var(--tp-muted);
}
.tp .tp-lookup__back:hover { color: var(--tp-cta); }

/* The found-order header: order number, and the one-line status of the whole
   order, before any of the per-stage detail. */
.tp .tp-orderhead {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-bottom: 22px;
  margin-bottom: 22px;
  border-bottom: 1px solid var(--tp-line);
}
.tp .tp-orderhead__mark {
  width: 48px;
  height: 48px;
  flex: none;
  border-radius: 50%;
  background: var(--tp-good-bg);
  color: var(--tp-good-ink);
  display: grid;
  place-items: center;
}
.tp .tp-orderhead__id { font-family: var(--tp-font-head); font-weight: 700; font-size: var(--tp-t-2xl); }
.tp .tp-orderhead__meta { margin: 2px 0 0; font-size: var(--tp-t-sm); color: var(--tp-muted); }

/* Label/value rows. Same idea as .tp-cfg__facts, but that one lives inside the
   configurator's right column and is capped at 460px. */
.tp .tp-facts { display: flex; flex-direction: column; font-size: var(--tp-t-base); }
.tp .tp-facts > div {
  display: flex;
  gap: 16px;
  align-items: baseline;
  padding: 11px 0;
  border-bottom: 1px solid var(--tp-line-soft);
}
.tp .tp-facts > div:last-child { border-bottom: 0; }
.tp .tp-facts span { color: var(--tp-muted); }
.tp .tp-facts b { margin-left: auto; text-align: right; color: var(--tp-ink); font-weight: 700; }

/* The stage track. A dot per stage joined by a rule, so "where is it" is
   answered by position rather than by reading four sentences. Colour carries
   the state; the icon and the wording repeat it, because colour alone fails for
   a colour-blind reader and in a printed confirmation. */
.tp .tp-track { display: flex; flex-direction: column; margin-top: 26px; }
.tp .tp-track__step { display: flex; gap: 16px; padding-bottom: 22px; position: relative; }
.tp .tp-track__step:last-child { padding-bottom: 0; }
.tp .tp-track__step::before {
  content: '';
  position: absolute;
  left: 15px;
  top: 34px;
  bottom: 0;
  width: 2px;
  background: var(--tp-line);
}
.tp .tp-track__step:last-child::before { display: none; }
.tp .tp-track__dot {
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: 50%;
  background: var(--tp-line);
  color: var(--tp-muted);
  display: grid;
  place-items: center;
  position: relative;
  z-index: 1;
}
.tp .tp-track__step.is-done .tp-track__dot { background: var(--tp-good-ink); color: var(--tp-white); }
.tp .tp-track__step.is-now .tp-track__dot { background: var(--tp-cta); color: var(--tp-white); }
.tp .tp-track__step.is-warn .tp-track__dot { background: var(--tp-warn-ink, #B54708); color: var(--tp-white); }
.tp .tp-track__name {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-md);
  color: var(--tp-ink);
}
.tp .tp-track__note { margin: 3px 0 0; font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-track__step.is-wait .tp-track__name { color: var(--tp-muted); }
.tp .tp-track__pay { margin-top: 10px; }

.tp .tp-tag--warn { background: var(--tp-warn-bg, #FEF0C7); color: var(--tp-warn-ink, #B54708); border-radius: 6px; }
.tp .tp-tag--wait { background: var(--tp-wash); color: var(--tp-muted); border-radius: 6px; }

@media (max-width: 620px) {
  .tp .tp-lookup__card { padding: 26px 20px; }
  .tp .tp-orderhead { flex-direction: column; text-align: center; gap: 12px; }
  .tp .tp-facts > div { flex-direction: column; gap: 2px; align-items: flex-start; }
  .tp .tp-facts b { margin-left: 0; text-align: left; }
}

/* ── 12. Ad landing (/start/) ────────────────────────────────────────────────
   Paid traffic lands here and has exactly two moves: start, or ring us. So the
   page keeps its own stripped header — no nav, no search, no basket — because
   every one of those is an exit paid for by the click that just arrived. That
   is why it does not take the marketplace chrome.

   Everything else is the v2 kit: CTA blue, Space Grotesk headline, flat
   surfaces. The old page ran a black CTA and its own type scale.               */

.tp .tp-adhead { border-bottom: 1px solid var(--tp-line); background: var(--tp-white); }
.tp .tp-adhead__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 0;
}
.tp .tp-adhead__logo img { height: 26px; width: auto; }

.tp .tp-adland { }
.tp .tp-adland__media { background: var(--tp-wash); overflow: hidden; }
.tp .tp-adland__media img { width: 100%; display: block; object-fit: cover; object-position: top; }
.tp .tp-adland__copy { padding: 24px 0 36px; }
.tp .tp-adland__copy h1 { font-size: var(--tp-t-3xl); font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; margin: 0 0 10px; }

/* Social proof without a star: there is no reviews model behind this page, so a
   rating would be invented. The count is a real number and carries the line. */
.tp .tp-adland__proof {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
  font-size: var(--tp-t-sm);
  font-weight: 600;
  color: var(--tp-muted);
}

/* Highlighter behind two words. Hard-stop, so it reads as a flat marker bar
   rather than a gradient. */
.tp .tp-adland__mark { background: linear-gradient(180deg, transparent 52%, var(--tp-yellow) 52%); }

.tp .tp-adland__ask { font-size: var(--tp-t-xl); font-weight: 700; color: var(--tp-ink); margin: 22px 0 16px; }

.tp .tp-adland__cta { display: flex; flex-direction: column; gap: 10px; }

/* Mobile: the two delivery promises side by side under the buttons. */
.tp .tp-adland__deliv { display: flex; margin-top: 24px; border-top: 1px solid var(--tp-line); padding-top: 16px; }
.tp .tp-adland__deliv > div { flex: 1; text-align: center; }
.tp .tp-adland__deliv > div:first-child { border-right: 1px solid var(--tp-line); }
.tp .tp-adland__deliv b { display: block; font-family: var(--tp-font-head); font-size: var(--tp-t-base); font-weight: 700; }
.tp .tp-adland__deliv span { font-size: var(--tp-t-sm); color: var(--tp-muted); }

/* Desktop: the same promises as a row above the button, with an icon. */
.tp .tp-adland__ship {
  display: flex;
  gap: 28px;
  flex-wrap: wrap;
  margin-bottom: 24px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--tp-line);
}
.tp .tp-adland__ship > div { display: flex; align-items: flex-start; gap: 11px; }
.tp .tp-adland__ship svg { flex: none; margin-top: 2px; color: var(--tp-ink); }
.tp .tp-adland__ship b { display: block; font-family: var(--tp-font-head); font-size: var(--tp-t-base); font-weight: 700; }
.tp .tp-adland__ship span { font-size: var(--tp-t-sm); color: var(--tp-muted); }

.tp .tp-adland__trust {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid var(--tp-line);
}
.tp .tp-adland__trust > div {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tp-muted);
}
.tp .tp-adland__trust svg { flex: none; color: var(--tp-ink); }

.tp .tp-adland__help { margin: 24px 0 0; text-align: center; font-size: var(--tp-t-sm); color: var(--tp-muted); }
.tp .tp-adland__help a { color: var(--tp-ink); font-weight: 700; }

/* The closing promise strip. Navy, because it is the same surface the footer
   and the hero use — the page ends inside the brand rather than on a stray
   near-black. */
.tp .tp-adland__strip { background: var(--tp-ink); color: rgba(255, 255, 255, 0.85); }
.tp .tp-adland__strip-inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px 34px;
  flex-wrap: wrap;
  padding: 18px 0;
  font-size: var(--tp-t-base);
}
.tp .tp-adland__strip-inner > span { display: inline-flex; align-items: center; gap: 8px; }
.tp .tp-adland__strip-inner b { color: var(--tp-white); }
.tp .tp-adland__strip-inner svg { flex: none; color: #4ade80; }
.tp .tp-adland__strip-inner a { color: var(--tp-white); font-weight: 700; text-decoration: underline; }

/* WhatsApp is a real second channel here, not decoration — some buyers will
   never fill a form. Its own green, so it is legible as WhatsApp rather than as
   a second, weaker version of the main CTA. */
.tp .tp-btn--wa { border-color: #25D366; color: #128C7E; background: var(--tp-white); }
.tp .tp-btn--wa:hover { background: #f0fdf4; border-color: #128C7E; color: #128C7E; }
.tp .tp-btn--wa svg { flex: none; }

/* Two columns from 992px, image-led. Below that the image runs full-bleed above
   the copy — on a phone the garment IS the ad. */
.tp .tp-adland--desk { display: none; }
@media (min-width: 992px) {
  .tp .tp-adland--mob { display: none; }
  .tp .tp-adland--desk { display: grid; grid-template-columns: minmax(0, 55fr) minmax(0, 45fr); gap: 48px; align-items: center; padding: 36px 0 48px; }
  .tp .tp-adland--desk .tp-adland__media { border-radius: var(--tp-r); }
  .tp .tp-adland--desk .tp-adland__copy { padding: 0; }
  .tp .tp-adland--desk .tp-adland__copy h1 { font-size: var(--tp-t-4xl); }
  .tp .tp-adland__cta { flex-direction: row; flex-wrap: wrap; }
  .tp .tp-adland__cta .tp-btn { width: auto; }
}
@media (max-width: 991px) {
  .tp .tp-adland__strip { display: none; }   /* desktop-only strip, as before */
  .tp .tp-adland__media img { max-height: 380px; }
}

/* ── Legal pages: terms + privacy ───────────────────────────────────────────
   One component for both, because they are the same object: a long numbered
   document nobody reads top to bottom. So they get the same two pieces of
   furniture — a sticky contents rail to jump with, and a measure capped near
   70ch so the clauses stay readable instead of running the full 1280px shell.

   List markers are restored explicitly here. The base reset strips them from
   every ul/ol on the site, and in a legal document the numbering IS the
   citation — support quotes "VI.10b" at a customer, so it has to be countable
   on the page. */
.tp .tp-legal {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 56px;
  align-items: start;
  padding-bottom: 80px;
}

.tp .tp-legal__toc { position: sticky; top: calc(var(--tp-header-h) + 16px); }
.tp .tp-legal__toclabel {
  font-size: var(--tp-t-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tp-muted);
  margin-bottom: 8px;
}
.tp .tp-legal__toc a {
  display: block;
  padding: 9px 0;
  border-top: 1px solid var(--tp-line);
  font-size: var(--tp-t-sm);
  line-height: 1.4;
  color: var(--tp-muted);
}
.tp .tp-legal__toc a:last-child { border-bottom: 1px solid var(--tp-line); }
.tp .tp-legal__toc a:hover { color: var(--tp-ink); }

.tp .tp-legal__body { min-width: 0; max-width: 70ch; line-height: 1.75; }
.tp .tp-legal__section {
  padding-bottom: 34px;
  margin-bottom: 34px;
  border-bottom: 1px solid var(--tp-line);
  /* Clear the sticky header when a contents link jumps here. */
  scroll-margin-top: calc(var(--tp-header-h) + 20px);
}
.tp .tp-legal__section:last-child { border-bottom: none; margin-bottom: 0; }

/* Body face, not Space Grotesk. These headings carry the document's own
   all-caps roman numerals ("III. PLACING ORDERS AND PAYMENTS") — the display
   face set in caps at 26px shouts, where Mulish at 18px reads as what it is:
   a clause heading in a contract. */
.tp .tp-legal__section > h2 {
  font-family: var(--tp-font-body);
  font-size: var(--tp-t-lg);
  font-weight: 800;
  letter-spacing: 0.01em;
  line-height: 1.35;
  margin-bottom: 18px;
}
.tp .tp-legal__section > h3 {
  font-family: var(--tp-font-body);
  font-size: var(--tp-t-md);
  font-weight: 800;
  line-height: 1.4;
  margin: 26px 0 10px;
}
.tp .tp-legal__section > h3:first-child { margin-top: 0; }

.tp .tp-legal__body ol { list-style: decimal; padding-left: 22px; }
.tp .tp-legal__body ol[type="a"] { list-style: lower-alpha; margin: 10px 0; }
.tp .tp-legal__body ul { list-style: disc; padding-left: 22px; }
.tp .tp-legal__body li { margin-bottom: 12px; }
.tp .tp-legal__body li:last-child { margin-bottom: 0; }
.tp .tp-legal__body li::marker { color: var(--tp-muted); }
.tp .tp-legal__body strong { font-weight: 800; }

/* The withdrawal-form annex. Dashed, because it is a thing to print and fill
   in rather than a thing to read — the border says "cut here". */
.tp .tp-legal__form {
  border: 1px dashed var(--tp-line-strong);
  border-radius: var(--tp-r);
  background: var(--tp-wash);
  padding: 24px;
}
.tp .tp-legal__form p:last-child { margin-bottom: 0; }
.tp .tp-legal__note { font-size: var(--tp-t-sm); color: var(--tp-muted); }

@media (max-width: 1000px) {
  .tp .tp-legal { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  /* Static, not a <details>: nine links is a short list, and collapsing it
     would hide the only navigation a 300-clause page has. */
  .tp .tp-legal__toc { position: static; }
}
@media (max-width: 560px) {
  .tp .tp-legal__body ol, .tp .tp-legal__body ul { padding-left: 18px; }
  .tp .tp-legal__form { padding: 18px 16px; }
}

/* A section that starts a second document inside one URL — the cookie policy
   under the privacy notice. Sentence case, so it can take the display face
   where the roman-numeral clause headings cannot. */
.tp .tp-legal__section--major > h2 {
  font-family: var(--tp-font-head);
  font-size: var(--tp-t-2xl);
  letter-spacing: -0.02em;
  margin-bottom: 14px;
}

/* ── Basket ─────────────────────────────────────────────────────────────────
   templates/teamprinted/v2/cart.html. Two columns: the item list, and a
   summary that follows the page down. The old basket carried each item row
   twice — a desktop <table> and a mobile card stack — so the layout here has
   one job: make a single row work at both widths. It does that by being a
   grid whose columns collapse rather than two markup trees that diverge.  */

.tp .tp-basket__head { padding: 26px 0 20px; }
.tp .tp-basket__head .tp-h1 { margin: 6px 0 16px; }

/* The rail sits under the page title rather than across a card, so it drops
   the card's frame: no side padding, no bottom rule. */
.tp .tp-steprail--bare { padding: 0; border-bottom: 0; max-width: 460px; }

.tp .tp-basket__loading {
  display: flex;
  justify-content: center;
  padding: 80px 0;
}

.tp .tp-basket {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 380px;
  gap: 28px;
  align-items: start;
  padding-bottom: 60px;
}
.tp .tp-basket__main { min-width: 0; }
.tp .tp-basket__side { position: sticky; top: calc(var(--tp-header-h) + 16px); }

/* ── The item list ─────────────────────────────────────────────────────── */

.tp .tp-basket__list { padding: 0; }

/* Whose products these are. A dark bar rather than a quiet grey line: a basket
   holding two clubs' kit has to read as two blocks at a glance, and the bar is
   also where "shop more from this club" earns its click. The card already
   clips (overflow: hidden), so the first bar takes its rounded top corners. */
.tp .tp-basket__group {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 22px;
  background: var(--tp-ink);
  font-size: var(--tp-t-md);
  font-weight: 700;
  color: var(--tp-white);
}
.tp .tp-basket__group img {
  border-radius: var(--tp-r-pill);
  object-fit: contain;
  background: var(--tp-white);
  padding: 2px;
}
/* The "shop more" button on the dark bar. Outlined white rather than a solid
   fill — the basket's one solid CTA is "proceed to checkout", and a second
   filled button in the same colour weight competes with it. */
.tp .tp-basket__group .tp-basket__more {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 16px;
  border: 1px solid rgba(255, 255, 255, .55);
  border-radius: var(--tp-r-pill);
  font-size: var(--tp-t-sm);
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--tp-white);
  text-decoration: none;
  transition: background .16s ease, border-color .16s ease;
}
.tp .tp-basket__group .tp-basket__more:hover {
  background: var(--tp-white);
  border-color: var(--tp-white);
  color: var(--tp-ink);
}

/* thumb | info | controls. The controls column drops under the info column on
   a phone, which is the whole of the mobile layout — see the media query. */
.tp .tp-basket__item {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) auto;
  gap: 18px;
  align-items: start;
  padding: 20px 22px;
  border-top: 1px solid var(--tp-line-soft);
}
.tp .tp-basket__list > :first-child .tp-basket__item,
.tp .tp-basket__group + .tp-basket__item { border-top: 0; }

.tp .tp-basket__thumb {
  position: relative;
  display: block;
  width: 96px;
  padding: 0;
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-sm);
  background: var(--tp-wash);
  cursor: pointer;
  overflow: hidden;
}
.tp .tp-basket__thumb img,
.tp .tp-basket__thumb canvas {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
}
/* The eye is the only affordance saying the thumbnail opens something. Always
   visible, not hover-revealed — there is no hover on the phone where most of
   these baskets are opened. */
.tp .tp-basket__eye {
  position: absolute;
  right: 5px;
  bottom: 5px;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--tp-r-pill);
  background: rgba(255, 255, 255, .92);
  color: var(--tp-ink);
  box-shadow: var(--tp-shadow-card);
}

.tp .tp-basket__info { min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.tp .tp-basket__name {
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-md);
  line-height: 1.25;
  color: var(--tp-ink);
}
.tp .tp-basket__name:hover { color: var(--tp-cyan-700); }

.tp .tp-basket__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}

/* The size button and its panel. Anchored, so the panel opens over the row
   below instead of pushing every following item down the page. */
.tp .tp-basket__optswrap { position: relative; display: inline-flex; }
.tp .tp-basket__sizebtn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r-xs);
  background: var(--tp-white);
  padding: 3px 8px;
  font-size: var(--tp-t-sm);
  font-weight: 700;
  color: var(--tp-ink);
  cursor: pointer;
}
.tp .tp-basket__sizebtn:hover { border-color: var(--tp-ink); }
.tp .tp-basket__sizebtn[aria-expanded="true"] svg { transform: rotate(180deg); }

.tp .tp-basket__opts {
  position: absolute;
  z-index: 20;
  top: calc(100% + 7px);
  left: 0;
  width: 280px;
  max-width: calc(100vw - 48px);
  padding: 16px;
  background: var(--tp-white);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  box-shadow: var(--tp-shadow-card-hover);
}
.tp .tp-basket__optsload { display: flex; justify-content: center; padding: 22px 0; }

/* Four sizes across a 280px popover is too tight for the 58px buy-page tile,
   so the compact grid trades height for columns. */
.tp .tp-sizes--compact { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; }
.tp .tp-size--sm { min-height: 38px; padding: 8px 4px; font-size: var(--tp-t-sm); border-radius: var(--tp-r-xs); }

.tp .tp-basket__sub { font-size: var(--tp-t-sm); color: var(--tp-muted); }

/* Personalisation reads as data, so it is set as label/value pairs rather than
   as a sentence — a shirt with a name and a number has two of them. */
.tp .tp-basket__pers {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  font-size: var(--tp-t-sm);
  color: var(--tp-muted);
}
.tp .tp-basket__pers b { color: var(--tp-ink); font-weight: 700; }
.tp .tp-basket__pers i { font-style: normal; opacity: 0.6; }

.tp .tp-basket__ctrls {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}
.tp .tp-qty--sm button { width: 34px; height: 34px; font-size: var(--tp-t-md); }
.tp .tp-qty--sm input { width: 46px; height: 34px; font-size: var(--tp-t-sm); }

.tp .tp-basket__price { text-align: right; line-height: 1.3; }
.tp .tp-basket__price b { font-size: var(--tp-t-md); font-weight: 800; }
.tp .tp-basket__price small { display: block; font-size: var(--tp-t-xs); color: var(--tp-muted); }

/* Grey until pointed at. Removing a line is the one destructive control on the
   page and it does not need to advertise itself in red the whole time. */
.tp .tp-basket__del {
  border: 0;
  background: none;
  padding: 4px;
  color: var(--tp-muted);
  cursor: pointer;
}
.tp .tp-basket__del:hover { color: var(--tp-bad-ink, #b42318); }

/* Desktop keeps the checkout button in the summary; this is its phone copy,
   sitting directly under the last item where the decision is made. */
.tp .tp-basket__mobcta { display: none; margin-top: 18px; }

/* The accepted-payment artwork, included under both checkout buttons. The old
   template styled this in its own <style> block, so it arrived here unstyled
   and stretched to the column width. Capped and centred instead. */
.tp .cart-pay-strip { margin-top: 14px; }
.tp .cart-pay img { width: 100%; max-width: 360px; margin: 0 auto; }

.tp .tp-basket__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px 2px 0;
}
.tp .tp-basket__more { font-weight: 700; font-size: var(--tp-t-sm); }
.tp .tp-basket__clear {
  border: 0;
  background: none;
  padding: 0;
  font-size: var(--tp-t-sm);
  font-weight: 700;
  color: var(--tp-muted);
  cursor: pointer;
  text-decoration: underline;
}
.tp .tp-basket__clear:hover { color: var(--tp-bad-ink, #b42318); }
.tp .tp-basket__clear:disabled { opacity: 0.5; cursor: not-allowed; }

.tp .tp-basket__block { margin-top: 20px; }

/* ── Processing tier + notes (trade only) ──────────────────────────────── */

.tp .tp-basket__tiers { display: flex; flex-direction: column; gap: 10px; }
.tp .tp-basket__tier {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--tp-line-strong);
  border-radius: var(--tp-r-sm);
  cursor: pointer;
}
.tp .tp-basket__tier.is-active { border-color: var(--tp-ink); box-shadow: inset 0 0 0 1px var(--tp-ink); }
/* Shown and struck rather than hidden: an express tier that vanishes on some
   baskets reads as a missing feature, where a greyed one with its reason reads
   as an answer. */
.tp .tp-basket__tier.is-out { opacity: 0.55; cursor: not-allowed; }
.tp .tp-basket__tier input { accent-color: var(--tp-cta); flex: none; }
.tp .tp-basket__tier .tp-tag { margin-left: auto; flex: none; }
.tp .tp-basket__tierbody { display: flex; flex-direction: column; min-width: 0; }
.tp .tp-basket__tierbody small { font-size: var(--tp-t-sm); color: var(--tp-muted); }

.tp .tp-basket__standard { display: flex; flex-direction: column; gap: 2px; }

.tp .tp-basket__notemeta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 8px;
  font-size: var(--tp-t-xs);
  color: var(--tp-muted);
}

/* ── Summary ───────────────────────────────────────────────────────────── */

.tp .tp-basket__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 7px 0;
  font-size: var(--tp-t-base);
}
.tp .tp-basket__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin: 12px 0 18px;
  padding-top: 14px;
  border-top: 1px solid var(--tp-line);
  font-family: var(--tp-font-head);
  font-weight: 700;
  font-size: var(--tp-t-xl);
}

/* Green for money saved, red for a rejected number. Two utilities rather than
   a modifier per block — they are applied to lines, spans and paragraphs. */
.tp .tp-good { color: var(--tp-good-ink); }
.tp .tp-bad { color: var(--tp-bad-ink, #b42318); }

/* VAT / NIP. Closed by default: most baskets are consumer, and an open tax
   field above the checkout button asks every shopper a question meant for a
   minority of them. */
.tp .tp-basket__biz { margin-top: 16px; border-top: 1px solid var(--tp-line); padding-top: 14px; }
.tp .tp-basket__biztoggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  border: 0;
  background: none;
  padding: 0;
  font-size: var(--tp-t-sm);
  font-weight: 700;
  color: var(--tp-ink);
  cursor: pointer;
}
.tp .tp-basket__biztoggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.tp .tp-basket__bizbody { padding-top: 10px; }
.tp .tp-basket__bizbody p { margin-bottom: 10px; }
.tp .tp-basket__lookup { display: flex; gap: 8px; }
.tp .tp-basket__lookup .tp-input { min-width: 0; flex: 1; }
.tp .tp-basket__lookup .tp-btn { flex: none; }

.tp .tp-basket__verified {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 16px;
  padding: 14px 16px;
  border-radius: var(--tp-r-sm);
  background: var(--tp-good-bg);
  font-size: var(--tp-t-sm);
}
.tp .tp-basket__verified button {
  align-self: flex-start;
  margin-top: 6px;
  border: 0;
  background: none;
  padding: 0;
  font-size: var(--tp-t-xs);
  font-weight: 700;
  color: var(--tp-muted);
  text-decoration: underline;
  cursor: pointer;
}

.tp .tp-basket__trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 16px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--tp-line-soft);
  font-size: var(--tp-t-xs);
  color: var(--tp-muted);
}
.tp .tp-basket__trust span { display: flex; align-items: center; gap: 6px; }
.tp .tp-basket__trust span::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-good-ink);
}

.tp .tp-basket__fulfiller {
  margin: 14px 0 0;
  font-size: var(--tp-t-xs);
  line-height: 1.5;
  color: var(--tp-muted);
  text-align: center;
}
.tp .tp-basket__fulfiller b { color: var(--tp-ink); }

/* ── Design modal ──────────────────────────────────────────────────────── */

.tp .tp-basket__views { display: grid; gap: 10px; margin-bottom: 20px; }
.tp .tp-basket__views.grid-1 { grid-template-columns: minmax(0, 1fr); }
.tp .tp-basket__views.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tp .tp-basket__views.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tp .tp-basket__view {
  position: relative;
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r-sm);
  background: var(--tp-wash);
  overflow: hidden;
}
.tp .tp-basket__view img,
.tp .tp-basket__view canvas {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
}
/* The view label only appears when there is more than one, so it is a caption
   distinguishing front from back rather than a label on a lone image. */
.tp .tp-basket__view span {
  position: absolute;
  left: 8px;
  bottom: 8px;
  padding: 2px 8px;
  border-radius: var(--tp-r-pill);
  background: rgba(255, 255, 255, .92);
  font-size: var(--tp-t-xs);
  font-weight: 700;
}

.tp .tp-basket__field { margin-bottom: 18px; }
.tp .tp-basket__field:last-child { margin-bottom: 0; }
.tp .tp-basket__field--row { display: flex; align-items: center; gap: 14px; }
.tp .tp-basket__tags { display: flex; flex-wrap: wrap; gap: 7px; }

@media (max-width: 1080px) {
  .tp .tp-basket { grid-template-columns: minmax(0, 1fr); }
  /* Unpinned once it is below the items rather than beside them — a sticky
     block in a single column just refuses to scroll away. */
  .tp .tp-basket__side { position: static; }
  /* The summary still carries the desktop checkout button; hiding it here and
     showing the mobile copy keeps exactly one visible at any width, so funnel
     events can't double-count. The payment strip goes with it — on its own it
     reads as a strip belonging to the VAT block underneath. */
  .tp .tp-basket__side .tp-btn--primary,
  .tp .tp-basket__side .cart-pay-strip { display: none; }
  .tp .tp-basket__mobcta { display: block; }
}

@media (max-width: 640px) {
  .tp .tp-basket__head { padding: 18px 0 16px; }
  .tp .tp-basket__item {
    grid-template-columns: 76px minmax(0, 1fr);
    gap: 14px;
    padding: 18px 16px;
  }
  .tp .tp-basket__thumb { width: 76px; }
  /* Controls move under both columns and go horizontal: quantity left, price
     and bin right, which is the row a phone basket is expected to have. */
  .tp .tp-basket__ctrls {
    grid-column: 1 / -1;
    flex-direction: row;
    align-items: center;
    gap: 12px;
  }
  .tp .tp-basket__price { margin-left: auto; }
  .tp .tp-basket__group { padding: 11px 14px; font-size: var(--tp-t-base); gap: 8px; }
  .tp .tp-basket__group .tp-basket__more { padding: 6px 12px; }
  .tp .tp-basket__opts { left: auto; right: 0; }
  .tp .tp-basket__views.grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Keyword landing pages ────────────────────────────────────────────────
   /koszulki-sportowe-z-wlasnym-nadrukiem/ and siblings. A reading page with a
   product grid in it, so it runs a narrower measure than the catalogue: the
   body copy is the thing being read and full-width prose is not read. */
.tp-lp { padding-block: 28px 64px; }

.tp-lp__head { max-width: 760px; margin-bottom: 28px; }
.tp-lp__lede { font-size: 18px; line-height: 1.6; color: var(--tp-muted); margin: 12px 0 0; }
.tp-lp__acts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }

.tp-lp__toc {
  display: flex; flex-wrap: wrap; gap: 8px 20px;
  padding: 14px 18px; margin-bottom: 40px;
  border: 1px solid var(--tp-line); border-radius: 10px; background: #fff;
}
.tp-lp__toc a { font-size: 14px; font-weight: 600; color: var(--tp-cta); text-decoration: none; }
.tp-lp__toc a:hover { text-decoration: underline; }

.tp-lp__section { margin-bottom: 52px; scroll-margin-top: 90px; }
.tp-lp__section > .tp-h3 { margin: 0 0 20px; }

/* Prose. Measure capped so the copy stays readable next to a 4-up grid. */
.tp-lp__prose { max-width: 760px; }
.tp-lp__prose h2 { font-size: 24px; margin: 32px 0 12px; }
.tp-lp__prose h3 { font-size: 19px; margin: 26px 0 8px; }
.tp-lp__prose p { line-height: 1.7; margin: 0 0 14px; }
.tp-lp__prose ul, .tp-lp__prose ol { line-height: 1.7; margin: 0 0 16px; padding-left: 22px; }
.tp-lp__prose li { margin-bottom: 6px; }
.tp-lp__prose a { color: var(--tp-cta); }

/* Price table. Scrolls in its own box rather than pushing the page sideways. */
.tp-lp__tablewrap { overflow-x: auto; }
.tp-lp__prices { width: 100%; border-collapse: collapse; min-width: 480px; }
.tp-lp__prices th, .tp-lp__prices td {
  text-align: left; padding: 12px 16px; border-bottom: 1px solid var(--tp-line);
}
.tp-lp__prices th {
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--tp-muted); font-weight: 700;
}
.tp-lp__prices td { font-size: 15px; }
.tp-lp__prices tbody tr:last-child td { border-bottom: 0; }
.tp-lp__prices a { color: var(--tp-ink); font-weight: 600; text-decoration: none; }
.tp-lp__prices a:hover { text-decoration: underline; }

/* FAQ. Open by default — a closed disclosure hides the answer the page is for. */
.tp-lp__faqs { display: grid; gap: 22px; max-width: 760px; }
.tp-lp__faq h3 { margin: 0 0 6px; }
.tp-lp__faq p { margin: 0; line-height: 1.65; color: var(--tp-muted); }

.tp-lp__proof { display: flex; flex-wrap: wrap; gap: 14px; }
.tp-lp__club {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px 10px 12px; border: 1px solid var(--tp-line);
  border-radius: 10px; background: #fff; text-decoration: none;
  color: var(--tp-ink); font-size: 14px; font-weight: 600;
}
.tp-lp__club:hover { border-color: var(--tp-line-strong); }
.tp-lp__club img { object-fit: contain; }

/* Silo links. Plain list, real anchor text, no footer link farm. */
.tp-lp__silo { border-top: 1px solid var(--tp-line); padding-top: 28px; }
.tp-lp__links { display: flex; flex-wrap: wrap; gap: 10px 24px; list-style: none; margin: 0; padding: 0; }
.tp-lp__links a { color: var(--tp-cta); font-size: 15px; text-decoration: none; }
.tp-lp__links a:hover { text-decoration: underline; }
.tp-lp__links--cross { margin-top: 12px; }
.tp-lp__links--cross a { color: var(--tp-muted); font-size: 14px; }

@media (max-width: 640px) {
  .tp-lp__lede { font-size: 16px; }
  .tp-lp__acts .tp-btn { flex: 1 1 100%; }
}
