@charset "UTF-8";
/* ==============================================================================================
   THEME TOKENS: the one place to re-skin a copied shop.
   Every colour, font stack, radius and page-level spacing value used below lives here; the rules
   after this block only refer to these variables. For another shop, change the values here (and
   brand.themeColor in store.js, which should match --soft), then check text contrast stays at
   4.5:1 or more for text colours and 3:1 for --focus-ring against the backgrounds it sits on.
   Media queries further down only switch between the -md / -sm / -xs variants defined here.
   The breakpoints (700 / 1000 / 1440px), gutters and card gaps are repeated in SIZES in app.js,
   which tells the browser how wide each photo is drawn; change both together.
   ============================================================================================== */
:root {
  /* Core palette: white page, near-black text, as on 米特製服部 (mitt.tw). */
  --ink: #171816; /* text, selected options, filled buttons; 17.8:1 on white */
  --paper: #fff; /* page and dialog background */
  --soft: #fff; /* = brand.themeColor (phone browser bar) */
  --header-bg: rgba(255, 255, 255, 0.96); /* see-through header over the page */
  --panel: #f4f3ef; /* rounded panels: opening card, garment card, story card, hovers */
  --footer-bg: #f5f5f1;
  --line: #deded7; /* dividers */
  --option-line: #c9c9c4; /* colour / size option borders */
  --muted: #60665d; /* secondary text: 5.9:1 on white, 5.3:1 on --panel */
  --on-ink: #fff; /* text on --ink: filled buttons, selected size, skip link, toast */
  --ink-hover: #3a3b38; /* filled button hover */
  --header-line: #e8e6de;
  --footer-line: #deded7;

  /* Home opening (proposal A, 阿邦 10/12): on the page here; a colour set can put it on a card
     (--hero-bg with --hero-inset), and phones keep or drop that card (--hero-card-bg-sm,
     --hero-card-pad-sm). Title 3:1 or more on its background (large text), the other text and the
     button text 4.5:1. */
  --hero-bg: transparent;
  --hero-inset: 0px;
  --hero-card-bg-sm: transparent;
  --hero-card-pad-sm: 0px;
  --hero-title: #171816; /* 17.8:1 on the page */
  --hero-text: #60665d; /* text, facts and photo caption, 5.9:1 */
  --hero-button-bg: #171816; /* filled pill: the page's main step */
  --hero-button-text: #fff;
  --hero-button-border: #171816;
  --hero-button-hover: #3a3b38; /* hover without the motion script; a dark button gets 米特's own fill
                                   (#39414b, mitt-motion.js) */
  --hero-button-hover-text: #fff;
  --hero-focus-ring: #171816;
  /* The opening photo box (width / height), where the shirt starts to fade into the page (keep it
     below the print; check-preview.cjs measures it) and how wide the fade is at each side. */
  --hero-window-ratio: 1.15;
  --hero-fade: 82%;
  --hero-fade-side: 7%;
  --hero-photo-max: 500px; /* the photo box at most this wide (阿邦 10/12「太大張了吧」, chose B) */

  /* Photo areas and labels on photos */
  --photo-bg: #f1f0ec; /* gallery and enlarged photo when the image manifest has no edge colours */
  --card-photo-bg: #f4f3ef; /* product cards */
  /* Product card photo areas for products with backdrop: 'ink' / 'accent' in store.js. A light
     shirt goes on the dark one, a dark shirt on the light one. */
  --backdrop-ink: #2e2e2c;
  --backdrop-accent: #e3e3de;
  --detail-bg: #f1f0ec; /* print close-up */
  --label-bg: rgba(255, 255, 255, 0.92); /* DESIGN / 01 on cards, caption on the opening photo */
  --disclosure-bg: rgba(255, 255, 255, 0.92); /* AI 商品示意 on the product photo */
  --hint-bg: rgba(255, 255, 255, 0.88); /* 點圖放大 on the product photo */
  --hint-text: #171816;
  --swatch-border: #a3a39c; /* colour choice dots */
  --dot-border: #b5b5ae; /* small colour dots on cards */

  /* Preview bar (top of every page) */
  --bar-bg: #171816;
  --bar-text: #fff;
  --bar-divider: rgba(255, 255, 255, 0.5);
  --bar-focus-ring: #fff;

  /* Brand story near the bottom of the home page: on this shop it sits on the page (阿邦 10/12); a
     colour set can give it a card with --dark-bg and an inner side padding (--story-inset). */
  --dark-bg: var(--paper);
  --story-inset: 0px;
  --dark-text: #171816;
  --dark-body: #60665d;
  --dark-muted: #60665d;
  --dark-link: #171816;
  --dark-line: #deded7;
  --dark-small: #60665d;
  --story-focus-ring: #171816;
  --story-button-bg: #171816; /* the closing button: filled, as the page's last step */
  --story-button-text: #fff;
  --story-button-hover: #3a3b38; /* hover without the motion script, or on a light button; a dark
                                    button gets 米特's own fill (#39414b, mitt-motion.js) */
  --story-button-hover-text: #fff;

  /* Garment facts in the home "fabric" section: rows on the page here; a colour set can put them on
     a card (--tag-bg with --tag-inset). */
  --tag-bg: var(--paper);
  --tag-inset: 0px;
  --tag-text: #171816; /* 16:1 on --tag-bg */
  --tag-muted: #60665d; /* the note beside each value, 5.9:1 */
  --tag-line: #deded7;
  --tag-label-bg: #171816; /* small garment-id label */
  --tag-label-text: #fff;

  /* Feedback, overlays */
  --error: #9c2525;
  --notice-bg: #fff; /* store-data problem box */
  --backdrop: rgba(20, 20, 18, 0.45);
  --shadow: rgba(0, 0, 0, 0.08);
  --loupe-edge: rgba(23, 24, 22, 0.22); /* the magnifier's fine ring, outside a white one: shows on white and black shirts */
  --loupe-shadow: rgba(0, 0, 0, 0.18);

  /* Focus ring: must reach 3:1 against the background it is drawn on */
  --focus-ring: var(--ink);
  --focus-width: 2px;
  --focus-offset: 5px;

  /* Type (as on 米特製服部): the system font stack, heavy titles with slightly tight tracking.
     Sizes are in rem so the reader's text-size setting applies. */
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans TC", "Microsoft JhengHei", sans-serif;
  --font-latin: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; /* eyebrows, prices, sizes */
  --weight-title: 750;
  --tracking-title: -0.035em; /* section, story and product titles */
  --tracking-display: -0.05em; /* the opening's big two-line title */
  --tracking-heading: -0.025em; /* section titles */
  --text-min: 0.75rem; /* 12px: smallest shopper-facing text at any width */
  /* 示意 notices, preview bar and purchase notes: as large as the body text around them. */
  --text-note: 0.875rem; /* 14px */
  --text-note-sm: 0.8125rem; /* 13px, up to 700px wide */

  /* Shape: pill buttons, softly rounded options, photos and panels (as on 米特製服部). */
  --radius-button: 999px; /* buttons, toast, round close buttons */
  --radius-control: 8px; /* colour / size options, quantity box */
  --radius-media: 12px; /* photos */
  --radius-card: 20px; /* opening card, garment card, story card */
  --radius-dialog: 16px;
  --radius-dot: 50%;

  /* Page spacing */
  --max: 1240px; /* content width */
  --max-wide: 1320px; /* content width from 1440px screens */
  --pad: 44px; /* side gutter */
  --pad-md: 30px; /* up to 1000px wide */
  --pad-sm: 20px; /* up to 700px wide */
  --header-h: 74px;
  --header-h-sm: 64px;
  --header-h-xs: 60px; /* up to 370px wide */
  --button-h: 52px;
  --button-h-sm: 48px;
  --target: 44px; /* smallest tap target: options, quantity buttons, 移除, close buttons, links */

  /* Logo box: any logo shape is scaled to fit inside it, never cropped. A square or tall logo
     needs a taller box (for example 44 x 44) to stay readable. */
  --logo-w: 96px;
  --logo-h: 38px;
  --logo-w-sm: 83px; /* header up to 700px wide */
  --logo-h-sm: 33px;
  --logo-w-footer-sm: 70px; /* footer up to 700px wide */
  --logo-h-footer-sm: 28px;

  /* Motion. Hover motion (nav pill, button fill, underlines) is 米特's shared motion in
     /uiux-motion/ with its own 500ms cubic-bezier(.3,1,.3,1). Everything else moves with these
     values: the MOTION rules at the end of this file and /motion/shop-motion.js read them.
     Personality A, "quiet confidence" (阿邦, 2026-09-29): presses answer at once; everything else
     moves a little slower and softer, over short distances, one lead at a time, never bouncing,
     and is still within 1.5s. The three scenes (opening, opening a product, adding to the bag) are
     storyboarded in output/20260929-jit-storyboard/. Base numbers measured on mtprint.com.tw;
     table in the abang-ui-craft skill, references/motion-system.md. Reduced motion stops it all. */
  --ease-soft: cubic-bezier(0.22, 1, 0.36, 1); /* personality A's one curve: moves at once, lands softly */
  --ease-primary: cubic-bezier(0.3, 1, 0.3, 1); /* 米特's hover curve */
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* drawers, phone sheets */
  --ease-lightbox: cubic-bezier(0.4, 0, 0.22, 1); /* enlarged photo */
  --hover-time: 0.5s; /* photo zoom, arrows, + turning */
  --press-time: 160ms; /* buttons shrink a little while pressed */
  --press-scale: 0.97;
  --reveal-time: 0.7s; /* a block fades in and rises this far when its first pixel shows */
  --reveal-y: 16px;
  --settle-time: 0.9s; /* the opening photo fades in and settles from a little larger */
  --settle-fade: 0.5s;
  --settle-scale: 1.03;
  /* ...and its print builds itself (hero.build; storyboard output/20261012-hero-animation/). This one
     scene may run to about 2s, past the 1.5s rule (阿邦 2026-10-12). */
  --build-start: 0.4s; /* the first block drops this long after the photo starts to fade in */
  --build-step: 0.07s; /* ...the next diagonal of blocks this much later */
  --build-time: 0.5s; /* ...each block drops into place in this long */
  --build-fade: 0.2s; /* ...and is solid after this much of it */
  --build-drop: 12px; /* ...from this far above */
  --build-rest: 0.08s; /* the block that steps out rests in line this long */
  --build-out-time: 0.7s; /* ...then turns and steps out of line to where it is printed */
  --build-name-delay: 0.1s; /* the name under the print fades in this long after that starts */
  --build-name-time: 0.5s;
  --build-exit: 0.25s; /* playing it again: the print fades away first */
  --build-gap: 0.1s; /* ...and the first blocks drop this much later */
  --build-swap: 0.3s; /* at the end the layer fades into the finished photo underneath */
  --replay-delay: 0.3s; /* the mouse rests on the photo this long before it plays again (one slide) */
  /* Several slides take turns (阿邦 10/12: 五款輪流演, never stopping, each staying 4s): */
  --build-hold: 4s; /* a finished slide stays this long, then the next comes in */
  --build-cross: 0.45s; /* ...its blank shirt fading in over the last photo */
  --intro-hold: 0.2s; /* 格子章 opening (hero build.intro): the big artwork, built, stays this long */
  --intro-zoom: 0.8s; /* ...then shrinks onto the shirt in this long, the blank shirt fading in (--build-cross) */
  --build-enter: 0.35s; /* ...and its first blocks dropping this long after that starts */
  --list-time: 0.7s; /* product cards arriving together follow one another */
  --list-y: 16px;
  --list-step: 0.12s;
  --list-time-sm: 0.6s; /* up to 700px wide */
  --list-y-sm: 12px;
  --list-step-sm: 0.08s;
  --image-fade: 0.5s; /* a photo fades in once loaded (mouse screens; phones show it at once) */
  --swipe-response: 0.5s; /* product photos: a swipe lands on a no-bounce spring this quick, keeping the finger's speed */
  --swipe-gap: 12px; /* ...between one product photo and the next while they move */
  --preview-delay: 120ms; /* the mouse rests on a card this long before it shows the next photo */
  /* The product photo's magnifier (阿邦 10/12, storyboard output/20261012-loupe/): a tool in the hand,
     so it comes and goes quickly and follows the cursor or the finger with no delay. */
  --loupe-delay: 200ms; /* the mouse rests (moving under 6px) this long before it shows; a click shows it at once */
  --loupe-hold: 300ms; /* a finger holds still this long; moving sooner is a swipe or a scroll */
  --loupe-in: 180ms; /* it appears from 0.94 */
  --loupe-out: 120ms; /* ...and goes to 0.96 */
  --loupe-sharpen: 200ms; /* the 4096 copy fades in over the photo standing in for it */
  --loupe-zoom: 2.5; /* times the photo as drawn (米特's product cards use 2.5 too) */
  --loupe-size: 0.36; /* of the photo's width with a mouse, 150-240px */
  --loupe-size-touch: 0.42; /* ...with a finger, 130-180px */
  --select-time: 0.35s; /* the size highlight slides to the chosen size */
  --select-lag: 0.1s; /* ...its trailing edge takes this much longer, so it stretches a little */
  --roll-time: 0.24s; /* a quantity rolls to its new number */
  --nudge: 4px; /* a quantity already at its limit nudges this far toward the pressed side */
  --nudge-time: 0.3s;
  --shake-time: 0.4s; /* a choice the shopper missed (colour, size) shakes this long, --nudge wide */
  --bag-fly-time: 0.7s; /* adding to the bag: a small photo flies into the bag icon */
  --bag-open-delay: 1.05s; /* ...then the bag opens */
  --highlight-time: 1.2s; /* ...where the line just added fades from grey to white */
  --drawer-time: 0.5s; /* cart drawer, phone sheets */
  --drawer-out-time: 0.35s;
  --modal-time: 0.4s; /* notes window */
  --modal-out-time: 0.2s;
  --lightbox-time: 0.333s; /* the photo flies from the page into the enlarged view and back */
  --photo-swap-time: 0.35s; /* the product photo cross-fades to another view or colour */
  --page-time: 0.2s; /* the old page fades out, the new one in */
  --photo-fly-time: 0.65s; /* a product photo flies from its card to the product page */
  --page-back-time: 0.15s; /* back to the list: the page fades quicker */
  --photo-back-time: 0.55s; /* ...and the photo flies back into its card */
  --radius-sheet: 20px; /* top corners of phone sheets */
}
/* ----------------------------------------------------------------------------------------------
   Colour sets. brand.theme in store.js picks one by name; without it the black-and-white set above
   applies. A set only overrides colours; keep brand.themeColor equal to its --soft.
   ---------------------------------------------------------------------------------------------- */
/* 海濤藍 #032A98 / 檸檬黃 #F8DF08 on a near-neutral off-white #F4F4EF (荷花白 #FBECDE read too
   peachy over a whole page). Contrast: blue on the page 10.6:1, yellow on blue 8.7:1, off-white on
   blue 10.6:1, blue on yellow 8.7:1. Yellow on the page is only 1.2:1, so yellow is never text on the
   page and off-white is never text on yellow: yellow is text only on blue (preview bar, opening
   block, story section, focus ring there), and a yellow block (button, tag card, card backdrop)
   carries blue text. */
:root[data-theme="blue-lemon"] {
  --ink: #032a98;
  --paper: #f4f4ef;
  --soft: #f4f4ef;
  --header-bg: rgba(244, 244, 239, 0.96);
  --line: rgba(3, 42, 152, 0.2);
  --muted: #4b5683; /* 6.4:1 on the page */
  --on-ink: #f4f4ef;
  --ink-hover: #021e6e;
  --header-line: rgba(3, 42, 152, 0.16);
  --footer-line: rgba(3, 42, 152, 0.16);

  --hero-bg: #032a98;
  --hero-inset: 36px;
  --hero-title: #f8df08; /* 8.7:1 */
  --hero-text: #f4f4ef; /* 10.6:1 */
  --hero-card-bg-sm: #032a98; /* phones keep the blue card */
  --hero-card-pad-sm: 20px;
  --hero-button-bg: #f8df08;
  --hero-button-text: #032a98; /* 8.7:1 */
  --hero-button-border: #f8df08;
  --hero-button-hover: #fff07a;
  --hero-button-hover-text: #032a98;
  --hero-focus-ring: #f8df08;
  --panel: #f4f4ef;
  --footer-bg: #f4f4ef;
  --option-line: rgba(3, 42, 152, 0.3);

  --backdrop-ink: #032a98;
  --backdrop-accent: #f8df08;
  --label-bg: rgba(244, 244, 239, 0.9);
  --disclosure-bg: rgba(244, 244, 239, 0.92);
  --hint-bg: rgba(244, 244, 239, 0.85);
  --hint-text: #032a98;
  --swatch-border: #8a93b8;
  --dot-border: #8a93b8;

  --bar-bg: #032a98;
  --bar-text: #f8df08;
  --bar-divider: rgba(248, 223, 8, 0.6);

  --dark-bg: #032a98;
  --story-inset: 48px;
  --dark-text: #f8df08;
  --dark-body: #f4f4ef;
  --dark-muted: #dcdcd3;
  --dark-link: #f8df08;
  --dark-line: rgba(248, 223, 8, 0.35);
  --dark-small: #dcdcd3;
  --story-button-bg: #f8df08;
  --story-button-text: #032a98; /* 8.7:1 */
  --story-button-hover: #fff07a;
  --story-button-hover-text: #032a98;

  --tag-bg: #f8df08;
  --tag-inset: 28px;
  --tag-text: #032a98; /* 8.7:1 */
  --tag-muted: #032a98;
  --tag-line: rgba(3, 42, 152, 0.4);
  --tag-label-bg: #032a98;
  --tag-label-text: #f8df08;

  --notice-bg: #fafaf7;
  --backdrop: rgba(2, 20, 70, 0.5);
  --shadow: rgba(3, 42, 152, 0.14);
  --bar-focus-ring: #f8df08;
  --story-focus-ring: #f8df08;
}
* {
  box-sizing: border-box;
}
html {
  color-scheme: light;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--paper);
  scroll-behavior: smooth;
  /* The header stays on top while scrolling; #section links stop below it. */
  scroll-padding-top: calc(var(--header-h) + 16px);
}
body {
  margin: 0;
}
/* While a window is open the page cannot scroll. On screens with a classic scrollbar its width
   (measured by app.js as --scrollbar-width) is kept as padding, so the page does not shift sideways. */
