/* Titan Lab — Drop 01 pre-order.
   Spacing ladder, grid and type rules from design-library/LAYOUT.md.
   Durations, curves and --dist from design-library/MOTION.md. Pasted before the first rule.

   The page is the floor the garments lie on: --floor is sampled from the renders' own concrete
   (#b6b3ae at every edge of comp/images/lay-*.jpg), so a garment has no image boundary.
   No deep green anywhere — Izzi, 25 Oct. Every colour is a token. */

:root {
  /* --- LAYOUT.md: component rhythm --- */
  --space-xs: 0.25rem;  --space-s: 0.375rem; --space-sm: 0.5rem;
  --space-m:  0.75rem;  --space-l: 1.125rem; --space-xl: 1.5rem;
  /* --- LAYOUT.md: section rhythm --- */
  --section-s: 2rem;  --section-sm: 2.5rem; --section-m: 3.75rem;
  --section-l: 5rem;  --section-xl: 7.5rem;

  /* --- MOTION.md --- */
  --dur-state: 150ms;
  --dur-ui:    250ms;
  --dur-enter: 400ms;
  --dur-hero:  800ms;
  --ease:      cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --ease-out:  cubic-bezier(0.19, 1, 0.22, 1);
  --ease-in:   cubic-bezier(0.4, 0, 1, 1);
  --ease-pop:  linear(
    0 0%, 0.8513 5%, 1.2484 10%, 1.3862 13%, 1.4709 16%, 1.5182 20%, 1.52 21%,
    1.5101 24%, 1.4817 27%, 1.4254 31%, 1.3633 35%, 1.3188 38%, 1.278 41%,
    1.2412 44%, 1.2083 47%, 1.1789 50%, 1.1296 55%, 1.0909 62%, 1.0654 67%,
    1.0451 72%, 1.032 76%, 1.0193 81%, 1.0101 86%, 1.005 90%, 1.0018 94%, 1 100%
  );
  --dist: 16px;

  /* --- Colour. Floor from the renders; gold ramp from LOGO GUIDELINES.pdf via BRIEF.md. --- */
  --floor:     #c9c6c0;   /* measured off every edge AND corner of the renders in comp/images:
                             flat to the byte once render_floor._unvignette has run. If the
                             renders are rebuilt, re-sample this or a pale box appears
                             around every garment. */
  --floor-lo:  #bcb9b3;   /* the bar, a shade down — still the same concrete */
  --ink:       #17140f;   /* warm near-black, tinted toward the gold not the green */
  --ink-dim:   #45413a;   /* measured 4.82:1 on --floor — the floor for small text */
  --gold:      #8a6f3b;   /* GOLD_DARK — marks and rules only */
  --gold-lite: #d9bd7d;   /* light end of the ramp - the gold swatch's highlight */
  --mask-on:   #000;      /* masks read ALPHA only; this is the opaque stop */
  --gold-ink:  #4e3c1b;   /* measured 5.13:1 on --floor. #6b5429 was 3.43:1 and failed AA;
                             the ramp's own #8a6f3b is 3.18:1, so it is rules and marks only. */
  --cloth-blk: #262628;
  --cloth-wht: #ecebe7;
  --rule:      rgba(23, 20, 15, 0.20);
  --rule-soft: rgba(23, 20, 15, 0.10);

  /* --- Type. JetBrains Mono only: it is on titanlabbrunei.com and printed on the garments'
         own data lines. Nothing on this page is set in anything else — the only large type in
         Drop 01 is printed on cloth. --- */
  --mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;

  --maxw: 1660px;
  --gutter: 12px;
  --bar: 54px;
}

@media (min-width: 768px) {
  :root {
    --space-s: 0.5rem;  --space-sm: 0.625rem; --space-m: 0.875rem;
    --space-l: 1.125rem; --space-xl: 1.625rem;
    --section-s: 2.25rem; --section-sm: 3.125rem; --section-m: 4.375rem;
    --section-l: 6.25rem; --section-xl: 9.375rem;
    --bar: 60px;
  }
}
@media (min-width: 1024px) {
  :root {
    --space-s: 0.5rem;  --space-sm: 0.75rem; --space-m: 1rem;
    --space-l: 1.25rem; --space-xl: 1.75rem;
    --section-s: 2.5rem; --section-sm: 3.75rem; --section-m: 5rem;
    --section-l: 7.5rem; --section-xl: 11.25rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------- base */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--floor);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* the fixed bar covers content and focus rings without these two */
  padding-bottom: var(--bar);
  scroll-padding-bottom: calc(var(--bar) + var(--space-xl));
}

