@font-face {
  font-family: "Anton";
  src: url("/anton-subset.ttf") format("truetype");
  font-display: swap;
  font-weight: 400;
  font-style: normal;
}

:root { --bg:#fff; --fg:#000;
  /* ONE content column. The photo, the statement, the slider, the number field,
     the size bars, the buy button, the disclosure and the footer list all sit
     in it and agree on both edges at every viewport. Nothing below the header
     sets its own width or its own horizontal margin.

     Width is set by the STATEMENT, which is the widest fixed thing on the
     page and the one that must not wrap. At the clamp ceiling of 1.5rem,
     "I PAID $99,999.99 FOR THIS SHIRT" — the longest string the preview
     ceiling permits — measures 336px, and $5,000.00 measures 324px. 20rem
     (320px) missed both, orphaning "SHIRT" onto its own line. 21.5rem clears
     the longest of them with a little room for a fallback font.
     Below ~545px the statement's 4.4vw sizing takes over and shrinks the text
     faster than the column narrows, so phones were never the problem. */
  --col:21.5rem; --gutter:1rem;
  /* Derived, not restated: these two drifting apart is a whole-page bug. */
  --col-box:calc(var(--col) + 2 * var(--gutter)); }
* { box-sizing: border-box; }
body { margin:0; background:var(--bg); color:var(--fg);
       font:16px/1.4 -apple-system, "Helvetica Neue", Arial, sans-serif;
       font-stretch:condensed; }
a { color:var(--fg); }
header { padding:1rem; font-size:.9rem; text-transform:lowercase; }
main { max-width:40rem; margin:0 auto; padding:0 1rem 1rem; }
/* The shop pages (index, success). Policy pages keep the wider prose measure,
   so this is opt-in rather than a change to `main` itself. */
main.col { max-width:var(--col-box); padding:0 var(--gutter) 1rem; }
/* ONE garment view. #mockup is a real product photograph from the printer;
   #artwork is the print file alone inside a scale drawing of the print area,
   shown only when no photograph can be produced.
   No t-shirt is drawn anywhere in this stylesheet. */
#preview { margin:0; max-width:none; }
/* Both states occupy the same fixed-aspect box, and the box is sized before any
   image loads, so the frame never empties, never reflows, and the photo swap
   (a decoded image assigned to an already-laid-out element) shifts nothing. */
#preview-frame { position:relative; width:100%; aspect-ratio:1 / 1; }
#mockup { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }
[hidden] { display:none !important; }
#artwork { position:absolute; inset:0; display:flex; align-items:center;
  justify-content:center; border:1px solid #000; }
/* aspect-ratio is set per size by public/app.js from the real print area
   dimensions served by /api/print-geometry. Never hardcode it here. */
#print-area { position:relative; height:94%; outline:1px dashed #bbb; }
/* left/top/width are set per frame by app.js from the placement fractions that
   are sent to Printify; translate(-50%,-50%) makes them centre coordinates,
   matching CHEST_X/CHEST_Y. */
#art { position:absolute; transform:translate(-50%,-50%); }
#art svg { display:block; width:100%; height:auto; }
#statement { text-align:left; font-size:clamp(1rem,4.4vw,1.5rem); font-weight:700;
  letter-spacing:.02em; margin:.25rem 0 0; overflow-wrap:break-word;
  /* If it ever does wrap — a fallback font, a longer string than the preview
     ceiling allows — split it into even lines rather than stranding one word.
     A lone "SHIRT" on line two is the specific ugliness this avoids. */
  text-wrap:balance; }
form { display:grid; gap:.75rem; max-width:none; margin:1rem 0 0; }
label { display:grid; gap:.25rem; font-size:.85rem; }
/* --- the segmented price slider ------------------------------------------
   A custom control, because travel is allocated by expected use rather than by
   value (see src/slider.ts). Painted as a track, a fill and a thumb; the
   geometry all comes from app.js as percentages. */
#price-label { font-size:.85rem; }
/* --pos is the track position, 0..1, written by app.js. The thumb travels
   between its own half-widths rather than between the track's edges, so at 0
   and 1 it sits fully inside the control instead of hanging half off it. Every
   position below is expressed against that inset travel, which is what keeps
   the fill, the thumb and the segment ticks agreeing at the ends. */
#price-track { position:relative; height:2.2rem; cursor:pointer;
  touch-action:none;                 /* the control owns horizontal drags */
  --track-h:4px; --thumb:1.15rem; --pos:0;
  --travel:calc(var(--pos) * (100% - var(--thumb)) + var(--thumb) / 2); }
#price-track::before { content:""; position:absolute;
  left:calc(var(--thumb) / 2); right:calc(var(--thumb) / 2);
  top:calc(50% - var(--track-h) / 2); height:var(--track-h); background:#e2e2e2; }