body:has(dialog[open]) {
  overflow: hidden;
  padding-right: var(--scrollbar-width, 0px);
}
button,
input,
a,
summary {
  -webkit-tap-highlight-color: transparent;
}
button,
input {
  font: inherit;
}
button,
a,
summary {
  touch-action: manipulation;
}
button {
  cursor: pointer;
}
button {
  color: inherit;
}
a {
  color: inherit;
  text-decoration: none;
}
button:disabled {
  cursor: not-allowed;
  opacity: 0.35;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
summary:focus-visible,
[tabindex="0"]:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}
.preview-bar {
  --focus-ring: var(--bar-focus-ring);
}
.story {
  --focus-ring: var(--story-focus-ring);
}
.hero {
  --focus-ring: var(--hero-focus-ring);
}
button,
a {
  transition:
    background 0.18s,
    color 0.18s,
    opacity 0.18s,
    transform var(--press-time) ease-out;
}
img {
  max-width: 100%;
  display: block;
}
figure,
h1,
h2,
h3,
p {
  margin: 0;
}
/* Long words or links in store.js wrap instead of widening the page. */
h1,
h2,
h3,
p,
li,
.cart-item-title {
  overflow-wrap: anywhere;
}
button {
  border: 0;
  background: transparent;
}
svg {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
}
/* Sections focused by the skip link and #section links: the view moves there, no ring needed. */
[tabindex="-1"]:focus {
  outline: 0;
}
/* Read by screen readers, not shown. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.page-width {
  max-width: var(--max);
  margin: 0 auto;
  padding-left: var(--pad);
  padding-right: var(--pad);
}
.eyebrow {
  font: 500 var(--text-min)/1.6 var(--font-latin);
  letter-spacing: 1.8px;
}
.micro {
  font-size: 0.75rem;
  line-height: 1.7;
  color: var(--muted);
}
.preview-bar {
  min-height: 28px;
  padding: 5px 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: var(--text-note);
  line-height: 1.5;
  letter-spacing: 0.6px;
  background: var(--bar-bg);
  color: var(--bar-text);
}
.preview-bar span {
  color: var(--bar-divider);
}
/* Stays on top while scrolling, slightly see-through (as on 米特製服部). */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--header-bg);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--header-line);
}
.header-inner {
  height: var(--header-h);
  max-width: 1440px;
  padding: 0 var(--pad);
  margin: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.brand-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--target);
  padding: 7px 0;
}
/* Any logo proportion fits the same box; no blend mode, so light logos stay visible. */
.logo {
  width: var(--logo-w);
  height: var(--logo-h);
  display: block;
  object-fit: contain;
  object-position: left center;
}
.desktop-nav {
  display: flex;
  align-items: center;
  gap: 36px;
  font-size: 0.9375rem;
  font-weight: 650;
}
/* 米特's motion (uiux-motion/mitt-motion.js) slides a black pill up behind the hovered or focused
   link; the pill reaches 10px past the text on each side, hence the wide gap and no side padding. */
.desktop-nav a {
  padding: 11px 0;
}
html:not([data-mitt-motion]) .desktop-nav a:hover {
  text-decoration: underline;
  text-underline-offset: 6px;
}
.header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  font-size: 1.875rem;
  font-weight: 300;
  padding: 8px;
}
.cart-toggle {
  gap: 7px;
}
.cart-toggle span {
  font-size: 0.75rem;
}
.menu-toggle {
  display: none;
}
.skip {
  position: fixed;
  z-index: 100;
  top: 8px;
  left: 8px;
  background: var(--ink);
  color: var(--on-ink);
  padding: 12px;
  transform: translateY(-160%);
}
.skip:focus {
  transform: none;
}
.skip:focus-visible {
  outline-color: var(--on-ink);
  outline-offset: -6px;
}
.button {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  min-height: var(--button-h);
  padding: 14px 24px;
  font-size: 1rem;
  font-weight: 650;
  border: 1px solid var(--ink);
  border-radius: var(--radius-button);
}
.button.primary {
  background: var(--ink);
  color: var(--on-ink);
}
/* Without the motion script only; with it, the fill rises from the bottom instead. */
html:not([data-mitt-motion]) .button.primary:hover {
  background: var(--ink-hover);
}
.button svg {
  width: 22px;
  height: 22px;
}
/* Press feedback (Emil Kowalski): whatever is pressed shrinks a little at once, so the page feels it. */
.button:active,
.icon-btn:active,
.zoom-close:active,
.quantity-control button:active,
.gallery-thumbs button:active,
.gallery-hint:active,
.card-color:active,
.version-option:active,
.color-option:active,
.size-chart tbody th button:active,
.size-options label:active {
  transform: scale(var(--press-scale));
}
/* The whole label shrinks, not the box inside it: the hidden radio stays on top of what shrinks. */
.color-option,
.size-options label {
  transition: transform var(--press-time) ease-out;
}
/* 米特's motion gives the buttons it fills its own transition list: keep its colour timing, add the press. */
html[data-mitt-motion="v1"] .button.mm-action {
  transition:
    color var(--mitt-motion-time) var(--mitt-motion-ease) 100ms,
    border-color var(--mitt-motion-time) var(--mitt-motion-ease),
    transform var(--press-time) ease-out;
}
.text-link {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  min-height: var(--target);
  padding: 10px 0;
  border-bottom: 1px solid currentColor;
  font-size: 0.875rem;
  line-height: 1.5;
}
.text-link svg {
  width: 20px;
  height: 20px;
}
.text-link:hover {
  opacity: 0.6;
}
/* Links the motion script underlines itself: its line replaces the border and the fade. */
html[data-mitt-motion="v1"] .text-link[data-mitt-underline] {
  border-bottom-color: transparent;
}
html[data-mitt-motion="v1"] .text-link[data-mitt-underline]:hover {
  opacity: 1;
}
/* Home opening (proposal A, 阿邦 2026-10-12): on the page, not on a card. Words on the left (brand
   line, a big two-line title and a short text; below them the filled button, a text link and three
   facts), one product photo on the right. Phones (see the 700px block) read title, text, photo,
   button. A colour set can still put it on a card (--hero-bg with --hero-inset).
   The photo box (.hero-window) is a window on the photo: hero.frame in store.js says how much of the
   photo's width shows and which point sits in the middle (--cam-*, written by app.js). The shirt then
   fades into the page below the print and at the sleeves. Without a frame the photo fills the box. */