/* Generous side gutters are most of the negative space on a phone. */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 clamp(20px, 6vw, 80px);
}

.grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 var(--gutter);
}
@media (min-width: 768px) {
  .grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
}

/* ---------------------------------------------------------------- type roles */

.tag {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 0;
}
.tag--ink { color: var(--ink); }
.tag--gold { color: var(--gold-ink); }

/* ---------------------------------------------------------------- rail */

.rail {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding: var(--space-xl) clamp(20px, 6vw, 80px);
}
.rail img { width: 26px; height: auto; display: block; }

/* ---------------------------------------------------------------- opening */

.open {
  padding: clamp(80px, 22vh, 220px) 0 var(--section-xl);
  text-align: center;
}
.open__name {
  font-size: clamp(13px, 2.2vw, 16px);
  font-weight: 700;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  margin: 0 0 var(--space-l);
  text-indent: 0.42em;          /* tracking adds a trailing gap; put it back on the left */
}
.open__drop { margin: 0 0 var(--space-xl); }
.open__count {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 0;
}

/* ---------------------------------------------------------------- a piece */

.piece { padding-bottom: var(--section-xl); }
.piece__grid { row-gap: var(--section-m); align-items: center; }

.piece__shot { grid-column: 1 / -1; }
.piece__data { grid-column: 1 / -1; }

@media (min-width: 768px) {
  .piece__shot { grid-column: 1 / span 6; }
  .piece__data { grid-column: 8 / span 4; }
  /* every other piece swaps sides, so the page is not a column of identical rows */
  .piece:nth-child(even) .piece__shot { grid-column: 7 / span 6; grid-row: 1; }
  .piece:nth-child(even) .piece__data { grid-column: 2 / span 4; grid-row: 1; }
}
@media (min-width: 1024px) {
  .piece__shot { grid-column: 2 / span 5; }
  .piece__data { grid-column: 8 / span 3; }
  .piece:nth-child(even) .piece__shot { grid-column: 6 / span 5; }
  .piece:nth-child(even) .piece__data { grid-column: 2 / span 3; }
}

/* Nothing here has a frame, a card or a border. Image edges feather into the page so the
   render's concrete and the page's read as one floor. */
.shot {
  display: block;
  width: 100%;
  height: auto;
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0, var(--mask-on) 8%, var(--mask-on) 92%, transparent 100%),
    linear-gradient(to right, transparent 0, var(--mask-on) 7%, var(--mask-on) 93%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0, var(--mask-on) 8%, var(--mask-on) 92%, transparent 100%),
    linear-gradient(to right, transparent 0, var(--mask-on) 7%, var(--mask-on) 93%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* The stage holds the plate and the garment in one fixed-ratio box, so opening a piece
   changes what is on the floor without moving anything else on the page. */
.stage {
  position: relative;
  aspect-ratio: 1400 / 1760;
  perspective: 1400px;
}
.stage > * { position: absolute; inset: 0; }

.plate-btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transition: transform var(--dur-ui) var(--ease), opacity var(--dur-ui) var(--ease);
}
.is-open .plate-btn {
  transform: scale(0.86);
  opacity: 0;
  pointer-events: none;
}
.piece--held .plate-btn { cursor: default; opacity: 0.82; }

/* The pop — the one signature move on the page, on MOTION.md's overshoot spring.
   Both states are explicit classes of equal specificity and the reveal is written last, so
   the hide can never outrank it and strand the garment invisible. A garment carrying neither
   class is visible, which is the safe failure. */

.garment {
  transition: opacity var(--dur-enter) var(--ease), transform var(--dur-enter) var(--ease-pop);
}

.garment.is-closed {
  opacity: 0;
  transform: scale(0.9);
  pointer-events: none;
}

