/* hattyhat — Tokyo Night meets Bitcoin. Retro terminal: Hyprland-style bar, CRT scanlines, synthwave sun.
   Self-hosted fonts (OFL), no external assets, no client JS. */

@font-face { font-family: 'JetBrains Mono'; font-weight: 400; font-display: swap; src: url(/fonts/jbm-400-latin.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'JetBrains Mono'; font-weight: 400; font-display: swap; src: url(/fonts/jbm-400-latinext.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'JetBrains Mono'; font-weight: 700; font-display: swap; src: url(/fonts/jbm-700-latin.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'JetBrains Mono'; font-weight: 700; font-display: swap; src: url(/fonts/jbm-700-latinext.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Press Start 2P'; font-weight: 400; font-display: swap; src: url(/fonts/ps2p-latin.woff2) format('woff2'); }

:root {
  /* Tokyo Night */
  --bg: #1a1b26;
  --bg-dark: #16161e;
  --surface: #1f2335;
  --surface-2: #24283b;
  --line: #292e42;
  --line-strong: #414868;
  --text: #c0caf5;
  --muted: #b8c2e5; /* a shade brighter than Tokyo Night's #a9b1d6 (review, 2026-10-31): most buying information is set in it */
  --faint: #8a93be; /* ~5.3:1 on the page: faint, but readable (review #30; brightened 2026-10-31 with --muted) */
  --blue: #7aa2f7;
  --cyan: #7dcfff;
  --magenta: #bb9af7;
  --pink: #f7768e;
  --green: #9ece6a;
  --yellow: #e0af68;
  /* Bitcoin */
  --btc: #f7931a;
  --btc-orange: #f7931a; /* the ₿ itself */
  --stage: #2f3549; /* the spotlight behind every product picture */
  --ring: #68739b; /* the outline of every picker button and colour dot */
  --accent-ink: #16161e;
  --ok: var(--green);
  --warn: var(--yellow);
  --bad: var(--pink);
  --radius: 6px;
  --glow: 0 0 18px rgba(187, 154, 247, 0.35);
  --border-grad: linear-gradient(135deg, var(--cyan), var(--magenta) 55%, var(--btc));
  /* Anything picked or current wears the hovered card's border (owner, 2026-11-01): set border-color transparent with it. */
  --lit: linear-gradient(var(--surface), var(--surface)) padding-box, var(--border-grad) border-box;
  /* The small boxes (picks, tabs) wear it on a 1px border with a fainter glow (owner, 2026-11-01: subtler than the cards). */
  --lit-soft: var(--lit);
  --glow-soft: 0 0 8px rgba(187, 154, 247, 0.2);
  --mono: 'JetBrains Mono', 'JetBrainsMono Nerd Font', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
  --pixel: 'Press Start 2P', var(--mono);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: radial-gradient(ellipse 120% 60% at 50% -10%, #24283b 0%, var(--bg) 55%, var(--bg-dark) 100%) fixed, var(--bg);
  color: var(--text);
  font: 15px/1.65 var(--mono);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
/* CRT scanlines, barely there: on the decoration only (the top bar, the home page's sun and the footer), never over
   text, prices or controls (review, 2026-10-31: the page-wide overlay dimmed every third row of every word). The sun
   draws its own (.sun), so they stay off the hat cycling in front of it (owner, 2026-11-02: a striped square over the
   hat looked odd). The z-index 51 rules elsewhere date from when it covered the page; harmless now. */
.site-footer::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.13) 0 1px, transparent 1px 3px);
  z-index: 2;
}
main { flex: 1; }
a { color: var(--blue); text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
a:hover { color: var(--cyan); text-decoration-color: var(--cyan); }
:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; border-radius: 3px; }
img, svg { max-width: 100%; display: block; }
h1, h2, h3 { font-family: var(--mono); font-weight: 700; letter-spacing: -0.01em; line-height: 1.2; margin: 0 0 0.5em; color: var(--text); }
h1 { font-family: var(--pixel); font-weight: 400; font-size: clamp(1.35rem, 4.6vw, 2.5rem); line-height: 1.35; letter-spacing: 0; }
h2 { font-size: clamp(1.15rem, 3vw, 1.5rem); }
h3, h2.h3 { font-size: 1rem; color: var(--magenta); } /* h2.h3: a panel heading straight under the h1, styled as before (accessibility review, 2026-11-03) */
p { margin: 0 0 1em; }
code, .mono { font-family: var(--mono); }
.neon { color: #e6e9ff; text-shadow: 0 0 6px rgba(125, 207, 255, 0.55), 0 0 22px rgba(187, 154, 247, 0.45), 3px 3px 0 #3d2a66; }
.prompt { color: var(--green); margin-right: 0.4em; }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.small { font-size: 0.875rem; } /* 14px: secondary information stays readable (review, 2026-11-01) */
.center { text-align: center; }
.wrap { width: 100%; max-width: 1140px; margin: 0 auto; padding: 0 16px; }
.skip { position: absolute; left: -999px; }
.skip:focus { left: 16px; top: 8px; background: var(--surface); padding: 8px 12px; z-index: 60; }
.dev-banner { background: repeating-linear-gradient(45deg, #2b1f12 0 12px, #241a10 12px 24px); color: var(--yellow); font-size: 0.72rem; text-align: center; padding: 5px 16px; border-bottom: 1px solid #4a3418; }

/* ---- top bar (waybar / Hyprland workspaces) ---- */
.bar { position: sticky; top: 0; z-index: 53; /* above the orange buttons (51) and the Shop's section links (52), with its own scanlines since it sits over the overlay */ background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.13) 0 1px, transparent 1px 3px), rgba(22, 22, 30, 0.92); backdrop-filter: blur(8px); border-bottom: 1px solid var(--line); }
.bar .wrap { display: flex; align-items: center; gap: 14px; height: 52px; }
.brand { display: flex; align-items: center; gap: 8px; text-decoration: none; color: var(--text); white-space: nowrap; margin-right: auto; }
.brand:hover { color: var(--text); }
/* The logo (owner, 2026-11-04, B1): '> hats' and an orange block cursor, on every screen. */
.brand .logo { font-family: var(--mono); font-weight: 700; font-size: 1.3rem; letter-spacing: -0.01em; color: var(--text); }
.brand .logo .gt { color: var(--btc-orange); }
.bar .cursor { display: inline-block; width: 0.55em; height: 1.2em; background: var(--btc-orange); animation: blink 1.1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.workspaces { display: flex; gap: 4px; background: var(--bg-dark); border: 1px solid var(--line); border-radius: 8px; padding: 3px; }
.workspaces a { display: flex; gap: 6px; align-items: center; padding: 3px 10px; border-radius: 5px; color: var(--muted); text-decoration: none; font-size: 0.8rem; }
.workspaces a .n { color: var(--faint); }
.workspaces a:hover { color: var(--text); background: var(--surface-2); }
/* Links go somewhere, boxes are choices (Fable's review, 2026-11-01): the current page is marked like waybar's active
   workspace, an orange bar under it, not the picked-choice border. */
.workspaces a[aria-current='page'] { background: var(--surface-2); box-shadow: inset 0 -2px 0 var(--btc); color: var(--text); }
.workspaces a[aria-current='page'] .n { color: var(--btc); font-weight: 700; }
.ticker { border: 0; width: 250px; height: 26px; background: transparent; color-scheme: dark; }
.bag { font-size: 0.82rem; color: var(--muted); text-decoration: none; display: flex; align-items: center; gap: 6px; }
.bag[aria-current='page'], .bag:hover { color: var(--text); }
.bag-count { background: var(--magenta); color: var(--accent-ink); border-radius: 4px; padding: 0 6px; font-size: 0.72rem; font-weight: 700; }

/* ---- footer (vim statusline) ---- */
.site-footer { position: relative; border-top: 1px solid var(--line); margin-top: 72px; padding: 36px 0 28px; font-size: 0.82rem; color: var(--muted); background: rgba(22, 22, 30, 0.6); }
.site-footer .wrap { position: relative; z-index: 3; } /* the links sit above the footer's scanlines */
.site-footer .cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 24px; margin-bottom: 28px; }
.site-footer h2 { font-weight: 700; color: var(--magenta); margin: 0 0 8px; font-size: 0.78rem; letter-spacing: 0; line-height: 1.6; } /* h2, not h4: no skipped levels (accessibility review, 2026-11-03) */
.site-footer h2::before { content: '## '; color: var(--faint); }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin: 4px 0; }
.site-footer a { text-decoration: none; color: var(--muted); }
.site-footer a:hover { color: var(--cyan); }
.statusline { display: flex; flex-wrap: wrap; gap: 0; margin: 0; background: var(--bg-dark); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; font-size: 0.75rem; }
.statusline span { padding: 3px 12px; }
.statusline .mode { background: var(--blue); color: var(--accent-ink); font-weight: 700; }
.statusline span:nth-child(2) { background: var(--surface-2); color: var(--btc); }

/* ---- hero (synthwave sun over a neon grid) ---- */
.hero { overflow: hidden; }
.hero-grid { display: grid; gap: 8px; grid-template-columns: 1fr; align-items: center; padding: 48px 16px 24px; }
@media (min-width: 880px) { .hero-grid { grid-template-columns: 1fr 1fr; padding-top: 72px; } }
.eyebrow { font-size: 0.78rem; color: var(--green); margin-bottom: 16px; display: block; }
.lede { font-size: 1rem; color: var(--muted); max-width: 36em; }
.hero-art { position: relative; aspect-ratio: 1 / 1; max-width: 560px; width: 100%; margin: 0 auto; }
.sun {
  position: absolute; left: 50%; top: 6%; width: 74%; aspect-ratio: 1; transform: translateX(-50%); border-radius: 50%;
  background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.13) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, #ffd27a 0%, var(--btc) 38%, var(--pink) 68%, var(--magenta) 100%);
  -webkit-mask: linear-gradient(#000 0 48%, transparent 48% 51%, #000 51% 59%, transparent 59% 63%, #000 63% 70%, transparent 70% 75.5%, #000 75.5% 81%, transparent 81% 88%, #000 88%);
          mask: linear-gradient(#000 0 48%, transparent 48% 51%, #000 51% 59%, transparent 59% 63%, #000 63% 70%, transparent 70% 75.5%, #000 75.5% 81%, transparent 81% 88%, #000 88%);
  filter: drop-shadow(0 0 40px rgba(247, 118, 142, 0.45));
  opacity: 0.92;
}
.floor {
  /* Drawn in perspective in public/grid.svg (owner, 2026-10-29): a CSS 3D-tilted gradient grid lost and regained its
     sub-pixel lines on phones as the hero animation repainted. */
  position: absolute; left: -30%; right: -30%; bottom: 0; height: 34%;
  background: url('/grid.svg') center bottom / 100% 100% no-repeat;
  -webkit-mask: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
          mask: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
}
.hero-hat { position: absolute; inset: 8% 4% 0; filter: drop-shadow(0 18px 30px rgba(0, 0, 0, 0.55)); }
.hero-hat img { width: 100%; height: auto; }
/* The home page hat slowly fades through every design (owner, 2026-10-27): 5 s each, CSS only (the CSP allows no
   inline styles, so the slots are listed here; keep in step with the number of designs: nine since 2026-11-10). */
.hero-hat.cycle > div { position: absolute; inset: 0; opacity: 0; animation: hero-cycle 45s linear infinite; }
.hero-hat.cycle > div:nth-of-type(2) { animation-delay: 4s; }
.hero-hat.cycle > div:nth-of-type(3) { animation-delay: 9s; }
.hero-hat.cycle > div:nth-of-type(4) { animation-delay: 14s; }
.hero-hat.cycle > div:nth-of-type(5) { animation-delay: 19s; }
.hero-hat.cycle > div:nth-of-type(6) { animation-delay: 24s; }
.hero-hat.cycle > div:nth-of-type(7) { animation-delay: 29s; }
.hero-hat.cycle > div:nth-of-type(8) { animation-delay: 34s; }
.hero-hat.cycle > div:nth-of-type(9) { animation-delay: 39s; }
.hero-hat.cycle > .hero-shadow { position: absolute; inset: 0; }
.hero-hat.cycle > div:nth-child(2) { position: relative; }
/* Each hat fades in on top of a still-solid one, then the one underneath disappears, so the sunset never shows
   through two half-faded hats. The last hat, on top of the stack, fades out over the first one instead. Over 45 s:
   1 s in, 6 s showing. */
@keyframes hero-cycle { 0% { opacity: 0; } 2% { opacity: 1; } 13.3% { opacity: 1; } 13.31% { opacity: 0; } 100% { opacity: 0; } }
@keyframes hero-cycle-last { 0% { opacity: 0; } 2% { opacity: 1; } 13.3% { opacity: 1; } 15.5% { opacity: 0; } 100% { opacity: 0; } }
.hero-hat.cycle > div:last-child { animation-name: hero-cycle-last; }
@media (prefers-reduced-motion: reduce) { .hero-hat.cycle > div { animation: none; } .hero-hat.cycle > div:first-of-type { opacity: 1; } }
/* The first hat is a still base layer, so a hat always shows even with animation stopped (review, 2026-11-01). */
.hero-hat.cycle > div:first-of-type { animation: none; opacity: 1; }
.hero-links { margin-top: 12px; }
.hero-links a { color: var(--blue); text-decoration: underline; } /* the site's links, blue and underlined (owner, 2026-11-07) */
.statusline .nostr { padding: 3px 12px; color: var(--blue); text-decoration: none; margin-left: auto; } /* a link, so link blue (colour pass, 2026-11-01) */
.statusline .nostr.x { margin-left: 0; } /* the X link sits beside the Nostr one (owner, 2026-11-09) */
.statusline .nostr:hover, .statusline .nostr:focus-visible { text-decoration: underline; }

/* ---- buttons / forms ---- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; font: 700 0.85rem/1.3 var(--mono); padding: 11px 18px; text-align: center; border-radius: 4px; border: 2px solid var(--line-strong); background: var(--surface-2); color: var(--text); text-decoration: none; cursor: pointer; min-height: 46px; box-shadow: 4px 4px 0 var(--bg-dark); transition: transform 0.06s, box-shadow 0.06s; }
.btn:hover { border-color: var(--cyan); color: var(--text); }
.btn:active { transform: translate(3px, 3px); box-shadow: 1px 1px 0 var(--bg-dark); }
/* Solid bitcoin orange: raised above the CRT scanline overlay (body::after, z-index 50) so it isn't striped. */
.btn-primary { position: relative; z-index: 51; }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn-block { width: 100%; }
.btn-small { padding: 6px 12px; min-height: 34px; font-size: 0.78rem; box-shadow: 3px 3px 0 var(--bg-dark); }
.btn-wallet { margin: 0 0 12px; } /* the pay page: tap to open the wallet with the invoice (owner, 2026-10-31) */
.btn-danger { border-color: #6b2a35; color: var(--bad); }
.actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 22px; }
form.inline { display: inline; }
label, .label { display: block; font-size: 0.8rem; color: var(--muted); margin: 0 0 6px; } /* .label: a heading over a picker, not tied to one input */
input[type='text'], input[type='email'], input[type='password'], input[type='number'], select, textarea {
  width: 100%; font: inherit; color: var(--text); background: var(--bg-dark); border: 1px solid var(--line-strong); border-radius: 4px; padding: 11px 12px; min-height: 46px;
}
input:focus, select:focus, textarea:focus { border-color: var(--cyan); outline: none; box-shadow: 0 0 0 3px rgba(125, 207, 255, 0.15); }
.field { margin-bottom: 16px; }
.grid-2 { display: grid; gap: 0 16px; grid-template-columns: 1fr; }
@media (min-width: 560px) { .grid-2 { grid-template-columns: 1fr 1fr; } }
.qty { width: 80px !important; }

/* ---- cards / product grid ---- */
.section { padding-top: 40px; padding-bottom: 40px; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); }
.card { border: 2px solid var(--line); border-radius: var(--radius); background: var(--surface); overflow: hidden; display: flex; flex-direction: column; transition: border-color 0.15s, box-shadow 0.15s; }
.card:hover { border-color: transparent; background: linear-gradient(var(--surface), var(--surface)) padding-box, var(--border-grad) border-box; box-shadow: var(--glow); }
.card { position: relative; }
.card-link { text-decoration: none; color: inherit; display: flex; flex-direction: column; }
.card-link::after { content: ''; position: absolute; inset: 0; z-index: 1; } /* the whole card is the link, prices and dots too (review, 2026-11-01) */
.card:focus-within { border-color: var(--cyan); }
.card-link:focus-visible { outline: none; }
.card-link:hover { color: inherit; }
.card .art { background: radial-gradient(ellipse at 50% 42%, var(--stage) 0%, var(--surface) 70%); }
.card .art img { width: 100%; height: auto; aspect-ratio: 1; }
.card .body { padding: 12px 16px 4px; display: flex; flex-direction: column; gap: 2px; }
.card .name { font-size: 1.05rem; font-weight: 700; display: flex; justify-content: space-between; gap: 8px; }
.card-foot { padding: 4px 16px 14px; display: flex; flex-direction: column; align-items: stretch; gap: 6px; } /* colours above the prices (owner, 2026-10-28) */
.badge { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; border: 1px solid var(--line-strong); color: var(--muted); padding: 1px 7px; border-radius: 3px; white-space: nowrap; align-self: center; font-weight: 400; }
.badge.accent { color: var(--magenta); border-color: #4b3b6e; }
.badge.ok { color: var(--ok); border-color: #3b5228; }
.badge.warn { color: var(--warn); border-color: #5a4524; }
.badge.bad { color: var(--bad); border-color: #6b2a35; }
.swatches-mini { display: flex; gap: 5px; flex-shrink: 0; align-items: center; }
.swatches-mini svg { width: 16px; height: 16px; }
.swatches-mini .n-colours { margin-left: 4px; color: var(--muted); font-size: 0.875rem; }

/* ---- live price frames ---- */
.live-price { border: 0; display: block; width: 100%; min-width: 0; flex: 1; background: transparent; color-scheme: dark; }
/* Room for enlarged text, and nothing hidden if it still overflows (review #31). */
.live-price.card { height: 66px; min-height: 0; flex: none; } /* three lines; no stretching now the dots sit above */
.live-price.full { height: 68px; min-height: 0; } /* two lines: the price and its sub-line (Fable's review, 2026-11-03) */
/* On a phone the small print wraps more the narrower it gets: each width's frame fits its tallest price (a hoodie's, with
   the size line) without an empty band under it (look-and-feel pass, 2026-11-02: 15rem cut the hoodie's last line at
   320px and left a 45-65px gap at 390px). The frame's text is 14px, so these are px, not rem. */
@media (max-width: 374px) { .live-price.full { height: 96px; } }
/* The public snapshot's estimate is three short lines (no live rate line): no empty band above the waiting-list button. */
/* The public site's prices, in the page itself (review, 2026-10-31): only as tall as they are. */
.price-inline { min-width: 0; }
body.live { background: transparent; min-height: 0; display: block; overflow: auto; font-size: 14px; }
body.live::after { display: none; }
.lp-card { font-size: 0.8rem; color: var(--muted); white-space: nowrap; line-height: 20px; }
/* The product page's price (review, 2026-10-31): the delivered price is the headline, no glow on figures; sats under
   it; one short line on where it's for; the small print behind "About this estimate". */
.lp .price-big { font-size: 1.7rem; font-weight: 700; color: var(--btc); line-height: 1.3; }
.lp .price-big .delivered { font-size: 0.55em; font-weight: 400; color: var(--btc); opacity: 0.9; }
.tk { font-size: 0.78rem; line-height: 26px; white-space: nowrap; color: var(--text); text-align: right; }
.tk .btc { color: var(--btc-orange); font-weight: 700; }
.tk .sep { color: var(--line-strong); margin: 0 8px; }
.dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--green); margin-right: 7px; vertical-align: 1px; box-shadow: 0 0 8px var(--green); animation: pulse 2s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.35; } }

/* ---- product page: CSS-only colourway switcher ---- */
.product { display: grid; gap: 28px; grid-template-columns: 1fr; padding-top: 28px; }
@media (min-width: 880px) { .product { grid-template-columns: 1.1fr 1fr; gap: 48px; padding-top: 44px; } }
/* On a computer the picture stays in view while the details scroll past it (review, 2026-10-31), under the top bar. */
/* Sticky makes its own layer, so it carries the picture's place above the scanlines (z-index 51) itself. */
@media (min-width: 880px) { .product > .gallery-col { position: sticky; top: 64px; z-index: 51; align-self: start; } }
/* Our maker's previews sit under the picture (owner, 2026-10-31: easier to find); opened, they're taller than the
   screen, so the column scrolls with the page again. */
@media (min-width: 880px) { .product > .gallery-col:has(details.supplier[open]) { position: static; } }
.window { border: 2px solid transparent; border-radius: 8px; background: linear-gradient(var(--surface), var(--surface)) padding-box, var(--border-grad) border-box; box-shadow: var(--glow); overflow: hidden; align-self: start; }
.titlebar { display: flex; justify-content: space-between; gap: 12px; font-size: 0.75rem; padding: 6px 12px; background: var(--bg-dark); border-bottom: 1px solid var(--line); color: var(--muted); }
.titlebar > span:last-child { white-space: nowrap; } /* "2026-10-31" never splits on a phone (review round 5) */
@media (max-width: 420px) { .titlebar .tb-file { display: none; } } /* small phones: the cap and its stitched size, not the file name (E2E review, 2026-11-04) */
/* The product picture sits above the CRT scanlines (body::after, z-index 50), so it's clean (owner, 2026-10-30); the
   texture stays on the rest of the page. Below the sticky bar (52). */
/* Card pictures too (owner, 2026-10-30), but not the home page's sunset hero. */
.card .art { position: relative; z-index: 51; }
.swatch.logo img { position: relative; z-index: 51; } /* the design picker's pictures too (owner, 2026-10-31) */
.gallery .views { position: relative; z-index: 51; aspect-ratio: 1; background: radial-gradient(ellipse at 50% 42%, var(--stage) 0%, var(--surface) 70%); }
/* Only the picked view is displayed: a product can have 150 pictures, and a hidden-but-displayed lazy image still loads. */
.gallery .view { position: absolute; inset: 0; display: none; }
.gallery .view img { width: 100%; height: 100%; object-fit: contain; }
.colorway-input { position: fixed; top: 0; left: 0; opacity: 0; pointer-events: none; } /* fixed: focusing one never scrolls the page back up to it (owner, 2026-11-01) */
.swatches { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 22px; }
.swatch { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--ring); border-radius: 4px; padding: 9px 12px 9px 8px; /* 40px+ tap targets (review, 2026-10-30) */ cursor: pointer; font-size: 0.8rem; color: var(--muted); margin: 0; background: var(--bg-dark); }
.swatch svg { width: 20px; height: 20px; }
/* Style and design pickers (owner, 2026-10-30): each chip shows the logo. The rules for which is lit, and which picture
   shows, are written per page (pickerStyle in src/web/shop.ts). */
.swatch .logo-thumb { width: auto; height: 22px; }
.swatches.logos { gap: 6px; }
.price-block { border-top: 1px dashed var(--line-strong); border-bottom: 1px dashed var(--line-strong); padding: 14px 0; margin: 8px 0 18px; }
.price-big { font-size: 1.6rem; font-weight: 700; }
.breakdown { font-size: 0.82rem; width: 100%; border-collapse: collapse; }
.breakdown td { padding: 5px 0; border-bottom: 1px dashed var(--line); vertical-align: top; }
.breakdown td:last-child { text-align: right; white-space: nowrap; padding-left: 12px; }
/* Second figure (sats, or £/$/€) sits under the first, so the words keep most of the width. */
.breakdown td .sub { display: block; font-weight: 400; font-size: 0.78rem; }
.breakdown tr.saving td { color: var(--green); } /* our maker discount, passed on */
.per-item { margin: 10px 0 4px; font-size: 0.86rem; }
.per-item summary { cursor: pointer; color: var(--cyan); }
.per-item .breakdown { margin-top: 6px; }
.breakdown tr.total td .sub .fx { display: block; }
.breakdown tr.total td .sub .sep { display: none; }
@media (min-width: 880px) { .two-col.wide-right { grid-template-columns: 1fr 1.1fr; } }
.profit-note { margin-top: 12px; }
.profit-note .star, .breakdown tr.profit td { color: var(--btc); }
.profit-note strong { color: var(--text); }
.breakdown tr.subtotal td { border-top: 1px solid var(--line-strong); color: var(--text); font-weight: 700; }
/* Bag lines: everything on one baseline, a compact quantity box, remove as a quiet text button under the item. */
table.data.bag-lines td { vertical-align: middle; }
.qty-form { display: inline-flex; align-items: center; gap: 8px; }
.bag-lines .qty { width: 58px !important; min-height: 34px; padding: 4px 8px; }
/* Phones: each bag line stacks, item and colour on the left, quantity and update on the right (end-to-end run,
   2026-11-01: the update button was off the edge of the screen). */
@media (max-width: 600px) {
  .bag-lines thead { display: none; }
  .bag-lines tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 12px; align-items: center; padding: 12px 0; border-bottom: 1px dashed var(--line); }
  table.data.bag-lines td { display: block; padding: 0; border: 0; }
  .bag-lines td:nth-child(1) { grid-column: 1; grid-row: 1; }
  .bag-lines td:nth-child(2) { grid-column: 1; grid-row: 2; color: var(--muted); }
  .bag-lines td:nth-child(3) { grid-column: 2; grid-row: 1 / span 2; }
  .bag-lines td:nth-child(4) { grid-column: 1; grid-row: 3; } /* name, colour, then remove (look review, 2026-11-02) */
  .bag-lines .qty-form { display: flex; gap: 6px; align-items: center; }
}
.text-btn { background: none; border: 0; padding: 0; margin-top: 4px; font: inherit; font-size: 0.78rem; color: var(--muted); text-decoration: underline; cursor: pointer; }
.text-btn:hover { color: var(--text); }
@media (max-width: 420px) { .breakdown td:last-child { white-space: normal; overflow-wrap: anywhere; } .breakdown tr.total td:last-child { white-space: nowrap; } } /* the total never splits "39,145 / sats" */
.two-col > * { min-width: 0; }
.breakdown tr.profit td { color: var(--btc); }
.breakdown tr.total td { border-bottom: 0; border-top: 1px solid var(--line-strong); font-size: 1rem; color: var(--text); padding-top: 10px; font-weight: 700; }
.details { padding-left: 18px; color: var(--muted); }
.details li::marker { content: '› '; color: var(--magenta); }
/* The product page's details as short labelled groups (review, 2026-10-31); the full list folds under them. */
.specs { margin: 0 0 12px; }
.specs > div { display: grid; grid-template-columns: 6.5em 1fr; gap: 0 12px; padding: 8px 0; border-bottom: 1px dashed var(--line); }
.specs dt { color: var(--magenta); font-size: 0.8rem; font-weight: 700; padding-top: 2px; }
.specs dd { margin: 0; color: var(--text); font-size: 0.92rem; }
.spec-full { margin: 0 0 12px; }
.spec-full summary { cursor: pointer; color: var(--cyan); font-size: 0.9rem; }
.spec-full .details { margin: 8px 0 0; font-size: 0.9rem; }
.mockup-note { margin-top: 4px; }

/* ---- generic panels / tables ---- */
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; }
.panel + .panel { margin-top: 16px; }
/* Side by side, the grid gap spaces the panels; the stacked-panel margin would push the second one down. */
.two-col > .panel + .panel { margin-top: 0; }
.two-col { display: grid; gap: 24px; grid-template-columns: 1fr; }
@media (min-width: 880px) { .two-col { grid-template-columns: 1.3fr 1fr; align-items: start; } }
.table-wrap { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: 0.84rem; }
table.data th { text-align: left; font-weight: 700; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--magenta); padding: 8px 10px; border-bottom: 1px solid var(--line-strong); white-space: nowrap; }
table.data td { padding: 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
table.data td.num, table.data th.num { text-align: right; white-space: nowrap; }
.notice { border: 1px solid var(--line-strong); border-left: 3px solid var(--magenta); background: var(--surface); padding: 12px 16px; border-radius: 4px; margin: 16px 0; font-size: 0.88rem; }
.notice.bad { border-left-color: var(--bad); }
.notice.ok { border-left-color: var(--ok); }
/* "Made elsewhere" warnings (owner, 2026-10-30: super clear): amber, bold, impossible to miss. */
.notice.warn { border: 2px solid var(--btc); border-left-width: 6px; background: rgb(247 147 26 / 12%); color: var(--text); }
.notice.warn strong { color: var(--btc); }
.import-warn { display: none; }
.art-size { display: none; } /* the picked design's stitched size shows, inline in the Embroidery group (pickerCss) */
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; font-size: 0.86rem; margin: 0; }
.kv dt { color: var(--magenta); font-size: 0.78rem; }
.kv dd { margin: 0; word-break: break-word; }
.prose { max-width: 740px; }
.prose h2 { margin-top: 1.6em; color: var(--text); } /* blue is for links (colour pass, 2026-11-01) */
.prose h2::before { content: '# '; color: var(--faint); }
.prose li { margin: 0.3em 0; }

/* ---- how-it-works strip ---- */
.steps { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); counter-reset: step; }
.step { border: 1px solid var(--line); border-radius: var(--radius); padding: 18px; background: var(--surface); }
.step::before { counter-increment: step; content: '[0' counter(step) ']'; color: var(--btc); font-size: 0.78rem; display: block; margin-bottom: 8px; }
.formula { font-size: clamp(0.95rem, 3.2vw, 1.3rem); border: 2px solid transparent; border-radius: 6px; padding: 22px; text-align: center; background: linear-gradient(var(--bg-dark), var(--bg-dark)) padding-box, var(--border-grad) border-box; box-shadow: var(--glow); margin-bottom: 14px; }
.formula .plus { white-space: nowrap; color: var(--btc); font-weight: 700; text-shadow: 0 0 12px rgba(247, 147, 26, 0.45); }