.hero {
  padding-top: 44px;
  padding-bottom: 36px;
}
.hero-card {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  grid-template-rows: auto auto;
  column-gap: 40px;
  padding: var(--hero-inset);
  background: var(--hero-bg);
  color: var(--hero-text);
  border-radius: var(--radius-card);
}
.hero-head {
  grid-column: 1;
  grid-row: 1;
  align-self: end;
}
.hero-kicker {
  color: var(--hero-text);
  font: 600 var(--text-min) / 1.4 var(--font-latin);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.hero h1 {
  margin-top: 20px;
  color: var(--hero-title);
  font-size: 6.5rem; /* rem at every width, so it doubles with 200% text */
  line-height: 1.04;
  letter-spacing: var(--tracking-display);
  font-weight: var(--weight-title);
}
.hero-description {
  max-width: 22em;
  margin-top: 24px;
  font-size: 1.0625rem;
  line-height: 1.8;
}
.hero-sub {
  margin-top: 10px;
  color: var(--hero-title);
  font-size: 1.0625rem;
  font-weight: var(--weight-title);
  line-height: 1.5;
}
.hero-visual {
  grid-column: 2;
  grid-row: 1/3;
  align-self: center;
  justify-self: center;
  width: min(100%, var(--hero-photo-max));
  min-width: 0;
}
.hero-window {
  position: relative;
  aspect-ratio: var(--hero-window-ratio);
  overflow: hidden;
}
.hero-settle,
.hero-cam {
  position: absolute;
  inset: 0;
}
.hero-photo,
.hero-blank {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The blank shirt is only for the animation: shop-motion.js shows it while the print builds. */
.hero-blank {
  display: none;
}
.hero-visual[data-frame] .hero-window {
  -webkit-mask-image:
    linear-gradient(to bottom, #000 var(--hero-fade), transparent 99%),
    linear-gradient(to right, transparent, #000 var(--hero-fade-side), #000 calc(100% - var(--hero-fade-side)), transparent);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom, #000 var(--hero-fade), transparent 99%),
    linear-gradient(to right, transparent, #000 var(--hero-fade-side), #000 calc(100% - var(--hero-fade-side)), transparent);
  mask-composite: intersect;
}
/* A framed photo is drawn 1 / --cam-share as wide as its box, with its point (--cam-x, --cam-y) in
   the middle of the box. */
.hero-visual[data-frame] .hero-cam {
  inset: auto;
  width: calc(100% / var(--cam-share));
  aspect-ratio: var(--photo-ratio);
  left: calc((0.5 - var(--cam-x) / var(--cam-share)) * 100%);
  top: calc((0.5 - var(--cam-y) * var(--hero-window-ratio) / (var(--cam-share) * var(--photo-ratio))) * 100%);
}
.hero-visual[data-frame] :is(.hero-photo, .hero-blank) {
  position: absolute;
  inset: 0;
}
.hero-visual figcaption {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-height: 32px;
  margin-top: 6px;
  color: var(--hero-text);
  font: var(--text-note-sm) / 1.5 var(--font-latin); /* 13px: the name ends with 示意 */
  letter-spacing: 0.04em;
}
/* Plays the animation again (shown by shop-motion.js); a 44px target inside the 32px caption row. */
.hero-replay {
  display: inline-flex;
  align-items: center;
  min-height: var(--target);
  margin-block: -6px;
  padding: 0 2px;
  color: var(--hero-title);
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
/* A margin, not a gap: 米特's underline wraps the icon and the words in one span. */
.hero-replay svg {
  width: 14px;
  height: 14px;
  margin-right: 6px;
  vertical-align: -0.2em;
}
/* Several slides: the name of the one showing on a row of its own, whole (never cut: 示意 is at its
   end), then a row with a dot for each and the pause button, every one a full target (--target).
   shop-motion.js shows that row; its room is kept before, so nothing below moves. */
.hero-caption {
  min-width: 0;
}
.hero-visual figcaption:has(.hero-picker) {
  flex-wrap: wrap;
  gap: 0;
  min-height: calc(var(--text-note-sm) * 1.5 + var(--target));
  align-content: flex-start;
}
.hero-picker {
  display: flex;
  flex: 1 0 100%;
  align-items: center;
  /* the first dot under the name's first letter, the pause icon at the edge */
  margin: 0 calc((16px - var(--target)) / 2) 0 calc((6px - var(--target)) / 2);
}
.hero-dot,
.hero-pause {
  position: relative;
  width: var(--target);
  min-height: var(--target);
  padding: 0;
  color: var(--hero-title);
  cursor: pointer;
}
.hero-dot::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: var(--radius-dot);
  background: var(--dot-border);
}
.hero-dot[aria-current="true"]::before {
  background: currentColor;
  transform: scale(1.35);
}
.hero-pause {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
}
.hero-pause svg {
  width: 16px;
  height: 16px;
}
.hero-pause[aria-pressed="true"] .when-playing,
.hero-pause[aria-pressed="false"] .when-paused {
  display: none;
}
/* shop-motion.js shows these only where they do something (the display rules above would win over
   the hidden attribute) */
.hero-replay[hidden],
.hero-picker[hidden],
.hero-pause[hidden] {
  display: none;
}
.hero-details {
  grid-column: 1;
  grid-row: 2;
  align-self: start;
}
.hero-action {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
  margin-top: 32px;
}
/* Filled pill; the motion script fills it from the bottom with --mm-fill-color on hover. */
.hero-cta {
  --mm-fill-color: var(--hero-button-hover);
  --mm-hover-ink: var(--hero-button-hover-text);
  background: var(--hero-button-bg);
  border-color: var(--hero-button-border);
  color: var(--hero-button-text);
}
html:not([data-mitt-motion]) .hero-cta:hover {
  background: var(--hero-button-hover);
  border-color: var(--hero-button-hover);
  color: var(--hero-button-hover-text);
}
.hero-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin-top: 36px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  color: var(--hero-text);
  font: 500 0.8125rem / 1.4 var(--font-latin);
  letter-spacing: 0.04em;
}
.collection {
  padding-top: 52px;
  padding-bottom: 76px;
}
.section-heading {
  display: flex;
  justify-content: space-between;
  align-items: end;
  margin-bottom: 30px;
}
.section-heading h2 {
  font-size: 2.375rem;
  line-height: 1.35;
  letter-spacing: var(--tracking-heading);
  font-weight: var(--weight-title);
  margin-top: 9px;
  text-wrap: balance; /* a narrow screen splits the title evenly, never one character alone */
}
.section-index {
  font: 0.75rem var(--font-latin);
  letter-spacing: 1px;
  color: var(--muted);
  padding-bottom: 8px;
}
.product-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 32px;
}
/* Filter chips above the grid (全部／胸前大圖／左胸小印花): pills with the sliding ink highlight. */
.collection-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: -8px 0 28px;
  padding: 0;
  border: 0;
}
.collection-filter label {
  position: relative;
  cursor: pointer;
}
.collection-filter input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  z-index: 1;
  cursor: pointer;
}
.collection-filter span {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  min-height: var(--target);
  padding: 0 18px;
  border: 1px solid var(--option-line);
  border-radius: var(--radius-button);
  font-size: 0.875rem;
}
.collection-filter small {
  font: var(--text-min) var(--font-latin);
  opacity: 0.65;
}
.collection-filter input:checked + span {
  background: var(--ink);
  color: var(--on-ink);
  border-color: var(--ink);
}
.collection-filter label:has(input:focus-visible) {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 3px;
  border-radius: var(--radius-button);
}
.collection-filter .select-indicator {
  border-radius: var(--radius-button);
}
/* Phone: even columns instead of a ragged wrap; three chips share a row, four take two columns
   (--chip-cols, app.js collectionFilter). A column is never narrower than 6em, so larger text (or a
   very narrow phone) drops to fewer columns instead of squeezing the chips. */
@media (max-width: 700px) {
  .collection-filter {
    --chip-min: calc((100% - (var(--chip-cols, 2) - 1) * 8px) / var(--chip-cols, 2));
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(max(min(100%, 6em), var(--chip-min)), 1fr));
  }
  .collection-filter label > span {
    justify-content: center;
    width: 100%;
    padding: 0 8px;
  }
}
.product-card[hidden],
.grid-label[hidden] {
  display: none;
}
/* Grouped by kind of print (app.js productGrid, 阿邦 10/12): a label opens each group. In two columns
   the groups stand side by side, one column each filled top to bottom (--group-rows: the label and
   the largest group), so a design's two versions share a row and no row is left half empty. One
   group shown alone (the filter) lies in plain rows under its label. */
.product-grid.is-grouped {
  grid-auto-flow: column;
  grid-template-rows: repeat(var(--group-rows), auto);
}
.product-grid.is-grouped[data-filtered] {
  grid-auto-flow: row;
  grid-template-rows: none;
}
.product-grid.is-grouped[data-filtered] .grid-label {
  grid-column: 1 / -1;
}
.grid-label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: 12px;
  margin-bottom: -14px; /* nearer its own cards than the grid's row gap */
  border-bottom: 1px solid var(--line);
}
.grid-label h3 {
  font-size: 1.125rem;
  line-height: 1.4;
  letter-spacing: -0.01em;
  font-weight: var(--weight-title);
}
.grid-label p {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: var(--text-note);
  line-height: 1.5;
  color: var(--muted);
}
.grid-label-price {
  font-family: var(--font-latin);
  color: var(--ink);
}
/* Wide screens: a full collection reads as a shop grid; grouped, a row per group under a label that
   runs across it (name, note, price on one line). */
@media (min-width: 1100px) {
  .product-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 40px 24px;
  }
  .product-grid.is-grouped {
    grid-auto-flow: row;
    grid-template-rows: none;
    grid-template-columns: repeat(var(--group-cols), minmax(0, 1fr));
    column-gap: 20px;
  }
  .grid-label {
    grid-column: 1 / -1;
    flex-direction: row;
    align-items: baseline;
    gap: 16px;
  }
  .grid-label p {
    flex: 1;
    flex-direction: row;
    justify-content: space-between;
    gap: 16px;
  }
  .grid-label:not([hidden]) ~ .grid-label {
    margin-top: 32px;
  }
  .is-grouped .card-name {
    font-size: 1.25rem;
  }
}
.card-photo {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-media);
  background: var(--card-photo-bg);
}
.card-photo {
  transition: transform var(--press-time) ease-out;
}
/* A card's photos are one strip, like the product page's (shop-motion.js, data-swipe): a finger or
   the mouse swipes it sideways, up-and-down still scrolls the page; --swipe-index is the photo in
   place. */
.card-photo[data-swipe] {
  touch-action: pan-y pinch-zoom;
  user-select: none;
  -webkit-user-select: none;
}
.card-track {
  display: flex;
  gap: var(--swipe-gap);
  transform: translateX(calc(var(--swipe-index, 0) * (-100% - var(--swipe-gap))));
}
.card-slide {
  flex: 0 0 100%;
  min-width: 0;
  overflow: hidden;
  border-radius: var(--radius-media);
}
.card-slide img {
  display: block;
  width: 100%;
  aspect-ratio: 1.02;
  object-fit: cover;
  height: auto;
  pointer-events: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  transition:
    transform var(--hover-time) var(--ease-primary),
    opacity var(--image-fade) var(--ease-primary);
}
/* Mouse only: a finger's tap would leave the photo zoomed. */
@media (hover: hover) and (pointer: fine) {
  .product-card a:hover .card-slide img {
    transform: scale(1.05);
  }
}
.product-card a[data-swipe-preview] {
  -webkit-user-drag: none;
}
.card-photo[data-dragging] {
  cursor: grabbing;
}
/* Which photo shows: small dots just under the photo, on the page's white whatever the shirt's
   colour, the one shown darker. They take no room: the caption keeps its place. */
.card-dots {
  position: relative;
  top: 9px;
  height: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 6px;
  pointer-events: none;
}
.card-dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink);
  opacity: 0.2;
  transition: opacity var(--select-time) var(--ease-soft);
}
.card-dots i[data-on] {
  opacity: 0.75;
}
@media (max-width: 700px) {
  .card-dots {
    top: 8px;
    gap: 5px;
  }
}
.product-card a:active .card-photo {
  transform: scale(0.98);
}
/* backdrop: 'ink' / 'accent' in store.js, for a photo shot on that colour. */
.card-photo[data-backdrop="ink"] {
  background: var(--backdrop-ink);
}
.card-photo[data-backdrop="accent"] {
  background: var(--backdrop-accent);
}
/* The caption stacks: English name, name, price (the same at every width, so five narrow columns
   fit). The shirt name, the same on every card, waits for the product page. */
.card-caption {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 18px;
}
.card-caption .eyebrow {
  font-size: var(--text-min);
  letter-spacing: 1px;
  color: var(--muted);
}
.card-name {
  font-size: 1.375rem;
  line-height: 1.4;
  letter-spacing: -0.03em;
  font-weight: var(--weight-title);
  margin: 4px 0 0; /* h3 or h4 (under a group label) */
}
.card-caption > p {
  font: 0.9375rem var(--font-latin);
}
.card-meta {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--text-min);
  color: var(--muted);
  margin-top: 16px;
}
.tiny-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: var(--radius-dot);
  border: 1px solid var(--dot-border);
  background: var(--swatch);
}
.tiny-swatch.dark {
  border-color: var(--swatch);
}
/* A card's colour dots are buttons (app.js cardColors, 阿邦 10/13 黑白兩色都要秀出來): a 44px target
   around each dot, each dot at its target's left edge so the first lines up with the caption. The
   targets stay inside the card: they start right under the card's link and never reach left of it
   (a taller neighbour's link, 200% text, 10/13), the dots land about where they were and the row
   takes no more room below than before. The colour on show has a ring. */
.card-meta:has(.card-colors) {
  margin-top: 0;
}
/* The row is centred on the targets' 30px margin box, the dots sit 7px lower (at 22px of 44): so does
   the colour count (hidden on phones). */
.card-meta:has(.card-colors) > span:last-child {
  position: relative;
  top: 7px;
}
.card-colors {
  --dot: 14px;
  display: flex;
}
.card-color {
  display: grid;
  place-items: center start;
  width: 44px;
  height: 44px;
  margin-bottom: calc(var(--dot) / 2 - 21px); /* the row ends 1px under the dot */
  padding: 0;
  border: 0;
  border-radius: var(--radius-dot);
  background: none;
  color: inherit;
  cursor: pointer;
  transition: transform var(--press-time) ease-out;
}
.card-color .tiny-swatch {
  width: var(--dot);
  height: var(--dot);
  transition: box-shadow var(--press-time) ease-out;
}
.card-color[aria-pressed='true'] .tiny-swatch {
  box-shadow: 0 0 0 2px var(--paper), 0 0 0 3px var(--ink);
}
@media (hover: hover) and (pointer: fine) {
  .card-color[aria-pressed='false']:hover .tiny-swatch {
    box-shadow: 0 0 0 2px var(--paper), 0 0 0 3px var(--dot-border);
  }
}
.card-meta > span:last-child {
  margin-left: 4px;
}
.asset-note {
  font-size: var(--text-note);
  color: var(--muted);
  line-height: 1.8;
  margin-top: 26px;
}
/* "The tee" section (阿邦 10/12): heading, the garment facts and the size link on the left, the
   close-up photo (copy.fabric.image) on the right; without a photo the facts take the right column.
   Phones and tablets stack everything in source order. */
/* 穿上身 (app.js lookbookSection, 阿邦 10/13): worn photos, three a row (two on phones), each with the
   design's name and colour under it; the photo zooms a little under the mouse, like a card's. */
.lookbook {
  border-top: 1px solid var(--line);
  padding-top: 72px;
  padding-bottom: 96px;
}
.lookbook-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 40px 24px;
}
.look {
  display: block;
  color: inherit;
  text-decoration: none;
}
.look-photo {
  display: block;
  overflow: hidden;
  border-radius: var(--radius-media);
  background: var(--card-photo-bg);
  transition: transform var(--press-time) ease-out;
}
.look-photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: center 30%;
  transition:
    transform var(--hover-time) var(--ease-primary),
    opacity var(--image-fade) var(--ease-primary);
}
@media (hover: hover) and (pointer: fine) {
  .look:hover .look-photo img {
    transform: scale(1.05);
  }
}
.look:active .look-photo {
  transform: scale(0.98);
}
.look-caption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-top: 14px;
}
.look-name {
  font-size: 1rem;
  font-weight: var(--weight-title);
}
.look-color {
  font-size: var(--text-note);
  color: var(--muted);
}
@media (max-width: 700px) {
  .lookbook {
    padding-top: 52px;
    padding-bottom: 64px;
  }
  .lookbook-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 28px 12px;
  }
  .look-caption {
    flex-direction: column;
    gap: 2px;
    margin-top: 10px;
  }
  .look-name {
    font-size: 0.9375rem;
  }
}
.fabric {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "head tag"
    "link tag";
  column-gap: 64px;
  border-top: 1px solid var(--line);
  padding-top: 72px;
  padding-bottom: 96px;
}
.fabric.has-photo {
  grid-template-rows: auto auto 1fr;
  grid-template-areas:
    "head photo"
    "tag photo"
    "link photo";
}
.fabric-heading {
  grid-area: head;
}
.fabric-heading h2 {
  font-size: 2.375rem;
  line-height: 1.35;
  margin-top: 16px;
  letter-spacing: var(--tracking-heading);
  font-weight: var(--weight-title);
}
.fabric > .text-link {
  grid-area: link;
  justify-self: start;
  align-self: start;
  margin-top: 22px;
}
.fabric-photo {
  grid-area: photo;
  align-self: start;
}
.fabric-photo img {
  width: 100%;
  height: auto;
  aspect-ratio: 4/3;
  object-fit: cover;
  border-radius: var(--radius-media);
}
.fabric-photo figcaption {
  font-size: var(--text-min);
  line-height: 1.6;
  color: var(--muted);
  padding-top: 10px;
}
.garment-tag {
  grid-area: tag;
  align-self: center;
  background: var(--tag-bg);
  color: var(--tag-text);
  border-radius: var(--radius-card);
  padding: var(--tag-inset);
}
.fabric.has-photo .garment-tag {
  align-self: start;
  margin-top: 32px;
}
.tag-label {
  display: inline-block;
  background: var(--tag-label-bg);
  color: var(--tag-label-text);
  font: 700 var(--text-min)/1.5 var(--font-latin);
  letter-spacing: 1px;
  padding: 4px 12px;
  border-radius: var(--radius-button);
}
/* One fact a row: the label and big value on the left, its note beside them. The rows share one
   value column (subgrid), as wide as the widest value, so every note starts at the same line. */
