/* shop.css: GENERATED by shop-build.js. Never hand-edited.

   Every colour is a token out of brand.css or an alpha of one. The ONE
   exception is the garment swatch, which carries its own rgb() inline from a
   colour SAMPLED off the photograph: a swatch is a claim about what the cloth
   looks like, and a hex typed by hand is a guess against a photograph that
   has its own lighting. No literal colour enters this stylesheet for it. */

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--label);
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Visually hidden, still read aloud. The swatches are colour only, so their
   name has to reach a screen reader some other way. */
.vh {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
/* The radios themselves. NOT display:none, which takes them out of the tab
   order and out of the accessibility tree: they are the actual control, and
   the label is only the thing you can see. */
.vh-input {
  position: absolute; opacity: 0; width: 1px; height: 1px;
  margin: 0; pointer-events: none;
}

.skip {
  position: absolute; left: -9999px;
  background: var(--surface); color: var(--label);
  padding: 10px 14px; border-radius: var(--radius-sm); z-index: 10;
}
.skip:focus { left: 12px; top: 12px; }

/* The strip above the bar that links chunkchunk.ca, shown below 900px only
   (see the narrow block). 44px tall for the tap floor; below 360px it says
   "Visit chunkchunk.ca", because the long line wraps at 250. */
.ca-strip { display: none; justify-content: center; background: var(--blue); }
.ca-strip a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px;
  color: #FFFFFF; font-weight: 600; font-size: 14px; line-height: 1.25;
  text-decoration: none; text-align: center;
}
.ca-strip a:hover { text-decoration: underline; }
.ca-strip .s-short { display: none; }

/* ── chrome ──────────────────────────────────────────────────────────── */

.chrome {
  background: var(--bg);
  border-bottom: 1px solid var(--separator);
  position: sticky; top: 0; z-index: 5;
}
.chrome-brand { display: inline-flex; align-items: center; }
.chrome-inner {
  max-width: 1120px; margin: 0 auto;
  padding: 14px 24px;
  display: flex; align-items: center; gap: 20px;
}
.chrome-nav { display: flex; align-items: center; gap: 10px; }
.chrome-here {
  font-size: 14px; font-weight: 650; color: var(--label);
  border-bottom: 2px solid var(--blue); padding-bottom: 2px;
}
.chrome-back {
  font-size: 14px; font-weight: 600; color: var(--label-2);
  text-decoration: none;
}
.chrome-back:hover { color: var(--label); }
.chrome-back::after { content: "/"; margin-left: 10px; color: var(--label-3); }
/* nowrap AND flex:none. Shrinkable, the flex row squeezes it until the label
   wraps; wrappable, nowrap alone lets the text spill its own box. */
.chrome-cta { margin-left: auto; white-space: nowrap; flex: none; }

/* An anchor does NOT centre its own text the way a button does, and any
   min-height floor splits the two. site.css has carried this since the
   landing page was built; it is here for the same reason. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  font-size: 14px; font-weight: 650; text-decoration: none;
  border: 1px solid transparent; cursor: pointer;
  font-family: inherit;
}
.btn-ghost {
  color: var(--blue);
  border-color: var(--blue);
  background: transparent;
}
.btn-ghost:hover { background: rgba(52, 96, 160, .08); }
.btn-solid {
  background: var(--blue); color: #FFFFFF; border-color: var(--blue);
}
.btn-solid:hover { filter: brightness(1.06); }
/* DARK KEEPS THE DAY BLUE ON A SOLID BUTTON (9 Oct 2026). Dark --blue is
   #6E9BD8, tuned as TEXT on a card (5.56:1), and white on it measured 2.86:1:
   every Buy button in the store was faint at night. The day blue #3460A0
   under white is 6.31:1. Vic picked it from three rendered side by side:
   "That works perfectly." Text and ghost buttons keep the night blue. */
:root[data-theme="dark"] .btn-solid { background: #3460A0; border-color: #3460A0; }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.btn[disabled] { opacity: .55; cursor: default; }
/* ONE flex item. .btn is inline-flex, and loose words beside the price
   spans became separate flex items whose spaces collapsed: the button read
   "Buy now,$45.00CAD" (Vic, 28 Sep 2026). Inside one span it is ordinary
   inline text again. */
.buy-label { white-space: normal; }

main { max-width: 1120px; margin: 0 auto; padding: 0 24px 80px; }

/* ── hero ────────────────────────────────────────────────────────────────
   The shot is the DETAIL crop, cut at build time. It used to be the whole
   photograph squeezed into a square by object-fit, which put the crop line
   across the model's mouth and left the artwork about a fifth of the frame:
   the thing being sold was the smallest thing on the page. */

.hero {
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: 48px; align-items: center;
  padding: 64px 0 56px;
}
.hero h1 {
  margin: 0 0 18px;
  font-size: 56px; font-weight: 800; letter-spacing: -.03em; line-height: 1.03;
}
.sub {
  margin: 0 0 22px; max-width: 42ch;
  font-size: 16.5px; color: var(--label-2);
}
.hero-actions { margin: 0 0 22px; display: flex; gap: 10px; flex-wrap: wrap; }
/* TWO PILLS NOW, because free shipping is a reason to buy rather than a
   detail to disclose. Wrapping is not optional: at 390px the content box is
   358px and the two pills together run close enough to it that a longer
   claim would overflow rather than stack. */
.hero-meta-row {
  margin: 0; display: flex; gap: 10px; flex-wrap: wrap;
}
.hero-meta {
  margin: 0; display: inline-flex; align-items: center; gap: 9px;
  background: var(--surface); border: 1px solid var(--separator);
  border-radius: 999px; padding: 8px 15px;
  font-size: 13.5px; font-weight: 650;
}
.dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--green); flex: none;
}

.hero-shot { position: relative; }
/* ROUNDED, which the cut-out never needed and a whole photograph does. The
   shot used to be a studio cut-out floating on the cream with no edge of its
   own; this one carries a blurred street to its corners, so without a radius
   it is a hard rectangle sitting on a soft page. The token, never a number. */
.hero-shot img { display: block; width: 100%; height: auto;
                 border-radius: var(--radius); }

/* ── marquee ─────────────────────────────────────────────────────────────
   Two identical runs slide as one so the loop has no seam. Motion is OFF for
   anyone who asked for less of it, which is not decoration: a permanently
   moving band is a genuine vestibular problem and this one carries no
   information. */