.garment.is-shown {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

/* the turn — a real flip, two faces back to back, on a plain curve so it never competes
   with the pop for attention */
.turner {
  position: absolute;
  inset: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transform-style: preserve-3d;
  transition: transform var(--dur-enter) var(--ease-out);
}
.turner.is-back { transform: rotateY(180deg); }
.face {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  backface-visibility: hidden;
}
.face--back { transform: rotateY(180deg); }

.hint {
  position: absolute;
  left: 50%;
  bottom: 2%;
  transform: translateX(-50%);
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
  white-space: nowrap;
  pointer-events: none;
}

/* what a plate says before it is opened: the piece, never the design */
.slug { margin-top: var(--space-l); text-align: center; }
.slug__name {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin: 0 0 var(--space-s);
}
.is-open .slug { visibility: hidden; }

/* --- the data beside it. The piece number is the only large thing on the page. --- */
.piece__n {
  font-size: clamp(54px, 13vw, 92px);
  font-weight: 700;
  line-height: 0.82;                 /* LAYOUT.md §4 — display leading below 1.0 */
  letter-spacing: -0.045em;
  margin: 0 0 var(--space-l);
  font-variant-numeric: tabular-nums;
}
.piece__name {
  font-size: clamp(13px, 1.5vw, 15px);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin: 0 0 var(--space-sm);
}
.piece__spec { margin: 0 0 var(--section-s); }

/* The price sits in the spec line rather than in a badge of its own: it is one more fact about
   the garment, the same weight as its print size. --gold-ink is the only gold measured to pass
   AA on the floor (5.13:1) — the ramp's own #8a6f3b is 3.18:1 and is for rules and marks. */
.price {
  color: var(--gold-ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

/* Izzi, 31 Oct 2026: "state the kuat tee is the team tshirt". It sits under the name, above the
   spec, because it says what the piece IS rather than how it is made. */
.piece__team {
  margin: calc(var(--space-sm) * -1) 0 var(--space-sm);
  color: var(--gold-ink);
  font-weight: 600;
}

/* --- choices. No boxes: a chip is cloth, a size is a letter. --- */
.choose { margin-bottom: var(--space-xl); }
.choose__label { margin-bottom: var(--space-m); }

.ways { display: flex; gap: var(--space-l); align-items: center; flex-wrap: wrap; }
.way {
  border: 0;
  background: none;
  font: inherit;
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding-bottom: var(--space-s);
  border-bottom: 1px solid transparent;
  cursor: pointer;
  transition: color var(--dur-ui) var(--ease), border-color var(--dur-ui) var(--ease);
}
.way i {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--rule);
}
.way i.blk { background: var(--cloth-blk); }
.way i.wht { background: var(--cloth-wht); }
.way i.gld { background: linear-gradient(135deg, var(--gold-lite), var(--gold)); }
.way--on { color: var(--ink); border-bottom-color: var(--ink); }

.sizes { display: flex; gap: var(--space-xl); }
.size {
  border: 0;
  background: none;
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  padding-bottom: var(--space-s);
  border-bottom: 1px solid transparent;
  cursor: pointer;
  min-width: 22px;
  text-align: center;
  transition: color var(--dur-ui) var(--ease), border-color var(--dur-ui) var(--ease);
}
.size--on { color: var(--ink); border-bottom-color: var(--ink); }

/* --- the one action. A rule and a labelled arrow, not a button. --- */
.add {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-l);
  width: 100%;
  border: 0;
  border-top: 1px solid var(--ink);
  background: none;
  padding: var(--space-l) 0 0;
  margin-top: var(--section-s);
  font: inherit;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.add__arrow {
  width: 24px; height: 24px;
  border: 1px solid var(--ink);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 10px;
  transition: background var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease);
}
.add:hover .add__arrow { background: var(--ink); color: var(--floor); }
.add--in { color: var(--gold-ink); border-top-color: var(--gold); }
.add--in .add__arrow { border-color: var(--gold); }

/* ---------------------------------------------------------------- held piece */

.held {
  padding: var(--section-l) 0 var(--section-xl);
  border-top: 1px solid var(--rule-soft);
}
.held__row { align-items: baseline; row-gap: var(--space-l); }
.held__n { grid-column: 1 / -1; }
.held__txt { grid-column: 1 / -1; }
@media (min-width: 768px) {
  .held__n { grid-column: 2 / span 2; }
  .held__txt { grid-column: 4 / span 6; }
}
.held__n .piece__n { color: var(--ink-dim); margin: 0; }
.held__txt p { margin: 0 0 var(--space-m); }

/* ---------------------------------------------------------------- the order */

.order { padding: var(--section-l) 0 var(--section-xl); border-top: 1px solid var(--rule-soft); }
.order__head { grid-column: 1 / -1; margin-bottom: var(--section-s); }
.order__msg  { grid-column: 1 / -1; }
@media (min-width: 768px) {
  .order__head { grid-column: 2 / span 4; margin-bottom: 0; }
  .order__msg  { grid-column: 7 / span 5; }
}
.order h2 {
  font-size: clamp(22px, 3.4vw, 34px);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  margin: 0 0 var(--space-l);
}
.order p { color: var(--ink-dim); margin: 0 0 var(--space-l); max-width: 36ch; font-size: 12px; }

.msg {
  border-top: 1px solid var(--rule);
  padding-top: var(--space-l);
  font-size: 12px;
  line-height: 1.9;
  color: var(--ink-dim);
}
.msg b { color: var(--ink); font-weight: 500; }
/* the composed message carries newlines, so it is rendered as typed */
.msg [data-msg] { white-space: pre-line; display: block; }

/* a held piece: the plate is there, slightly back, and the reason is said plainly */
.piece--held .piece__data--held { align-self: center; }
.piece--held .piece__data--held .tag { max-width: 34ch; }
.msg__to {
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-ink);
  margin-bottom: var(--space-m);
}