.fabric-facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: 24px;
  margin-top: 18px;
}
.fabric-facts > div {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  row-gap: 2px;
  align-items: center;
  padding: 18px 0;
  border-top: 1px solid var(--tag-line);
}
.fabric-facts > div:last-child {
  border-bottom: 1px solid var(--tag-line);
}
.fact-index {
  grid-column: 1;
  align-self: end;
  font: var(--text-min) var(--font-latin);
  letter-spacing: 1px;
  color: var(--tag-muted);
}
.fabric-facts h3 {
  grid-column: 1;
  grid-row: 2;
  font-size: 1.625rem;
  line-height: 1.3;
  letter-spacing: -0.02em;
  font-weight: var(--weight-title);
}
.fabric-facts h3 > span {
  font: 0.875rem var(--font-latin);
}
.fabric-facts p {
  grid-column: 2;
  grid-row: 1 / 3;
  font-size: 0.875rem;
  line-height: 1.8;
  color: var(--tag-muted);
  word-break: keep-all; /* breaks at spaces and punctuation, not inside 纖維 */
}
/* Brand story: the page's closing statement, a big title, one line and the button back to the
   collection. On this shop it sits on the page; a colour set can give it a card (--dark-bg,
   --story-inset). */
/* 一件 T 怎麼做出來 (app.js makingSection, 阿邦 10/13): numbered steps, each a 4:3 close-up, its
   number, a short title and a few lines; three across, one under another on phones. */
.making {
  border-top: 1px solid var(--line);
  padding-top: 72px;
  padding-bottom: 96px;
}
.making-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 40px 24px;
}
.making-photo {
  overflow: hidden;
  border-radius: var(--radius-media);
  background: var(--detail-bg);
  margin-bottom: 20px;
}
.making-photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: opacity var(--image-fade) var(--ease-primary);
}
.making-step h3 {
  font-size: 1.375rem;
  line-height: 1.4;
  font-weight: var(--weight-title);
  letter-spacing: var(--tracking-heading);
  margin-top: 6px;
}
.making-step p {
  margin-top: 10px;
  font-size: 0.9375rem;
  line-height: 1.9;
  color: var(--muted);
  max-width: 30em;
}
.making-note {
  margin-top: 40px;
  font-size: 1.0625rem;
  line-height: 1.8;
  font-weight: var(--weight-title);
}
@media (max-width: 700px) {
  .making {
    padding-top: 52px;
    padding-bottom: 64px;
  }
  .making-steps {
    grid-template-columns: minmax(0, 1fr);
    gap: 36px;
  }
  .making-note {
    margin-top: 32px;
    font-size: 1rem;
  }
}
.story {
  padding: 0 var(--pad) 104px;
}
.story-inner.page-width {
  max-width: calc(var(--max) - 2 * var(--pad));
  padding: max(24px, var(--story-inset)) var(--story-inset) var(--story-inset);
  background: var(--dark-bg);
  color: var(--dark-text);
  border-radius: var(--radius-card);
}
.story .eyebrow {
  color: var(--dark-muted);
}
.story h2 {
  font-size: 4rem;
  line-height: 1.2;
  letter-spacing: var(--tracking-title);
  font-weight: var(--weight-title);
  margin-top: 18px;
  text-wrap: balance;
}
/* Paragraphs after the first (story.text as a list, 阿邦 10/13): closer to the one above. */
.story-inner > p:not(.eyebrow) + p:not(.eyebrow) {
  margin-top: 12px;
}
.story-inner > p:not(.eyebrow) {
  font-size: 1.0625rem;
  line-height: 1.9;
  color: var(--dark-body);
  max-width: 34em;
  margin-top: 22px;
  /* Even lines that break at the punctuation, never one character alone at the end (a line too
     long to fit still breaks: overflow-wrap anywhere). */
  text-wrap: balance;
  word-break: keep-all;
}
/* Filled, as the page's last step; the motion script fills it from the bottom on hover. */
.story-cta {
  margin-top: 34px;
  background: var(--story-button-bg);
  color: var(--story-button-text);
  border-color: var(--story-button-bg);
  --mm-fill-color: var(--story-button-hover);
  --mm-hover-ink: var(--story-button-hover-text);
}
html:not([data-mitt-motion]) .story-cta:hover {
  background: var(--story-button-hover);
  border-color: var(--story-button-hover);
  color: var(--story-button-hover-text);
}
.story-bottom {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  border-top: 1px solid var(--dark-line);
  padding-top: 22px;
  margin-top: 62px;
  font: var(--text-min) var(--font-latin);
  letter-spacing: 1.5px;
  color: var(--dark-small);
}
.story-bottom > span:last-child {
  font-family: inherit;
  font-size: var(--text-min);
  letter-spacing: 0.5px;
}
footer {
  background: var(--footer-bg);
}
.footer-inner {
  max-width: var(--max);
  padding: 56px var(--pad) 32px;
  margin: auto;
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 30px;
}
.footer-inner p {
  font-size: 0.75rem;
  margin-top: 14px;
  color: var(--muted);
}
/* Social links (footer) and contact links (聯絡 dialog); only rendered when store.js lists some. */
.brand-links {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0 18px;
  margin: 8px 0 0;
  padding: 0;
  font-size: 0.75rem;
}
.brand-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  text-decoration: underline;
  text-underline-offset: 3px;
}
#info-content .brand-links {
  font-size: 0.9375rem;
  margin-top: 12px;
}
.footer-inner nav {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px 48px;
  font-size: 0.8125rem;
}
.footer-inner nav > * {
  display: flex;
  align-items: center;
  min-height: var(--target);
  padding: 10px 0;
  text-align: left;
}
.footer-bottom {
  max-width: var(--max);
  margin: auto;
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding: 20px var(--pad);
  border-top: 1px solid var(--footer-line);
  font: var(--text-min) var(--font-latin);
  letter-spacing: 1.2px;
  color: var(--muted);
}
.product-page {
  padding-top: 20px;
  padding-bottom: 56px;
}
.back-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--target);
  gap: 9px;
  font-size: 0.8125rem;
  margin-bottom: 18px;
}
.back-link svg {
  width: 19px;
}
.product-layout {
  display: grid;
  grid-template-columns: 1.14fr 0.86fr;
  gap: 72px;
  align-items: start;
}
/* The photo stays in view below the header while the details beside it scroll (as on mtprint). */
@media (min-width: 701px) {
  .gallery {
    position: sticky;
    top: calc(var(--header-h) + 16px);
  }
}
/* --band-*: the edge colours of a photo (set by app.js), for the empty sides of a whole photo. */
.gallery-slide,
.zoom-stage {
  background: linear-gradient(
    var(--band-direction, to right),
    var(--band-start, var(--photo-bg)) 50%,
    var(--band-end, var(--photo-bg)) 50%
  );
}
/* The product photos are one strip: a finger or the mouse swipes it sideways (shop-motion.js,
   data-swipe); up-and-down still scrolls the page. --swipe-index (app.js) is the photo in place. */
.gallery-main {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-media);
  background: var(--paper);
  touch-action: pan-y pinch-zoom;
  user-select: none;
  -webkit-user-select: none;
}
.gallery-track {
  display: flex;
  gap: var(--swipe-gap);
  width: 100%;
  transform: translateX(calc(var(--swipe-index, 0) * (-100% - var(--swipe-gap))));
}
.gallery-slide {
  position: relative;
  flex: 0 0 100%;
  min-width: 0;
  overflow: hidden;
  border-radius: var(--radius-media);
}
/* A photo the chosen colour does not have (a worn photo in one colour only, app.js hasView) leaves
   the strip and the thumbnails until a colour that has it is chosen. */
.gallery-slide[hidden],
.gallery-thumbs button[hidden] {
  display: none;
}
.gallery-zoom {
  position: relative;
  width: 100%;
  display: block;
  padding: 0;
  overflow: hidden;
}
/* The photo box clips anything outside it, so this ring is drawn inside, above the photo, with a
   light edge so it shows on dark and light shirts alike. */
.gallery-zoom:focus-visible {
  outline: 0;
}
.gallery-zoom:focus-visible::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 6px;
  border: var(--focus-width) solid var(--focus-ring);
  box-shadow:
    0 0 0 2px var(--paper),
    inset 0 0 0 2px var(--paper);
  pointer-events: none;
}
.gallery-slide img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 0.94;
  object-fit: cover;
  pointer-events: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}
.gallery-slide[data-view='detail'] img {
  transform: scale(1.8);
}
/* A left-chest print (store.js print: 'chest'): the close-up goes to the chest — the wearer's left,
   the photo's right. */
[data-print='chest'] .gallery-slide[data-view='detail'] img {
  transform: scale(2.4);
  transform-origin: 64% 32%;
}
/* A back print (print: 'back', 阿邦 10/13 後背款): the close-up enlarges the back photo, centred on the
   upper back where every back print sits (their middles at 34–42% of the photo's height). */
[data-print='back'] .gallery-slide[data-view='detail'] img {
  transform-origin: 50% 20%;
}
.gallery-slide[data-view='lifestyle'] img {
  object-fit: cover;
  object-position: center;
}
.image-disclosure {
  position: absolute;
  bottom: 14px;
  left: 16px;
  font-size: var(--text-note);
  letter-spacing: 0.5px;
  background: var(--disclosure-bg);
  padding: 6px 12px;
  border-radius: var(--radius-button);
  pointer-events: none;
}
/* 看大圖: opens the enlarged photo. A click or tap on the photo itself is the magnifier now (阿邦
   10/12); the pill stays small and a clear frame around it makes the tap target 44px. */
.gallery-hint {
  position: absolute;
  bottom: 17px;
  right: 14px;
  font-size: var(--text-min);
  line-height: 1.5;
  color: var(--hint-text);
  background: var(--hint-bg);
  padding: 4px 10px;
  border: 0;
  border-radius: var(--radius-button);
  transition: transform var(--press-time) ease-out;
}
.gallery-hint::before {
  content: "";
  position: absolute;
  inset: -10px -8px;
}
/* the ink ring sits outside a white one, so it shows over a black shirt and a white one */
.gallery-hint:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--paper);
}
/* Thumbnails under the photo (as on 米特's product cards): every one in its true colour (a faded black
   shirt would read as grey) inside a hairline; the one shown gets an ink ring. */
.gallery-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 14px;
}
.gallery-thumbs button {
  position: relative;
  width: 64px;
  height: 64px;
  padding: 0;
  overflow: hidden;
  border-radius: var(--radius-control);
  background: var(--photo-bg);
  box-shadow: inset 0 0 0 1px var(--line);
  transition:
    box-shadow var(--select-time) var(--ease-soft),
    transform var(--press-time) ease;
}
.gallery-thumbs button::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--line);
  pointer-events: none;
}
.gallery-thumbs img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.gallery-thumbs [data-gallery='detail'] img {
  transform: scale(2);
}
[data-print='chest'] ~ .gallery-thumbs [data-gallery='detail'] img {
  transform: scale(2.6);
  transform-origin: 64% 32%;
}
[data-print='back'] ~ .gallery-thumbs [data-gallery='detail'] img {
  transform-origin: 50% 23%;
}
.gallery-thumbs [data-gallery='wornFront'] img,
.gallery-thumbs [data-gallery='wornBack'] img {
  object-position: center 20%;
}
@media (hover: hover) and (pointer: fine) {
  .gallery-thumbs button:hover::after {
    box-shadow: inset 0 0 0 1px var(--option-line);
  }
}
.gallery-main[data-dragging] .gallery-zoom {
  cursor: grabbing;
}
.gallery-thumbs .is-active {
  box-shadow:
    0 0 0 2px var(--paper),
    0 0 0 3px var(--ink);
}
.gallery-thumbs button:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 4px;
}
.product-info {
  padding-top: 22px;
  max-width: 430px;
}
.product-info > .eyebrow {
  color: var(--muted);
}
.product-info h1 {
  font-size: 2.5rem;
  line-height: 1.35;
  margin-top: 15px;
  letter-spacing: var(--tracking-title);
  font-weight: var(--weight-title);
}
.product-info h1 span {
  display: block;
  font-size: 0.875rem;
  font-weight: 400;
  letter-spacing: 0;
  margin-top: 7px;
}
.product-lead {
  font-size: 0.875rem;
  line-height: 1.9;
  color: var(--muted);
  margin-top: 20px;
}
.price-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 25px 0 26px;
  padding-bottom: 23px;
  border-bottom: 1px solid var(--line);
}
.price-row strong {
  font: 1.5rem var(--font-latin);
}
.price-row > span {
  font-size: var(--text-note);
  color: var(--muted);
}
/* 印法 (app.js versionSwitch, 阿邦 10/13): links to a picture's other versions at the right of the
   price row, drawn like the colour options; the version shown has the selected border. The row wraps
   when the text is large. */