#price-fill { position:absolute; left:calc(var(--thumb) / 2);
  top:calc(50% - var(--track-h) / 2); height:var(--track-h); background:#000;
  width:calc(var(--travel) - var(--thumb) / 2); }
#price-thumb { position:absolute; top:50%; left:var(--travel);
  width:var(--thumb); height:var(--thumb);
  margin:0; transform:translate(-50%,-50%); border-radius:50%; background:#000; }
/* A price off the track (below the floor, or past the slider's right end) has
   no position on it. The thumb still parks at the nearest end, so it is hollowed
   out rather than left claiming to represent the value. */
#price-track.off-track #price-thumb { background:var(--bg); box-shadow:inset 0 0 0 2px #000; }
#price-track.off-track #price-fill { background:#bbb; }
#price-track:focus-visible { outline:2px solid #000; outline-offset:3px; }
/* Segment boundaries. A hairline and nothing else: the change in sensitivity is
   worth seeing coming, and is not worth copy on a page that has almost none. */
#price-ticks { position:absolute; inset:0; pointer-events:none; }
.price-tick { position:absolute; top:50%; width:1px; height:.7rem;
  transform:translate(-50%,-50%); background:#bbb;
  /* Same inset travel as the thumb, so a boundary tick sits exactly where the
     thumb lands when it is on that boundary. */
  left:calc(var(--tick-pos) * (100% - var(--thumb)) + var(--thumb) / 2); }

/* The price, typed. The statement line is the only place a price is entered,
   so the number carries an underline to read as editable while the sentence
   around it stays plain text. */
#price-input { cursor:text; border-bottom:2px solid currentColor;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
  white-space:pre; }
#price-input:focus { outline:none; background:#f2f2f2; }

/* Free shipping, and the reason a price cannot be bought. Both are single
   lines under the button; #price-note is empty in the ordinary case. */
#free-shipping { margin:.15rem 0 0; font-size:.8rem; }
#price-note { margin:.15rem 0 0; font-size:.8rem; }
/* These lines are empty in the ordinary case. Collapsed rather than merely
   blank, so the form's grid gap does not reserve a row for something that is
   saying nothing. */
#price-note:empty, #error:empty, #free-shipping[hidden] { display:none; }

/* --- the inquire form ----------------------------------------------------
   Shown only above the card cap. Takes a contact address and a note; charges
   nothing. */
#inquire { display:grid; gap:.5rem; margin:1rem 0 0; }
#inquire-lead { margin:0; font-size:.85rem; }
#inquire textarea, #inquire input { width:100%; font:inherit; }
#inquire-actions { display:flex; gap:.5rem; }
#inquire-status:empty { display:none; }
#inquire-status { margin:0; font-size:.8rem; }

/* Big drag readout. Fixed and pointer-events:none, so it can never shift the
   page or cover a control it would block. */
:root { --drag-fade:180ms; }
/* Narrow: across the top, clear of the slider and buttons lower down.
   Wide: out at the side, vertically centred, clear of the 40rem column. */
#drag-readout { position:fixed; left:0; right:0; top:0; text-align:center;
  background:var(--bg); padding:.25rem 0 .35rem;
  font-weight:800; font-size:clamp(2.5rem,13vw,4.5rem); line-height:1;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
  color:#000; pointer-events:none; opacity:0; visibility:hidden;
  transition:opacity var(--drag-fade) ease-out, visibility 0s linear var(--drag-fade);
  z-index:10; }
#drag-readout.on { opacity:1; visibility:visible; transition:opacity 0s, visibility 0s; }
@media (min-width: 56rem) {
  #drag-readout { left:auto; right:3vw; top:50%; transform:translateY(-50%);
    text-align:right; font-size:clamp(4rem,9vw,9rem); background:none; padding:0; }
}
@media (prefers-reduced-motion: reduce) {
  #drag-readout { transition:none; }
}
/* app.js mirrors the same query onto <html> so the state object and the
   stylesheet can never disagree about it. */