/* ---- mock checkout ---- */
.mock-pay { max-width: 520px; margin: 32px auto; }
.big-sats { font-size: 2rem; font-weight: 700; color: var(--btc); }
.sim-grid { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 520px) { .sim-grid { grid-template-columns: 1fr 1fr; } }
.sim-grid form, .sim-grid .btn { width: 100%; }

/* ---- admin ---- */
.admin-nav { display: flex; gap: 14px; flex-wrap: wrap; font-size: 0.82rem; margin: 20px 0; }
.stat-row { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); margin-bottom: 20px; }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; }
.stat .v { font-size: 1.35rem; font-weight: 700; color: var(--btc); }
.stat .k { font-size: 0.75rem; color: var(--muted); }
pre.email { white-space: pre-wrap; background: var(--bg-dark); border: 1px solid var(--line); padding: 12px; border-radius: 4px; font-size: 0.8rem; }

/* colourway switcher rules (radio inputs precede .gallery and .info inside .product) */

@media (max-width: 760px) {
  .ticker { display: none; }
}
@media (max-width: 520px) {
  .bar .wrap { gap: 8px; }
  .bar .wrap { gap: 6px; } /* the bag stays on screen at 360px */
  .workspaces { gap: 2px; padding: 2px; }
  .workspaces a { padding: 1px 7px; min-height: 44px; }
  .workspaces a .l { font-size: 0.875rem; }
  .workspaces a .n { display: none; }
  .preview .bar .bag { display: none; } /* the opening banner already says it (review, 2026-11-01) */
}
@media (max-width: 600px) {
  .section { padding-top: 24px; padding-bottom: 24px; }
  .site-footer { margin-top: 40px; }
  .site-footer li { margin: 0; }
  .site-footer li a { display: inline-block; padding: 7px 0; } /* a comfortable tap without the gaps (owner, 2026-11-01: 44px rows looked odd) */
  .site-footer .cols { gap: 20px 16px; }
  .specs > div { grid-template-columns: 1fr; gap: 4px; padding-block: 12px; } /* label above its value (review, 2026-11-01) */
}
.swatch { min-height: 44px; }
@media (prefers-reduced-motion: reduce) {
  .bar .cursor, .dot { animation: none; }
  .btn { transition: none; }
}
.info h3, .info h2.h3 { margin-top: 26px; }
.hero-title { font-size: clamp(1.3rem, 3.6vw, 2.1rem); margin-bottom: 0.35em; text-wrap: balance; } /* "Hats out." never splits (review, 2026-10-30) */
/* Under the lede: the three facts a first-time visitor needs (review, 2026-10-31: the product, the payment, the reach). */
.hero-facts { font-size: 0.92rem; color: var(--cyan); margin: -0.4em 0 1.2em; }
.span-all { grid-column: 1 / -1; }
details.supplier { margin-top: 12px; }
.gallery-col { min-width: 0; }
/* /shop's sections all look alike (owner, 2026-10-31: on a phone "ls hats/" was bigger): its one h1 in the h2 style. */
h1.ls-head { font-family: var(--mono); font-weight: 700; font-size: clamp(1.15rem, 3vw, 1.5rem); line-height: 1.2; letter-spacing: -0.01em; }
details.supplier summary { cursor: pointer; color: var(--cyan, #7dcfff); font-size: 0.9rem; }
details.supplier p { margin: 8px 0; }
.maker-zoom { position: relative; z-index: 51; /* above the CRT scanlines, like the orange buttons */ overflow: hidden; border: 2px solid transparent; border-radius: 8px; background: linear-gradient(#fff, #fff) padding-box, var(--border-grad) border-box; box-shadow: var(--glow); } /* one blue-to-orange frame round the whole preview, like the picture window (owner, 2026-11-04) */
.maker-set { display: none; position: relative; background: #fff; }
.maker-set img:not(.pan) { display: block; width: 100%; height: auto; }
/* Only the picked colour's set shows (and, being lazy, only it downloads). */
/* ---- direct payment page ---- */
.pay { max-width: 760px; margin: 36px auto; }
.pay-body { display: grid; gap: 24px; padding: 22px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .pay-body { grid-template-columns: 260px 1fr; } }
.qr { display: block; align-self: start; position: relative; z-index: 60; background: #fff; border-radius: 6px; padding: 6px; max-width: 280px; margin: 0 auto; box-shadow: 0 0 24px rgba(247, 147, 26, 0.35); }
.qr svg { width: 100%; height: auto; }
textarea.copy, input.copy { font-family: var(--mono); font-size: 0.72rem; word-break: break-all; margin-bottom: 12px; resize: none; }

/* ---- order page: the "./make-my-hat" job (docs/archive/concepts/payment-flow). No JS: the page refreshes itself; CSS does the motion. ---- */
.job { max-width: 760px; margin: 28px auto 60px; }
.job .titlebar .dots { display: inline-flex; gap: 6px; align-items: center; }
.job .titlebar .dots i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.job .titlebar .dots i:nth-child(1) { background: var(--cyan); }
.job .titlebar .dots i:nth-child(2) { background: var(--magenta); }
.job .titlebar .dots i:nth-child(3) { background: var(--btc); }
.job .body { padding: 18px 20px 22px; font-family: var(--mono); font-size: 0.86rem; line-height: 1.75; }
.job .cmd { color: var(--text); margin-bottom: 14px; }
.job .cmd .path { color: var(--blue); } .job .cmd .sig { color: var(--green); margin: 0 .5em; }
.jl { display: grid; grid-template-columns: 1.6em 6.2em 1fr auto; gap: 0 .6em; align-items: baseline; color: var(--faint); }
.jl .ic { text-align: center; }
.jl .k { color: var(--faint); }
.jl .v { color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jl .t { color: var(--faint); font-size: .78rem; }
.jl.done .ic { color: var(--green); } .jl.done .k { color: var(--magenta); } .jl.done .v { color: var(--muted); }
.jl.done .v b { color: var(--text); font-weight: 600; }
.jl.now .ic { color: var(--btc); animation: pulse 1s steps(2) infinite; }
.jl.now .k { color: var(--btc); } .jl.now .v { color: var(--text); }
@keyframes type { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
.bar-wrap { margin: 16px 0 6px; display: flex; gap: 12px; align-items: center; font-size: .78rem; color: var(--muted); }
.meter { flex: 1; height: 14px; border: 1px solid var(--line-strong); border-radius: 3px; padding: 2px; background: var(--bg-dark); }
.meter span { display: block; height: 100%; border-radius: 2px; background: repeating-linear-gradient(90deg, var(--cyan) 0 8px, transparent 8px 10px), var(--border-grad); background-blend-mode: multiply; background: linear-gradient(90deg, var(--cyan), var(--magenta) 55%, var(--btc)); -webkit-mask: repeating-linear-gradient(90deg, #000 0 8px, transparent 8px 10px); mask: repeating-linear-gradient(90deg, #000 0 8px, transparent 8px 10px); }
.sats { margin-top: 20px; border-top: 1px dashed var(--line-strong); padding-top: 14px; }
.sats .sats-title { margin: 0 0 6px; font-size: .78rem; color: var(--faint); font-weight: 400; } .sats .sats-title::before { content: '# '; color: var(--faint); }
/* 13em holds "£18.65 (31,200 sats)" on one line; on phones the sats drop to a second line in a narrower column
   (owner, 2026-11-09, from a customer: every line in pounds and sats). */
.sats .row { display: grid; grid-template-columns: 13em 1.4em 1fr; color: var(--muted); }
.sats .row .alt { white-space: nowrap; }
@media (max-width: 600px) { .sats .row { grid-template-columns: 8.5em 1.4em 1fr; } .sats .row .alt { display: block; } }
.sats .row .arrow { color: var(--faint); }
.sats .row.us { color: var(--btc); }
.sats.where { font-size: 0.86rem; }
.hint { color: var(--faint); font-size: .75rem; margin-top: 14px; }
.hint .cursor-inline { display: inline-block; width: .55em; height: 1em; background: var(--btc); vertical-align: -2px; animation: blink 1s steps(1) infinite; }
.done-banner { font-family: var(--pixel); font-size: 1.05rem; line-height: 1.6; margin: 6px 0 16px; }
.order-side { display: grid; grid-template-columns: 88px 1fr; gap: 14px; align-items: center; margin-top: 18px; padding: 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--bg-dark); }
.order-side img { width: 88px; height: 88px; object-fit: contain; }
.order-side .mono { color: var(--muted); font-size: .8rem; }
@media (max-width: 560px) { .jl { grid-template-columns: 1.4em 4.8em 1fr; } .jl .v { white-space: normal; } .job .cmd { overflow-wrap: anywhere; } .jl .t { display: none; } .job .body { font-size: .8rem; padding: 14px; } }

/* ---- the Lightning moment: plays once, on the first view after the invoice settles (~2.4 s) ----
   Server side: .zapped only while the payment is under ZAP_WINDOW_SECONDS old (src/web/shop.ts), so the refresh never replays it. */
.job { position: relative; }
.strike { position: absolute; inset: 0 0 auto; height: min(100%, 80vh); pointer-events: none; z-index: 5; display: grid; place-items: center; overflow: hidden; } /* on the first screen, not mid-page on a phone (look review) */
.strike .flash { position: absolute; inset: 0; background: radial-gradient(circle at 50% 40%, #fff 0, rgba(255, 158, 100, .85) 30%, rgba(187, 154, 247, .4) 60%, transparent 80%); opacity: 0; animation: flash .5s ease-out both; }
.strike .big { width: 120px; filter: drop-shadow(0 0 10px var(--btc)) drop-shadow(0 0 34px rgba(255, 158, 100, .8)) drop-shadow(5px 5px 0 #3d2a66); animation: strike 1.5s cubic-bezier(.2, .9, .3, 1) both; }
.strike .big svg { display: block; width: 100%; fill: var(--btc); }
.strike .stamp { position: absolute; top: 46%; font-family: var(--pixel); font-size: 2.4rem; color: #fff; text-shadow: 0 0 8px rgba(125, 207, 255, .7), 0 0 26px rgba(187, 154, 247, .6), 4px 4px 0 #3d2a66; text-align: center; animation: stampIn 2.6s ease-out both; }
.strike .stamp small { display: block; font-size: .7rem; margin-top: .6em; color: var(--btc); text-shadow: 0 0 8px rgba(255, 158, 100, .7); }
.strike .sp { position: absolute; left: 50%; top: 42%; width: 6px; height: 6px; background: var(--btc); box-shadow: 0 0 8px var(--btc); opacity: 0; animation: spark .7s .25s ease-out both; }
.strike .s1 { --dx: -170px; --dy: -60px; } .strike .s2 { --dx: 160px; --dy: -80px; background: var(--cyan); box-shadow: 0 0 8px var(--cyan); }
.strike .s3 { --dx: -120px; --dy: 90px; background: var(--magenta); box-shadow: 0 0 8px var(--magenta); } .strike .s4 { --dx: 190px; --dy: 60px; }
.strike .s5 { --dx: -40px; --dy: -120px; background: #fff; } .strike .s6 { --dx: 60px; --dy: 120px; background: var(--cyan); }
.job.zapped { animation: shake .45s .08s steps(6) both; }
.jl.zap { margin: 2px -8px; padding: 2px 8px; border-radius: 4px; animation: rowSweep 1.6s 1.05s ease-out forwards; }
.jl.zap .zi svg { width: .8em; height: 1.1em; fill: var(--btc); vertical-align: -2px; filter: drop-shadow(0 0 4px var(--btc)); animation: pop .5s 1.15s cubic-bezier(.3, 1.6, .5, 1) both; }
.jl.zap .k { color: var(--btc); }
.jl.zap .pill { display: inline-block; margin-left: .4em; padding: 0 .5em; border-radius: 999px; font-size: .74rem; line-height: 1.5; color: #1a1b26; background: linear-gradient(90deg, var(--cyan), var(--magenta), var(--btc)); background-size: 200% 100%; animation: pop .45s 1.35s cubic-bezier(.3, 1.6, .5, 1) both, shimmer 3s 2s linear infinite; }
.zapped .meter span { animation: fill 1.1s 1.3s ease-out both; position: relative; }
@keyframes flash { 0% { opacity: 0; } 12% { opacity: 1; } 30% { opacity: .2; } 42% { opacity: .7; } 100% { opacity: 0; } }
@keyframes strike {
  0% { transform: translateY(-260px) scale(1.5); opacity: 0; }
  14% { transform: translateY(0) scale(1.15); opacity: 1; }
  22% { transform: translateY(0) scale(.95); }
  30%, 55% { transform: translateY(0) scale(1); opacity: 1; }
  38% { opacity: .55; } 44% { opacity: 1; }
  100% { transform: translate(-366px, -178px) scale(.08); opacity: 0; }
}
@keyframes stampIn { 0%, 36% { transform: scale(2.6) rotate(-4deg); opacity: 0; } 46% { transform: scale(.94) rotate(-4deg); opacity: 1; } 52%, 84% { transform: scale(1) rotate(-4deg); opacity: 1; } 100% { transform: scale(1.05) rotate(-4deg); opacity: 0; } }
@keyframes spark { 0% { transform: translate(0, 0) scale(1.4); opacity: 1; } 100% { transform: translate(var(--dx), var(--dy)) scale(.2); opacity: 0; } }
@keyframes shake { 0%, 100% { transform: none; } 17% { transform: translate(-4px, 2px); } 33% { transform: translate(4px, -2px); } 50% { transform: translate(-3px, -1px); } 67% { transform: translate(2px, 2px); } 83% { transform: translate(-1px, 0); } }
@keyframes rowSweep { 0% { background: rgba(255, 158, 100, .55); box-shadow: 0 0 22px rgba(255, 158, 100, .6); } 100% { background: rgba(255, 158, 100, .08); box-shadow: 0 0 0 transparent; } }
@keyframes pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
@keyframes shimmer { to { background-position: -200% 0; } }
@keyframes fill { from { width: 0; } }
@media (prefers-reduced-motion: reduce) { .strike { display: none; } .job.zapped, .jl.zap, .jl.zap *, .zapped .meter span, .jl.now .ic, .hint .cursor-inline { animation: none !important; } }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.order-page .notice, .order-page .pay-cta { max-width: 760px; margin: 0 auto 16px; }
.jl .v a { color: inherit; }
/* 7.5em: "chargebacks" (11 characters) spilt out of 5.5em on phones and desktops alike (owner, 2026-11-09, from a customer). */
.why .row { grid-template-columns: 7.5em 1.4em 1fr; margin-bottom: 4px; }
.why .row span:first-child { color: var(--green); }
.receipt { margin-top: 16px; font-size: .8rem; }
.receipt summary { cursor: pointer; color: var(--faint); }
.receipt summary::before { content: '# '; }
.receipt .breakdown { margin-top: 10px; }
/* Progress in sixths (one per job step): CSP allows no inline styles. */
.meter .w0 { width: 0; }
.meter .w1 { width: 16.67%; }
.meter .w2 { width: 33.33%; }
.meter .w3 { width: 50%; }
.meter .w4 { width: 66.67%; }
.meter .w5 { width: 83.33%; }
.meter .w6 { width: 100%; }

/* The logo's blinking cursor, right after 'hats'. */
.brand .cursor { margin-left: -4px; }

/* ---- why bitcoin only: card vs lightning, as a terminal diff ---- */
.compare-win { margin-top: 28px; }
.compare-win .titlebar .dots { display: inline-flex; gap: 6px; align-items: center; }
.compare-win .titlebar .dots i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.compare-win .titlebar .dots i:nth-child(1) { background: var(--cyan); }
.compare-win .titlebar .dots i:nth-child(2) { background: var(--magenta); }
.compare-win .titlebar .dots i:nth-child(3) { background: var(--btc); }
.compare { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.compare th, .compare td { padding: 9px 14px; text-align: left; vertical-align: top; border-bottom: 1px dashed var(--line); }
.compare th { font-weight: 400; font-size: 0.75rem; text-transform: lowercase; letter-spacing: 0.04em; color: var(--faint); border-bottom: 1px solid var(--line-strong); }
.compare td:first-child { color: var(--muted); }
.compare .cc { color: color-mix(in srgb, var(--muted) 65%, var(--faint)); } /* duller than lightning, still readable (~5:1) */
.compare td.cc::before { content: '- '; color: var(--pink); }
.compare th.ln { color: var(--btc); }
.compare td.ln { color: var(--text); background: color-mix(in srgb, var(--btc) 6%, transparent); }
.compare td.ln::before { content: '+ '; color: var(--green); }
.compare tbody tr:first-child td.ln { color: var(--btc); font-weight: 700; }
.compare tbody tr:last-child td { border-bottom: 0; }
.compare-intro { margin: 0; padding: 12px 14px; font-size: 0.875rem; color: var(--muted); border-bottom: 1px solid var(--line); } /* what the table compares: money into the bank either way (owner, #233) */
.compare-note { margin: 0; padding: 10px 14px 12px; font-size: 0.875rem; color: var(--faint); border-top: 1px solid var(--line); }
@media (max-width: 560px) { .compare th, .compare td { padding: 8px 8px; font-size: 0.76rem; } }

/* Pricing page: today's profit as a terminal window (D46), with the ladder and the do-it-yourself comparison. */
.profit-win { margin: 4px 0 28px; }
.profit-win .titlebar .dots { display: inline-flex; gap: 6px; align-items: center; }
.profit-win .titlebar .dots i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.profit-win .titlebar .dots i:nth-child(1) { background: var(--cyan); }
.profit-win .titlebar .dots i:nth-child(2) { background: var(--magenta); }
.profit-win .titlebar .dots i:nth-child(3) { background: var(--btc); }
.profit-body { padding: 16px 18px 6px; }
.profit-body .breakdown { font-size: 0.9rem; }
.profit-body .breakdown tr.total td:last-child { color: var(--btc); }
.profit-body > p { margin: 14px 0; }
.compare.ladder { margin: 6px 0 4px; }
.compare.ladder td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
.compare.ladder th:last-child { text-align: right; }
.compare.ladder tr.now td { color: var(--text); background: color-mix(in srgb, var(--btc) 8%, transparent); }
.compare.ladder .here { color: var(--btc); font-size: 0.75rem; margin-left: 6px; }

/* Dollars and euros next to pounds (D49): quieter than the pound figure. */
.fx { color: var(--faint); font-size: 0.92em; white-space: nowrap; }
.price-big .fx { display: block; font-size: 0.45em; font-weight: 400; text-shadow: none; margin-top: 2px; }
.price-big .fx .sep { display: none; }
.lp-card { overflow: hidden; text-overflow: ellipsis; }

/* Under the fixed headline (owner, 2026-10-29). */
.hero-title .keep { white-space: nowrap; } /* "Hats* out." stays on one line */
/* "Hats* out.", then "* and stuff" typed out under it, with the bar's blinking cursor (owner, 2026-10-31). */
.hero-title .star { color: var(--btc); font-size: 0.7em; vertical-align: 0.5em; margin-left: 0.02em; text-shadow: 0 0 12px rgba(247, 147, 26, 0.75); }
.hero-title .footnote { display: block; width: fit-content; margin-top: 0.45em; font: 400 max(0.42em, 0.9rem)/1.3 var(--mono); letter-spacing: 0.06em; color: var(--cyan); text-shadow: 0 0 10px rgba(125, 207, 255, 0.55); white-space: nowrap; clip-path: inset(0 100% 0 0); animation: type 0.9s steps(11, end) 0.8s forwards; }
.hero-title .footnote .star { font-size: 1em; vertical-align: 0; margin: 0; }
.hero-title .footnote::after { content: ''; display: inline-block; width: 0.55em; height: 1.05em; margin-left: 0.3em; vertical-align: -0.2em; background: var(--btc); animation: blink 1.1s steps(1) infinite; }
.hero-tagline { font-size: clamp(1rem, 2.2vw, 1.25rem); color: var(--text); margin: 0.2em 0 0.6em; }
.price-note { margin-top: 12px; }
/* Card prices: the lead region first, the others under it (owner, 2026-10-28; elsewhere 2026-11-04). */
.rps { display: block; }
/* Review, 2026-10-30: one price per card, the UK one, with US and EU in a faint line. */
.rp-main { display: block; line-height: 24px; font-size: 1rem; color: var(--text); font-weight: 700; } /* the card's price at 16px (review, 2026-11-01) */
.rp-main .rp-sats { color: var(--muted); font-weight: 400; }
.rp-other { display: block; line-height: 20px; font-size: 0.875rem; color: var(--muted); white-space: normal; } /* review, 2026-10-31: was 11px and faint */
/* On cards the UK note and the US/EU prices take a line each (they wrapped after "EU"). */
.lp-card .rp-other .rp-sep, .price-inline .rp-other .rp-sep { display: none; }
.lp-card .rp-other .rp-part, .price-inline .rp-other .rp-part { display: block; }
/* Region buttons on the hat page price (owner, 2026-10-28): small, quiet, one highlighted. */
.rtabs { display: flex; gap: 4px; margin-bottom: 6px; }
.rtabs a { font-size: 0.875rem; padding: 5px 12px; min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--line-strong); border-radius: 3px; color: var(--muted); text-decoration: none; } /* the bag's tabs, sized like the product page's (look review, 2026-11-02) */
.rtabs a[aria-current] { color: var(--text); border-color: transparent; background: var(--lit-soft); box-shadow: var(--glow-soft); font-weight: 700; } /* lit like every other pick (owner, 2026-11-01) */
/* On the product page the region buttons are labels for radios (so the page can react to them); the picked one is lit
   like the frame's own buttons, and only its price frame is displayed. */
.rtabs label { margin: 0; font-size: 0.875rem; padding: 5px 12px; min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--ring); border-radius: 3px; color: var(--muted); cursor: pointer; }
/* The picked tab is lit by the product's picker sheet (pickerCss in src/web/shop.ts), since ids are per hat. */
.price-r { display: none; }
/* One add-to-bag button per region, so the bag opens on the region picked (no script); the picked one shows. */
.btn.add-r { display: none; }
/* While checkout is closed: the waiting-list button under the price (review, 2026-10-30). */
.opening-soon { margin: 10px 0 4px; }
.opening-soon p { margin: 0 0 8px; }
.opening-soon .btn { margin-bottom: 8px; }
.fit-note { margin: -12px 0 18px; color: var(--text); }
/* Size guide (owner, 2026-10-30): folded away under the sizes. */
.size-guide { margin: -10px 0 18px; font-size: 0.82rem; }
.size-guide summary { cursor: pointer; color: var(--cyan); }
.size-guide table { font-size: 0.78rem; margin-top: 8px; }
#size-guide { scroll-margin-top: 140px; }
.size-guide td .faint { display: block; font-size: 0.78rem; }
.link-note { margin-top: 14px; }
/* Hoodie sizes: radio chips like the colour swatches; the checked one is highlighted (no script). */
.size-input { position: absolute; opacity: 0; pointer-events: none; }
.sizes .swatch { min-width: 3em; justify-content: center; }
.hats .swatch { text-decoration: none; min-width: 6em; justify-content: center; }
.hats .swatch:hover { color: var(--text); border-color: var(--cyan); }
.hats .swatch.on { border-color: transparent; background: var(--lit-soft); color: var(--text); box-shadow: var(--glow-soft); cursor: default; }
/* A design's hats share one page (hatChoice in src/web/shop.ts): the picked hat's part shows, so switching never
   loads a page or moves the screen. */
.hat-body { display: none; }
.hat-switch > #hp-0:checked ~ .hb-0, .hat-switch > #hp-1:checked ~ .hb-1, .hat-switch > #hp-2:checked ~ .hb-2 { display: block; }

/* Keyboard: the hat radios are off-screen like the others, so their label shows the focus. */
.hat-switch > #hp-0:focus-visible ~ .hat-body label[for='hp-0'], .hat-switch > #hp-1:focus-visible ~ .hat-body label[for='hp-1'],
.hat-switch > #hp-2:focus-visible ~ .hat-body label[for='hp-2'] { outline: 2px solid var(--cyan); outline-offset: 2px; }
.size-input:checked + .swatch { border-color: transparent; background: var(--lit-soft); color: var(--text); box-shadow: var(--glow-soft); }
.size-input:checked + .swatch::after, .hats .swatch.on::after { content: '✓'; color: var(--btc); } /* the picked choice has a tick, not just a colour (review, 2026-11-01) */
.size-input:focus-visible + .swatch { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* Close-up of a small chest logo: hover on a mouse, tap to toggle on touch (no script). */
.zoom-input { position: absolute; opacity: 0; pointer-events: none; }
.gallery .views.zoomable { display: block; cursor: zoom-in; margin: 0; }
.gallery .view .zoom { position: absolute; inset: 0; opacity: 0; transition: opacity 0.2s; }
.zoom-hint { position: absolute; right: 10px; bottom: 10px; padding: 2px 8px; font-size: 0.78rem; border: 1px solid var(--line-strong); background: rgb(22 22 30 / 80%); color: var(--text); pointer-events: none; }
.zoom-hint::before { content: '⊕ '; color: var(--btc); }
.tap-only { display: none; }
/* Mouse: the whole hoodie at 1800 px, magnified 3x around the pointer. No script: an invisible 8 x 8 grid of
   cells over the picture sets the zoom's centre to the cell under the pointer (ZOOM_GRID in src/web/shop.ts). */
.gallery .views.zoomable { overflow: hidden; }
.gallery .view .pan { transform-origin: 62.8% 30.7%; }
.maker-zoom.deep .pan { transform: scale(5); } /* hoodies: a small chest logo on three small views (owner, 2026-10-30) */
.pan-zoom .pan { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; opacity: 0; transform: scale(3); transition: opacity 0.2s, transform-origin 0.25s ease-out; }
.zgrid { position: absolute; inset: 0; display: none; grid-template: repeat(8, 1fr) / repeat(8, 1fr); z-index: 1; }
@media (hover: hover) {
  .zgrid { display: grid; }
  .pan-zoom:hover .pan { opacity: 1; }
  .maker-zoom { cursor: zoom-in; }
.pan-zoom:has(.zgrid > :nth-child(1):hover) .pan { transform-origin: 6.25% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(2):hover) .pan { transform-origin: 18.75% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(3):hover) .pan { transform-origin: 31.25% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(4):hover) .pan { transform-origin: 43.75% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(5):hover) .pan { transform-origin: 56.25% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(6):hover) .pan { transform-origin: 68.75% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(7):hover) .pan { transform-origin: 81.25% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(8):hover) .pan { transform-origin: 93.75% 6.25%; }
.pan-zoom:has(.zgrid > :nth-child(9):hover) .pan { transform-origin: 6.25% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(10):hover) .pan { transform-origin: 18.75% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(11):hover) .pan { transform-origin: 31.25% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(12):hover) .pan { transform-origin: 43.75% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(13):hover) .pan { transform-origin: 56.25% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(14):hover) .pan { transform-origin: 68.75% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(15):hover) .pan { transform-origin: 81.25% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(16):hover) .pan { transform-origin: 93.75% 18.75%; }
.pan-zoom:has(.zgrid > :nth-child(17):hover) .pan { transform-origin: 6.25% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(18):hover) .pan { transform-origin: 18.75% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(19):hover) .pan { transform-origin: 31.25% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(20):hover) .pan { transform-origin: 43.75% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(21):hover) .pan { transform-origin: 56.25% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(22):hover) .pan { transform-origin: 68.75% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(23):hover) .pan { transform-origin: 81.25% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(24):hover) .pan { transform-origin: 93.75% 31.25%; }
.pan-zoom:has(.zgrid > :nth-child(25):hover) .pan { transform-origin: 6.25% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(26):hover) .pan { transform-origin: 18.75% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(27):hover) .pan { transform-origin: 31.25% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(28):hover) .pan { transform-origin: 43.75% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(29):hover) .pan { transform-origin: 56.25% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(30):hover) .pan { transform-origin: 68.75% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(31):hover) .pan { transform-origin: 81.25% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(32):hover) .pan { transform-origin: 93.75% 43.75%; }
.pan-zoom:has(.zgrid > :nth-child(33):hover) .pan { transform-origin: 6.25% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(34):hover) .pan { transform-origin: 18.75% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(35):hover) .pan { transform-origin: 31.25% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(36):hover) .pan { transform-origin: 43.75% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(37):hover) .pan { transform-origin: 56.25% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(38):hover) .pan { transform-origin: 68.75% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(39):hover) .pan { transform-origin: 81.25% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(40):hover) .pan { transform-origin: 93.75% 56.25%; }
.pan-zoom:has(.zgrid > :nth-child(41):hover) .pan { transform-origin: 6.25% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(42):hover) .pan { transform-origin: 18.75% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(43):hover) .pan { transform-origin: 31.25% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(44):hover) .pan { transform-origin: 43.75% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(45):hover) .pan { transform-origin: 56.25% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(46):hover) .pan { transform-origin: 68.75% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(47):hover) .pan { transform-origin: 81.25% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(48):hover) .pan { transform-origin: 93.75% 68.75%; }
.pan-zoom:has(.zgrid > :nth-child(49):hover) .pan { transform-origin: 6.25% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(50):hover) .pan { transform-origin: 18.75% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(51):hover) .pan { transform-origin: 31.25% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(52):hover) .pan { transform-origin: 43.75% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(53):hover) .pan { transform-origin: 56.25% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(54):hover) .pan { transform-origin: 68.75% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(55):hover) .pan { transform-origin: 81.25% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(56):hover) .pan { transform-origin: 93.75% 81.25%; }
.pan-zoom:has(.zgrid > :nth-child(57):hover) .pan { transform-origin: 6.25% 93.75%; }
.pan-zoom:has(.zgrid > :nth-child(58):hover) .pan { transform-origin: 18.75% 93.75%; }
.pan-zoom:has(.zgrid > :nth-child(59):hover) .pan { transform-origin: 31.25% 93.75%; }
.pan-zoom:has(.zgrid > :nth-child(60):hover) .pan { transform-origin: 43.75% 93.75%; }
.pan-zoom:has(.zgrid > :nth-child(61):hover) .pan { transform-origin: 56.25% 93.75%; }
.pan-zoom:has(.zgrid > :nth-child(62):hover) .pan { transform-origin: 68.75% 93.75%; }
.pan-zoom:has(.zgrid > :nth-child(63):hover) .pan { transform-origin: 81.25% 93.75%; }
.pan-zoom:has(.zgrid > :nth-child(64):hover) .pan { transform-origin: 93.75% 93.75%; }
}
@media (hover: none) {
  .hover-only { display: none; }
  .tap-only { display: inline; }
  .zoom-input:checked + .views .zoom { opacity: 1; }
}
.zoom-input:focus-visible + .views { outline: 2px solid var(--btc); outline-offset: -2px; }

/* Christmas order-by line under the price (CHRISTMAS in src/web/shop.ts). */
.christmas-note { margin: 6px 0 0; color: var(--text); }
.christmas-note strong { color: var(--btc); font-weight: 700; }

/* Error pages as terminal sessions (404, 402). */
.term-page .body a { color: var(--cyan); margin-right: 0.6em; }
.term-page .err { color: #f7768e; }
.term-page .warn { color: var(--btc); }
.term-page .path { color: var(--magenta); }
.term-page .sig { color: var(--btc); }
.term-page .cursor { animation: blink 1.1s steps(1) infinite; color: var(--text); }
@media (prefers-reduced-motion: reduce) { .term-page .cursor { animation: none; } }


/* Last, so it beats the footnote's own rules above (review, 2026-11-01: they restored the typing). */
@media (prefers-reduced-motion: reduce) {
  .hero-title .footnote { animation: none; clip-path: none; }
  .hero-title .footnote::after { animation: none; }
}
.crumbs { margin: 18px 0 -6px; color: var(--faint); }
.crumbs a { color: var(--muted); }
.type-line { margin: -4px 0 10px; }
.browse { margin-top: 28px; }
.lede-box { border-left: 3px solid var(--btc); padding: 8px 14px; background: var(--surface); }
.term-h1 { font-size: 1.3rem; margin: 18px 0 10px; }
.jump { position: sticky; top: 52px; z-index: 52; /* above the card pictures (51), which scrolled over it (owner, 2026-11-01) */ padding: 8px 0; margin-bottom: 4px; }
/* A full-width translucent band like the top bar, not a flat rectangle the width of the page column. */
.jump::before { content: ''; position: absolute; inset: 0 -50vw; z-index: -1; background: rgba(22, 22, 30, 0.92); backdrop-filter: blur(8px); border-bottom: 1px solid var(--line); }
main { overflow-x: clip; }
.jump { display: flex; flex-wrap: wrap; align-items: center; gap: 0 6px; min-height: 44px; }
.jump a { display: inline-block; padding: 9px 0; } /* plain links (owner, 2026-11-01), a 41px tap row */
#hats, #stuff, #seasonal { scroll-margin-top: 112px; }
/* Wide screens: explanations at a readable line length (review, 2026-11-01). */
.formula + p { max-width: 70ch; margin-inline: auto; }
.price-note { max-width: none; } /* the full width of the page, like the rows above it (owner, 2026-11-05) */
.season-line { max-width: none; } /* the full width of its row, like the cards under it (owner, 2026-11-05: it looked phone-width on desktop) */
@media (min-width: 880px) { .prose { font-size: 1rem; max-width: 70ch; } }
/* Phones: Shop and the related grids as compact rows, the picture beside the words (review, 2026-11-01). Owner,
   2026-11-01: the picture was small and stuck at the top, the words too big and out of line; now the picture is bigger
   and centred down the card's height, and the name, colours and price share one column. The link box (display:
   contents) still carries the whole-card ::after overlay. */
@media (max-width: 600px) {
  .grid.rows { gap: 10px; }
  .grid.rows .card { display: grid; grid-template-columns: clamp(148px, 42vw, 172px) minmax(0, 1fr); grid-template-rows: auto auto; column-gap: 10px; align-items: center; }
  .grid.rows .card-link { display: contents; }
  .grid.rows .card .art { grid-row: 1 / span 2; align-self: center; margin: 4px 0 4px 4px; border-radius: 4px; }
  .grid.rows .card .body { grid-column: 2; align-self: end; padding: 10px 10px 0 0; gap: 1px; }
  .grid.rows .card .body > .muted.small { display: none; } /* the tagline: the right side was too busy (owner, 2026-11-01) */
  .grid.rows .card .name { font-size: 1.15rem; line-height: 1.25; margin-bottom: 2px; } /* the name stands out (owner, 2026-11-01) */
  .grid.rows .card .body .small { font-size: 0.75rem; line-height: 1.35; }
  .grid.rows .card-foot { grid-column: 2; align-self: start; padding: 4px 10px 10px 0; gap: 4px; }
  .grid.rows .swatches-mini { flex-wrap: wrap; gap: 3px; }
  .grid.rows .swatches-mini svg { width: 13px; height: 13px; }
  .grid.rows .swatches-mini .n-colours { font-size: 0.75rem; }
  /* The public site draws the price inline, not in a frame, so the frame's narrow rules are repeated here. */
  .grid.rows .price-inline .lp-card { white-space: normal; }
  .grid.rows .price-inline .rp-main { font-size: 0.875rem; line-height: 22px; white-space: nowrap; }
  .grid.rows .price-inline .rp-other { font-size: 0.75rem; line-height: 17px; white-space: nowrap; } /* 14 and 12 px, the labels shortened to fit (look review, 2026-11-02) */
}
/* A row's price frame is narrow on a phone: smaller type, so the price fits one line, then
   "UK estimate, delivered" and the US and EU prices a line each, never wrapping (owner, 2026-11-01). */
@media (max-width: 260px) {
  .rp-main { font-size: 0.875rem; line-height: 22px; }
  .rp-other { font-size: 0.75rem; line-height: 17px; white-space: nowrap; }
  .rp-other .rp-sep { display: none; }
  .rp-other .rp-part { display: block; }
}
/* Phones: the slice's sums keep each number with its unit, label above (review, 2026-11-01). */
@media (max-width: 600px) {
  .profit-body .breakdown td { display: block; padding: 2px 0; border: 0; text-align: left; }
  .profit-body .breakdown td:last-child { text-align: left; padding-left: 0; padding-bottom: 8px; border-bottom: 1px dashed var(--line); }
  .profit-body .breakdown tr { display: block; }
  /* The card vs Lightning table: one topic per block, each value labelled. */
  .compare-win .compare thead { display: none; }
  .compare-win .compare tr, .compare-win .compare td { display: block; }
  .compare-win .compare td { padding: 4px 14px; border: 0; font-size: 0.875rem; }
  .compare-win .compare td:first-child { padding-top: 12px; color: var(--text); font-weight: 700; }
  .compare-win .compare tr { border-bottom: 1px dashed var(--line); padding-bottom: 8px; }
  .compare-win .compare td.cc::before { content: '- card: '; }
  .compare-win .compare td.ln::before { content: '+ lightning: '; }
}

/* Look review, 2026-11-02 */
.oid { white-space: nowrap; } /* an order number never breaks: it's what people type into Track and quote by email */
.bag-item { display: inline-block; padding: 6px 0; } /* the whole "₿ Classic dad cap" is the link */
.hero-facts .fact { white-space: nowrap; }
html { scroll-padding-top: 110px; } /* a focused control or anchor never lands under the sticky bars */
.glyph-name { font-size: clamp(2.2rem, 7vw, 3.4rem); } /* a one-character design name (₿) reads at the size of the others */
.checkout-wait { margin-top: 6px; }
.section-head > a, .crumbs a, .hero-links a { display: inline-block; padding: 7px 0; } /* standalone links are tappable */
.rp-short { display: none; }
.grid.rows .rp-long { display: none; } .grid.rows .rp-short { display: inline; } /* rows on phones: the short label, so the text can be bigger */
@media (max-width: 520px) {
  .bag, .brand { min-height: 44px; }
  .titlebar .path { display: none; }
}

.range-note { color: var(--muted); max-width: none; margin: 18px 0 0; } /* why the range is small (owner, 2026-11-02: wording B) */
.range-note .prompt { color: var(--green); }

/* Round-2 review, 2026-11-02 */
/* Inside a card's price frame on a narrow row (the frame is its own viewport, ~150-170 px on a phone): the short label
   and a slightly smaller price, so nothing is cut off at 360 px. No page is this narrow, so this only reaches frames. */
@media (max-width: 230px) { .rp-long { display: none; } .rp-short { display: inline; } .rp-main { font-size: 0.8125rem; } }
.cmd-word { white-space: nowrap; }
.size-guide td .faint { white-space: nowrap; }
@media (min-width: 601px) { .bag-lines th:first-child { width: 40%; } } /* desktop: names on one or two lines; phones use the stacked grid */
@media (max-width: 520px) { .job .cmd .path { display: none; } }

/* Round-3 review, 2026-11-02 */
.notice.added { scroll-margin-top: 110px; }
.glyph-name { font-family: var(--mono); font-weight: 700; } /* the pixel font has no ₿: the bold mono reads better than the regular fallback */
@media (max-width: 374px) { .grid.rows .price-inline .rp-main { font-size: 0.8125rem; } } /* the public site's inline row prices fit at 360 px */

/* Sonnet retest, 2026-11-02: a clearer focus cue on form fields for keyboard users */
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }

/* ls magic/ (owner, 2026-11-03, on test to look at): the tagline above the sun, the casting wizard. (The flickering header bolt went with the B1 logo, 2026-11-04.)
   CSS only; everything stands still for reduced motion. */
.hero-side { min-width: 0; }
svg.wizard { overflow: visible; }
.wizard .wz-glow { fill: #ff9e64; opacity: 0; }
.wizard.cast .wz-glow { animation: wz-glow 6s infinite; }
.wizard.cast .wz-zap { animation: wz-zap 6s infinite; }
.wizard.cast .wz-bolts { animation: wz-bolts 6s infinite; }
@keyframes wz-glow { 0%, 40% { opacity: 0; } 46% { opacity: 0.55; } 60% { opacity: 0.4; } 80%, 100% { opacity: 0; } }
@keyframes wz-zap { 0%, 44% { opacity: 0; } 45%, 47% { opacity: 1; } 48% { opacity: 0.2; } 49%, 78% { opacity: 1; } 84%, 100% { opacity: 0; } }
@keyframes wz-bolts { 0%, 50% { opacity: 0; } 52% { opacity: 1; } 54% { opacity: 0.3; } 56%, 76% { opacity: 1; } 84%, 100% { opacity: 0; } }
.foot-wizard { display: inline-block; margin-top: 10px; }
.foot-wizard .wizard { width: 64px; height: auto; }
@media (prefers-reduced-motion: reduce) {
  .wizard.cast .wz-glow, .wizard.cast .wz-zap, .wizard.cast .wz-bolts { animation: none; }
  .wizard.cast .wz-zap, .wizard.cast .wz-bolts { opacity: 1; }
}
/* Wizard polish (owner, 2026-11-03): the headline comes up level with the wizard's tagline; the wizard beside
   ls magic/, on the empty bag and on the 404. */
.ls-head .wizard { display: inline-block; width: 1.9em; height: auto; margin-left: 0.35em; vertical-align: -0.15em; }
.magic-empty { display: flex; align-items: center; gap: 14px; }
.magic-empty .wizard { width: 72px; height: auto; flex: none; }
.wz-line { display: inline-flex; align-items: center; gap: 12px; padding: 6px 0; }
.wz-line .wizard { width: 56px; height: auto; flex: none; }
.foot-wizard .wizard.cast .wz-glow, .foot-wizard .wizard.cast .wz-zap, .foot-wizard .wizard.cast .wz-bolts { animation-delay: -3s; }

/* Bag and exact price (review, 2026-11-03): what you're buying, the total big, where it goes, the receipt behind a click. */
.review-items { list-style: none; margin: 0 0 14px; padding: 0; display: grid; gap: 8px; }
.review-items li { display: flex; align-items: center; gap: 12px; }
.review-items img { width: 64px; height: 64px; border-radius: 6px; background: var(--stage); flex: none; }
/* The delivery address on the review page, set apart so it gets a proper look (owner, 2026-11-09). */
.ship-to { font-style: normal; font-size: 0.95rem; line-height: 1.55; color: var(--text); background: var(--bg-dark); border: 1px solid var(--line-strong); border-left: 3px solid var(--cyan); border-radius: 4px; padding: 12px 16px; margin: 0 0 12px; overflow-wrap: anywhere; }
.addr-prompt { color: var(--muted); margin: 0 0 14px; }
.addr-prompt a { color: var(--cyan); }
.notice.addr-check { border-left-color: var(--warn); margin: 0 0 12px; }
.total-line { margin: 4px 0 6px; }
.total-line .label { display: block; margin-bottom: 0; }
.total-line .price-big { color: var(--btc); line-height: 1.2; }
.full-receipt { margin-top: 14px; }
.full-receipt > summary { cursor: pointer; color: var(--blue); font-size: 0.875rem; }
.full-receipt[open] > summary { margin-bottom: 8px; }
/* Pricing: a real hat as the example, up front. */
.info .lede.story { margin-top: 22px; }

/* Product price (Fable's review, 2026-11-03): two lines, region pills, one arrival line, one link. */
.lp .price-sub { font-size: 0.86rem; margin-top: 2px; }
.rtabs.inline { display: flex; align-items: center; gap: 6px; margin: 10px 0 6px; }
.rtabs.inline > span { margin-right: 2px; }
.rtabs.inline label { min-height: 36px; min-width: 40px; padding: 4px 10px; font-size: 0.8rem; }
.price-block .arrival { margin: 4px 0 0; }
.price-block .open-book { margin: 2px 0 0; }
/* where your sats go: our maker's discount, passed on */
.sats .row.saving { color: var(--green); }
/* Pricing: one cap, through us vs yourself; the steps as four lines; the caveats as a list. */
.price-lines { padding-left: 1.4em; } .price-lines li { margin: 4px 0; }
.caveats { padding-left: 1.2em; } .caveats li { margin: 3px 0; }
/* Pricing (owner, 2026-11-03): the slice's footnote, its working behind a click, and one cap in words. */
.formula .star { color: var(--btc); }
.slice-note { text-align: center; color: var(--muted); font-size: 0.9rem; margin: 0 auto 10px; }
.formula + p.slice-note { max-width: none; } /* the full width, like the home page's note (owner, 2026-11-05) */
.slice-note .star { color: var(--btc); margin-right: 4px; }
.slice-more { margin: 0 0 18px; }
.slice-more > summary { cursor: pointer; color: var(--blue); text-align: center; font-size: 0.9rem; margin-bottom: 10px; }
.slice-more ~ .diy strong { color: var(--btc); } /* the numbers in orange, as the original (owner, 2026-11-03) */
.slice-more ~ .diy { border-left: 2px solid var(--btc); padding: 2px 0 2px 14px; }
/* One cap: through us vs yourself (owner, 2026-11-03). */
table.breakdown.vs th { font-weight: 400; color: var(--faint); font-size: 0.8rem; text-align: right; }
table.breakdown.vs th, table.breakdown.vs td:nth-child(n+2) { text-align: right; white-space: nowrap; width: 6em; }
table.breakdown.vs tr.saving td { color: var(--green); }
table.breakdown.vs + p strong.btc { color: var(--btc); }
@media (max-width: 420px) { table.breakdown.vs th, table.breakdown.vs td:nth-child(n+2) { width: 4.6em; } table.breakdown.vs td:first-child { font-size: 0.8rem; } }
#vs + p strong.btc { color: var(--btc); }
.formula + p.formula-note { max-width: none; } /* the line under the home page formula uses the full width (owner, 2026-11-03) */
.vs-body { padding: 16px 18px 6px; } .vs-body > p { margin: 0 0 12px; } .vs-body table.breakdown.vs { font-size: 0.9rem; margin-bottom: 12px; }
/* Phones (owner, 2026-11-03): the name, price, regions and delivery above the picture, then the picture, then the
   choices; .info steps aside so its two halves sit either side of the gallery. */
@media (max-width: 879px) {
  .product { gap: 18px; padding-top: 18px; }
  .product > .info { display: contents; }
  .product .info-head { order: -1; }
  .product .info-head .price-block { margin-bottom: 0; }
  .product .info-head h1 { margin-bottom: 0.2em; }
}
/* Phones: the details as a compact two-column list, and a shorter footer (owner, 2026-11-03: too many gaps). */
@media (max-width: 600px) {
  .specs > div { grid-template-columns: 5.4em 1fr; gap: 0 10px; padding-block: 7px; }
  .specs dt { font-size: 0.74rem; }
  .specs dd { font-size: 0.86rem; }
  .site-footer { padding: 24px 0 20px; }
  .site-footer .cols { grid-template-columns: 1fr; gap: 14px; margin-bottom: 18px; }
  .site-footer ul { display: flex; flex-wrap: wrap; gap: 0 18px; }
  .site-footer li a { padding: 6px 0; }
  .site-footer .cols > div:first-child { display: grid; grid-template-columns: 1fr auto; align-items: center; column-gap: 12px; }
  .site-footer .cols > div:first-child h2 { grid-column: 1 / -1; }
  .site-footer .cols > div:first-child p { margin: 0; }
  .foot-wizard { margin-top: 0; }
  .foot-wizard .wizard { width: 48px; }
}
@media (max-width: 600px) { .vs-body { padding: 12px 12px 4px; } .vs-body table.breakdown.vs th, .vs-body table.breakdown.vs td:nth-child(n+2) { width: 4em; } }
.compare th.ln .ln-head { display: inline-flex; align-items: center; gap: 6px; } .compare th.ln .wizard { width: 38px; height: auto; flex: none; } /* the wizard on the lightning side (owner, 2026-11-03) */
.vs-more > summary { text-align: left; }
.name-wizard { display: inline-block; vertical-align: -0.12em; margin-left: 0.3em; }
.name-wizard .wizard { width: 1.6em; height: auto; display: block; }
h1 .name-wizard .wizard { width: 1.3em; }
.name .keep, h1 .keep { white-space: nowrap; }
@media (max-width: 600px) { .art-word { display: none; } } /* the stitched size by the picture: just the size on phones */
/* The smallest phones (320 px, E2E review 2026-11-04): the bag stayed cut off the header, and the home page's command
   line and magic line ran off the edge. */
/* 4 stats (owner, 2026-11-04; first called 4 sales): tiles, monthly pixel bars (this month in purple), a short log. Bar heights are classes
   in 20 steps, since the page has no inline styles. */
.stats h1 { margin-bottom: 8px; }
.stats .lede { color: var(--text); font-size: 1.05rem; }
.stats .btc { color: var(--btc); }
.stats .cyan { color: var(--cyan); }
.stats .tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; margin: 28px 0; }
.stats .tile { display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; border: 2px solid transparent; border-radius: 8px; background: var(--lit); box-shadow: var(--glow); }
.stats .tile .k { font-size: 0.75rem; color: var(--faint); }
.stats .tile .v { font-family: var(--pixel); font-size: 1.4rem; color: var(--btc); }
.stats .tile .s { font-size: 0.8rem; color: var(--muted); }
/* The ChatGPT challenge (owner, 2026-11-04): a quote with a gradient rule, "stunned" in orange. */
.stats .challenge { margin: 24px 0 28px; padding: 4px 0 4px 18px; border-left: 3px solid; border-image: var(--border-grad) 1; }
.stats .challenge p { margin: 6px 0; }
.stats .challenge .ch-head { font-weight: 700; color: var(--text); }
.stats .challenge blockquote { margin: 10px 0; font-size: 1.05rem; color: var(--text); }
.stats .challenge blockquote p { margin: 0; }
.stats .challenge footer { margin-top: 4px; font-size: 0.875rem; color: var(--faint); }
.stats-chart { margin-top: 8px; }
.stats-chart .bars { --u: 10px; position: relative; display: flex; align-items: stretch; gap: 10px; height: 280px; padding: 20px 20px 10px; background: var(--bg-dark); }
.stats-chart .col { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 4px; }
.stats-chart .n { font-size: 0.75rem; color: var(--text); white-space: nowrap; }
.stats-chart .m { font-size: 0.75rem; color: var(--faint); }
.stats-chart .sb { display: block; width: 100%; border-radius: 3px 3px 0 0; background: repeating-linear-gradient(to top, var(--btc) 0 8px, #e0821a 8px 10px); box-shadow: 0 0 14px rgba(247, 147, 26, 0.35); }
.stats-chart .now .sb { background: repeating-linear-gradient(to top, var(--magenta) 0 8px, #a688e0 8px 10px); box-shadow: 0 0 14px rgba(187, 154, 247, 0.4); }
.stats-chart .now .n { color: var(--magenta); }
.stats-chart .sb.h0 { height: 4px; background: none; box-shadow: none; border: 1px dashed var(--line); }
.sb.h1 { height: calc(var(--u) * 1); }
.sb.h2 { height: calc(var(--u) * 2); }
.sb.h3 { height: calc(var(--u) * 3); }
.sb.h4 { height: calc(var(--u) * 4); }
.sb.h5 { height: calc(var(--u) * 5); }
.sb.h6 { height: calc(var(--u) * 6); }
.sb.h7 { height: calc(var(--u) * 7); }
.sb.h8 { height: calc(var(--u) * 8); }
.sb.h9 { height: calc(var(--u) * 9); }
.sb.h10 { height: calc(var(--u) * 10); }
.sb.h11 { height: calc(var(--u) * 11); }
.sb.h12 { height: calc(var(--u) * 12); }
.sb.h13 { height: calc(var(--u) * 13); }
.sb.h14 { height: calc(var(--u) * 14); }
.sb.h15 { height: calc(var(--u) * 15); }
.sb.h16 { height: calc(var(--u) * 16); }
.sb.h17 { height: calc(var(--u) * 17); }
.sb.h18 { height: calc(var(--u) * 18); }
.sb.h19 { height: calc(var(--u) * 19); }
.sb.h20 { height: calc(var(--u) * 20); }
.stats-chart .bars-note { position: absolute; inset: 0 0 40px; display: flex; align-items: center; justify-content: center; margin: 0; color: var(--muted); font-size: 0.85rem; }
.stats-foot { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 28px; margin-top: 28px; }
.stats-log { margin: 0; font: 0.82rem/1.7 var(--mono); color: var(--muted); background: var(--bg-dark); border: 1px solid var(--line); border-radius: 6px; padding: 12px 16px; overflow-x: auto; }
.stats-log .ok { color: var(--green); }
@media (max-width: 600px) {
  .stats-chart .bars { gap: 3px; padding: 16px 8px 8px; height: 240px; }
  .stats-chart .bars { --u: 8px; }
  .stats-chart .n, .stats-chart .m { font-size: 0.625rem; }
  .stats-chart .now .n { white-space: normal; text-align: center; }
}

/* Four workspaces don't fit beside the logo and the bag on a small phone (4 stats, 2026-11-04): they get their own row. */
@media (max-width: 440px) {
  /* 108px: 6 + a 44px row + 2 + the 50px workspaces + 6; the shop's sticky section links sit just under it. */
  .bar .wrap { flex-wrap: wrap; height: 108px; align-content: center; row-gap: 2px; }
  .brand, .bar .bag { min-height: 44px; }
  .workspaces { order: 3; width: 100%; justify-content: space-between; }
  .workspaces a { flex: 1; justify-content: center; }
  .jump { top: 108px; }
  html { scroll-padding-top: 166px; }
  #hats, #stuff, #seasonal { scroll-margin-top: 168px; }
  #size-guide, .notice.added { scroll-margin-top: 196px; }
}

/* Real samples (owner, 2026-11-04): the photo in the site's gradient frame. */
.samples { display: grid; gap: 32px; margin-top: 24px; align-items: start; } /* phones: stacked, in order */
.sample { margin: 0; }
.sample img { display: block; width: 100%; height: auto; padding: 0; } /* the whole photo, never cropped or stretched */
.sample figcaption { margin-top: 8px; }
.sample.portrait { max-width: 480px; } /* a tall photo stays a sensible size (owner's autumn shot, 2026-11-05) */
/* Desktop: the photos side by side, two equal columns, tops lined up, each caption under its photo (owner, 2026-11-06). */
@media (min-width: 768px) {
  .samples { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
  .sample.portrait { max-width: none; }
}
.real-link { margin: 10px 0 0; }

.pick-line { margin: 6px 0 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* one line, even on a phone (owner, 2026-11-04) */

.workspaces .tiny-t, .season-line .tiny-t { font-size: 0.55em; } /* "stats" that reads as sats (owner, 2026-11-04) */

/* "For bitcoiners, by bitcoiners" (owner, 2026-11-04): each "bitcoiners" a link in the line's own colour, underlined;
   no symbol, so the asterisk stays the logo's and the headline's. */
.hero-tagline a.quiet-link { color: inherit; text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.hero-tagline a.quiet-link:hover, .hero-tagline a.quiet-link:focus-visible { color: var(--cyan); }
/* The logo's asterisk, answered by the home page's "* and stuff". */
.brand .logo .star { color: var(--btc); }
.anyone-wizard { margin: 8px 0 20px; }
.anyone-wizard .wizard { width: 160px; height: auto; }

/* The home page hero on phones (owner, 2026-11-04, option D: "a little busy"): every word kept, the small print quieter
   (grey instead of green and blue), and the wizard's line under the hat, so the button leads straight to it. Desktop
   unchanged. */
@media (max-width: 600px) {
  .hero-grid { padding-top: 28px; gap: 4px; }
  .hero .eyebrow { font-size: 0.72rem; color: var(--faint); margin-bottom: 10px; }
  .hero-title { margin-bottom: 0.2em; }
  .hero-tagline { display: none; } /* desktop only: the phone hero was still busy (owner, #233) */
  .hero .lede { font-size: 0.95rem; color: var(--muted); margin: 0 0 0.3em; }
  .hero-facts { color: var(--muted); font-size: 0.85rem; margin: 0 0 1.4em; }
  .hero-links { font-size: 0.8rem; }
}

@media (max-width: 340px) {
  .brand .logo { font-size: 1.05rem; }
  .bar .wrap { gap: 4px; }
  .workspaces a { padding: 1px 5px; }
  .workspaces a .l { font-size: 0.8125rem; }
  .cmd-word { white-space: normal; }
}

/* ===== polish: rails ===== */
/* The home page's sections as Apple-style shelves (owner, 2026-11-05: less vertical scrolling). One row a section,
   snapping card by card; the row runs to the window's edges so the next card peeks in at the right, while the first
   card (and every snap) lines up with the page's content edge. /shop keeps its full grid. No JS: swipe, trackpad,
   shift+wheel, the scrollbar, or Tab through the cards (each is a link, so focus scrolls it into view). */
:is(.shelved, .shelf-wide) { container-type: inline-size; } /* 100cqw: the content width, for the row's edges below */
:is(.shelved, .shelf-wide) .grid {
  --shelf-card: 300px;
  --shelf-gap: 18px;
  --shelf-edge: calc((100vw - 100cqw) / 2); /* from the window's edge to the content edge (lengths, so scroll-padding agrees) */
  display: grid;
  grid-auto-flow: column;
  grid-template-columns: none;
  grid-auto-columns: var(--shelf-card);
  gap: var(--shelf-gap);
  margin-inline: calc(-1 * var(--shelf-edge));
  padding: 6px var(--shelf-edge) 18px; /* top: room for the hover glow; bottom: the scrollbar's lane */
  scroll-padding-inline: var(--shelf-edge);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
:is(.shelved, .shelf-wide) .grid > .card { scroll-snap-align: start; }
:is(.shelved, .shelf-wide) .grid::-webkit-scrollbar { height: 8px; }
:is(.shelved, .shelf-wide) .grid::-webkit-scrollbar-track { background: transparent; }
:is(.shelved, .shelf-wide) .grid::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 8px; }
:is(.shelved, .shelf-wide) .grid:hover { scrollbar-color: var(--faint) transparent; }
:is(.shelved, .shelf-wide) .grid:hover::-webkit-scrollbar-thumb { background: var(--faint); }
:is(.shelved, .shelf-wide) .grid:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; } /* if the browser makes the row itself focusable */
/* Touch screens swipe; the bar is for mice. */
@media (hover: none) {
  :is(.shelved, .shelf-wide) .grid { scrollbar-width: none; padding-bottom: 8px; }
  :is(.shelved, .shelf-wide) .grid::-webkit-scrollbar { display: none; }
}
@media (max-width: 600px) {
  .shelved .grid { --shelf-card: 78vw; --shelf-gap: 12px; }
}
/* Wide screens: the row stays inside the page's content width (running to the window's edge looked odd full screen,
   owner 2026-11-05); the right edge fades, so the next card still peeks in. */
@media (min-width: 1200px) {
  :is(.shelved, .shelf-wide) .grid {
    margin-inline: 0;
    padding-inline: 0 72px; /* trailing room, so the last card scrolls clear of the fade (Codex review, 2026-11-05) */
    scroll-padding-inline: 0;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 72px), transparent);
            mask-image: linear-gradient(90deg, #000 calc(100% - 72px), transparent);
  }
}
@media (prefers-reduced-motion: no-preference) { :is(.shelved, .shelf-wide) .grid { scroll-behavior: smooth; } }
/* The shop's section pages and a product page's related row (owner, 2026-11-06: no lone card on the last row of a
   grid): the same one-row shelf from 601 px. Phones keep their compact rows (one column: nothing is ever alone). */
@media (max-width: 600px) {
  .shelf-wide .grid {
    display: grid; grid-auto-flow: row; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
    margin-inline: 0; padding: 0; overflow: visible; scroll-snap-type: none;
  }
}
/* A section's own page (/shop/hats …): everything at once on wide screens, as a grid whose columns suit the count
   (gridColumns in shop.ts), so "all 7" looks different from the listing's row (owner, 2026-11-06). Narrower screens
   keep the row; phones their compact list. */
@media (min-width: 1200px) {
  .shelf-wide.full .grid {
    grid-auto-flow: row; grid-auto-columns: auto; overflow: visible; scroll-snap-type: none;
    margin-inline: 0; padding: 6px 0 0; -webkit-mask-image: none; mask-image: none;
  }
  .shelf-wide.full.cols-1 .grid { grid-template-columns: minmax(0, 340px); }
  .shelf-wide.full.cols-2 .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shelf-wide.full.cols-3 .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .shelf-wide.full.cols-4 .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* ===== end polish: rails ===== */
/* ===== polish: glow ===== */
/* A soft orange-to-deep-navy wash at the top of every page, after spiral.xyz's glowing gradients (owner, 2026-11-05:
   "instead of pink to dark blue we have orange to dark blue"). Both layers sit behind everything (z-index -1, the
   body background paints on the canvas beneath them), so no text, price or control is ever tinted or grained. */
body::before, body::after { content: ''; position: absolute; left: 0; right: 0; top: 0; z-index: -1; pointer-events: none; }
/* Both fade out well before their box ends, so no edge shows on the darker night (owner, 2026-11-05: "a very distinct
   line where it changes" on a full-screen desktop). */
body::before { -webkit-mask: linear-gradient(#000 45%, transparent 96%); mask: linear-gradient(#000 45%, transparent 96%); }
/* The wash: a quiet version on every page... */
body::before {
  height: 760px;
  background:
    radial-gradient(42% 60% at 82% 0%, rgba(247, 147, 26, 0.13), transparent 70%),
    radial-gradient(55% 75% at 18% 8%, rgba(0, 83, 151, 0.22), transparent 72%),
    radial-gradient(70% 90% at 50% -20%, rgba(20, 28, 64, 0.55), transparent 75%);
}
/* ...and a fuller one behind the home page's hero: the bitcoin-orange bloom behind the sunset, deep navy beside the copy. */
body:has(.hero-art)::before {
  height: 1150px;
  background:
    radial-gradient(38% 46% at 74% 30%, rgba(247, 147, 26, 0.22), rgba(247, 147, 26, 0.06) 55%, transparent 78%),
    radial-gradient(30% 34% at 90% 8%, rgba(255, 165, 58, 0.10), transparent 70%),
    radial-gradient(52% 58% at 14% 26%, rgba(0, 83, 151, 0.30), transparent 74%),
    radial-gradient(80% 50% at 50% 62%, rgba(20, 28, 64, 0.35), transparent 70%),
    radial-gradient(90% 60% at 50% -10%, rgba(20, 28, 64, 0.5), transparent 75%);
  animation: glow-breathe 22s ease-in-out infinite alternate;
}
/* One column on phones: the bloom moves down behind the sunset, under the copy. */
@media (max-width: 879px) {
  body:has(.hero-art)::before {
    height: 1250px;
    background:
      radial-gradient(70% 30% at 55% 58%, rgba(247, 147, 26, 0.22), rgba(247, 147, 26, 0.05) 60%, transparent 80%),
      radial-gradient(80% 28% at 10% 18%, rgba(0, 83, 151, 0.32), transparent 74%),
      radial-gradient(100% 40% at 50% -5%, rgba(20, 28, 64, 0.5), transparent 75%);
  }
}
@keyframes glow-breathe { from { opacity: 0.82; } to { opacity: 1; } }
/* Fine film grain, barely there (SVG noise, no image file), fading out down the wash. */
body::after {
  height: 1150px;
  opacity: 0.07;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1.4 -0.4'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") 0 0 / 180px 180px;
  -webkit-mask: linear-gradient(#000 40%, transparent);
          mask: linear-gradient(#000 40%, transparent);
}
body.live::before, body.live::after { display: none; } /* the ticker iframe stays clean */
@media (prefers-reduced-motion: reduce) { body:has(.hero-art)::before { animation: none; } }

/* Accents: the same orange-to-blue, used sparingly. */
/* Section heads (`$ ls hats/`...) get a hairline that glows orange and fades through navy into the page. */
.section-head {
  background: linear-gradient(90deg, rgba(247, 147, 26, 0.55), rgba(0, 83, 151, 0.5) 38%, rgba(0, 83, 151, 0) 85%) left bottom / 100% 1px no-repeat;
  padding-bottom: 10px;
}
/* The orange buttons: modern outline, no hard blue shadow (owner, 2026-11-05). Bitcoin-orange border and text on a
   faint orange tint; they fill orange on hover and focus. */
.btn-primary { border-radius: 10px; background: rgba(247, 147, 26, 0.08); border-color: var(--btc); color: var(--btc); box-shadow: none; transition: transform 0.15s ease, box-shadow 0.25s ease, background-color 0.2s, color 0.2s; }
.btn-primary:hover, .btn-primary:focus-visible { background: var(--btc); border-color: var(--btc); color: var(--accent-ink); box-shadow: 0 8px 28px -6px rgba(247, 147, 26, 0.5); transform: translateY(-1px); }
.btn-primary:active { box-shadow: 0 2px 8px -4px rgba(247, 147, 26, 0.45); transform: none; }
/* The pay page's "Paid? See your order" (owner, 2026-11-07): like the orange buttons, in the Zap yellow, for when the
   page hasn't moved on by itself. */
.btn-paid { border-radius: 10px; background: rgba(255, 204, 0, 0.08); border-color: #ffcc00; color: #ffcc00; box-shadow: none; margin: 0 0 12px; transition: transform 0.15s ease, box-shadow 0.25s ease, background-color 0.2s, color 0.2s; }
.btn-paid:hover, .btn-paid:focus-visible { background: #ffcc00; border-color: #ffcc00; color: var(--accent-ink); box-shadow: 0 8px 28px -6px rgba(255, 204, 0, 0.45); transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) { .btn-paid { transition: none; } .btn-paid:hover, .btn-paid:focus-visible { transform: none; } }
@media (prefers-reduced-motion: reduce) { .btn-primary { transition: none; } .btn-primary:hover, .btn-primary:focus-visible { transform: none; } }
@media (forced-colors: active) {
  body::before, body::after { display: none; }
}
/* ===== end polish: glow ===== */
/* ===== polish: glass ===== */
/* Frosted glass (owner, 2026-11-05, cues from umbrel.com): every boxed card and panel is a translucent pane over the
   page, blurred and a touch more saturated behind, with a hairline border that is brighter along the top edge (light
   catching glass), a soft inner highlight, large gentle corners and a soft depth shadow. Boxes are still choices and
   links still go somewhere: the cards lift and brighten on hover, the picked boxes keep their lit border.
   Solid surfaces where backdrop-filter is missing or the visitor asks for less transparency (end of this block). */
:root {
  --glass-tint: rgb(31 35 53 / 0.86); /* the old card blue (--surface), mostly solid: subtle glass, Tokyo Night dark (owner, 2026-11-05) */
  --glass-tint-hover: rgb(36 40 59 / 0.88);
  --glass-tint-deep: rgb(16 16 22 / 0.6); /* an inset well inside a pane: inputs, the chart, the title bars */
  --glass-sheen: radial-gradient(120% 70% at 0% 0%, rgb(255 255 255 / 0.025), rgb(255 255 255 / 0) 60%), linear-gradient(180deg, rgb(255 255 255 / 0.025), rgb(255 255 255 / 0.006) 40%, rgb(255 255 255 / 0) 100%);
  --glass-sheen-hover: radial-gradient(120% 70% at 0% 0%, rgb(255 255 255 / 0.045), rgb(255 255 255 / 0) 60%), linear-gradient(180deg, rgb(255 255 255 / 0.04), rgb(255 255 255 / 0.01) 40%, rgb(255 255 255 / 0) 100%);
  --glass-bg: var(--glass-sheen), var(--glass-tint);
  --glass-bg-hover: var(--glass-sheen-hover), var(--glass-tint-hover);
  --glass-blur: blur(18px) saturate(165%);
  --glass-line: rgb(192 202 245 / 0.08);
  --glass-line-top: rgb(222 230 255 / 0.16);
  --glass-shadow: inset 0 1px 0 rgb(255 255 255 / 0.045), 0 1px 2px rgb(0 0 0 / 0.22), 0 14px 34px -16px rgb(0 0 0 / 0.65);
  --glass-shadow-hover: inset 0 1px 0 rgb(255 255 255 / 0.1), 0 2px 4px rgb(0 0 0 / 0.2), 0 22px 44px -18px rgb(0 0 0 / 0.7), 0 0 22px rgb(187 154 247 / 0.16);
  --glass-radius: 16px;
}

/* The panes. */
div.card, .panel, .step, .stat, .stats .tile, .window:not(img), .formula, .order-side, .stats-log {
  position: relative;
  isolation: isolate; /* the lit ring (::before) stays inside the pane, under the sticky bars */
  border: 1px solid var(--glass-line);
  border-top-color: var(--glass-line-top);
  border-radius: var(--glass-radius);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
}
.step, .stat, .order-side, .stats-log { border-radius: 12px; }
.formula, .stats .tile { box-shadow: var(--glass-shadow), var(--glow); }
.formula { background: var(--glass-sheen), rgb(22 22 30 / 0.94); } /* the price box stays dark inside, as before (owner, 2026-11-05) */
.window:not(img), .formula, .stats .tile { border-color: transparent; } /* the ring is their edge; the glass shows through the border */
.window:not(img) { box-shadow: var(--glass-shadow), 0 0 22px rgb(187 154 247 / 0.22); }

/* The lit border (cyan, purple, orange) as a 1px ring over the hairline, so the glass stays see-through: always on the
   terminal windows, the formula and the stats tiles (they wore it before), on a card only while hovered. */
div.card::before, .window:not(img)::before, .formula::before, .stats .tile::before {
  content: '';
  position: absolute;
  inset: 0; /* inside the 1px border: the panes clip at their padding edge (overflow: hidden) */
  z-index: 100;
  border-radius: inherit;
  padding: 1px;
  background: var(--border-grad);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
  opacity: 0.85;
}

/* Product cards: lift and brighten on hover, no layout shift (a transform and paint only). */
div.card {
  transition: transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.25s, border-color 0.25s, background 0.25s;
}
div.card::before { opacity: 0; transition: opacity 0.25s; }
div.card:hover {
  transform: translateY(-3px);
  border-color: transparent;
  background: var(--glass-bg-hover);
  box-shadow: var(--glass-shadow-hover);
}
div.card:hover::before { opacity: 1; }
div.card:focus-within { border-color: var(--cyan); }
div.card:has(.card-link:focus-visible) { outline: 2px solid var(--cyan); outline-offset: 3px; transform: translateY(-3px); box-shadow: var(--glass-shadow-hover); }
/* The picture's spotlight fades into the glass rather than ending in a solid square. */
.card .art { background: radial-gradient(ellipse at 50% 42%, rgb(47 53 73 / 0.9) 0%, rgb(47 53 73 / 0) 70%); }
.grid.rows .card .art { border-radius: 10px; }
.gallery .views { background: radial-gradient(ellipse at 50% 42%, rgb(47 53 73 / 0.95) 0%, rgb(47 53 73 / 0) 72%); }

/* Terminal windows: glass panes with a smoked title bar. */
.titlebar { background: var(--glass-tint-deep); border-bottom: 1px solid var(--glass-line); }
.stats-chart .bars { background: var(--glass-tint-deep); }
.formula { border-radius: 14px; }

/* The product page's price: a pane of its own instead of two dashed rules. */
.price-block { border: 1px solid var(--glass-line); border-top-color: var(--glass-line-top); border-radius: 14px; padding: 14px 18px; background: var(--glass-bg); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); box-shadow: var(--glass-shadow); }

/* Notices and the lede box: smoked glass, their coloured edge kept. */
.notice, .lede-box { background: var(--glass-bg); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); border-radius: 12px; box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.05); }
.notice.warn { background: linear-gradient(rgb(247 147 26 / 12%), rgb(247 147 26 / 12%)), var(--glass-tint); }
.lede-box { border-radius: 0 12px 12px 0; }

/* Choices (boxes): small panes of glass, rounder; the picked one keeps its lit border. */
.swatch, .rtabs a, .rtabs label { border-radius: 9px; }
.swatch { background: linear-gradient(180deg, rgb(255 255 255 / 0.045), rgb(255 255 255 / 0) 60%), var(--glass-tint-deep); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.05); transition: border-color 0.15s, background 0.15s; }
.swatch:hover { border-color: var(--muted); }
input[type='text'], input[type='email'], input[type='password'], input[type='number'], select, textarea { background: var(--glass-tint-deep); border-radius: 10px; box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.25); }
.meter { border-radius: 6px; }

/* The top bar and the shop's section links: one frosted band. */
.bar {
  background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.13) 0 1px, transparent 1px 3px), linear-gradient(180deg, rgb(255 255 255 / 0.035), rgb(255 255 255 / 0)), rgb(22 22 30 / 0.62);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid var(--glass-line);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.04), 0 10px 30px -18px rgb(0 0 0 / 0.8);
}
.jump::before { background: rgb(22 22 30 / 0.62); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); border-bottom: 1px solid var(--glass-line); }
.workspaces { background: rgb(255 255 255 / 0.035); border: 1px solid var(--glass-line); border-top-color: var(--glass-line-top); border-radius: 12px; box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.05); }
.workspaces a { border-radius: 8px; }
.workspaces a:hover { background: rgb(255 255 255 / 0.06); }
.workspaces a[aria-current='page'] { background: rgb(255 255 255 / 0.08); }

@media (prefers-reduced-motion: reduce) {
  div.card, div.card::before, .swatch { transition: none; }
  div.card:hover, div.card:has(.card-link:focus-visible) { transform: none; }
}
/* No backdrop-filter, or the visitor asks for less transparency: solid surfaces, same shapes. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root { --glass-tint: var(--surface); --glass-tint-hover: var(--surface-2); --glass-tint-deep: var(--bg-dark); }
  .bar, .jump::before { background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.13) 0 1px, transparent 1px 3px), rgb(22 22 30 / 0.97); }
}
@media (prefers-reduced-transparency: reduce) {
  :root { --glass-tint: var(--surface); --glass-tint-hover: var(--surface-2); --glass-tint-deep: var(--bg-dark); --glass-blur: none; }
  .bar, .jump::before { background: var(--bg-dark); }
  .card .art, .gallery .views { background: radial-gradient(ellipse at 50% 42%, var(--stage) 0%, var(--surface) 70%); }
}
/* ===== end polish: glass ===== */

/* ===== polish: night (owner, 2026-11-05: "I love the colour, but not the bolt") ===== */
/* A darker, bluer night than Tokyo Night's grey-blue, a cue from spiral.xyz. */
:root { --bg: #0f1226; --bg-dark: #0a0c1a; }
body { background: radial-gradient(ellipse 120% 60% at 50% -10%, #1b2140 0%, var(--bg) 55%, var(--bg-dark) 100%) fixed, var(--bg); }
/* ===== end polish: night ===== */

/* ===== polish: wide screens (owner, 2026-11-05) ===== */
/* Pages with rows of cards (home, shop, sections) grow in steps on big screens, so a shelf shows more; text pages keep
   their width, so lines and tables read as before. No zoom: the owner didn't want text enlarged at full screen, and 4K
   screens scale the page themselves. */
@media (min-width: 1500px) { .wrap { max-width: 1260px; } }
@media (min-width: 1700px) { .wrap { max-width: 1340px; } }
@media (min-width: 2000px) { .wrap { max-width: 1440px; } }
/* The pay box and the order page's notices grow in the same steps as the page (owner, 2026-11-06). */
@media (min-width: 1500px) { .pay, .order-page .notice, .order-page .pay-cta { max-width: 840px; } }
@media (min-width: 1700px) { .pay, .order-page .notice, .order-page .pay-cta { max-width: 890px; } }
@media (min-width: 2000px) { .pay, .order-page .notice, .order-page .pay-cta { max-width: 960px; } }
/* Text pages (pricing, about, why bitcoin…) on big screens: the text column grows a step at a time, never past a
   comfortable line, and the number tables keep about their old width, so their columns don't drift apart (owner,
   2026-11-05: "pricing and about too narrow… without making the tables horrible"). */
/* Every page the same width as home and shop (owner, 2026-11-05): text pages fill the column; the number tables and
   terminal windows stay at a readable width, centred, so their columns don't drift apart. */
@media (min-width: 880px) { .prose { max-width: none; } .wrap.prose { max-width: 1140px; } .prose .window:not(img), .prose > table { max-width: 80ch; margin-inline: auto; } .prose .profit-win { max-width: 72ch; } }
@media (min-width: 1280px) { .prose .steps { grid-template-columns: repeat(4, 1fr); } }
/* A text page is .wrap and .prose at once: the same width steps as home and shop, never the whole screen (owner). */
@media (min-width: 1500px) { .wrap.prose { max-width: 1260px; } }
@media (min-width: 1700px) { .wrap.prose { max-width: 1340px; } }
@media (min-width: 2000px) { .wrap.prose { max-width: 1440px; } }
/* ===== end polish: wide screens ===== */

/* Rows of section links never wrap on a phone: they scroll sideways, like the home page's shelves (owner, 2026-11-05). */
.jump { flex-wrap: nowrap; white-space: nowrap; overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; }
.jump::-webkit-scrollbar { display: none; }
.section-head .ls-links { white-space: nowrap; overflow-x: auto; scrollbar-width: none; min-width: 0; }
.section-head .ls-links::-webkit-scrollbar { display: none; }
.section-head:has(.ls-links) { flex-wrap: nowrap; } .section-head:has(.ls-links) h2 { flex: none; }

/* The shop's section links: a full-width band straight under the top bar, sticky, its links centred; a row too long for
   a phone scrolls sideways from its start (owner, 2026-11-05). */
.jump-band { position: sticky; top: 52px; z-index: 52; background: rgb(22 22 30 / 0.62); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); border-bottom: 1px solid var(--glass-line); }
/* (The 440px `top: 108px` override from the two-row header went on 2026-11-08: the bar is 52px on phones, so the band
   had floated 55px adrift and covered the `$ ls shop/` heading; owner-approved P2.) */
.jump-band .jump { position: static; margin: 0 auto; padding-top: 0; padding-bottom: 0; gap: 0 8px; }
.jump-band .jump::before { content: none; }
.jump-band .jump > :first-child { margin-left: auto; } .jump-band .jump > :last-child { margin-right: auto; }
.jump-band .sep { color: var(--faint); }
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) { .jump-band { background: rgb(22 22 30 / 0.97); } }
@media (prefers-reduced-transparency: reduce) { .jump-band { background: var(--bg-dark); -webkit-backdrop-filter: none; backdrop-filter: none; } }
@media (max-width: 400px) { .jump-band .jump { gap: 0 5px; font-size: 0.78rem; } }
.formula + p.diy-line { margin: 4px auto 10px; color: var(--muted); max-width: none; } .diy-line strong { color: var(--btc-orange); } /* the other way round, under the formula box (owner, 2026-11-05) */

/* The picture's title bar stays one line (owner, 2026-11-05): stuff shows only its stitched size; anything still too long
   is cut with an ellipsis rather than wrapping. */
.titlebar > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.titlebar > span.faint { flex: none; }

/* The footer's status line as tiles (owner, 2026-11-05: it often wraps): each segment its own small box, a little gap
   between, growing to fill its row, so a wrapped line looks laid out on purpose rather than broken. */
.statusline { gap: 4px; background: none; border: 0; border-radius: 0; overflow: visible; }
.statusline > span, .statusline > a.nostr { flex: 1 1 auto; text-align: center; padding: 4px 12px; background: var(--bg-dark); border: 1px solid var(--line); border-radius: 4px; margin-left: 0; }
.statusline > span.mode { flex: 0 0 auto; background: var(--blue); border-color: var(--blue); }
.statusline > span:nth-child(2) { background: var(--surface-2); }
/* ===== polish: readability (owner, 2026-11-06) =====
   Secondary text a touch easier to read: card descriptions and details, the product picture's title bar and the
   section links, each moved about 8% towards the main text colour. Still quieter than names and prices; sizes,
   weights, decorative text and borders unchanged. */
.card .body .muted, .card-foot .n-colours, .product .titlebar { color: color-mix(in srgb, var(--muted) 92%, var(--text)); }
.card .body .faint, .product .titlebar .faint { color: color-mix(in srgb, var(--faint) 92%, var(--text)); }
.jump a, .section-head .ls-links a { color: color-mix(in srgb, var(--blue) 92%, var(--text)); }
/* ===== end polish: readability ===== */

/* One menu (owner, 2026-11-07): an orange outline "> menu" button at the top right (like the browse-the-shop buttons); a
   very dark navy panel whose edge runs our orange into our blue; inside, "< shop" folds the section cards away (and
   reads "> shop" when folded), then the pages. Light words, orange arrows. <details>, so no script, keyboard-friendly.
   Fable's refinements (2026-11-07): the pictures zoom on the embroidery; the current page wears an orange light; the
   panel fades in (not when motion is reduced); the bar is 52px on phones again (the removed workspaces had made it 108). */
.menu { position: relative; }
.bar .ticker { margin-left: auto; }
.menu-btn { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 0.5em; min-height: 38px; padding: 7px 14px; font-size: 0.85rem; }
.menu-btn::-webkit-details-marker, .menu-shop > summary::-webkit-details-marker { display: none; }
.menu-btn .gt { display: inline-block; transition: transform 0.2s; }
.menu[open] .menu-btn .gt { transform: rotate(90deg); }
.menu[open] .menu-btn { background: var(--btc); color: var(--accent-ink); box-shadow: 0 8px 28px -6px rgba(247, 147, 26, 0.5); }
/* The edge runs our orange into our blue, like the site's gradient-bordered cards (owner, 2026-11-07), with a soft glow of each. */
.menu-panel { position: absolute; right: 0; top: calc(100% + 12px); width: 392px; padding: 12px 14px 14px; border-radius: 16px; border: 1px solid transparent; background: linear-gradient(#0a0c1a, #0a0c1a) padding-box, linear-gradient(160deg, var(--btc) 0%, var(--blue) 65%, var(--cyan) 100%) border-box; box-shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.85), -6px -4px 32px -14px rgba(247, 147, 26, 0.55), 6px 10px 32px -14px rgba(122, 162, 247, 0.55); z-index: 60; transform-origin: top right; visibility: hidden; opacity: 0; transform: translateY(-6px) scale(0.985); pointer-events: none; transition: opacity 0.18s ease-out, transform 0.18s ease-out, visibility 0s linear 0.18s; }
/* Shown when the menu is open; on the page all along, so its cursor keeps time with the logo's (owner, 2026-11-07). */
.menu-wrap { position: relative; }
.menu[open] + .menu-panel { visibility: visible; opacity: 1; transform: none; pointer-events: auto; transition: opacity 0.18s ease-out, transform 0.18s ease-out; }
.menu-panel ul { list-style: none; margin: 0; padding: 0; }
.menu .tiny-t { font-size: 0.55em; } /* "stats" that reads as sats, as in the old tabs (owner, 2026-11-07) */
/* "< shop": a whole tappable row; the arrow is the hinge. */
.menu-shop > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 0.6em; min-height: 44px; margin: 0 -6px 2px; padding: 4px 8px; border-radius: 10px; font: 700 1rem/1 var(--mono); color: var(--text); transition: background-color 0.2s, color 0.2s; }
.menu-shop > summary:hover, .menu-shop > summary:focus-visible { color: #fff; background-color: rgba(255, 255, 255, 0.04); }
.menu-shop > summary .fold { color: var(--btc-orange); width: 1ch; text-align: center; }
.menu-shop > summary .fold::before { content: '>'; }
.menu-shop[open] > summary .fold::before { content: '<'; }
.menu-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* Each card wears the site's orange-to-blue edge, faint (owner, 2026-11-07: the same border, more subtle inside the menu). */
.menu-cards a { position: relative; display: flex; align-items: center; gap: 10px; min-height: 54px; padding: 6px 10px; border-radius: 12px; text-decoration: none; background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.015)) padding-box, linear-gradient(#12152a, #12152a) padding-box, linear-gradient(135deg, rgba(247, 147, 26, 0.32), rgba(122, 162, 247, 0.32)) border-box; border: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); color: var(--text); transition: border-color 0.2s, background-color 0.2s, transform 0.15s, box-shadow 0.2s; }
.menu-cards a:hover, .menu-cards a:focus-visible { background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02)) padding-box, linear-gradient(#171a33, #171a33) padding-box, linear-gradient(135deg, var(--btc-orange), var(--blue)) border-box; transform: translateY(-1px); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 6px 18px -10px rgba(247, 147, 26, 0.6); }
.menu-cards a:focus-visible { outline-offset: 1px; }
.menu-cards b { font-size: 0.95rem; color: var(--text); }
/* The current page: an orange edge and a small orange light at its right. */
.menu-cards a[aria-current='page'] { background: linear-gradient(180deg, rgba(247, 147, 26, 0.10), rgba(122, 162, 247, 0.05)) padding-box, linear-gradient(#12152a, #12152a) padding-box, linear-gradient(135deg, rgba(247, 147, 26, 0.85), rgba(122, 162, 247, 0.85)) border-box; }
/* The pictures: the section's first design, zoomed on the stitching, on a lit stage so a black hat still has a shape. */
.menu-cards .pic { width: 40px; height: 40px; border-radius: 9px; overflow: hidden; flex: none; background: radial-gradient(circle at 50% 30%, #4d567c, #2a2f45 72%); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07); }
.menu-cards .pic img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.6); transform-origin: 50% 40%; }
.menu-cards .pic-close img { transform: scale(1.15); transform-origin: 50% 45%; } /* already a close-up */
.menu-cards .all { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 9px; background: rgba(247, 147, 26, 0.12); color: var(--btc-orange); font: 1rem var(--pixel); flex: none; }
.menu-pages { margin-top: 12px !important; padding-top: 12px !important; border-top: 1px solid rgba(122, 162, 247, 0.14); }
.menu-pages a { min-height: 44px; }
.menu-pages .gt { color: var(--btc-orange); transition: transform 0.15s; }
.menu-pages a:hover .gt { transform: translateX(2px); }
@media (prefers-reduced-motion: reduce) {
  .menu-panel, .menu[open] + .menu-panel { transition: none; transform: none; }
  .menu-btn .gt, .menu-cards a, .menu-pages .gt, .menu-shop > summary { transition: none; }
  .menu-cards a:hover { transform: none; }
  .menu-pages a:hover .gt { transform: none; }
}
@media (max-width: 720px) {
  .menu-panel { position: fixed; left: 12px; right: 12px; top: 62px; width: auto; max-height: calc(100vh - 80px); overflow-y: auto; transform-origin: top center; }
  .menu-pages { grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
  .menu-pages a { padding: 6px 8px 6px 10px; gap: 6px; }
  .menu-pages .gt { font-size: 0.9rem; }
}
/* The bar is one 52px row again on small phones: the four workspaces that needed a second row are gone (the rule above
   at 440px still sized the bar for them, so the menu's panel hung under 56px of empty bar). */
@media (max-width: 440px) {
  .bar .wrap { flex-wrap: nowrap; height: 52px; }
  .jump { top: 52px; }
  html { scroll-padding-top: 110px; }
  #hats, #stuff, #seasonal { scroll-margin-top: 112px; }
  #size-guide { scroll-margin-top: 140px; }
  .notice.added { scroll-margin-top: 110px; }
}
/* Fable C, "listing": the pages are a plain listing under the shop's cards, "> pricing  > about  > stats ...", each a
   44px tap, like a terminal's ls; the current one in orange. Lighter than a second set of boxes. */
.menu-ls { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 2px; margin-top: 10px !important; padding-top: 8px !important; border-top: 1px solid rgba(122, 162, 247, 0.14); }
/* Three columns on phones and desks alike; the last row stays in the columns above, not stretched (owner, 2026-11-08). */
.menu-ls li { min-width: 0; }
.menu-ls a { display: flex; align-items: center; gap: 0.45em; min-height: 44px; padding: 0 10px; border-radius: 8px; color: var(--text); text-decoration: none; font-weight: 700; font-size: 0.95rem; transition: background-color 0.2s, color 0.2s; }
.menu-ls a:hover, .menu-ls a:focus-visible { background: rgba(255, 255, 255, 0.045); color: #fff; }
.menu-ls .gt, .menu-ls .zap { color: var(--btc-orange); transition: transform 0.15s; }
.menu-ls a:hover .gt { transform: translateX(2px); }
.menu-ls a[aria-current='page'] b { color: var(--btc-orange); }
.menu-ls .tiny-t { font-size: 0.55em; }
.menu-addr { margin-top: 6px; }
@media (prefers-reduced-motion: reduce) { .menu-ls a, .menu-ls .gt { transition: none; } .menu-ls a:hover .gt { transform: none; } }
/* Click anywhere outside the open menu to close it (owner, 2026-11-07), without a script: while it's open, the button
   stretches an invisible layer over the page, so a click outside the panel is a click on the button, which closes it.
   Sized from the top of the screen (the sticky bar's backdrop-filter makes it the box for fixed children). */
.menu[open] > .menu-btn::before { content: ''; position: fixed; left: 0; right: 0; top: 0; height: 100vh; z-index: -1; cursor: default; }
.menu[open] > .menu-btn, .menu[open] > .menu-btn:hover { transform: none; }
/* Word cards (owner, 2026-11-07: no pictures): centred, a little shorter. */
.menu-cards a { justify-content: center; min-height: 46px; }
/* Terminal touches (owner, 2026-11-07: "all it's missing is terminal vibes"): a prompt line at the top, as on the 404
   page, and the words as the site's links, blue and underlined; the current page's in orange. */
.menu-prompt { margin: 0 0 6px; padding: 2px 6px 8px; font-size: 0.8rem; color: var(--text); border-bottom: 1px solid rgba(122, 162, 247, 0.14); }
.menu-prompt .path { color: var(--magenta); }
.menu-prompt .sig { color: var(--btc); }
.menu-prompt .cursor { display: inline-block; width: 0.55em; height: 1.05em; margin-left: 4px; vertical-align: -0.15em; background: var(--btc-orange); animation: blink 1.1s steps(1) infinite; }
@media (prefers-reduced-motion: reduce) { .menu-prompt .cursor { animation: none; } }
.menu-cards b, .menu-ls b { color: var(--blue); text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; text-decoration-thickness: 1px; }
.menu-cards a:hover b, .menu-cards a:focus-visible b, .menu-ls a:hover b, .menu-ls a:focus-visible b { color: var(--cyan); text-decoration-color: var(--cyan); }
.menu-cards a[aria-current='page'] b, .menu-ls a[aria-current='page'] b { color: var(--btc-orange); text-decoration-color: rgba(247, 147, 26, 0.6); }
/* Owner, 2026-11-07: the words the size of the "all hats" links. The panel the very dark blue of the home page's foot,
   no glow inside; the cards as they were. */
.menu-cards b, .menu-ls b { font-size: 0.875rem; }
/* The rate drawn into the page (owner, 2026-11-07): the frame's size, its line on the bar. */
span.ticker { display: block; width: 250px; height: 26px; overflow: hidden; }

/* ===== polish: deeper panes (owner, 2026-11-08: the terminal windows and tables a touch darker, so they read as
   terminal windows against the page; the spotlight behind every product picture a touch darker too) ===== */
:root {
  --glass-tint-pane: rgb(26 29 45 / 0.9); /* the glass of a terminal window or table pane: was --glass-tint, rgb(31 35 53 / 0.86) */
  --stage: #2a2f42; /* was #2f3549 */
}
/* The panes that hold tables and terminal output (the receipt and quote breakdowns, ./slice --today, the stats tiles,
   chart and log, the order page's job window and item card). Product cards keep the lighter glass. */
.panel, .step, .stat, .stats .tile, .window:not(img), .order-side, .stats-log { background: var(--glass-sheen), var(--glass-tint-pane); }
.card .art { background: radial-gradient(ellipse at 50% 42%, rgb(42 47 66 / 0.9) 0%, rgb(42 47 66 / 0) 70%); }
.gallery .views { background: radial-gradient(ellipse at 50% 42%, rgb(42 47 66 / 0.95) 0%, rgb(42 47 66 / 0) 72%); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { :root { --glass-tint-pane: #1b1e2e; } }
@media (prefers-reduced-transparency: reduce) { :root { --glass-tint-pane: #1b1e2e; } }
/* ===== end polish: deeper panes ===== */
/* Exchange-app note on the pay page (owner, 2026-11-08): exchanges ask who you're paying, so this one stands out. */
.notice.exchange { border-left-color: var(--btc); margin: 0; grid-column: 1 / -1; } /* above the QR code, full width (owner, 2026-11-08) */
.notice.exchange strong { color: var(--btc); }
/* Spendabit's link back on /media (owner, 2026-11-08). */
.spendabit-social-icon img { vertical-align: -3px; margin-right: 4px; }
/* ===== polish: phone header ticker (owner, 2026-11-08, approved P1) =====
   On phones the 250px rate ticker ran under the orange "> menu" button and "£1 = 1,783 sats" showed as "£:". Under 440px
   the ticker shrinks to its first segment ("₿ £56,072"); the sats-per-pound line is on /pricing and in every price box. */
@media (max-width: 440px) {
  span.ticker { width: auto; max-width: 150px; }
  .tk .sep, .tk .muted { display: none; }
}
/* ===== polish: from "added" to "checkout" in one tap (owner, 2026-11-08, approved P5) =====
   The added-to-bag notice carries a "> checkout" button under its line; on the bag page the button follows the estimated
   total, with the breakdown and receipt after it. */
.notice.added .added-actions { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.notice.added .added-actions .btn { min-width: 10em; }
.total-line + .btn-block { margin-top: 12px; } /* the bag's checkout button sits right under the estimated total */
.btn-block + .christmas-note { margin-top: 12px; }
/* ===== polish: the pay step (owner, 2026-11-08, approved P8) ===== */
.pay .first-time { grid-column: 1 / -1; margin: 0; color: var(--muted); } /* two lines for a first Lightning payment, above the exchange note */
.pay .first-time strong { color: var(--text); }
.notice .again { margin-top: 10px; } /* "> get a fresh price for the same order" on an expired order */
/* ===== polish: reassurance by the buy button (owner, 2026-11-08, approved P7) =====
   Three muted lines under "> add to bag" and the bag's "> checkout", each a link to the page that says it in full. */
.assure { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; color: var(--muted); font-family: var(--mono); }
.assure li::before { content: '✓'; color: var(--green); margin-right: 0.6em; }
.assure a { color: inherit; text-decoration: none; }
.assure a:hover, .assure a:focus-visible { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.btn-block + .assure { margin-top: 12px; }
.assure + .christmas-note { margin-top: 12px; }
/* The other hat bodies' product name (a <p>, not a second h1; e2e B4, 2026-11-08) looks exactly like the h1. */
.product .info-head .alt-name { font-family: var(--pixel); font-weight: 400; font-size: clamp(1.35rem, 4.6vw, 2.5rem); line-height: 1.35; letter-spacing: 0; margin: 0 0 0.5em; }
.product .info-head .alt-name.glyph-name { font-family: var(--mono); font-weight: 700; font-size: clamp(2.2rem, 7vw, 3.4rem); }
@media (max-width: 600px) { .product .info-head .alt-name { margin-bottom: 0.2em; } }
/* ===== polish: the real thing where the decision is made (owner, 2026-11-08, approved P6) =====
   Two round thumbnails of the stitched Blinded sample (the /embroidery photos) with a line to that page, under the
   product picture and under the home page's hats shelf. */
.real-strip { display: inline-flex; align-items: center; gap: 12px; margin: 12px 0 0; color: var(--muted); text-decoration: none; }
.real-strip:hover, .real-strip:focus-visible { color: var(--text); }
.real-strip:hover span:last-child, .real-strip:focus-visible span:last-child { text-decoration: underline; text-underline-offset: 3px; }
.real-thumbs { display: inline-flex; flex: none; }
.real-thumb { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; border: 2px solid var(--bg-dark); box-shadow: 0 0 0 1px var(--glass-line), 0 6px 18px -8px rgb(0 0 0 / 0.8); background: var(--bg-dark); }
.real-thumb + .real-thumb { margin-left: -18px; } /* the pair overlap a little, like two coins */
.shelved .real-strip { margin-top: 16px; }
@media (max-width: 600px) { .real-thumb { width: 56px; height: 56px; } .real-thumb + .real-thumb { margin-left: -14px; } }
/* ===== polish: phone home (owner, 2026-11-08, approved P3) =====
   On phones each home shelf is a two-column grid rather than a one-card-at-a-time swipe, and a card keeps only its
   name, colours and price (the tagline, hat kinds and the other regions' prices are on the product page and the shop). */
.hero-price { margin: 10px 0 0; color: var(--text); }
.hero-price .btc { color: var(--btc-orange); }
.shelf-line { margin: -4px 0 10px; }
@media (max-width: 600px) {
  .shelved .grid.compact {
    display: grid; grid-auto-flow: row; grid-template-columns: 1fr 1fr; grid-auto-columns: auto; gap: 10px;
    margin-inline: 0; padding: 4px 0 6px; overflow: visible; scroll-snap-type: none;
    -webkit-mask-image: none; mask-image: none;
  }
  .shelved .grid.compact .card .body > .muted.small, .shelved .grid.compact .card .hat-kinds, .shelved .grid.compact .card .rp-other { display: none; }
  .shelved .grid.compact .card .body { padding: 8px 10px 0; gap: 0; }
  .shelved .grid.compact .card .name { font-size: 0.95rem; line-height: 1.25; }
  .shelved .grid.compact .card-foot { padding: 2px 10px 10px; gap: 2px; }
  .shelved .grid.compact .swatches-mini .n-colours { font-size: 0.75rem; }
  .shelved .grid.compact .price-inline .rp-main { font-size: 0.875rem; line-height: 22px; white-space: nowrap; }
  .shelved .grid.compact .live-price.card { height: 24px; }
  .hero-price { font-size: 0.85rem; }
}
/* ===== polish: sticky buy row on phone product pages (owner, 2026-11-08, approved P4) =====
   The quantity and "> add to bag" row sticks to the bottom of the screen while the pickers scroll by, on a pane of
   the terminal glass with the live sats beside it. Outline button as the owner chose (D72). */
.buy-sats { display: none; }
@media (max-width: 600px) {
  .product .buy-row { position: sticky; bottom: 0; z-index: 50; margin: 14px -16px 0; padding: 10px 16px; display: flex; align-items: center; gap: 10px; background: var(--glass-sheen), var(--glass-tint-pane); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); border-top: 1px solid var(--glass-line); }
  .product .buy-row .buy-sats { display: block; margin-right: auto; font: 700 0.95rem/1.2 var(--mono); color: var(--btc-orange); white-space: nowrap; }
  .product .buy-row .btn { flex: none; }
}
@media (max-width: 600px) and (prefers-reduced-transparency: reduce) { .product .buy-row { background: var(--bg-dark); -webkit-backdrop-filter: none; backdrop-filter: none; } }
.arrival .arrival-days { color: var(--faint); } /* the working days behind the dates (P13, 2026-11-08) */
.assure + .arrival { margin-top: 10px; } /* the bag's arrival line under the reassurance lines */
/* A phone shelf with an odd count never ends with one small card alone (owner, D86): the last one takes the whole row
   in the shop's row layout, picture left, words right (P3 follow-up, 2026-11-08). */
@media (max-width: 600px) {
  .shelved .grid.compact .card:last-child:nth-child(odd) { grid-column: 1 / -1; display: grid; grid-template-columns: clamp(148px, 42vw, 172px) minmax(0, 1fr); grid-template-rows: auto auto; column-gap: 10px; align-items: center; }
  .shelved .grid.compact .card:last-child:nth-child(odd) .card-link { display: contents; }
  .shelved .grid.compact .card:last-child:nth-child(odd) .art { grid-row: 1 / span 2; align-self: center; margin: 4px 0 4px 4px; border-radius: 4px; }
  .shelved .grid.compact .card:last-child:nth-child(odd) .body { grid-column: 2; align-self: end; padding: 10px 10px 0 0; }
  .shelved .grid.compact .card:last-child:nth-child(odd) .card-foot { grid-column: 2; align-self: start; padding: 4px 10px 10px 0; }
}
@media (max-width: 600px) { .shelved .grid.compact .price-inline .rp-main { white-space: normal; font-size: 0.8125rem; } } /* "72,484 sats" fits a half-width card; never clipped to "sat" */
/* Spendabit's social icon at the end of the footer statusline, after the nostr link (owner, 2026-11-09). */
.statusline .spendabit-social-icon { display: inline-flex; align-items: center; padding: 3px 12px 3px 0; }
.statusline .spendabit-social-icon img { vertical-align: 0; margin: 0; }

/* Real photos (owner, 2026-11-09): our samples and customers' photos, from src/catalog/photos.ts; nothing renders when
   a product has none. The product page reuses the /embroidery sample grid, one column wide under the picture. */
.real-photos { margin-top: 16px; }
.real-photos .samples { margin-top: 12px; }
.gallery-col .real-photos .samples { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; } /* side by side, so the picker and button stay near the top */
.gallery-col .real-photos .sample.portrait { max-width: none; }
.photo-kind { color: var(--magenta); }
.photo-review { font-style: italic; color: var(--text); }
.stars { color: var(--btc); letter-spacing: 1px; }
/* The delivery address on the order page, one line each as on the label (owner, 2026-11-09, from a customer). */
.order-side address.ship-to { font-style: normal; line-height: 1.5; }
/* Phones (owner, 2026-11-09: "you can't see all styles and colours without scrolling"): the maker's previews and the
   real photos share a line (opened, the previews take the full width under it), every hat's picture is 4:3 rather than
   square, so switching hats never moves the page (owner, 2026-11-09): a trucker or snapback fills its width (only empty
   backdrop is cut), a dad cap, taller, fits its height, and the caps come out the same height; the pickers sit closer. */
.media-links { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin-top: 12px; }
.media-links details.supplier, .media-links .real-strip { margin-top: 0; }
.media-links details.supplier { flex: 1 1 0; min-width: 0; } /* closed, its hidden pictures mustn't widen it */
.media-links details.supplier[open] { flex-basis: 100%; order: 1; }
.media-links .real-strip { flex: none; }
.media-links details.supplier summary { white-space: nowrap; }
.cw-name { display: none; }
svg.tile { display: none; }
@media (min-width: 880px) { .cw-picked { display: none; } } /* the chips carry the names */
@media (max-width: 879px) {
  .gallery.hat .views { aspect-ratio: 4 / 3; }
  .gallery.crop .view img { object-fit: cover; }
  .product .info-rest .swatches { margin: 6px 0 14px; }
  .product .info-rest .pick-line { margin: 2px 0 10px; }
  .product .info-rest .swatches { gap: 6px; }
  /* Colours as a row of rounded squares (owner, 2026-11-09), the picked one named by "Colour" (pickerRules in src/web/shop.ts). */
  .product .info-rest .swatches.colours .swatch { width: 44px; height: 44px; padding: 3px; justify-content: center; border-radius: 10px; }
  .product .info-rest .swatches.colours .swatch svg { display: none; }
  .product .info-rest .swatches.colours .swatch svg.tile { display: block; width: 100%; height: 100%; }
  .product .info-rest .swatches.colours .sw-name { display: none; }
  .product .info-rest .swatches.colours .swatch::after { content: none !important; }
  .cw-picked { color: var(--text); }
}
@media (max-width: 600px) { .media-links .real-thumb { width: 40px; height: 40px; } .media-links .real-thumb + .real-thumb { margin-left: -10px; } }
/* Phones: the picture, then the hat and colour, then the price box (owner, 2026-11-09), with one slim price line under
   the name. A column, so the pickers can go first while the buy bar stays sticky across the whole of .info-rest. */
.slim-price { display: none; }
@media (max-width: 879px) {
  .slim-price { display: block; margin: 2px 0 0; font-size: 0.95rem; color: var(--btc-orange); }
  .product .info-rest { display: flex; flex-direction: column; }
  .product .info-rest > .pickers { order: -1; margin-bottom: 4px; }
}
/* Switching hats never moves what's under the colours (owner, 2026-11-09): on a hat page the colour row is the height
   of the tallest hat's (seven colours: three rows of chips on a computer; one row of tiles on a phone, shrinking a
   little on the narrowest). */
@media (min-width: 880px) { .hat-switch .swatches.colours { min-height: 148px; align-content: flex-start; } }
@media (max-width: 879px) {
  .hat-switch .product .info-rest .swatches.colours { display: grid; grid-template-columns: repeat(7, minmax(0, 44px)); }
  .hat-switch .product .info-rest .swatches.colours .swatch { width: auto; height: auto; aspect-ratio: 1; }
}
/* After "add to bag" the page comes back where it was, at the picture (#added, under the top bar), not down at the
   notice (owner, 2026-11-09: on phones the page jumped about); on phones the notice sits under the colours, in view. */
.gallery-col { position: relative; }
@media (min-width: 880px) { .product > .gallery-col { position: sticky; } }
.added-anchor { position: absolute; top: 0; scroll-margin-top: -42px; } /* with html scroll-padding-top (110px): just under the top bar */
@media (max-width: 879px) { .product .info-rest > .notice.added { order: -1; margin: 0 0 14px; } }
/* The design picker as picture tiles, five across, the picked design named by "Design" (owner, 2026-11-10: cleaner,
   with up to ten designs). */
.lg-name { display: none; }
.lg-picked { color: var(--text); }
.swatches.logos { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; }
.swatches.logos .swatch.logo { height: 48px; min-height: 0; padding: 5px; justify-content: center; }
.swatches.logos .swatch .logo-thumb { width: 100%; height: 100%; object-fit: contain; }
.swatches.logos .sw-name { display: none; }
.swatches.logos .swatch::after { content: none !important; }