.price-row {
  flex-wrap: wrap;
}
.version-switch {
  display: flex;
  gap: 8px;
  margin-left: auto;
}
.version-option {
  display: flex;
  gap: 8px;
  align-items: center;
  min-height: var(--target);
  border: 1px solid var(--option-line);
  border-radius: var(--radius-control);
  padding: 0 14px;
  font-size: 0.8125rem;
  color: inherit;
  text-decoration: none;
  transition:
    border-color 0.18s,
    transform var(--press-time) ease-out;
}
.version-option[aria-current='page'] {
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}
@media (hover: hover) and (pointer: fine) {
  .version-option:not([aria-current]):hover {
    border-color: var(--ink);
  }
}
/* 你可能也喜歡 (app.js relatedSection): the home page's cards, four across, two on phones. */
.related {
  border-top: 1px solid var(--line);
  margin-top: 56px;
  padding-top: 56px;
}
fieldset {
  margin: 0 0 20px;
  padding: 0;
  border: 0;
  min-width: 0;
  position: relative;
}
legend {
  font-size: 0.875rem;
  margin-bottom: 12px;
  padding: 0;
}
#selected-color {
  font-size: 0.75rem;
  color: var(--muted);
  margin-left: 15px;
}
/* 尺寸 legend: label on the left, 尺寸說明 on the right, in normal flow so larger text cannot overlap. */
#size-field legend {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
}
.color-options {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.color-option {
  display: flex;
  gap: 8px;
  align-items: center;
  min-height: var(--target);
  border: 1px solid var(--option-line);
  border-radius: var(--radius-control);
  padding: 8px 12px;
  font-size: 0.8125rem;
  position: relative;
  cursor: pointer;
}
.color-option:has(input:checked) {
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}
.swatch {
  background: var(--swatch);
  border: 1px solid var(--swatch-border);
  border-radius: var(--radius-dot);
  width: 16px;
  height: 16px;
}
.color-option input,
.size-options input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
/* The size's radio covers its whole (invisible) box, above the text: a press lands on the radio
   itself. A 1px radio could end up under its own text once #product-form measures its width
   (container-type) — the text then took the press. */
.size-options input {
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  z-index: 1;
  cursor: pointer;
}
.color-option:has(input:focus-visible),
.size-options label:has(input:focus-visible) {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: 4px;
}
/* Negative margins keep the 44px tap area without making the legend row taller. */
.size-help {
  flex: none;
  gap: 0.3em;
  min-height: var(--target);
  margin: -12px 0;
  padding: 0;
  font-size: 0.75rem;
  border: 0;
}
.size-options {
  display: grid;
  /* One row (app.js sets --size-cols, 5 to 8); two rows below where 44px buttons fit (see
     @container below). */
  grid-template-columns: repeat(var(--size-cols, 5), 1fr);
  gap: 8px;
}
.size-options label {
  display: block;
  position: relative;
  cursor: pointer;
}
/* The form measures itself: 8 sizes in one row need 8 x 44 + 7 x 8 = 408px for 44px buttons. */
#product-form {
  container-type: inline-size;
}
@container (max-width: 407.98px) {
  .size-options {
    grid-template-columns: repeat(var(--size-cols-narrow, var(--size-cols, 5)), 1fr);
  }
}
.size-options span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  border: 1px solid var(--option-line);
  border-radius: var(--radius-control);
  font: 0.875rem var(--font-latin);
}
.size-options input:checked + span {
  background: var(--ink);
  color: var(--on-ink);
  border-color: var(--ink);
}
.field-error {
  font-size: 0.75rem;
  color: var(--error);
  line-height: 1.5;
  margin-top: 7px;
}
.field-error:empty {
  display: none;
}
.quantity-line {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.875rem;
  margin: 24px 0;
}
.quantity-control {
  display: flex;
  align-items: center;
  border: 1px solid var(--option-line);
  border-radius: var(--radius-control);
  height: calc(var(--target) + 2px);
}
.quantity-control button {
  min-width: var(--target);
  min-height: var(--target);
  font-size: 1.125rem;
}
.quantity-control input {
  width: 48px;
  min-width: 0;
  border: 0;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  text-align: center;
  background: transparent;
  height: var(--target);
  /* 16px or more: iOS Safari zooms the whole page into smaller inputs. */
  font: 1rem var(--font-latin);
  -moz-appearance: textfield;
}
.quantity-control input::-webkit-outer-spin-button,
.quantity-control input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.add-button {
  width: 100%;
}
.purchase-note {
  font-size: var(--text-note);
  color: var(--muted);
  margin: 11px 0 0;
  line-height: 1.6;
  text-align: center;
}
.delivery-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  border-top: 1px solid var(--line);
  width: 100%;
  text-align: left;
  margin-top: 23px;
  padding: 19px 0;
  font-size: 0.75rem;
}
.delivery-link svg {
  width: 18px;
}
.product-story {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 72px;
  align-items: center;
  border-top: 1px solid var(--line);
  padding-top: 60px;
  margin-top: 66px;
}
.product-story h2 {
  font-size: 1.875rem;
  margin: 16px 0 23px;
}
.product-story p:not(.eyebrow):not(.asset-note) {
  font-size: 0.9375rem;
  line-height: 2;
  color: var(--muted);
  max-width: 400px;
}
.print-detail {
  overflow: hidden;
  border-radius: var(--radius-media);
  aspect-ratio: 1.45;
  background: var(--detail-bg);
}
.print-detail img {
  height: 100%;
  width: 100%;
  object-fit: cover;
  transform: scale(1.6);
}
.print-detail.is-photo img {
  transform: none;
}
/* The story figure goes to the print too (阿邦 10/13: 左胸款、後背款), not the middle of the shirt. */
.print-detail[data-print='chest'] img {
  transform: scale(2.6);
  transform-origin: 67% 6%;
}
.print-detail[data-print='back'] img {
  transform-origin: 50% 0;
}
.product-bottom {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 72px;
  padding-top: 60px;
  margin-top: 55px;
  border-top: 1px solid var(--line);
}
.product-bottom h2 {
  font-size: 1.875rem;
  margin: 15px 0 20px;
}
.product-bottom > div > p:last-child {
  font-size: 0.875rem;
  line-height: 2;
  color: var(--muted);
}
details {
  border-bottom: 1px solid var(--line);
}
details:first-child {
  border-top: 1px solid var(--line);
}
summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 58px;
  padding: 15px 0;
  font-size: 0.875rem;
}
summary::-webkit-details-marker {
  display: none;
}
summary span {
  font: 1.25rem var(--font-latin);
  transition: transform var(--hover-time) var(--ease-primary);
}
details[open] summary span {
  transform: rotate(45deg);
}
details p {
  font-size: 0.875rem;
  line-height: 1.9;
  color: var(--muted);
  padding: 0 0 22px;
}
/* The answer rises in while its row opens (the row's height moves in the MOTION rules). */
details[open] p {
  animation: motion-rise 0.3s var(--ease-primary) 0.08s both;
}
.bottom-back {
  margin-top: 45px;
  margin-bottom: 0;
}
dialog {
  border: 0;
  padding: 0;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 18px 80px var(--shadow);
}
dialog::backdrop {
  background: var(--backdrop);
}
/* The enlarged photo covers the whole screen in white: no dark layer fading in under it. */
.zoom-dialog::backdrop {
  background: none;
}
/* The grab bar on phone sheets (see the 700px block); drag it, or the heading, down to close. */
.sheet-handle {
  display: none;
}
.dialog-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 24px 28px;
  border-bottom: 1px solid var(--line);
}
.dialog-heading h2 {
  font-size: 1.3125rem;
  font-weight: 600;
  letter-spacing: -0.4px;
}
.dialog-heading h2 span {
  font: 0.9375rem var(--font-latin);
  color: var(--muted);
  margin-left: 6px;
}
.cart-dialog {
  position: fixed;
  inset: 0 0 0 auto;
  margin: 0;
  width: min(480px, 100vw);
  max-width: 100vw;
  max-height: 100dvh;
  height: 100dvh;
}
.cart-dialog .dialog-heading {
  position: sticky;
  top: 0;
  background: var(--paper);
  z-index: 2;
}
#cart-content {
  padding: 0 28px 28px;
}
.empty-cart {
  padding-top: 65px;
  text-align: center;
}
.empty-cart h3 {
  font-size: 1.5625rem;
  margin-top: 20px;
}
.empty-cart > p:not(.eyebrow) {
  font-size: 0.875rem;
  line-height: 1.9;
  color: var(--muted);
  margin: 18px 0 30px;
}
.empty-cart .button {
  width: 100%;
}
.cart-item {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 20px;
  padding: 25px 0;
  border-bottom: 1px solid var(--line);
}
.cart-item > a img {
  width: 110px;
  height: 145px;
  object-fit: cover;
}
.cart-item-title {
  display: inline-flex;
  align-items: center;
  min-width: var(--target);
  min-height: var(--target);
  font-size: 1.125rem;
  font-weight: 600;
}
.cart-item-info p {
  font-size: 0.75rem;
  color: var(--muted);
  margin: 7px 0 12px;
}
.cart-item-info > strong {
  font: 0.9375rem var(--font-latin);
}
.cart-item-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 15px;
}
.quantity-control.small button {
  font-size: 1rem;
}
.quantity-control .cart-qty {
  width: 28px;
  text-align: center;
  font: 0.8125rem var(--font-latin);
}
.remove-item {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  min-width: var(--target);
  min-height: var(--target);
  padding: 0;
  font-size: 0.75rem;
  text-decoration: underline;
}
.cart-summary {
  padding-top: 28px;
}
.cart-summary > div {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.cart-summary strong {
  font: 1.4375rem var(--font-latin);
}
.cart-summary > p {
  font-size: var(--text-note);
  color: var(--muted);
  margin-top: 12px;
}
.cart-summary > .button {
  margin-top: 25px;
  width: 100%;
}
.cart-summary .cart-safety {
  text-align: center;
  line-height: 1.8;
}
.info-dialog {
  border-radius: var(--radius-dialog);
  width: min(580px, calc(100vw - 36px));
  max-height: calc(100dvh - 48px);
  margin: auto;
}
.info-dialog .dialog-heading {
  padding: 16px 24px;
}
.info-dialog .dialog-heading h2 {
  font-size: 1.25rem;
}
#info-content {
  padding: 24px;
  font-size: 0.9375rem;
  line-height: 1.9;
}
#info-content > p {
  color: var(--muted);
}
.order-preview {
  list-style: none;
  padding: 0;
  margin: 25px 0;
}
.order-preview li {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 7px 20px;
  font-size: 0.875rem;
  border-bottom: 1px solid var(--line);
  padding: 15px 0;
}
.order-preview li > span:first-of-type {
  grid-column: 1;
  color: var(--muted);
  font-size: 0.75rem;
}
.order-preview li > span:last-child {
  grid-column: 2;
  grid-row: 1/3;
  align-self: center;
  font: 0.9375rem var(--font-latin);
}
.preview-total {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 25px;
  font-size: 0.8125rem;
}
.preview-total strong {
  font: 1.25rem var(--font-latin);
}
#info-content > .button {
  width: 100%;
}
#info-content > .order-next {
  font-size: 0.875rem;
  line-height: 1.8;
  margin-bottom: 22px;
}
/* Enlarged product photo over the whole screen; app.js (fitZoom) picks fit or fill-and-scroll. */
.zoom-dialog {
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
}
.zoom-dialog[open] {
  display: flex;
  flex-direction: column;
}
.zoom-bar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 16px 10px 24px;
  border-bottom: 1px solid var(--line);
}
.zoom-bar h2 {
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.4;
}
.zoom-note {
  font-size: var(--text-note);
  line-height: 1.5;
  color: var(--muted);
}
.zoom-close {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: var(--target);
  min-height: var(--target);
  padding: 0 12px;
  border: 1px solid var(--ink);
  border-radius: var(--radius-button);
  font-size: 0.9375rem;
  font-weight: 600;
}
.zoom-close:hover {
  background: var(--soft);
}
.zoom-close svg {
  width: 20px;
  height: 20px;
}
.zoom-stage {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow: auto;
}
/* margin: auto centres the photo and still lets a larger (filled) photo scroll to every edge. */
.zoom-stage img {
  flex: none;
  margin: auto;
  max-width: none;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.zoom-stage:focus-visible {
  outline-offset: -6px;
}
.menu-dialog {
  position: fixed;
  inset: 0 0 0 auto;
  margin: 0;
  width: min(420px, 100vw);
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  padding-bottom: 30px;
}
.mobile-links {
  padding: 24px 28px 45px;
  display: flex;
  flex-direction: column;
}
.mobile-links a {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  font-size: 1.375rem;
  border-bottom: 1px solid var(--line);
  padding: 24px 0;
  transition: background-color 180ms ease;
}
/* Press feedback on phones, as 米特's menu. */
.mobile-links a:active {
  background: var(--panel);
}
.menu-dialog > .micro {
  padding-left: 28px;
  font: var(--text-min) var(--font-latin);
  letter-spacing: 1.5px;
}
.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translate(-50%, 20px);
  opacity: 0;
  pointer-events: none;
  background: var(--ink);
  color: var(--on-ink);
  border-radius: var(--radius-button);
  font-size: 0.8125rem;
  padding: 13px 20px;
  z-index: 100;
  transition:
    opacity 0.2s,
    transform 0.2s;
  max-width: calc(100vw - 40px);
  width: max-content;
  box-shadow: 0 3px 15px var(--shadow);
}
.toast.visible {
  opacity: 1;
  transform: translate(-50%, 0);
}
/* ------------------------------------------------------------------------------------------------
   Info sections (版型與尺寸, 材質與洗滌方式, 配送與售後): size finder and chart, care, shipping.
   Drawn in the product page's accordion and in the info window (app.js infoContent).
   ------------------------------------------------------------------------------------------------ */