/* ── the shop wordmark ────────────────────────────────────────────────────
   IN THE COPY COLUMN, ABOVE THE HEADLINE, and that placement is the whole
   correction. It shipped once as a full-width band between the hero and the
   marquee, which put it alone on a screen of cream with nothing beside it to
   give it scale, and left the top of the page with no identity at all: a
   bare "Shop" link, a headline and a model, on a store. Vic caught both on
   the deployed page.

   WIDTH, NOT VIEWPORT WIDTH. It belongs to the column and never to the
   viewport: at 1440 the copy column measures 499.5px, at 1024 it measures
   452.7px and at 768 the hero stacks to 720px, so a vw rule would make the
   mark tower on a tablet and shrink on a desktop.

   480px, RAISED FROM 420 on 3 September 2026 when the brand-colour banner
   went in. Vic: "make the banner a bit bigger, this really shows the brand
   colours". 480 is the largest cap that still leaves the mark inside its own
   column at every width, measured rather than picked: at 1440 it renders
   480 of the column's 499.5, and from 1024 down the column is narrower than
   the cap so width:100% governs and the mark spans the column exactly, the
   same edge the 56px headline under it sits on. Below 480 nothing changes at
   all, because a phone was already at 100%.

   The intrinsic width/height attributes on the tag reserve the right box so
   the headline does not jump when the mark loads. */
.shopmark {
  display: block;
  width: 100%;
  max-width: 480px;
  height: auto;
  margin: 0 0 18px;
}

/* The same mark in the chrome bar, where the Chunk Chunk lockup used to be.
   SIZED BY HEIGHT, not width: the bar is a fixed-height row, the old lockup
   was 48px tall, and matching that keeps the bar from changing height. */
.chrome-mark {
  display: block;
  height: 34px;
  width: auto;
  /* OPTICAL, not geometric. The ribbon sits above the lettering, so centring
     the box leaves the LETTERS low against the text beside them. Measured on
     the plate: the alpha weighted centroid of the navy letters is 444.2px
     below the box centre of a 5003x1721 image, which is 8.8px at this 34px
     box. Do not nudge this by eye, and re measure it if the mark is redrawn. */
  margin-top: -8.8px;
}

.marquee {
  display: flex; gap: 0;
  overflow: hidden;
  border-top: 1px solid var(--separator);
  border-bottom: 1px solid var(--separator);
  padding: 15px 0;
  user-select: none;
}
.run {
  display: flex; align-items: center; gap: 22px;
  flex: none; padding-right: 22px;
  font-size: 13px; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--label-2);
  white-space: nowrap;
  animation: slide 34s linear infinite;
}
.run i { color: var(--blue); font-style: normal; }
@keyframes slide { to { transform: translateX(-100%); } }

@media (prefers-reduced-motion: reduce) {
  .run { animation: none; }
}

/* ── the rack ────────────────────────────────────────────────────────────
   Four cards, one per colourway, on equal columns. The old 2x2 read as a
   contact sheet because every card wore the WHOLE photograph: same pose, same
   lighting, same framing, four times. The detail crop is what fixed that, not
   the grid. A 7/5 alternation was tried here and reverted: the two cards in a
   row keep their own aspect ratio, so the narrower image is shorter and its
   caption floats above a band of empty card. */
.rack-h {
  margin: 56px 0 20px;
  font-size: 13px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--label-2);
  scroll-margin-top: 90px;
}
.rack {
  display: grid; gap: 22px;
  grid-template-columns: repeat(12, minmax(0, 1fr));
}
/* FOUR UP on a desktop, stepping down to one. Two up was right for four
   colourways; with eight designs it made a page nobody scrolls to
   the end of. The card is a thumbnail that leads to the product page, not
   the product page itself. */
.tee { margin: 0; grid-column: span 3; }
.tee-link {
  display: block; height: 100%;
  color: inherit; text-decoration: none;
  border-radius: var(--radius);
  transition: transform .25s var(--ease);
}
.tee-link:hover { transform: translateY(-4px); }
.tee-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
@media (prefers-reduced-motion: reduce) {
  .tee-link, .tee-link:hover { transition: none; transform: none; }
}
/* CUT OUT AND FLOATING, with no photo box at all. The blanks are shot on a
   green screen and keyed at build time so the model stands on the page's own
   ground, the way a real apparel store shows a garment. A white card behind a
   cutout just reintroduces the box the cutout was for, and a background here
   would be the one colour on this page that is not a brand token. */
.tee-shot { position: relative; display: block; }
/* 3:4 and cropped to the upper body. The full figure is right on a product
   page where the shirt is the subject; in a grid it is mostly jeans, and
   eight of them is a very long page. */
.tee-shot img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 3 / 4; object-fit: cover; object-position: 50% 16%;
}
/* ── THE "PRINTED IN CANADA" CHIP IS GONE, 14 SEPTEMBER 2026 ─────────────
   Vic: "remove printed in canada that can be on the size page", and "that
   badge did annoy me from the beginning". Every rack card wore it and it
   competed with the one claim that actually sells a shirt.

   NOTHING IS LOST, and that was checked rather than assumed before it went.
   The claim still appears in three places: the storefront hero pill, the
   honest panel's "printed to order by Printful in Mississauga, Ontario", and
   specFor() on EVERY product page, which puts "Printed and shipped from
   Mississauga, Ontario" directly beside the print size and the size chart.
   That last one is the size page he meant.

   The rule it was carrying, that "Printed in Canada" names only the printing
   step and so is safe where "Made in Canada" would be a regulated claim about
   cost thresholds, still holds wherever the phrase survives. The blanks are
   Bella+Canvas and made offshore. Do not upgrade the wording anywhere.

   The .chip rule is deleted rather than orphaned: it had exactly one use and
   no gate referenced it, both confirmed by grep before the edit. */

/* ── FREE SHIPPING, THE BADGE ────────────────────────────────────────────
   Vic, 14 September 2026: "add Free Shipping badge to all the shirts big
   selling feature", and "badge should be red and use the branding colors for
   the text". So --red #B03A2E with --bg #FAF2E8 on it, measured at 5.42:1,
   which is the pairing the shop wordmark's own ribbon already ships rather
   than a new colour on a palette this file's docs call full.

   BOTTOM LEFT, AND THAT IS NOT TASTE. .tee-shot img crops at
   object-position 50% 16%, so anything at top: 12px lands on the model's
   forehead. The white chip above gets away with it by being light enough to
   read as a label; a solid red pill there covers a person's face on all 110
   cards. Chosen off a four-candidate sheet rendered into the real page by
   badgetry.js, and the fault was caught by LOOKING at the first render with
   every gate green.

   AND IT CANNOT SIT BESIDE THE CHIP. The rack is four across, so a card is
   252px at 1440 and 168px on a phone against a 133px chip plus a 107px
   badge: they overlap by 12px on a desktop and 96px on a phone. The first
   render printed the badge straight through the word "Canada" on all 22
   cards at all three widths and looked entirely deliberate until the numbers
   were read. If the rack ever goes to three across, re-measure before
   moving this back up. */
.ship-badge {
  position: absolute; left: 12px; bottom: 12px;
  background: var(--red); color: var(--bg);
  border-radius: 999px; padding: 7px 14px;
  font-size: 12.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase;
  box-shadow: 0 2px 6px rgba(28, 42, 61, .26);
}
/* The product page's shot is several times the area of a rack card, so the
   same badge on it reads as a sticker nobody removed. Stepped up to match
   the picture it sits on. */