.reduced-motion #drag-readout { transition:none; }
input, select, button { font:inherit; padding:.6rem; border:1px solid #000;
  background:#fff; color:#000; }
button { text-transform:lowercase; cursor:pointer; }
button:disabled { opacity:.4; cursor:not-allowed; }
/* One focus ring for the whole page, in the page's own ink rather than the
   browser default. */
input:focus-visible, button:focus-visible, a:focus-visible, summary:focus-visible {
  outline:2px solid #000; outline-offset:2px; }
#size-bars { display:flex; width:100%; }
.size-bar { flex:1; padding:.6rem 0; border:1px solid #000; background:#fff; color:#000;
  text-transform:lowercase; cursor:pointer; margin-left:-1px; }
.size-bar:first-child { margin-left:0; }
.size-bar[aria-checked="true"] { background:#000; color:#fff; }
/* Visually hidden but still in the accessibility tree, so aria-describedby and
   the live region resolve. Never display:none, which would remove them. */
.sr-only { position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
  border:0; }

/* --- buy button, and the one loading affordance on the page ---------------
   The garment image deliberately has none: it holds the last good photograph.
   This is the only place the page admits to waiting. */
#submit-row { position:relative; display:grid; }
#submit { display:flex; align-items:center; justify-content:center; gap:.5rem; }
#submit[aria-disabled="true"] { opacity:.55; cursor:progress; }
#submit-spinner { display:none; }
/* A plain monochrome ring with one open quadrant. No icon font, no SVG. */
#submit-row.updating #submit-spinner { display:block; width:.8em; height:.8em;
  border:2px solid currentColor; border-top-color:transparent; border-radius:50%;
  animation:submit-spin .8s linear infinite; }
@keyframes submit-spin { to { transform:rotate(360deg); } }
/* Reduced motion: keep the indicator, drop the rotation. */
@media (prefers-reduced-motion: reduce) {
  #submit-row.updating #submit-spinner { animation:none; border-top-color:currentColor;
    opacity:.45; }
}
.reduced-motion #submit-row.updating #submit-spinner { animation:none;
  border-top-color:currentColor; opacity:.45; }

/* Tooltip, on pointer hover AND keyboard focus, never hover-only. */
/* Below the button, not above: above would sit on top of the size bars. */
#submit-tip { position:absolute; top:calc(100% + .4rem); left:50%;
  transform:translateX(-50%); z-index:5; pointer-events:none;
  background:#000; color:#fff; font-size:.7rem; line-height:1;
  padding:.35rem .5rem; white-space:nowrap; opacity:0; visibility:hidden; }
#submit-row.updating #submit:hover ~ #submit-tip,
#submit-row.updating #submit:focus-visible ~ #submit-tip { opacity:1; visibility:visible; }

/* --- disclosure ----------------------------------------------------------
   Sits below the buy action and opens downward, so nothing above it moves. */
#detail { border-top:1px solid #000; margin-top:1.5rem; padding-top:.6rem; }
#detail > summary { cursor:pointer; font-size:.75rem; text-transform:lowercase;
  list-style:none; display:flex; align-items:center; gap:.4rem; padding:.15rem 0; }
#detail > summary::-webkit-details-marker { display:none; }
/* Explicit open/closed affordance drawn in CSS: a chevron that rotates. */
#detail > summary::before { content:""; width:.4rem; height:.4rem; flex:none;
  border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor;
  transform:rotate(-45deg); transform-origin:center; }
#detail[open] > summary::before { transform:rotate(45deg); }
#detail > summary:focus-visible { outline:2px solid #000; outline-offset:3px; }
#fit { font-size:.75rem; min-height:1.2em; margin:.5rem 0 .6rem; }
#breakdown { margin-top:0; }
/* The figures count from the old value to the new one (see renderBreakdown in
   app.js). While a newer quote is in flight the numbers on screen are real but
   not current, so they are dimmed rather than removed: emptying and refilling
   the table on every change is the flicker this replaces. */
#breakdown td { font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1; }
#breakdown.stale { opacity:.45; }
#breakdown { transition:opacity 120ms ease-out; }
@media (prefers-reduced-motion: reduce) { #breakdown { transition:none; } }
.reduced-motion #breakdown { transition:none; }
/* Footer. A stacked list, aligned to the same optical column as the page body
   (main's 40rem measure), set well below the buy action so it reads as a
   separate region rather than more content. No rules, fills, bullets or icons:
   the separation is done with space and with a step down in weight. */
/* Same box as main.col, so the list's left and right edges land exactly on the
   size bars' at every width, including narrow, where both sit against the
   1rem gutter. */
footer { max-width:var(--col-box); margin:0 auto; padding:5rem var(--gutter) 4rem; }
footer ul { list-style:none; margin:0; padding:0; }
footer li { margin:0; }
footer a { display:inline-block; padding:.3rem 0; font-size:.75rem;
  line-height:1.35; letter-spacing:.01em; color:#555; text-decoration:none; }
footer a:hover { color:#000; text-decoration:underline; text-underline-offset:.2em; }
footer a:focus-visible { outline:2px solid #000; outline-offset:3px; color:#000; }
@media (min-width: 40rem) {
  footer { padding-top:6rem; }
}
.doc { max-width:34rem; margin:0 auto; padding:2rem 1rem 4rem; font-size:.9rem; }
.doc h1 { font-size:1rem; text-transform:lowercase; }
table.plain { border-collapse:collapse; width:100%; font-size:.85rem; }
table.plain td, table.plain th { border:1px solid #000; padding:.4rem .5rem; text-align:left; }