.info-note {
  margin-top: 12px;
  color: var(--muted);
  font-size: var(--text-note);
  line-height: 1.7;
}
.size-guide {
  display: grid;
  gap: 20px;
  margin-top: 18px;
}
.size-finder {
  display: grid;
  gap: 6px;
  padding: 18px 20px 16px;
  border-radius: var(--radius-card);
  background: var(--panel);
}
.finder-title {
  margin-bottom: 4px;
  font-weight: 700;
}
.finder-field {
  display: grid;
  grid-template-columns: 2.5em minmax(0, 1fr) 4.5em;
  align-items: center;
  gap: 12px;
  min-height: var(--target);
}
.finder-field input[type='range'] {
  width: 100%;
  height: var(--target);
  margin: 0;
  accent-color: var(--ink);
  cursor: pointer;
}
.finder-field output {
  text-align: right;
  font: 0.875rem / 1 var(--font-latin);
  font-variant-numeric: tabular-nums;
}
.finder-result {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.finder-result > span {
  color: var(--muted);
  font-size: var(--text-note);
}
.finder-result strong {
  display: inline-block;
  min-width: 2.2em;
  font: var(--weight-title) 1.75rem / 1 var(--font-latin);
}
.finder-pick {
  justify-content: center;
  gap: 0.35em;
  margin-left: auto;
}
.fabric-points,
.ship-returns,
.care-list,
.ship-methods,
.ship-steps {
  margin-left: 0;
  padding: 0;
  list-style: none;
}
.size-chart-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(150px, 220px);
  align-items: center;
  gap: 20px;
}
.size-chart {
  width: 100%;
  border-collapse: collapse;
  font: 0.875rem / 1 var(--font-latin);
  font-variant-numeric: tabular-nums;
}
.size-chart caption {
  padding-bottom: 8px;
  text-align: left;
  color: var(--muted);
  font: var(--text-note) var(--font-body);
}
.size-chart th,
.size-chart td {
  height: var(--target);
  padding: 0 4px;
  text-align: center;
  border-bottom: 1px solid var(--line);
}
.size-chart thead th {
  color: var(--muted);
  font-family: var(--font-body);
  font-weight: 500;
}
.size-chart tbody th {
  font-weight: 700;
}
.size-chart tbody th button {
  width: 100%;
  min-height: var(--target);
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
/* The size picked in the form, in the same ink as its size button. */
.size-chart tr[data-chosen] > * {
  background: var(--ink);
  color: var(--on-ink);
}
.size-chart tr[data-recommended] th::after {
  content: '建議';
  display: block;
  margin-top: 2px;
  font: 0.625rem / 1 var(--font-body);
  color: var(--muted);
}
.size-chart tr[data-recommended][data-chosen] th::after {
  color: var(--on-ink);
}
.size-chart tr[data-recommended] th button {
  min-height: 0;
}
.measure-diagram {
  width: 100%;
  height: auto;
}
.measure-diagram .tee {
  fill: var(--panel);
  stroke: var(--option-line);
  stroke-width: 1.2;
}
.measure-diagram .measure {
  opacity: 0.7;
}
.measure-diagram .measure path {
  fill: none;
  stroke: var(--muted);
  stroke-width: 1.4;
  stroke-dasharray: 3 3;
  stroke-linecap: round;
}
.measure-diagram .measure text {
  fill: var(--muted);
  font: 12px var(--font-body);
}
/* Pointing at a column lights the column and its line on the drawing. */
.size-guide:has([data-measure='length']:hover) td[data-measure='length'],
.size-guide:has([data-measure='chest']:hover) td[data-measure='chest'],
.size-guide:has([data-measure='shoulder']:hover) td[data-measure='shoulder'],
.size-guide:has([data-measure='sleeve']:hover) td[data-measure='sleeve'] {
  background: var(--panel);
}
/* (the line) */
.size-guide:has([data-measure='length']:hover) .measure[data-measure='length'],
.size-guide:has([data-measure='chest']:hover) .measure[data-measure='chest'],
.size-guide:has([data-measure='shoulder']:hover) .measure[data-measure='shoulder'],
.size-guide:has([data-measure='sleeve']:hover) .measure[data-measure='sleeve'] {
  opacity: 1;
}
.size-guide:has([data-measure='length']:hover) .measure[data-measure='length'] path,
.size-guide:has([data-measure='chest']:hover) .measure[data-measure='chest'] path,
.size-guide:has([data-measure='shoulder']:hover) .measure[data-measure='shoulder'] path,
.size-guide:has([data-measure='sleeve']:hover) .measure[data-measure='sleeve'] path {
  stroke: var(--ink);
  stroke-dasharray: none;
}
.fabric-points,
.ship-returns {
  display: grid;
  gap: 8px;
  margin-top: 16px;
}
.fabric-points li,
.ship-returns li {
  position: relative;
  padding-left: 16px;
  line-height: 1.7;
}
.fabric-points li::before,
.ship-returns li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 0.8em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink);
}
.care-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
  gap: 18px 12px;
  margin-top: 22px;
}
.care-list li {
  display: grid;
  justify-items: center;
  gap: 8px;
  text-align: center;
  color: var(--muted);
  font-size: var(--text-note);
  line-height: 1.5;
}
.care-list svg {
  width: 34px;
  height: 34px;
  color: var(--ink);
}
.ship-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin-top: 20px;
}
.ship-steps li {
  position: relative;
  display: grid;
  align-content: start;
  gap: 6px;
  padding-top: 46px;
}
.ship-steps li > span:last-child {
  color: var(--muted);
  font-size: var(--text-note);
  line-height: 1.6;
}
.step-no {
  position: absolute;
  top: 0;
  left: 0;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  font: 0.75rem / 1 var(--font-latin);
}
.ship-steps li:not(:last-child)::after {
  content: '';
  position: absolute;
  top: 17px;
  left: 44px;
  right: -8px;
  height: 1px;
  background: var(--line);
}
.ship-methods {
  margin-top: 22px;
  border-top: 1px solid var(--line);
}
.ship-methods li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: var(--target);
  border-bottom: 1px solid var(--line);
}
.ship-methods strong {
  font: 0.875rem var(--font-latin);
  font-variant-numeric: tabular-nums;
}
.ship-free {
  color: var(--muted);
}
@media (max-width: 700px) {
  .size-chart-wrap {
    grid-template-columns: minmax(0, 1fr);
  }
  .measure-diagram {
    max-width: 240px;
    justify-self: center;
    order: -1;
  }
  .ship-steps {
    grid-template-columns: minmax(0, 1fr);
    gap: 18px;
  }
  .ship-steps li {
    padding: 4px 0 0 48px;
    min-height: 34px;
  }
  .ship-steps li:not(:last-child)::after {
    top: 40px;
    left: 17px;
    right: auto;
    width: 1px;
    height: calc(100% - 26px);
  }
}