.pdp-shots .ship-badge {
  left: 16px; bottom: 16px;
  padding: 9px 18px; font-size: 14px;
}
.tee figcaption {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 2px 12px;
  padding: 14px 2px 0;
}
/* Placed explicitly. Left to source order in a two-column grid the price
   lands on the second row under the name and the colour sits where the price
   belongs, which is what the first build did. */
.tee-name { grid-column: 1; grid-row: 1; font-size: 15.5px; font-weight: 650; }
.tee-price {
  grid-column: 2; grid-row: 1;
  font-size: 15.5px; font-weight: 650; text-align: right;
  font-variant-numeric: tabular-nums;
}
.tee-colour { grid-column: 1 / -1; grid-row: 2; font-size: 13px; color: var(--label-2); }

/* THE CARD IS THE CONTAINER, NOT THE VIEWPORT, and that is the whole reason
   this is a container query rather than another breakpoint. The rack is
   four-up from 761px all the way to 1440, so one card is about 168px at 768,
   235px at 1024 and 320px at 1440: the same viewport rule produces a caption
   that fits at one width and wraps at another. Guessing breakpoints to cover
   that means re-guessing them every time the grid changes.

   It shipped wrapped. "Chunk Chunk tee" broke over two lines at 768 and 1024,
   dropping its colour subtitle below the three cards beside it, and no
   desktop capture could show it. checkCardCaptions() in preview.js measures
   line boxes now and found it the first time it ran.

   The 620px media block below stays as the fallback for anything without
   container query support: it is the same treatment, just less precise. */
.tee { container-type: inline-size; }
@container (max-width: 260px) {
  .tee figcaption { grid-template-columns: 1fr; gap: 1px; }
  .tee-name { grid-column: 1; grid-row: 1; font-size: 14.5px; }
  .tee-price { grid-column: 1; grid-row: 2; text-align: left; font-size: 14.5px; }
  .tee-colour { grid-column: 1; grid-row: 3; }
}

/* ── the product page ────────────────────────────────────────────────────
   Colour and size are RADIO INPUTS and every selected state below is a
   :checked sibling rule. Nothing here needs JavaScript: the images swap, the
   swatches light up and the size fills in with scripting switched off. The
   only thing shop.js does on this page is read the two checked values and
   post them, which means a script that fails to load costs the buy button
   and nothing else. */

.pdp { display: block; }
.pdp-grid {
  display: grid;
  grid-template-columns: 1fr 400px;
  gap: 48px;
  padding: 40px 0 8px;
  align-items: start;
}
/* min-width: 0 ON THE CHILDREN, and this is a structural fix rather than a
   tweak. A grid track written 1fr is really minmax(AUTO, 1fr), so the auto
   minimum is the widest child's min-content and a single unwrappable row can
   drag the whole column past the viewport. Adding the red colourway did
   exactly that on 3 September: .pdp-thumbs became five 72px thumbs and four
   10px gaps, 400px of min-content against the 358px a 390px phone has after
   padding, and every one of the sixteen product pages overflowed by 27px.

   The overflow was not the worst of it. The page measured 416px wide inside
   a 390px viewport, which pushed the controls out of the tappable area: the
   colour swatch, the size picker and Buy all stopped responding to a real
   tap while working perfectly when clicked from script. THREE dead controls
   on a storefront, and the only gate that could see it was the one driving
   real coordinates. */
.pdp-grid > * { min-width: 0; }
.pdp-media { position: sticky; top: 100px; }
.pdp-shots { position: relative; }
/* Every colourway is in the markup with its own width and height, and the
   unselected ones are hidden rather than absent. That is not a performance
   nicety: it keeps both src attributes visible to the reference gates in
   shop-build.js and site-package.js, which read MARKUP and cannot see a src
   that only ever gets assigned by a script. It also means the swap causes no
   layout shift and no flash. */
.shot { display: none; width: 100%; height: auto; }

/* ── front and back ──────────────────────────────────────────────────────
   The back is a real photograph of the same person turned around, generated
   with its own front as the reference. It carries no print yet: the back
   artwork is text Vic has not written, and drawing a placeholder on a
   garment somebody can buy would be inventing a product. */

.pdp-controls {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 12px; flex-wrap: wrap;
}
.view-tabs {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--surface-2); border-radius: 999px;
}
.view-tab {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 34px; padding: 0 16px; border-radius: 999px;
  font-size: 13px; font-weight: 650; color: var(--label-2); cursor: pointer;
}
.view-tab:hover { color: var(--label); }
/* WRAPS, because five thumbs genuinely do not fit a phone row and the
   count is not fixed: a sixth colourway would break any width chosen to
   make five fit. Wrapping keeps every thumb visible and needs no
   arithmetic redone when the rack grows. */
.pdp-thumbs { display: flex; gap: 10px; flex-wrap: wrap; }
.pdp-thumb {
  display: block; width: 84px; flex: none; cursor: pointer;
  border-radius: var(--radius-sm); overflow: hidden;
  border: 2px solid var(--separator);
  background: var(--surface-2);
}
.pdp-thumb img { display: block; width: 100%; height: auto; }

.pdp-buy h1 {
  margin: 0 0 6px;
  font-size: 40px; font-weight: 800; letter-spacing: -.03em; line-height: 1.05;
}
.pdp-price {
  margin: 0 0 14px;
  font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.pdp-price span { font-size: 14px; font-weight: 650; color: var(--label-2); }
.pdp-blurb { margin: 0 0 26px; font-size: 15px; color: var(--label-2); max-width: 46ch; }

.pdp-field { margin: 0 0 22px; }
.pdp-label {
  margin: 0 0 10px;
  display: flex; align-items: baseline; gap: 8px;
  font-size: 12.5px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--label-2);
}
/* The chosen colour's name. All of them are in the markup and CSS shows the
   one whose radio is checked, so the label cannot fall out of step with the
   swatch the way a JavaScript-written string can. */
.pick-colour { display: none; text-transform: none; letter-spacing: 0;
  font-size: 13.5px; font-weight: 650; color: var(--label); }
.size-link {
  margin-left: auto; text-transform: none; letter-spacing: 0;
  font-size: 13px; font-weight: 600; color: var(--blue);
}

.swatch-row { display: flex; gap: 10px; }
.swatch {
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  background: var(--sw);
  /* THE RING IS THEME-AWARE AND IT HAS TO BE. It was a fixed ink alpha, and
     checkFieldContrast caught the black swatch at 1.079:1 on the night
     card the first time it ever swept this page: a black disc on a near
     black surface, with an ink ring around it that was equally invisible.
     --label-3 is an ink alpha in day and a cream one in night, so the disc
     keeps an edge in both. 2px, because 1px of a .34 alpha is a suggestion. */
  box-shadow: inset 0 0 0 2px var(--label-3);
}