/* ---------------------------------------------------------------- the bar */

.bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  height: var(--bar);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding: 0 clamp(20px, 6vw, 80px);
  background: var(--floor-lo);
  border-top: 1px solid var(--rule);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  z-index: 10;
}
.bar__go {
  display: inline-flex;
  align-items: center;
  gap: var(--space-m);
  color: var(--ink);
  text-decoration: none;
  font-weight: 500;
}
.bar__go .add__arrow { width: 22px; height: 22px; }

/* ---------------------------------------------------------------- foot */

.foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m) var(--space-xl);
  justify-content: space-between;
  padding: var(--section-s) clamp(20px, 6vw, 80px) var(--section-m);
  border-top: 1px solid var(--rule-soft);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.foot a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--rule-soft);
  transition: border-color var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease);
}
.foot a:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* a visible focus ring everywhere, on the floor rather than the browser default blue */
:where(a, button):focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 2px;
}

/* The form and the confirmation swap with [hidden]; an author `display` on either would beat
   the browser's own [hidden] rule and show both at once, as it did in the admin panel. */
[hidden] { display: none !important; }

/* --- the pre-order form -----------------------------------------------------------------
   Izzi, 27 Oct: the customer leaves a name and a number and Titan Lab does the contacting, so
   the page needed fields where it had a WhatsApp button. Same tokens as everything else; the
   input is a ruled line rather than a box, because a box is the one shape nothing else on this
   page has. */
.form { margin-top: var(--section-s); }
.field { margin-bottom: var(--space-xl); }
.field label { display: block; margin-bottom: var(--space-s); }
.field .opt { text-transform: none; letter-spacing: 0; color: var(--ink-dim); opacity: 0.7; }

.field input,
.field textarea {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--ink);
  background: none;
  padding: var(--space-sm) 0;
  font: inherit;
  font-size: 15px;            /* 16px or more stops iOS zooming the page on focus; 15 plus the
                                 page's own scale keeps it from doing so without shouting */
  color: var(--ink);
  border-radius: 0;           /* iOS rounds inputs by default and it reads as someone else's UI */
  -webkit-appearance: none;
  transition: border-color var(--dur-state) var(--ease);
}
.field textarea { resize: vertical; line-height: 1.6; }
.field input:focus-visible,
.field textarea:focus-visible { outline: 0; border-bottom-color: var(--gold-ink); }