.empty-page {
  min-height: 65vh;
  padding-top: 90px;
}
.empty-page h1 {
  font-size: 2rem;
  margin: 20px 0 35px;
}
.no-script {
  padding: 30px;
}
/* Store-owner notice for mistakes in store.js (see validate.js). */
.data-problems {
  padding-top: 24px;
}
.data-problems-box {
  border: 1px solid var(--error);
  border-left-width: 4px;
  background: var(--notice-bg);
  padding: 18px 20px;
  font-size: 0.875rem;
  line-height: 1.8;
}
.data-problems h2 {
  font-size: 1.125rem;
  line-height: 1.5;
  color: var(--error);
}
.data-problems p {
  margin-top: 6px;
  color: var(--muted);
}
.data-problems ul {
  margin: 10px 0 0;
  padding-left: 20px;
}
.data-problems li {
  overflow-wrap: anywhere;
}
@keyframes motion-rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}
@media (min-width: 1440px) {
  :root {
    --max: var(--max-wide);
  }
  .hero h1 {
    font-size: 7rem;
  }
}
@media (max-width: 1000px) {
  :root {
    --pad: var(--pad-md);
  }
  .hero-card {
    column-gap: 28px;
  }
  .hero h1 {
    font-size: 5rem;
  }
  .section-heading h2,
  .fabric-heading h2 {
    font-size: 2rem;
  }
  .story h2 {
    font-size: 3rem;
  }
  .fabric {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    grid-template-areas:
      "head"
      "tag"
      "link";
    row-gap: 28px;
    padding-top: 56px;
    padding-bottom: 72px;
  }
  .fabric.has-photo {
    grid-template-rows: none;
    grid-template-areas:
      "head"
      "photo"
      "tag"
      "link";
    row-gap: 28px;
  }
  .fabric.has-photo .garment-tag {
    margin-top: 0;
  }
  .fabric > .text-link {
    margin-top: 0;
  }
  .fabric-heading h2 br {
    display: none;
  }
  .product-layout {
    gap: 38px;
  }
  .product-info {
    padding-top: 4px;
  }
  .product-info h1 {
    font-size: 2rem;
  }
  .product-lead {
    font-size: 0.8125rem;
  }
  .product-story,
  .product-bottom {
    gap: 40px;
  }
  .color-option {
    padding: 8px;
  }
  .product-grid {
    gap: 22px;
  }
}
@media (max-width: 700px) {
  :root {
    --pad: var(--pad-sm);
    --header-h: var(--header-h-sm);
    --button-h: var(--button-h-sm);
    --text-note: var(--text-note-sm);
    --list-time: var(--list-time-sm);
    --list-y: var(--list-y-sm);
    --list-step: var(--list-step-sm);
  }
  .preview-bar {
    font-size: var(--text-note);
    letter-spacing: 0;
    min-height: 27px;
    padding-top: 4px;
    padding-bottom: 4px;
    gap: 6px;
  }
  .logo {
    width: var(--logo-w-sm);
    height: var(--logo-h-sm);
  }
  .desktop-nav {
    display: none;
  }
  /* Icon with a small 選單 label under it, as on 米特製服部. */
  .menu-toggle {
    display: inline-flex;
    flex-direction: column;
    gap: 2px;
  }
  .menu-toggle svg {
    width: 22px;
    height: 22px;
  }
  .menu-label {
    font-size: var(--text-min);
    font-weight: 600;
    line-height: 1;
  }
  .header-actions {
    gap: 4px;
  }
  /* Phones: no card (unless a colour set keeps one); title, short text, photo and a full-width
     button. The title keeps its authored line breaks; the longer text, the text link and the facts
     wait for wider screens so the button stays in the first screen. */
  .hero {
    padding-top: 26px;
    padding-bottom: 0;
  }
  .hero-card {
    display: block;
    padding: var(--hero-card-pad-sm);
    background: var(--hero-card-bg-sm);
  }
  .hero h1 {
    margin-top: 12px;
    font-size: 4rem;
  }
  .hero-description {
    margin-top: 12px;
    font-size: 0.9375rem;
    line-height: 1.75;
  }
  .hero-more,
  .hero-link,
  .hero-facts {
    display: none;
  }
  .hero-visual {
    margin-top: 16px;
  }
  .hero-visual figcaption {
    margin-top: 4px;
  }
  .hero-action {
    margin-top: 10px;
  }
  .hero-cta {
    width: 100%;
    min-height: 50px;
  }
  .button {
    padding: 12px 22px;
    font-size: 0.9375rem;
  }
  .collection {
    padding-top: 31px;
    padding-bottom: 39px;
  }
  .section-heading {
    margin-bottom: 21px;
    gap: 8px;
  }
  .section-heading h2 {
    font-size: 1.6875rem;
    margin-top: 8px;
  }
  .section-heading .eyebrow {
    font-size: var(--text-min);
    letter-spacing: 1.3px;
  }
  .section-index {
    font-size: var(--text-min);
    white-space: nowrap;
  }
  /* Two cards per row on phones: ten designs would otherwise be a long scroll. The whole card is the
     link; the colour count waits for the product page. */
  .product-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 28px 12px;
  }
  .grid-label h3 {
    font-size: 1rem;
  }
  .grid-label p {
    font-size: 0.8125rem;
  }
  .card-meta > span:last-child {
    display: none;
  }
  .card-caption {
    margin-top: 16px;
  }
  .card-meta {
    font-size: var(--text-min);
    gap: 5px;
    margin-top: 11px;
    flex-wrap: wrap;
  }
  .tiny-swatch {
    width: 10px;
    height: 10px;
  }
  .card-colors {
    --dot: 12px;
  }
  .asset-note {
    font-size: var(--text-note);
    margin-top: 21px;
  }
  .fabric,
  .fabric.has-photo {
    row-gap: 22px;
    padding-top: 40px;
    padding-bottom: 56px;
  }
  .fabric-heading > .eyebrow {
    font-size: var(--text-min);
    letter-spacing: 1px;
  }
  .fabric-heading h2 {
    font-size: 1.6875rem;
    margin-top: 12px;
  }
  .fabric-heading h2 br {
    display: none;
  }
  .garment-tag {
    padding: calc(var(--tag-inset) * 0.75);
  }
  /* Phones: label, value, then the note under it, so no column gets too narrow to read. */
  .fabric-facts {
    grid-template-columns: minmax(0, 1fr);
    margin-top: 12px;
  }
  .fabric-facts > div {
    row-gap: 4px;
    padding: 16px 0;
  }
  .fabric-facts h3 {
    font-size: 1.375rem;
  }
  .fabric-facts p {
    grid-column: 1;
    grid-row: 3;
    font-size: 0.8125rem;
    line-height: 1.75;
  }
  .fabric-facts h3 > span {
    font-size: var(--text-min);
  }
  .story {
    padding-bottom: 56px;
  }
  .story-inner.page-width {
    padding: max(16px, calc(var(--story-inset) * 0.75)) calc(var(--story-inset) * 0.45)
      calc(var(--story-inset) * 0.45);
  }
  .story h2 {
    font-size: 2.25rem;
    margin-top: 14px;
  }
  .story-inner > p:not(.eyebrow) {
    font-size: 0.9375rem;
    line-height: 1.95;
    margin-top: 16px;
  }
  .story-cta {
    width: 100%;
    margin-top: 26px;
  }
  .story-bottom {
    margin-top: 35px;
    padding-top: 18px;
    font-size: var(--text-min);
    letter-spacing: 0.8px;
  }
  .story-bottom > span:last-child {
    font-size: var(--text-min);
  }
  .story .eyebrow {
    font-size: var(--text-min);
  }
  .footer-inner {
    padding-top: 30px;
    padding-bottom: 28px;
    gap: 24px;
    align-items: start;
  }
  .footer-inner nav {
    font-size: 0.75rem;
    gap: 6px 20px;
  }
  .footer-inner nav > * {
    padding: 9px 0;
  }
  .footer-inner p {
    font-size: var(--text-min);
    line-height: 1.8;
    margin-top: 10px;
  }
  .footer-bottom {
    font-size: var(--text-min);
    letter-spacing: 0.7px;
    padding-top: 17px;
    padding-bottom: 19px;
  }
  .footer-inner .logo {
    width: var(--logo-w-footer-sm);
    height: var(--logo-h-footer-sm);
  }
  .product-page {
    padding-top: 6px;
    padding-bottom: 30px;
  }
  .back-link {
    margin-bottom: 2px;
    font-size: 0.75rem;
  }
  .product-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 23px;
  }
  /* A product photo is 4:5 with the shirt in its middle square: 1.25 fills the box's height with
     the shirt. Photos of people show whole. */
  .gallery-slide img {
    height: 256px;
    aspect-ratio: auto;
    object-fit: contain;
    transform: scale(1.25);
  }
  .gallery-slide[data-view='detail'] img {
    transform: scale(1.9);
  }
  .gallery-slide[data-view='lifestyle'] img {
    object-fit: cover;
    transform: none;
  }
  .gallery-slide[data-view='wornFront'] img,
  .gallery-slide[data-view='wornBack'] img {
    transform: none;
  }
  .image-disclosure {
    left: 9px;
    bottom: 9px;
    font-size: var(--text-note);
    padding: 4px 5px;
  }
  .gallery-hint {
    right: 8px;
    bottom: 8px;
    font-size: var(--text-min);
  }
  .gallery-thumbs {
    gap: 8px;
    margin-top: 10px;
  }
  .gallery-thumbs button {
    width: 44px;
    height: 44px;
    border-radius: 6px;
  }
  .product-info {
    max-width: none;
    padding: 0;
  }
  .product-info > .eyebrow {
    font-size: var(--text-min);
    letter-spacing: 1.1px;
  }
  .product-info h1 {
    display: flex;
    align-items: baseline;
    gap: 14px;
    font-size: 1.8125rem;
    margin-top: 8px;
    line-height: 1.3;
  }
  .product-info h1 span {
    font-size: 0.75rem;
    margin: 0;
  }
  .product-lead {
    font-size: 0.75rem;
    line-height: 1.7;
    margin-top: 8px;
  }
  .price-row {
    margin: 16px 0 14px;
    padding-bottom: 16px;
  }
  .price-row strong {
    font-size: 1.375rem;
  }
  .price-row > span {
    font-size: var(--text-note);
  }
  legend {
    font-size: 0.8125rem;
    margin-bottom: 10px;
  }
  #selected-color {
    font-size: var(--text-min);
  }
  fieldset {
    margin-bottom: 18px;
  }
  .color-option {
    font-size: 0.75rem;
    padding: 8px 14px;
  }
  .color-options {
    gap: 8px;
  }
  .swatch {
    width: 14px;
    height: 14px;
  }
  .size-help {
    font-size: 0.8125rem;
  }
  .size-options {
    gap: 7px;
  }
  .size-options span {
    min-height: 44px;
    font-size: 0.8125rem;
  }
  .quantity-line {
    margin: 20px 0;
    font-size: 0.8125rem;
  }
  .purchase-note {
    font-size: var(--text-note);
  }
  .delivery-link {
    margin-top: 19px;
    font-size: 0.75rem;
    padding: 14px 0;
  }
  .product-story {
    grid-template-columns: 1fr;
    gap: 26px;
    margin-top: 30px;
    padding-top: 32px;
  }
  .product-story h2 {
    font-size: 1.5rem;
    margin: 13px 0 16px;
  }
  .product-story p:not(.eyebrow):not(.asset-note) {
    font-size: 0.875rem;
  }
  .print-detail {
    aspect-ratio: 1.35;
  }
  .product-bottom {
    grid-template-columns: 1fr;
    gap: 25px;
    margin-top: 32px;
    padding-top: 30px;
  }
  .product-bottom h2 {
    font-size: 1.5625rem;
    margin-top: 12px;
    margin-bottom: 13px;
  }
  .product-bottom > div > p:last-child {
    font-size: 0.8125rem;
  }
  .product-story .eyebrow,
  .product-bottom .eyebrow {
    font-size: var(--text-min);
    letter-spacing: 1.2px;
  }
  .bottom-back {
    margin-top: 28px;
  }
  .dialog-heading {
    padding: 17px 20px;
  }
  .dialog-heading h2 {
    font-size: 1.25rem;
  }
  #cart-content {
    padding: 0 20px 24px;
  }
  .cart-item {
    grid-template-columns: 94px 1fr;
    gap: 15px;
    padding: 23px 0;
  }
  .cart-item > a img {
    width: 94px;
    height: 128px;
  }
  .cart-item-info > strong {
    font-size: 0.875rem;
  }
  .cart-item-controls {
    margin-top: 10px;
  }
  .remove-item {
    font-size: var(--text-min);
  }
  .info-dialog .dialog-heading {
    padding: 14px 20px;
  }
  .info-dialog .dialog-heading h2 {
    font-size: 1.125rem;
  }
  #info-content {
    padding: 20px;
    font-size: 0.875rem;
  }
  /* The menu and the notes rise from the bottom edge as sheets (as on mtprint). Dragging the bar or
     the heading down past a third, or flicking it, closes them (/motion/shop-motion.js). */
  .menu-dialog,
  .info-dialog {
    inset: auto 0 0;
    width: 100%;
    max-width: 100%;
    height: auto;
    max-height: calc(100dvh - 60px);
    margin: 0;
    padding-bottom: max(16px, env(safe-area-inset-bottom));
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    overscroll-behavior: contain;
  }
  .sheet-handle {
    display: block;
    position: relative;
    width: 40px;
    height: 5px;
    margin: 8px auto 0;
    border-radius: var(--radius-button);
    background: var(--option-line);
    touch-action: none;
  }
  /* A larger area to grab than the bar itself. */
  .sheet-handle::before {
    content: "";
    position: absolute;
    inset: -8px -60px -4px;
  }
  [data-sheet] .dialog-heading {
    padding-top: 10px;
    touch-action: none;
  }
  /* A long press on the grip must not start text selection or the iOS callout (it would cancel the
     drag); a sheet pulled up past its resting place shows its own colour below, not the page. */
  [data-sheet-drag] {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
  [data-sheet] {
    box-shadow:
      0 18px 80px var(--shadow),
      0 120px 0 var(--paper);
  }
  .mobile-links {
    padding-bottom: 24px;
  }
  .zoom-bar {
    padding: 8px 12px 8px 20px;
  }
  .zoom-bar h2 {
    font-size: 1rem;
  }
  .toast {
    bottom: 18px;
    font-size: 0.75rem;
  }
  .empty-page h1 {
    font-size: 1.625rem;
  }
}
@media (max-width: 700px) {
  .product-layout {
    gap: 16px;
  }
  .product-info > .eyebrow,
  .product-lead {
    display: none;
  }
  .product-info h1 {
    margin-top: 0;
  }
  #product-form {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 12px;
  }
  #product-form fieldset,
  #product-form .purchase-note {
    grid-column: 1/-1;
  }
  #product-form fieldset {
    margin-bottom: 8px;
  }
  .quantity-line {
    margin: 0;
    align-self: center;
  }
  .quantity-line > span {
    display: none;
  }
  .quantity-line .quantity-control {
    height: 48px;
  }
  .quantity-line .quantity-control button {
    min-height: 46px;
  }
  .quantity-line .quantity-control input {
    width: 48px;
    height: 46px;
  }
  .add-button {
    gap: 8px;
    padding: 12px 14px;
    min-width: 0;
    align-self: center;
  }
  .purchase-note {
    margin-top: 12px;
  }
}
@media (max-width: 370px) {
  .hero h1 {
    font-size: 3.625rem;
  }
  :root {
    --header-h: var(--header-h-xs);
  }
  /* Keeps 加入購物袋 in the first screen on 360 x 800 phones now that notes and options are larger. */
  .price-row {
    margin: 14px 0 12px;
    padding-bottom: 14px;
  }
  #product-form fieldset {
    margin-bottom: 8px;
  }
  .section-heading h2 {
    font-size: 1.5rem;
  }
  .collection-filter span {
    font-size: 0.8125rem; /* three chips in one row on a 320px screen */
  }
  .card-meta > span:last-child {
    margin-left: 0;
  }
  .gallery-slide img {
    height: 232px;
  }
  .cart-item-controls {
    gap: 4px;
  }
  .footer-inner nav {
    gap: 6px 14px;
  }
}
/* ==============================================================================================
   MOTION: the shop's own motion layer, with the Motion tokens at the top. The states come from
   /motion/shop-motion.js (data-reveal-state, data-img-state, data-flying, data-dragging,
   data-closing="drag") and app.js (data-closing="" while a window plays its closing animation,
   data-closing="reverse" while a window closed during its opening plays that backwards,
   view-transition names while the page changes).
   Without the script, or with reduced motion, nothing waits hidden and nothing moves.
   ============================================================================================== */
.site-header {
  view-transition-name: site-header;
}
/* A page change from an open window only cross-fades (app.js): the header drawn on its own would
   show above the window. */
[data-page-change-plain] .site-header {
  view-transition-name: none;
}
/* Holds the loading line of a photo that fades in. */
[data-img-fade] {
  position: relative;
}
/* The opening photo's print building itself (hero.build): while it plays, a layer over the photo
   holds the blank shirt and each block of the print, cut out of the photo itself. shop-motion.js
   adds it, and takes it away when the play ends. */
.build-layer,
.build-print,
.build-piece,
.build-piece img {
  position: absolute;
  inset: 0;
}
/* Drawn exactly like the photo underneath. No will-change: a layer kept from the start would stay
   drawn at the size of that moment (the photo is still settling) and blur against the photo. */
.build-layer .hero-blank,
.build-piece img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
}
/* 格子章 opening (hero build.intro, 阿邦 10/13): the layer is the page's own white until the shirt comes
   in, so the photo's print never shows through the artwork's cut-out letters; the artwork's blocks sit
   in a box at the print's place on the photo (shop-motion.js scales the box up first). */
.build-layer[data-intro] {
  background: var(--paper); /* the page's own colour (html; --hero-bg is transparent when the opening has no card) */
}
.build-art {
  position: absolute;
  transform-origin: 50% 50%;
}
/* The artwork loading with the page (never drawn itself; the blocks show it). */
.build-art-source {
  display: none;
}
/* ...and over them the photo cut to that box (its own print), which comes in as the box lands; cut 2px
   wider all round, so the print's edge pixels are the photo's too (around it is the same shirt). */
.build-art-photo {
  position: absolute;
  inset: 0;
  clip-path: inset(-2px);
}
.build-art-photo img {
  position: absolute;
  display: block;
  max-width: none;
}
[data-build-ready] .hero-window {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent; /* a tap plays the print again: no blue flash over the photo on phones */
}
/* The dot of the slide showing grows a little; the dots and the pause button shrink when pressed,
   like every button. */