.size-row { display: flex; flex-wrap: wrap; gap: 8px; }
/* WRAPPING, not seven across. At 390px with 16px gutters the content box is
   358px, and seven 44px buttons plus six 8px gaps is 356: two pixels of
   headroom, against a checkOverflow that fails on one. It wraps to 4 and 3
   rather than overflowing by a pixel on a phone nobody tested. */
.size-btn {
  flex: 1 1 44px; min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--separator);
  background: var(--surface);
  font-size: 13.5px; font-weight: 650; cursor: pointer;
}
.size-btn:hover { border-color: var(--label-3); }
.size-hint { margin: 10px 0 0; font-size: 13px; color: var(--label-2); }
/* Once a size is picked the prompt has done its job. :has() is the whole
   mechanism and a browser without it simply keeps a true sentence on screen,
   which is the right way round for a fallback. */
.pdp:has(input[name="size"]:checked) .size-hint { display: none; }

/* THE UPCHARGE IS STATED BEFORE A SIZE IS PICKED, unlike .size-hint above,
   which is a prompt that has done its job once you choose. A price that only
   appears after the click is the thing this whole change was about. */
.size-extra { margin: 6px 0 0; font-size: 13px; color: var(--label-2); }

/* Directly under the price, which is where the old shipping line used to
   ambush somebody at the last step. */
.pdp-ship {
  margin: -2px 0 0; font-size: 13.5px; color: var(--label-2);
}
.pdp-ship b { color: var(--label); }

/* Every price variant is in the markup and exactly one paints. The base is
   what shows with nothing picked, which is honest both before a choice is
   made and for the five sizes that really do cost it. Generated from
   SIZE_EXTRA so a third upcharge cannot arrive with no rule to reveal it. */
.pp-2XL, .bp-2XL, .mp-2XL, .sb-2XL, .pp-3XL, .bp-3XL, .mp-3XL, .sb-3XL { display: none; }

.buy { width: 100%; margin: 4px 0 0; padding: 14px 18px; font-size: 15.5px; }
.buy-msg {
  margin: 10px 0 0; font-size: 13.5px; font-weight: 600; color: var(--red);
}
/* "Added" is good news, so it is not red (Mindy: it read as the same fault as
   "Pick a size first"), and it carries the way on to the cart. */
.buy-msg.ok { color: var(--label); }
.buy-msg.ok a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 10px; margin: -14px 0 -14px 2px;
  color: var(--blue); font-weight: 700; }   /* 44px: it was a 17px text link (Tess's review) */

/* ── THE PHONE BUY PATH, 11 October 2026 ─────────────────────────────────
   On a 390 phone the first screen of a product page was the photo and
   nothing else: the name started at 1066px, Buy now at 1600px (the "Mindy"
   walk). Someone arriving from a video saw no price and no button. Below
   900px the name and price sit above the photo, and a bar holds the button
   at the foot of the screen until the real buttons are in view. Desktop is
   untouched: both are display:none above 900. */
.pdp-mhead, .buy-bar { display: none; }
.bb-go { display: none; }
.pdp:has(input[name="size"]:checked) .bb-go { display: inline; }
.pdp:has(input[name="size"]:checked) .bb-pick { display: none; }
.note {
  margin: 12px 0 0; font-size: 13px; color: var(--label-2);
  background: var(--green-wash);
  border-radius: var(--radius-sm); padding: 10px 12px;
}
.note b { color: var(--label); }

.spec { margin: 24px 0 0; padding: 0; list-style: none;
  border-top: 1px solid var(--separator); }
.spec li {
  padding: 9px 0; font-size: 13.5px; color: var(--label-2);
  border-bottom: 1px solid var(--separator);
}

/* ── the size chart ──────────────────────────────────────────────────── */

.size-guide {
  margin: 40px 0 0;
  background: var(--surface);
  border: 1px solid var(--separator);
  border-radius: var(--radius);
  scroll-margin-top: 90px;
}
.size-guide summary {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 20px; min-height: 44px;
  cursor: pointer; list-style: none;
}
.size-guide summary::-webkit-details-marker { display: none; }
.size-guide summary::after {
  content: ""; margin-left: auto;
  width: 9px; height: 9px; flex: none;
  border-right: 2px solid var(--label-2); border-bottom: 2px solid var(--label-2);
  transform: rotate(45deg) translate(-2px, -2px);
}
.size-guide[open] summary::after { transform: rotate(225deg) translate(-2px, -2px); }
.size-title { font-size: 15.5px; font-weight: 650; }
.size-sub { font-size: 13px; color: var(--label-2); }
.size-body { padding: 0 20px 20px; }
.size-note { margin: 0 0 16px; font-size: 13.5px; color: var(--label-2); max-width: 68ch; }
.size-note b { color: var(--label); }
.size-note-last { margin: 14px 0 0; }

/* The chart is eight columns wide and a phone is not. The WRAPPER scrolls, so
   the document never widens and checkOverflow stays honest. Shrinking the
   type until it fits would also pass every check and be unreadable, which is
   why min-width is set high enough that the scroll is real. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.size-table {
  border-collapse: collapse; min-width: 520px; width: 100%;
  font-size: 13.5px; font-variant-numeric: tabular-nums;
}
.size-table th, .size-table td {
  padding: 9px 12px; text-align: right; white-space: nowrap;
  border-bottom: 1px solid var(--separator);
}
.size-table thead th {
  color: var(--label-2); font-weight: 700; font-size: 12.5px;
}
/* The row label stays put while the sizes scroll under it, so you never lose
   which row you are reading. It MUST paint an opaque surface of its own or
   the cells scroll visibly through it. */
.size-table th[scope="row"] {
  position: sticky; left: 0; z-index: 1;
  text-align: left; font-weight: 650;
  background: var(--surface);
}

/* ── the honest note ─────────────────────────────────────────────────────
   A rule and a hairline rather than a full-width filled slab. The old
   version was a grey rectangle of three paragraphs below the fold at every
   width, which is what a disclosure looks like when nobody wants it read. */

.honest {
  margin-top: 56px;
  background: var(--surface);
  border: 1px solid var(--separator);
  border-left: 3px solid var(--blue);
  border-radius: var(--radius);
  padding: 26px 28px;
  /* Two columns above 760px. Capped at 62ch, one column of prose in a
     1120px box leaves half the panel empty, which is what the first build
     looked like. The heading takes the gutter instead. */
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 8px 32px;
}
/* THE HEADING SITS IN COLUMN 1 AND EVERY PARAGRAPH IN COLUMN 2, placed
   explicitly rather than by a row span on the heading. It used to read
   grid-row: 1 / span 3, which is the paragraph count of the day hardcoded
   into the layout: adding a fourth paragraph on 5 September 2026 left row 4
   of column 1 free, so auto-placement dropped the closing paragraph into the
   LEFT column underneath the heading, orphaned at the bottom of the panel.
   Nothing errored, nothing overflowed and the gate stayed green. It was
   caught by looking at the capture.
   Placing the columns instead of counting the rows is count-independent, so
   a fifth paragraph cannot bring it back. */