/* The honeypot. Off-screen rather than display:none, because some bots skip what is not
   rendered; a person never reaches it, having neither sight of it nor a tab stop into it. */
.trap {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.form__err {
  margin: 0 0 var(--space-l);
  font-size: 12px;
  color: var(--ink);
  border-left: 2px solid var(--gold-ink);
  padding-left: var(--space-m);
}
.form__fallback { color: var(--gold-ink); white-space: nowrap; }
.form__fine { margin-top: var(--space-m); max-width: 34ch; }

.add--send { margin-top: var(--space-xl); }
.add--send[disabled] { opacity: 0.45; cursor: progress; }
/* nothing chosen: the button is still reachable, and says why when pressed */
.form--empty .add--send { opacity: 0.45; }

.done {
  margin-top: var(--section-s);
  border-top: 1px solid var(--ink);
  padding-top: var(--space-l);
}
.done__ref {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 0 0 var(--space-m);
}
.done__ref strong { color: var(--ink); font-weight: 500; }

/* --- a closer look ----------------------------------------------------------------------
   Izzi, 27 Oct: "lets also add a zoom in button to the designs", then "the zoom in shouldnt be
   a pop out like that, just a detailed zoom in of the product".

   In place, inside the piece's own stage. Nothing covers the page and nothing on it moves. */

/* `.stage > *` stretches every child to fill the stage, so the button has to opt out of that
   before it can sit in a corner. The closeup layer WANTS to fill it, so it says nothing. */
.zoomer {
  inset: auto;
  right: 4%;
  bottom: 4%;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border: 1px solid var(--ink);
  border-radius: 50%;
  background: var(--floor);
  color: var(--ink);
  cursor: pointer;
  padding: 0;
  opacity: 0;
  pointer-events: none;
  z-index: 2;                      /* above the closeup, or it cannot be pressed to come back */
  transition: opacity var(--dur-ui) var(--ease), background var(--dur-state) var(--ease);
}
/* The reveal outranks the rest state AND comes after it. The other way round the button is
   permanently invisible and nothing says why - a specificity trap this project has paid for. */
.is-open .zoomer { opacity: 1; pointer-events: auto; }
.zoomer:hover { background: var(--floor-lo); }
.zoomer:focus-visible { outline: 2px solid var(--gold-ink); outline-offset: 2px; }
/* pressed, the magnifier loses its upright bar and reads as a minus: zoom out */
.piece.is-closeup .zoomer__plus { opacity: 0; }
.zoomer__plus { transition: opacity var(--dur-state) var(--ease); }

/* The clip lives HERE and not on .stage. An overflow on the stage would give the 3D flip a
   clipping context and flatten it - the turn would stop reading as a turn. */
.closeup {
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  cursor: grab;
  touch-action: none;              /* the drag is ours; the page must not scroll under it */
  background: var(--floor);
  transition: opacity var(--dur-ui) var(--ease);
}
.closeup:active { cursor: grabbing; }
.piece.is-closeup .closeup { opacity: 1; pointer-events: auto; }

.closeup__img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-origin: center 47%;    /* where the garment actually sits in these frames */
  user-select: none;
  -webkit-user-drag: none;
}

/* The garment steps aside while the closeup is up. `.piece.is-closeup .garment` outranks
   `.garment.is-shown`, which would otherwise hold it at opacity 1. */
.piece.is-closeup .garment { opacity: 0; pointer-events: none; }
.piece.is-closeup .hint { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .closeup, .closeup__img { transition: none; }
}

/* --- how many ------------------------------------------------------------------------------
   Izzi, 31 Oct 2026: "maybe they want more than one tshirt or cap?"

   Same idiom as the sizes: no box, no spinner, no select. A size is a letter and a quantity is
   a number, both bare on the floor. The glyphs borrow the size chip's weight and tracking so
   the three choose-rows read as one family down the column. */
.qty { display: flex; align-items: center; gap: var(--space-xl); }
.qty__b {
  border: 0;
  background: none;
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  line-height: 1;
  color: var(--ink-dim);
  padding: var(--space-s) 0;
  min-width: 22px;
  text-align: center;
  cursor: pointer;
  transition: color var(--dur-ui) var(--ease), opacity var(--dur-ui) var(--ease);
}
.qty__b:hover:not(:disabled) { color: var(--ink); }
/* At 1, minus does nothing; at 10 the server would refuse an eleventh anyway. Shown rather
   than silently ignored - a control that swallows a press reads as broken. */