.hero-dot::before {
  transition:
    transform var(--hover-time) var(--ease-soft),
    background-color var(--hover-time) var(--ease-soft);
}
.hero-dot,
.hero-pause {
  transition: transform var(--press-time) ease-out;
}
.hero-dot:active,
.hero-pause:active {
  transform: scale(var(--press-scale));
}
@media (prefers-reduced-motion: no-preference) {
  /* Scroll reveals: a block waits hidden until its first pixel shows, then fades in while rising a
     little (personality A: 16px, 0.7s, the soft curve). Cards in a list that show together follow
     one another (data-reveal-list). Keyframes, not transitions, so a button keeps its own
     transitions (米特's fill, the press) while it rises. The animation ends on the element's own
     style; shop-motion.js then marks it done. */
  [data-reveal-state="pending"] {
    opacity: 0;
    transform: var(--reveal-from, translateY(var(--reveal-y)));
  }
  /* The keyboard moving into a block that still waits shows it at once, focus ring included. */
  [data-reveal-state="pending"]:focus-within {
    opacity: 1;
    transform: none;
  }
  [data-reveal-state="in"] {
    animation: motion-reveal var(--reveal-dur, var(--reveal-time)) var(--ease-soft)
      var(--reveal-delay, 0ms) both;
  }
  [data-reveal-list] > * {
    --reveal-from: translateY(var(--list-y));
    --reveal-dur: var(--list-time);
  }
  /* The opening (storyboard scene 1): photo first, then the brand line, the title line by line, the
     text, and the button last. Delays are set in app.js (data-reveal-delay). */
  .hero-line {
    display: inline-block;
  }
  .hero-kicker {
    --reveal-from: none;
  }
  .hero-sub,
  .hero-description {
    --reveal-y: 12px;
    --reveal-dur: 0.6s;
  }
  .hero-action,
  .hero-facts {
    --reveal-y: 8px;
    --reveal-dur: 0.6s;
  }
  /* The opening photo: its box stays, the photo (everything in [data-settle]: the blank shirt and the
     print while it builds, too) fades in and settles from a little larger inside the box; the caption
     fades in with it. Phones only fade it. */
  [data-reveal="settle"][data-reveal-state="pending"] {
    opacity: 1;
    transform: none;
  }
  [data-reveal="settle"][data-reveal-state="in"] {
    animation: none;
  }
  [data-reveal="settle"][data-reveal-state="pending"] [data-settle] {
    opacity: 0;
    transform: scale(var(--settle-scale));
  }
  [data-reveal="settle"][data-reveal-state="in"] [data-settle] {
    animation:
      motion-fade-in var(--settle-fade) var(--ease-soft) var(--reveal-delay, 0ms) both,
      motion-settle var(--settle-time) var(--ease-soft) var(--reveal-delay, 0ms) both;
  }
  [data-reveal="settle"][data-reveal-state="pending"] figcaption {
    opacity: 0;
  }
  [data-reveal="settle"][data-reveal-state="in"] figcaption {
    animation: motion-fade-in var(--settle-fade) var(--ease-soft) var(--reveal-delay, 0ms) both;
  }
  @media (max-width: 700px) {
    [data-reveal="settle"][data-reveal-state="pending"] [data-settle] {
      transform: none;
    }
    [data-reveal="settle"][data-reveal-state="in"] [data-settle] {
      animation: motion-fade-in var(--settle-fade) var(--ease-soft) var(--reveal-delay, 0ms) both;
    }
  }
  /* A product page (storyboard scene 2): the name and price wait until the flying photo is nearly
     down, then the rows of choices follow one another. */
  .product-info > [data-reveal] {
    --reveal-y: 12px;
    --reveal-dur: 0.6s;
  }
  #product-form [data-reveal] {
    --reveal-y: 8px;
    --reveal-dur: 0.4s;
  }

  /* A photo still loading shows a thin moving line; mouse screens fade the photo in when it
     arrives, phones show it at once (as on mtprint). */
  [data-img-state="loading"]::after {
    content: "";
    position: absolute;
    left: calc(50% - 24px);
    top: calc(50% - 1px);
    width: 48px;
    height: 2px;
    background: currentColor;
    opacity: 0.3;
    transform-origin: left;
    animation: motion-loading 1.2s ease-in-out infinite;
  }
  .fabric-photo img,
  .print-detail img {
    transition: opacity var(--image-fade) var(--ease-primary);
  }
  @media (hover: hover) and (pointer: fine) {
    [data-img-state="loading"] img {
      opacity: 0;
    }
  }

  /* Answers open and close smoothly where the browser can animate to auto height (Chrome 131+,
     Edge); elsewhere they open at once. */
  @supports (interpolate-size: allow-keywords) and selector(::details-content) {
    .accordion {
      interpolate-size: allow-keywords;
    }
    .accordion details::details-content {
      block-size: 0;
      overflow-y: clip;
      transition:
        block-size 0.25s var(--ease-primary),
        content-visibility 0.25s allow-discrete;
    }
    .accordion details[open]::details-content {
      block-size: auto;
      transition-duration: 0.3s;
    }
  }

  /* Windows. They open with an animation; app.js adds data-closing, waits for the closing one and
     then closes. The cart slides in from the right and back out the same way; the notes window grows
     from just smaller; on phones the menu and the notes rise from the bottom edge. */
  dialog[open] {
    animation: motion-fade-in var(--modal-time) var(--ease-primary);
  }
  dialog[open]::backdrop {
    animation: motion-fade-in var(--drawer-time) ease;
  }
  dialog[data-closing=""] {
    animation: motion-fade-out var(--modal-out-time) ease-out forwards;
  }
  dialog:is([data-closing=""], [data-closing="drag"])::backdrop {
    animation: motion-fade-out var(--drawer-out-time) ease forwards;
  }
  .cart-dialog[open],
  .menu-dialog[open] {
    animation: motion-drawer-in var(--drawer-time) var(--ease-drawer);
  }
  .cart-dialog[data-closing=""],
  .menu-dialog[data-closing=""] {
    animation: motion-drawer-out var(--drawer-out-time) var(--ease-drawer) forwards;
  }
  .info-dialog[open] {
    animation: motion-modal-in var(--modal-time) var(--ease-primary);
  }
  .info-dialog[data-closing=""] {
    animation: motion-modal-out var(--modal-out-time) ease-out forwards;
  }
  /* Its backdrop leaves with it; a longer fade would be cut off when the window closes. */
  .info-dialog[data-closing=""]::backdrop {
    animation-duration: var(--modal-out-time);
  }
  @media (min-width: 701px) {
    .info-dialog::backdrop {
      backdrop-filter: blur(12px);
    }
  }
  @media (max-width: 700px) {
    [data-sheet][open] {
      animation: motion-sheet-in var(--drawer-time) var(--ease-drawer);
    }
    [data-sheet][data-closing=""] {
      animation: motion-sheet-out var(--drawer-out-time) var(--ease-drawer) forwards;
    }
    /* A sheet the finger dragged away is moved by shop-motion.js itself. */
    [data-sheet][data-closing="drag"] {
      animation: none;
    }
    /* The page behind brightens as the sheet is dragged down, and dims again with the sheet's
       spring back when let go short (it follows the finger exactly while dragging). */
    [data-sheet]::backdrop {
      opacity: var(--sheet-dim, 1);
      transition: opacity 0.3s var(--ease-drawer);
    }
    [data-sheet][data-dragging]::backdrop {
      transition: none;
    }
  }

  /* The enlarged photo: the page photo flies into place (a copy, .zoom-ghost, drawn by
     shop-motion.js) while the white screen fades in; closing flies it back. */
  .zoom-dialog[open] {
    animation: motion-screen-in var(--lightbox-time) var(--ease-lightbox);
  }
  .zoom-dialog[data-closing=""] {
    animation: motion-screen-out var(--lightbox-time) var(--ease-lightbox) forwards;
  }
  .zoom-dialog[open] > .zoom-bar,
  .zoom-dialog[open] > .zoom-stage {
    animation: motion-fade-in var(--lightbox-time) var(--ease-lightbox);
  }
  .zoom-dialog[data-closing=""] > .zoom-bar,
  .zoom-dialog[data-closing=""] > .zoom-stage {
    animation: motion-fade-out var(--lightbox-time) var(--ease-lightbox) forwards;
  }
  .zoom-dialog[data-flying] #zoom-image {
    opacity: 0;
  }

  /* Page changes (View Transitions, where the browser has them): the page cross-fades, the header
     glides to its new place, and a product photo flies between its card and the product page. */
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: var(--page-time);
  }
  ::view-transition-group(site-header),
  ::view-transition-group(product-photo) {
    animation-duration: var(--photo-fly-time);
    animation-timing-function: var(--ease-soft);
  }
  /* Back to the list (storyboard): the details leave quicker, the photo flies back a little faster. */
  :root[data-page-back]::view-transition-old(root),
  :root[data-page-back]::view-transition-new(root) {
    animation-duration: var(--page-back-time);
  }
  :root[data-page-back]::view-transition-group(product-photo),
  :root[data-page-back]::view-transition-group(site-header) {
    animation-duration: var(--photo-back-time);
  }
  ::view-transition-old(product-photo),
  ::view-transition-new(product-photo) {
    height: 100%;
    object-fit: cover;
    animation-duration: var(--photo-fly-time);
  }
}
/* Storyboard scene 3, adding to the bag. shop-motion.js draws these only with motion on.
   - Sizes: one dark highlight slides between the sizes; its trailing edge takes --select-lag longer
     than the leading one, so it stretches a little on the way. Without it the chosen size is dark.
   - Quantity: the number rolls to the new one (a copy over the field while it moves).
   - The bag: a small copy of the photo flies into the bag icon, whose count rolls. */
[data-slide-select] {
  position: relative;
}
[data-slide-select] > label {
  z-index: 1;
}
.select-indicator {
  position: absolute;
  z-index: 0;
  border-radius: var(--radius-control);
  background: var(--ink);
  pointer-events: none;
  opacity: 0;
}
.select-indicator.is-shown {
  opacity: 1;
}
[data-slide-select].has-indicator input:checked + span {
  background: transparent;
  border-color: transparent;
  color: var(--on-ink);
}
.quantity-control {
  position: relative;
}
.qty-roll,
.count-roll {
  position: absolute;
  overflow: hidden;
  pointer-events: none;
}
.count-roll {
  position: fixed;
  z-index: 91;
}
.qty-roll > span,
.count-roll > span {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* The product photo's magnifier (shop-motion.js, [data-loupe]): a layer of its own on top, so the lens
   can sit on the cursor at the photo's edge and above a finger; under the bag flight, over the header. */
.loupe {
  position: fixed;
  z-index: 80;
  left: 0;
  top: 0;
  pointer-events: none;
}
.loupe-lens {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: var(--radius-dot);
  background: var(--photo-bg);
  box-shadow:
    0 0 0 2px var(--paper),
    0 0 0 3px var(--loupe-edge),
    0 12px 32px var(--loupe-shadow);
  opacity: 0;
  transform: scale(0.94);
}
.loupe img {
  position: absolute;
  left: 0;
  top: 0;
  max-width: none;
  transform-origin: 0 0;
}
.loupe-sharp {
  opacity: 0;
}
/* the photo on the page stands in, a little soft, until the 4096 copy has loaded */
.loupe-loading {
  position: absolute;
  left: calc(50% - 24px);
  bottom: 14%;
  width: 48px;
  height: 2px;
  background: var(--ink);
  opacity: 0.3;
  transform-origin: left;
  animation: motion-loading 1.2s ease-in-out infinite;
}
@media (hover: hover) and (pointer: fine) {
  [data-loupe] .gallery-zoom {
    cursor: zoom-in;
  }
  /* the lens is the cursor */
  [data-loupe-on="mouse"] .gallery-zoom {
    cursor: none;
  }
}
.bag-flight {
  position: fixed;
  z-index: 90;
  left: 0;
  top: 0;
  pointer-events: none;
}
.bag-flight > div {
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: var(--radius-media);
  background: var(--photo-bg);
  box-shadow: 0 6px 24px var(--shadow);
}
.bag-flight img {
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}
@media (prefers-reduced-motion: no-preference) {
  .select-indicator {
    transition:
      left var(--time-left, var(--select-time)) var(--ease-soft),
      right var(--time-right, var(--select-time)) var(--ease-soft),
      top var(--select-time) var(--ease-soft),
      bottom var(--select-time) var(--ease-soft),
      opacity 0.2s ease;
  }
  [data-slide-select].has-indicator label > span {
    transition: color 0.15s ease;
  }
  [data-slide-select].has-indicator input:checked + span {
    transition: color 0.2s ease 0.1s;
  }
  /* The line just added fades from grey to white as the bag opens. */
  .cart-item[data-just-added] {
    animation: motion-highlight var(--highlight-time) ease both;
  }
  /* Adding without a colour or size: the missing choice shakes a little, once. */
  [data-shake] {
    animation: motion-shake var(--shake-time) var(--ease-soft);
  }
  /* Size guide: the chosen row takes the ink like the size button (same pace as its slide); a
     column's line on the drawing lights up; the recommended size ticks up when it changes. */
  .size-chart tbody tr > * {
    transition:
      background-color var(--select-time) var(--ease-soft),
      color var(--select-time) var(--ease-soft);
  }
  .measure-diagram .measure {
    transition: opacity var(--select-time) var(--ease-soft);
  }
  [data-finder-size][data-tick] {
    animation: motion-tick var(--roll-time) var(--ease-soft);
  }
  /* Filtering the collection: the cards that stay rise again, one list step apart. */
  .product-card[data-filter-in] {
    --reveal-from: translateY(var(--list-y));
    animation: motion-reveal var(--list-time) var(--ease-soft) calc(var(--i, 0) * var(--list-step)) both;
  }
}
@keyframes motion-highlight {
  from {
    background-color: var(--panel);
  }
}
@keyframes motion-tick {
  from {
    opacity: 0.3;
    transform: translateY(6px);
  }
}
@keyframes motion-shake {
  20% {
    transform: translateX(calc(-1 * var(--nudge)));
  }
  45% {
    transform: translateX(var(--nudge));
  }
  70% {
    transform: translateX(calc(-0.5 * var(--nudge)));
  }
}
/* The copy of the photo that flies into the enlarged view (and the fading copy when the product photo
   changes) is drawn only while it moves. */
.zoom-ghost {
  position: fixed;
  z-index: 3;
  margin: 0;
  overflow: hidden;
  pointer-events: none;
  transform-origin: 0 0;
}
.zoom-ghost img {
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}
.photo-fade {
  position: absolute;
  z-index: 1;
  max-width: none;
  pointer-events: none;
}
@keyframes motion-reveal {
  from {
    opacity: 0;
    transform: var(--reveal-from, translateY(var(--reveal-y)));
  }
}
@keyframes motion-settle {
  from {
    transform: scale(var(--settle-scale));
  }
}
@keyframes motion-grow {
  from {
    transform: scaleX(0);
  }
}
@keyframes motion-fade-in {
  from {
    opacity: 0;
  }
}
@keyframes motion-fade-out {
  to {
    opacity: 0;
  }
}
@keyframes motion-drawer-in {
  from {
    transform: translateX(100%);
  }
}
@keyframes motion-drawer-out {
  to {
    transform: translateX(100%);
  }
}
@keyframes motion-sheet-in {
  from {
    transform: translateY(100%);
  }
}
@keyframes motion-sheet-out {
  to {
    transform: translateY(100%);
  }
}
@keyframes motion-modal-in {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
}
@keyframes motion-modal-out {
  to {
    opacity: 0;
    transform: scale(0.98);
  }
}
@keyframes motion-screen-in {
  from {
    background-color: transparent;
  }
}
@keyframes motion-screen-out {
  to {
    background-color: transparent;
  }
}
@keyframes motion-loading {
  0% {
    transform: scaleX(0);
  }
  50% {
    transform: scaleX(1);
  }
  100% {
    transform: translateX(48px) scaleX(0);
  }
}
@media print {
  [data-reveal-state] {
    opacity: 1 !important;
    transform: none !important;
  }
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
  /* Own rules: a selector list with a pseudo-element the browser does not know is dropped whole. */
  *::backdrop {
    animation: none !important;
    transition: none !important;
  }
  @supports selector(::details-content) {
    *::details-content {
      transition: none !important;
    }
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}