.honest h2 {
  margin: 0; grid-column: 1; grid-row: 1;
  font-size: 17px; font-weight: 700;
}
.honest p { grid-column: 2; margin: 0 0 10px; font-size: 14.5px; color: var(--label-2); max-width: 62ch; }
.honest p:last-child { margin-bottom: 0; }
.honest b { color: var(--label); }

/* ── foot ────────────────────────────────────────────────────────────── */

.foot {
  max-width: 1120px; margin: 0 auto;
  padding: 0 24px 56px;
  display: flex; gap: 16px; flex-wrap: wrap;
  font-size: 13px; color: var(--label-2);
}
.foot p { margin: 0; }
.foot p + p { margin-left: auto; }
.foot a { color: var(--blue); text-decoration: none; }
.foot a:hover { text-decoration: underline; }

/* ── the selected states ─────────────────────────────────────────────────
   GENERATED from the same two constants the markup is generated from, so a
   colourway or a size can never exist as a control with no rule to light it
   up. Hand-written, these are the first thing to fall out of step. */


#c-white:checked ~ #v-front:checked ~ .pdp-grid .shot-white-front { display: block; }
#c-white:checked ~ #v-back:checked ~ .pdp-grid .shot-white-back { display: block; }
#c-white:checked ~ .pdp-grid .pick-white { display: inline; }
#c-white:checked ~ .pdp-grid .swatch-white {
  box-shadow: inset 0 0 0 2px var(--label-3),
              0 0 0 2px var(--surface), 0 0 0 4px var(--label);
}
#c-white:checked ~ .pdp-grid .pdp-thumb-white { border-color: var(--label); }

#c-navy:checked ~ #v-front:checked ~ .pdp-grid .shot-navy-front { display: block; }
#c-navy:checked ~ #v-back:checked ~ .pdp-grid .shot-navy-back { display: block; }
#c-navy:checked ~ .pdp-grid .pick-navy { display: inline; }
#c-navy:checked ~ .pdp-grid .swatch-navy {
  box-shadow: inset 0 0 0 2px var(--label-3),
              0 0 0 2px var(--surface), 0 0 0 4px var(--label);
}
#c-navy:checked ~ .pdp-grid .pdp-thumb-navy { border-color: var(--label); }

#c-heather:checked ~ #v-front:checked ~ .pdp-grid .shot-heather-front { display: block; }
#c-heather:checked ~ #v-back:checked ~ .pdp-grid .shot-heather-back { display: block; }
#c-heather:checked ~ .pdp-grid .pick-heather { display: inline; }
#c-heather:checked ~ .pdp-grid .swatch-heather {
  box-shadow: inset 0 0 0 2px var(--label-3),
              0 0 0 2px var(--surface), 0 0 0 4px var(--label);
}
#c-heather:checked ~ .pdp-grid .pdp-thumb-heather { border-color: var(--label); }

#c-black:checked ~ #v-front:checked ~ .pdp-grid .shot-black-front { display: block; }
#c-black:checked ~ #v-back:checked ~ .pdp-grid .shot-black-back { display: block; }
#c-black:checked ~ .pdp-grid .pick-black { display: inline; }
#c-black:checked ~ .pdp-grid .swatch-black {
  box-shadow: inset 0 0 0 2px var(--label-3),
              0 0 0 2px var(--surface), 0 0 0 4px var(--label);
}
#c-black:checked ~ .pdp-grid .pdp-thumb-black { border-color: var(--label); }

#c-red:checked ~ #v-front:checked ~ .pdp-grid .shot-red-front { display: block; }
#c-red:checked ~ #v-back:checked ~ .pdp-grid .shot-red-back { display: block; }
#c-red:checked ~ .pdp-grid .pick-red { display: inline; }
#c-red:checked ~ .pdp-grid .swatch-red {
  box-shadow: inset 0 0 0 2px var(--label-3),
              0 0 0 2px var(--surface), 0 0 0 4px var(--label);
}
#c-red:checked ~ .pdp-grid .pdp-thumb-red { border-color: var(--label); }

#c-lightblue:checked ~ #v-front:checked ~ .pdp-grid .shot-lightblue-front { display: block; }
#c-lightblue:checked ~ #v-back:checked ~ .pdp-grid .shot-lightblue-back { display: block; }
#c-lightblue:checked ~ .pdp-grid .pick-lightblue { display: inline; }
#c-lightblue:checked ~ .pdp-grid .swatch-lightblue {
  box-shadow: inset 0 0 0 2px var(--label-3),
              0 0 0 2px var(--surface), 0 0 0 4px var(--label);
}
#c-lightblue:checked ~ .pdp-grid .pdp-thumb-lightblue { border-color: var(--label); }

#c-lightpink:checked ~ #v-front:checked ~ .pdp-grid .shot-lightpink-front { display: block; }
#c-lightpink:checked ~ #v-back:checked ~ .pdp-grid .shot-lightpink-back { display: block; }
#c-lightpink:checked ~ .pdp-grid .pick-lightpink { display: inline; }
#c-lightpink:checked ~ .pdp-grid .swatch-lightpink {
  box-shadow: inset 0 0 0 2px var(--label-3),
              0 0 0 2px var(--surface), 0 0 0 4px var(--label);
}
#c-lightpink:checked ~ .pdp-grid .pdp-thumb-lightpink { border-color: var(--label); }

#s-XS:checked ~ .pdp-grid .size-btn[for="s-XS"] {
  background: var(--label); color: var(--bg); border-color: var(--label);
}
#s-S:checked ~ .pdp-grid .size-btn[for="s-S"] {
  background: var(--label); color: var(--bg); border-color: var(--label);
}
#s-M:checked ~ .pdp-grid .size-btn[for="s-M"] {
  background: var(--label); color: var(--bg); border-color: var(--label);
}
#s-L:checked ~ .pdp-grid .size-btn[for="s-L"] {
  background: var(--label); color: var(--bg); border-color: var(--label);
}
#s-XL:checked ~ .pdp-grid .size-btn[for="s-XL"] {
  background: var(--label); color: var(--bg); border-color: var(--label);
}
#s-2XL:checked ~ .pdp-grid .size-btn[for="s-2XL"] {
  background: var(--label); color: var(--bg); border-color: var(--label);
}
#s-3XL:checked ~ .pdp-grid .size-btn[for="s-3XL"] {
  background: var(--label); color: var(--bg); border-color: var(--label);
}
/* Not made in Canada in this colour: the size is not offered. */

.pdp-tee #c-white:checked ~ .pdp-grid .size-btn[for="s-XS"] { display: none; }
.pdp-tee #c-navy:checked ~ .pdp-grid .size-btn[for="s-XS"] { display: none; }
.pdp-tee #c-heather:checked ~ .pdp-grid .size-btn[for="s-XS"] { display: none; }
.pdp-tee #c-red:checked ~ .pdp-grid .size-btn[for="s-XS"] { display: none; }
.pdp-tee #c-red:checked ~ .pdp-grid .size-btn[for="s-3XL"] { display: none; }