.qty__b:disabled { opacity: 0.3; cursor: default; }
.qty__n {
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--ink);
  min-width: 22px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* --- the policy pages ----------------------------------------------------------------------
   Same floor and the same single typeface as the drop. A privacy notice that arrives in a
   different font reads as something bolted on by somebody else, which is the opposite of what
   it is for: these are the pages where a customer decides whether to trust the page that asked
   for their number.

   One measure, long enough to read and short enough to track: ~66 characters. */
.doc-body { background: var(--floor); color: var(--ink); }
.doc {
  max-width: 62ch;
  margin: 0 auto;
  padding: var(--section-m) var(--gutter) var(--section-l);
  font-size: 14px;
  line-height: 1.75;
}
.doc__kicker { margin: 0 0 var(--space-sm); }
.doc__h1 {
  margin: 0 0 var(--space-sm);
  font-size: clamp(30px, 7vw, 46px);
  font-weight: 700;
  line-height: 0.95;            /* display leading below 1.0 — design-library/LAYOUT.md */
  letter-spacing: -0.01em;
}
.doc__date { margin: 0 0 var(--section-s); color: var(--ink-dim); }
/* The summary first, because most people read only this much and they are entitled to the
   answer without wading. */
.doc__lede {
  margin: 0 0 var(--section-s);
  padding-left: var(--space-l);
  border-left: 2px solid var(--gold);
  font-size: 15px;
}
.doc__lede p { margin: 0 0 var(--space-m); }
.doc h2 {
  margin: var(--section-s) 0 var(--space-m);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.doc p, .doc li { margin: 0 0 var(--space-l); }
.doc ul, .doc ol { margin: 0 0 var(--space-l); padding-left: var(--space-xl); }
.doc li { margin-bottom: var(--space-m); }
.doc a { color: var(--gold-ink); text-underline-offset: 3px; }
.doc b { font-weight: 700; }
.mono { font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }

.doc__t { width: 100%; border-collapse: collapse; margin: 0 0 var(--space-l); font-size: 13px; }
.doc__t th {
  text-align: left;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding: 0 var(--space-l) var(--space-s) 0;
  border-bottom: 1px solid var(--rule);
}
.doc__t td { padding: var(--space-m) var(--space-l) var(--space-m) 0; border-bottom: 1px solid var(--rule-soft); vertical-align: top; }
.doc__t td:first-child { font-weight: 500; }
/* nowrap ONLY where there is room for it. It used to sit on the base rule with the mobile
   override trying to undo it from inside a media query - but a media query adds no specificity,
   so `.doc__t td:first-child` (0,2,1) kept beating `.doc__t td` (0,1,1) and the longest label
   refused to wrap, pushing the page 74px sideways at 375. Same family as the reveal-rule trap
   that once blanked a whole page: if a rule needs undoing, undo it at equal or higher weight,
   or never set it in the first place. */
@media (min-width: 561px) {
  .doc__t td:first-child { white-space: nowrap; }
}
@media (max-width: 560px) {
  .doc__t thead { display: none; }
  .doc__t tr { display: block; padding: var(--space-m) 0; border-bottom: 1px solid var(--rule-soft); }
  .doc__t td { display: block; padding: 0 0 var(--space-xs); border: 0; }
  .doc__t td:first-child { font-weight: 700; }
}

/* Every outstanding decision, marked so it cannot be read as settled and cannot be missed on
   the page. These are not decoration: each one is a fact only Titan Lab can supply, and a
   policy that quietly invented them would be worse than one that admits the gap. */
mark.tbc {
  background: rgba(138, 111, 59, 0.14);
  color: var(--gold-ink);
  padding: 0.1em 0.35em;
  font-weight: 500;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.foot--doc { flex-wrap: wrap; gap: var(--space-m); }
.foot__nav { display: flex; gap: var(--space-xl); flex-wrap: wrap; }