#s-2XL:checked ~ .pdp-grid .pp-base,
#s-2XL:checked ~ .pdp-grid .bp-base,
#s-2XL:checked ~ .pdp-grid .mp-base,
#s-2XL:checked ~ .pdp-grid .sb-base { display: none; }
#s-2XL:checked ~ .pdp-grid .pp-2XL,
#s-2XL:checked ~ .pdp-grid .bp-2XL,
#s-2XL:checked ~ .pdp-grid .mp-2XL,
#s-2XL:checked ~ .pdp-grid .sb-2XL { display: inline; }
#s-3XL:checked ~ .pdp-grid .pp-base,
#s-3XL:checked ~ .pdp-grid .bp-base,
#s-3XL:checked ~ .pdp-grid .mp-base,
#s-3XL:checked ~ .pdp-grid .sb-base { display: none; }
#s-3XL:checked ~ .pdp-grid .pp-3XL,
#s-3XL:checked ~ .pdp-grid .bp-3XL,
#s-3XL:checked ~ .pdp-grid .mp-3XL,
#s-3XL:checked ~ .pdp-grid .sb-3XL { display: inline; }

#v-front:checked ~ .pdp-grid .view-tab-front {
  background: var(--surface); color: var(--label);
  box-shadow: 0 1px 2px rgba(28, 42, 61, .12);
}
#v-front:focus-visible ~ .pdp-grid .view-tab-front {
  box-shadow: 0 0 0 3px var(--ring);
}
#v-back:checked ~ .pdp-grid .view-tab-back {
  background: var(--surface); color: var(--label);
  box-shadow: 0 1px 2px rgba(28, 42, 61, .12);
}
#v-back:focus-visible ~ .pdp-grid .view-tab-back {
  box-shadow: 0 0 0 3px var(--ring);
}

/* The focus ring has to follow the LABEL, because the input it belongs to is
   the one pixel nobody can see. Without this the whole picker is invisible to
   a keyboard. */
.vh-input:focus-visible ~ .pdp-grid .swatch[for="c-white"],
.vh-input:focus-visible ~ .pdp-grid .size-btn { outline: none; }

#c-white:focus-visible ~ .pdp-grid .swatch-white {
  box-shadow: inset 0 0 0 2px var(--label-3), 0 0 0 3px var(--ring);
}
#c-navy:focus-visible ~ .pdp-grid .swatch-navy {
  box-shadow: inset 0 0 0 2px var(--label-3), 0 0 0 3px var(--ring);
}
#c-heather:focus-visible ~ .pdp-grid .swatch-heather {
  box-shadow: inset 0 0 0 2px var(--label-3), 0 0 0 3px var(--ring);
}
#c-black:focus-visible ~ .pdp-grid .swatch-black {
  box-shadow: inset 0 0 0 2px var(--label-3), 0 0 0 3px var(--ring);
}
#c-red:focus-visible ~ .pdp-grid .swatch-red {
  box-shadow: inset 0 0 0 2px var(--label-3), 0 0 0 3px var(--ring);
}
#c-lightblue:focus-visible ~ .pdp-grid .swatch-lightblue {
  box-shadow: inset 0 0 0 2px var(--label-3), 0 0 0 3px var(--ring);
}
#c-lightpink:focus-visible ~ .pdp-grid .swatch-lightpink {
  box-shadow: inset 0 0 0 2px var(--label-3), 0 0 0 3px var(--ring);
}

#s-XS:focus-visible ~ .pdp-grid .size-btn[for="s-XS"] {
  box-shadow: 0 0 0 3px var(--ring);
}
#s-S:focus-visible ~ .pdp-grid .size-btn[for="s-S"] {
  box-shadow: 0 0 0 3px var(--ring);
}
#s-M:focus-visible ~ .pdp-grid .size-btn[for="s-M"] {
  box-shadow: 0 0 0 3px var(--ring);
}
#s-L:focus-visible ~ .pdp-grid .size-btn[for="s-L"] {
  box-shadow: 0 0 0 3px var(--ring);
}
#s-XL:focus-visible ~ .pdp-grid .size-btn[for="s-XL"] {
  box-shadow: 0 0 0 3px var(--ring);
}
#s-2XL:focus-visible ~ .pdp-grid .size-btn[for="s-2XL"] {
  box-shadow: 0 0 0 3px var(--ring);
}
#s-3XL:focus-visible ~ .pdp-grid .size-btn[for="s-3XL"] {
  box-shadow: 0 0 0 3px var(--ring);
}

/* ── narrow ──────────────────────────────────────────────────────────── */

@media (max-width: 1000px) {
  .pdp-grid { grid-template-columns: 1fr 340px; gap: 32px; }
  .pdp-buy h1 { font-size: 34px; }
}
@media (max-width: 900px) {
  .hero { grid-template-columns: minmax(0, 1fr); gap: 32px; padding: 44px 0 40px; }
  .hero h1 { font-size: 44px; }
  .hero-shot { max-width: 460px; }
  /* THE ROUTE BACK TO THE RACK. This nav was display:none here, so a phone
     on a product page had no way back to the shop but the browser's own back
     button: the mark and the button beside it both leave for the landing
     page. Vic, 4 September: "there is no menu".

     The BACK half only. .chrome-here names the page you are already on, which
     is worth 42px of a 390px bar to nobody. */
  .chrome-nav { display: flex; }
  .chrome-here { display: none; }
  /* The slash separates a crumb from the next crumb, and there is no next
     crumb here. A left chevron instead, so it reads as a way back rather
     than as a nav item. */
  .chrome-back::after { content: none; }
  .chrome-back::before { content: "\2039"; margin-right: 7px; font-size: 17px; line-height: 1; }
  .chrome-back {
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 0 4px;
  }
  /* AND THE ROUTE HOME LEAVES THE BAR, which is what pays for it.
     .chrome-brand carries the identical href one thumb-width away, so on a
     phone these are two controls for one destination: the same fault the
     app's #budgetLink comment describes, and the reason it is hidden from
     that menu. Measured on the built pages by injection, never by rebuilding:
     with both, the back link overflowed 390 by 13px and 320 by 83px; with the
     button gone it fits every width from 320 up with 48px to spare.

     The footer's "Back to Chunk Chunk" is untouched, so the two-places rule
     this bar was built on still holds: the mark here, and the footer there.

     STALE SINCE THE STORE MOVED TO chunkchunk.shop: the mark goes to the rack
     there, not to chunkchunk.ca, so hiding this left a phone no route to ca
     but the footer. The .ca-strip above the bar is that route now (v3.1.4). */
  .chrome-cta { display: none; }
  .ca-strip { display: flex; }
  /* The auto margin moves onto the cart, because the item that carried it is
     the one that just left. Without this the cart sits against the back link
     with 118px of dead bar to its right, which is exactly what the first
     build produced.

     .chrome-inner-prefixed, and that is the whole reason it works. .cart-link
     declares its own margin-left BELOW this block, a media query adds no
     specificity, and (0,1,0) later beats (0,1,0) earlier. Measured on the
     built page rather than assumed: margin-left resolved to 4px at 320, 360
     and 390 with the bare selector. */
  .chrome-inner .cart-link { margin-left: auto; }
  .cta-long { display: none; }
  .pdp-grid { grid-template-columns: 1fr; gap: 28px; }
  .pdp-media { position: static; max-width: 460px; }
  .pdp-buy { max-width: 460px; }
  .pdp-mhead {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 12px; margin: 0 0 10px;
  }
  .pdp-mtitle { font-size: 20px; font-weight: 800; letter-spacing: -.02em; line-height: 1.15; min-width: 0; }
  .pdp-mprice { font-size: 18px; font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }
  .pdp-mprice small { font-size: 12.5px; font-weight: 650; color: var(--label-2); }
  /* The swatches already pick the colour; five thumbs repeated them on two rows. */
  .pdp-thumbs { display: none; }
  .buy-bar {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
    background: var(--surface); border-top: 1px solid var(--separator);
    box-shadow: 0 -6px 20px rgba(28, 42, 61, .10);
  }
  .buy-bar.off { display: none; }
  .bb-btn { width: 100%; min-height: 48px; font-size: 15.5px; }
  /* The bar must never sit over the last thing on the page. */
  body:has(.buy-bar) { padding-bottom: 76px; }
}
@media (max-width: 1000px) { .tee { grid-column: span 4; } }
@media (max-width: 760px)  { .tee { grid-column: span 6; } }
@media (max-width: 359px) {
  .ca-strip .s-long { display: none; }
  .ca-strip .s-short { display: inline; }
}
/* THE NARROWEST PHONES. At 320 the bar was 8px over with the cart in it, and
   8px is exactly the padding and the cart margin. Nothing is hidden here. */
@media (max-width: 380px) {
  .chrome-inner { padding-left: 12px; padding-right: 12px; }
  /* .cart-link's 4px margin used to be reclaimed here, and that rule is
     GONE rather than fixed. It was written when the bar carried the mark,
     "Back to Chunk Chunk" and the cart and was 8px over at 320; the route
     home has since left the bar, which gives back 125px, and the cart now
     takes the auto margin instead. Prefixed and left in place it would have
     beaten that auto margin on source order and pinned the cart 64px from
     the right edge at 320 and 104px at 360. Measured, after a build that
     did exactly that. */
}

@media (max-width: 620px) {
  .tee { grid-column: span 6; }
  /* STACKED, not two columns. At 390 a card is about 170px wide and the name
     and the price cannot sit side by side in it: "Chunk Chunk tee" broke over
     three lines and "$42.00 CAD" split after the amount, which is the one
     thing on a storefront that must never wrap. No screenshot of the desktop
     grid could show this. */
  .tee figcaption { grid-template-columns: 1fr; gap: 1px; }
  .tee-name { grid-column: 1; grid-row: 1; font-size: 14.5px; }
  .tee-price { grid-column: 1; grid-row: 2; text-align: left; font-size: 14.5px; }
  .tee-colour { grid-column: 1; grid-row: 3; }
  .hero h1 { font-size: clamp(24px, 10.625vw, 34px); }  /* 34px at 320 and up */
  .sub { font-size: 15.5px; }
  .pdp-buy h1 { font-size: 30px; }
  main, .chrome-inner, .foot { padding-left: 16px; padding-right: 16px; }
  .honest { padding: 20px 18px; grid-template-columns: 1fr; gap: 10px; }
  .honest h2 { grid-column: auto; grid-row: auto; }
  .honest p  { grid-column: auto; }
  .size-body { padding: 0 16px 16px; }
  .size-guide summary { padding: 14px 16px; }
  .foot { flex-direction: column; }
  .foot p + p { margin-left: 0; }
  /* The scroll affordance, and only where the table can actually scroll. */
  .table-scroll { mask-image: linear-gradient(to right, #000 90%, rgba(0, 0, 0, .3)); }
}

/* ── the thank-you page ──────────────────────────────────────────────── */

.thanks { padding: 72px 0 24px; max-width: 640px; }
.thanks h1 {
  margin: 0 0 16px;
  font-size: 48px; font-weight: 800; letter-spacing: -.03em; line-height: 1.05;
}
.thanks-ref { margin: 0 0 24px; font-size: 13px; color: var(--label-2); }
.thanks-ref code {
  display: inline-block; margin-top: 6px;
  background: var(--surface-2); border-radius: var(--radius-sm);
  padding: 8px 12px;
  font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--label); word-break: break-all;
}
@media (max-width: 620px) {
  .thanks { padding: 44px 0 16px; }
  .thanks h1 { font-size: 34px; }
}

/* The touch floor, keyed on the POINTER as well as the width. Written as a
   width query alone it becomes a phone rule rather than a thumb rule, and
   every control on an iPad measures 32px. That cost a session on 27 August. */
@media (max-width: 640px), (hover: none) and (pointer: coarse) {
  .btn { min-height: 44px; padding: 10px 16px; }
  .foot a { display: inline-block; padding: 11px 0; }
  .size-link { display: inline-flex; align-items: center; min-height: 44px; }
  /* 34px is a mouse target. checkTapTargets found these at 1024 the moment
     .view-tab was added to its allowlist, on an iPad-width coarse pointer. */
  .view-tab { min-height: 44px; padding: 0 18px; }
  .pdp-thumb { width: 72px; }
  /* And the breadcrumb, for exactly the same reason one query later. Above
     900px the phone rule does not apply and .chrome-back is a 22px inline
     link, which on an iPad in landscape is a coarse pointer at a desktop
     width: the band that put five faults on one in August. Found by
     checkTapTargets the moment .chrome-back was named in its allowlist, as
     34 failures across every product page, the cart and the thanks page. The
     bar is 72px, so a 44px item does not grow it. */
  .chrome-back { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ── the cart ────────────────────────────────────────────────────────────
   NO NEW COLOUR. The glyph is currentColor and the badge is --blue with
   --bg on it, which is the pair the app's Pro pill already carries and which
   was measured in both themes when that shipped. */

.cart-link {
  position: relative; display: inline-flex; align-items: center;
  justify-content: center; min-width: 44px; min-height: 44px;
  margin-left: 4px; color: var(--label); text-decoration: none;
  border-radius: var(--radius-sm); flex: none;
}
.cart-link:hover { color: var(--brand-ink); }
.cart-count {
  position: absolute; top: 3px; right: 0;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px; background: var(--blue); color: var(--bg);
  font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
  font-variant-numeric: tabular-nums;
}

.pdp-actions { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 10px; }
.pdp-actions .btn { flex: 1 1 auto; }

.cart-wrap { padding: 40px 0 8px; max-width: 760px; }
.cart-wrap h1 {
  margin: 0 0 18px;
  font-size: 40px; font-weight: 800; letter-spacing: -.03em;
}
.cart-empty { margin: 0 0 24px; font-size: 16.5px; color: var(--label-2); }

.cart-lines { list-style: none; margin: 0 0 24px; padding: 0; }
.cart-line {
  display: grid;
  grid-template-columns: 88px 1fr auto auto;
  gap: 16px; align-items: center;
  padding: 16px 0; border-bottom: 1px solid var(--separator);
}
/* The same lesson the product grid cost on 3 September: 1fr is
   minmax(AUTO, 1fr), so a child that will not wrap drags the track out. */
.cart-line > * { min-width: 0; }
.cart-thumb {
  display: block; border-radius: var(--radius-sm); overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--separator);
}
.cart-thumb img { display: block; width: 100%; height: auto; }
.cart-meta { min-width: 0; }
.cart-name {
  display: block; font-size: 16px; font-weight: 700; color: var(--label);
  text-decoration: none;
}
.cart-name:hover { text-decoration: underline; }
.cart-variant { margin: 3px 0 0; font-size: 13.5px; color: var(--label-2); }
.cart-line-price { margin: 2px 0 0; font-size: 13px; color: var(--label-2); }
.cart-controls { display: flex; align-items: center; gap: 10px; }
.cart-qty-sel {
  min-height: 44px; padding: 0 8px;
  border: 1px solid var(--separator); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--label); font: inherit; font-size: 14px;
}
.cart-remove {
  min-height: 44px; padding: 0 6px;
  background: none; border: 0; cursor: pointer;
  color: var(--label-2); font: inherit; font-size: 13.5px;
  text-decoration: underline; text-underline-offset: 2px;
}
.cart-remove:hover { color: var(--red); }
.cart-line-total {
  margin: 0; font-size: 16px; font-weight: 700;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.cart-foot { display: grid; gap: 10px; justify-items: start; }
.cart-total-row {
  margin: 0; display: flex; align-items: baseline; gap: 12px;
  font-size: 17px;
}
.cart-total-row strong { font-size: 24px; font-variant-numeric: tabular-nums; }
.cart-note { margin: 0; font-size: 13.5px; color: var(--label-2); }
.cart-pay { min-width: 200px; }

@media (max-width: 620px) {
  .cart-wrap h1 { font-size: 30px; }
  /* The thumb and the name keep the top row; the controls and the line total
     drop beneath them rather than squeezing four columns into 358px. */
  .cart-line { grid-template-columns: 72px 1fr; row-gap: 12px; }
  .cart-controls { grid-column: 1 / -1; }
  .cart-line-total { grid-column: 2; grid-row: 1; text-align: right; align-self: start; }
  .cart-pay { width: 100%; }
}

/* LARGER TEXT, v3.1.1, 28 September 2026. A 375pt iPhone with Larger Text on
   lays this page out in about 250px, and every page here was wider than that:
   265 on every product page (the cart was pushed out of the bar) and 290 on
   the front page. iOS answers a page wider than the screen by zooming the
   WHOLE page out, which is what Vic saw on chunkchunk.ca: every section
   stopping at 84% of the screen.

   Three floors caused it, each fixed where it lives: .rack's 12 tracks were
   1fr (minmax(AUTO, 1fr), so content set a minimum), the stacked .hero was
   1fr too, and the h1 is ONE unbreakable word, 274px at 34px. The rest is
   here: below 300px the bar tightens and the mark steps down to 28px, and
   the rack goes to one column, because 11 gaps of 22px are 242px on their
   own. Measured by injection in WebKit at 250 to 1440 before it was written:
   nothing moves at 320 or wider. preview.js's site-zoom pass measures it. */
@media (max-width: 300px) {
  .chrome-inner { gap: 10px; padding-left: 12px; padding-right: 12px; }
  .chrome-mark { height: 28px; margin-top: -7.2px; }
  .rack { grid-template-columns: minmax(0, 1fr); }
  .tee { grid-column: auto; }
}

/* ── the ebook ───────────────────────────────────────────────────────── */
.eb-band {
  display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 36px; align-items: center;
  margin: 40px 0 8px; padding: 28px; border-radius: var(--radius-lg, 20px);
  background: var(--surface); border: 1px solid var(--separator);
}
.eb-band-cover img, .eb-cover img, .eb-peek img {
  display: block; width: 100%; height: auto; border-radius: 6px;
  box-shadow: 0 14px 34px rgba(28, 42, 61, .22), 0 2px 6px rgba(28, 42, 61, .10);
}
.eb-band h2 { margin: 0 0 10px; font-size: 40px; font-weight: 800; letter-spacing: -.02em; line-height: 1.05; }
.eb-band .sub { margin-bottom: 18px; }
.eb-band .hero-actions { margin-bottom: 12px; }
.eb-pro { margin: 0; font-size: 14px; color: var(--label-2); }
.eb-pro b { color: var(--label); }
.eb-cover { max-width: 460px; }
.eb-subtitle { margin: -4px 0 14px; font-size: 17px; font-weight: 600; color: var(--label-2); }
.eb-pdp .buy { margin-top: 0; }
.eb-inside { padding-top: 24px; }
.eb-peek { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 22px; margin: 0 0 40px; }
.eb-toc { margin: 0; padding-left: 26px; columns: 2; column-gap: 40px; font-size: 15.5px; }
.eb-toc li { margin: 0 0 8px; break-inside: avoid; }
.eb-toc li.eb-extra { list-style: none; margin-left: -26px; font-weight: 650; color: var(--blue); }
.eb-pdp .pdp-ship { margin-bottom: 14px; }
.eb-thanks .thanks-help a { color: var(--blue); font-weight: 600; }
.eb-thanks .buy-msg { margin-top: -8px; }
.eb-thanks button.btn { cursor: pointer; }
@media (max-width: 760px) {
  .eb-band { grid-template-columns: minmax(0, 1fr); gap: 22px; padding: 22px; }
  .eb-band-cover { max-width: 220px; }
  .eb-band h2 { font-size: 32px; }
  .eb-peek { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
  .eb-toc { columns: 1; }
}

/* ── the coloring book ───────────────────────────────────────────────── */
.cb-band { background: linear-gradient(135deg, #2b1747 0%, #3d1f5c 55%, #5a2a3f 100%); border-color: #3d1f5c; color: #fff; }
.cb-band .kicker { color: #ffb347; }
.cb-band h2, .cb-band .sub { color: #fff; }
.cb-band .sub { opacity: .9; }
.cb-band .btn-solid { background: #ff8a1f; border-color: #ff8a1f; color: #1c1230; }
.cb-peek { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cb-peek img { box-shadow: 0 0 0 1px var(--separator), 0 10px 26px rgba(28, 42, 61, .14); }
.cb-list { margin: 0 0 8px; padding-left: 22px; font-size: 15.5px; }
.cb-list li { margin: 0 0 8px; }
@media (max-width: 760px) {
  .cb-peek { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
