/* deliverableco.com site layout
   Depends on styles/tokens.css. Tokens are not redefined here and no colour
   value appears here that is not one of the four brand colours or a derived
   token already declared in tokens.css.

   Two size decisions are made here, because the brand guide and CLAUDE.md pull
   in opposite directions on them:

   - Labels that classify the thing next to them (SERVICE 01, NEXT OPEN START,
     FLAT RATE) sit in the guide's 11 to 13 band, at --t-label.
   - Anything a visitor has to read to use the page (nav links, button labels,
     the capacity dates) is set at --t-control, 16px, to hold the floor in
     CLAUDE.md. Both are mono, uppercase, and carry the word gap treatment. */

:root {
  --t-control: 16px;      /* DERIVED: mono floor for anything read to act on */
  /* Same value tokens.css uses for secondary text on ink. Graphite is too dark
     on an ink ground: it measures 2.8:1, so labels get this instead. */
  --secondary-on-ink: rgba(239, 238, 233, 0.68);
  /* CLAUDE.md: on ink grounds use a hairline at 12% paper rather than a
     lightened panel. tokens.css --rule-on-ink is 16% and stays the value for
     plain rules; this is the panel border the contract names. */
  --panel-border-on-ink: rgba(239, 238, 233, 0.12);
  /* DERIVED. tokens.css sets --radius-card at 10px, echoing the logo square's
     8% loosely. This is the softer radius asked for on the tier boxes. */
  --radius-card-lg: 20px;
  /* DERIVED. A shadow is shading, not a colour, so it does not add a fifth to
     the palette. It has to go darker than ink to read on an ink ground. */
  --lift: 0 16px 36px rgba(0, 0, 0, 0.38);
  --t-data: 18px;         /* DERIVED: mono at reading size, for the capacity dates */
  --nav-h: 76px;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + var(--s-3));
  -webkit-text-size-adjust: 100%;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Bricolage carries an opsz axis. Let it resolve against the rendered size so
   the headings and the wordmark are the same optical cut. */
.statement, .heading, .subhead { font-optical-sizing: auto; }

/* tokens.css reroutes .body--secondary on ink but not .label, and the default
   label colour is graphite. Reroute it the same way. */
.on-ink .label { color: var(--secondary-on-ink); }

img, svg { max-width: 100%; }
h1, h2, h3, p, ul, ol, figure { margin: 0; }
ul { padding: 0; list-style: none; }

/* ---------------------------------------------------------------- structure */

.wrap {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--s-7); }
.section + .section { border-top: 1px solid var(--rule-on-paper); }

/* An ink section is separated by the change of ground, so it takes no rule,
   and neither does whatever follows it. */
.section--ink { background: var(--ink); border-top: 0; }
.section--ink + .section { border-top: 0; }

.section__head { max-width: var(--measure-max); }
.section__head .label { display: block; margin-bottom: var(--s-3); }
.section__head .heading + .body { margin-top: var(--s-4); }

.skip {
  position: absolute;
  left: var(--s-3);
  top: calc(-1 * var(--s-6));
  z-index: 10;
  padding: var(--s-2) var(--s-3);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-size: var(--t-control);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  text-decoration: none;
  border-radius: var(--radius-control);
}

/* The skip link carries its own paper background on both grounds, so its ring
   is ink on both. Without this it would inherit the paper ring on ink pages
   and disappear into itself. */
.skip:focus-visible { top: var(--s-3); outline-color: var(--ink); }

/* The period, as the square, in signal.

   DECISION 20 Aug 2026, by the owner, overriding the guide's signal rule. The
   guide reserves signal for the lockup square and status marks, and names "a
   heading" first among the things signal is never. This site now spends it on
   every period in display type and in the nav: eleven marks on the homepage,
   plus the one in the capacity strip. Recorded in CLAUDE.md so it is not
   quietly reverted.

   Construction is the lockup's: side 0.274 cap, corner radius 8% of the side.
   Bricolage's cap is 0.66em so the side is 0.181em; Plex Mono's is 0.698em so
   it is 0.191em. Bricolage's own full stop is 206 x 181 units, the same height
   but wider, which is why the square reads as deliberate rather than as a
   period that happens to be red. */
.sq {
  display: inline-block;
  width: 0.181em;
  height: 0.181em;
  border-radius: 8%;
  background: var(--signal);
  vertical-align: baseline;
  margin-left: 0.03em;
}

.nav__links .sq { width: 0.191em; height: 0.191em; margin-left: 0.07em; }

/* ---------------------------------------------------------- claims band ----
   Continuous motion, which is outside the motion budget in CLAUDE.md. It stops
   dead under prefers-reduced-motion, and the pause control exists because
   anything that moves for more than five seconds needs a way to stop it. */

.marquee {
  display: flex;
  align-items: center;
  border-top: 1px solid var(--rule-on-paper);
  border-bottom: 1px solid var(--rule-on-paper);
  background: var(--paper);
}

.marquee__viewport { flex: 1; overflow: hidden; min-width: 0; }

.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee 46s linear infinite;
}

/* The visible pause button was removed on request. What is left: the band
   stops on hover and on keyboard focus, and never starts at all under
   prefers-reduced-motion. That covers users who have asked their system for
   less motion, and users on a pointer or a keyboard. It does not cover a touch
   user who has set no preference, so this no longer meets WCAG 2.2.2. */
.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track { animation-play-state: paused; }

@keyframes marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.marquee__list { display: flex; align-items: center; }

.marquee__list li {
  display: flex;
  align-items: center;
  /* The gap after the square is a word space, measured: the band's own word
     gaps run 17.1px at this size, so the square is followed by the same. */
  gap: 17px;
  padding: var(--s-3) var(--s-6) var(--s-3) 0;
  font-family: var(--mono);
  font-weight: var(--w-mono);
  font-size: var(--t-control);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

/* The square leads each phrase, the way the lockup's square terminates the
   wordmark: square, a word space, then the writing. Signal, per the decision
   recorded in CLAUDE.md. Same construction as everywhere else: 0.274 cap, and
   Plex Mono's cap is 0.698em, so 0.191em. */
.marquee__list li::before {
  content: "";
  flex: none;
  width: 0.191em;
  height: 0.191em;
  border-radius: 8%;
  background: var(--signal);
}

@media (prefers-reduced-motion: reduce) {
  .marquee__viewport { overflow-x: auto; }
  .marquee__track { animation: none; }
  .marquee__list[aria-hidden="true"] { display: none; }
}

/* -------------------------------------------------------------------- links */

a { color: inherit; }

.link {
  font-family: var(--mono);
  font-weight: var(--w-mono);
  font-size: var(--t-control);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  text-decoration: none;
  /* The rule is signal, the type is not. Signal measures 4.2:1 on paper and
     3.36:1 on ink. A rule is a non-text element and needs 3:1, so it clears
     both grounds. Red type would need 4.5:1 and would fail on either. */
  border-bottom: 2px solid var(--signal);
  padding-bottom: 2px;
  transition: border-color var(--dur) var(--ease);
}

.link:hover { border-bottom-color: var(--ink); }
.on-ink .link:hover { border-bottom-color: var(--paper); }

/* Inside a paragraph the link takes the paragraph's voice. Mono uppercase mid
   sentence reads as a label, which it is not.

   The highlight is signal, but on the rule rather than on the type. Red text at
   18px measures 3.36:1 on ink and 4.2:1 on paper, and normal text needs 4.5:1,
   so a red word would fail on either ground. A rule is a non-text element and
   needs 3:1, which it clears. The link reads as red and stays legible. */
.body .link {
  font-family: inherit;
  font-size: inherit;
  font-weight: var(--w-body-strong);
  letter-spacing: inherit;
  word-spacing: inherit;
  text-transform: none;
  padding-bottom: 1px;
}

/* ------------------------------------------------------------------ buttons */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 15px var(--s-4);
  border: 1px solid var(--ink);
  border-radius: var(--radius-control);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-weight: var(--w-mono-strong);
  font-size: var(--t-control);
  line-height: 1.2;
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.btn:hover { background: var(--paper); color: var(--ink); }

/* On ink the button inverts, and so does its hover. Signal is never a button. */
.on-ink .btn {
  border-color: var(--paper);
  background: var(--paper);
  color: var(--ink);
}

.on-ink .btn:hover { background: var(--ink); color: var(--paper); }

/* ---------------------------------------------------------------------- nav */

.nav {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--ink);
  border-bottom: 1px solid var(--rule-on-ink);
}

.nav__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: var(--nav-h);
  padding-block: var(--s-3);
}

.nav__home { display: inline-flex; line-height: 0; }

.nav__logo {
  display: block;
  width: clamp(150px, 17vw, 176px);
  height: auto;
}

.nav__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  justify-content: flex-end;
}

.nav__links a {
  font-family: var(--mono);
  font-weight: var(--w-mono);
  font-size: var(--t-control);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--paper);
  padding-bottom: 3px;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease);
}

.nav__links a:hover { border-bottom-color: var(--paper); }


/* --------------------------------------------------------------------- hero */

.hero {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  padding-block: clamp(var(--s-6), 11vw, var(--s-8)) var(--s-7);
}

/* The collage. Twelve tiles at every width, so every supplied photograph is
   always on screen. Files are assets/hero/01.jpg to 12.jpg. A missing file
   leaves the ink ground showing, which is why the markup needs no editing. */
.hero__collage {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(2, 1fr);
}

.hero__collage span {
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  /* The photographs carry no colour of their own. Signal is the only colour on
     the page, and a saturated object in a photograph competes with it: one of
     these frames holds a red telephone. Desaturated, the collage reads as tone
     and the status square stays the only thing on the page that is coloured. */
  filter: grayscale(1);
}

.hero__collage span:nth-child(1) { background-image: url("../assets/hero/01.jpg"); }
.hero__collage span:nth-child(2) { background-image: url("../assets/hero/02.jpg"); }
.hero__collage span:nth-child(3) { background-image: url("../assets/hero/03.jpg"); }
.hero__collage span:nth-child(4) { background-image: url("../assets/hero/04.jpg"); }
.hero__collage span:nth-child(5) { background-image: url("../assets/hero/05.jpg"); }
.hero__collage span:nth-child(6) { background-image: url("../assets/hero/06.jpg"); }
.hero__collage span:nth-child(7) { background-image: url("../assets/hero/07.jpg"); }
.hero__collage span:nth-child(8) { background-image: url("../assets/hero/08.jpg"); }
.hero__collage span:nth-child(9) { background-image: url("../assets/hero/09.jpg"); }
.hero__collage span:nth-child(10) { background-image: url("../assets/hero/10.jpg"); }
.hero__collage span:nth-child(11) { background-image: url("../assets/hero/11.jpg"); }
.hero__collage span:nth-child(12) { background-image: url("../assets/hero/12.jpg"); }

/* The wash. Photographs are darkened toward ink, never tinted with a colour.
   0.72 is not a taste value: it is the point at which paper type clears 4.5:1
   even where a photograph is pure white. Verified by sampling, not estimated. */
.hero__collage::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  opacity: 0.72;
}

/* The scrim. Type never depends on what the photograph happens to be doing
   underneath it, so the copy side is carried to solid ink and the collage is
   left to read on the other side. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(100deg, var(--ink) 0%, var(--ink) 26%, transparent 74%);
}

.hero__in {
  position: relative;
  z-index: 1;
}

/* Secondary text is paper at 68% everywhere else. Over photography that is
   the first thing to fall below 4.5:1, so on this ground it goes to full
   paper. The hierarchy is carried by size and weight instead. */
.hero .label { color: var(--paper); }

.hero .statement { max-width: 15ch; }

.hero__sub {
  margin-top: var(--s-4);
  max-width: 44ch;
  font-size: var(--t-body);
}

.hero__cta {
  margin-top: var(--s-5);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
}

/* ------------------------------------------------------------- paper stock -
   Crumpled paper under the delivery section.

   The reference for this was a blue crumple. Blue is not in the palette: the
   guide retires Rock Blue and says the retired tints do not come back as
   supporting colours. So the texture file is greyscale and centred on 128, and
   it is composited with soft-light over --paper. The colour therefore comes
   from the token and nothing else. A greyscale file cannot introduce a hue
   however it is blended, which is the point of building it this way.

   assets/paper-texture.jpg is generated, not licensed: a ridged multifractal
   lit from the top left, tiling exactly. reference/build-texture.py rebuilds it. */

.section--stock {
  background-color: var(--paper);
  background-image: url("../assets/paper-texture.jpg");
  background-size: 512px 512px;
  background-repeat: repeat;
  background-blend-mode: soft-light;
}

/* -------------------------------------------------------- delivery table --
   Mono led rather than carded. Four rows of input and output, which is the
   shape the status views in the brand guide use, and about half the height a
   card would need for the same words. */

.deliver {
  margin-top: var(--s-6);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-4);
}

.deliver__item {
  position: relative;
  background: var(--card);
  border-radius: var(--radius-card-lg);
  box-shadow: var(--lift);
  padding: var(--s-4);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* The badge. Ink filled, paper numeral, and a circle rather than the brand's
   square: a filled square this size would compete with the mark. */
.deliver__badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-weight: var(--w-mono-strong);
  font-size: var(--t-control);
  letter-spacing: 0.04em;
  margin-bottom: var(--s-3);
}

.deliver__item .subhead { margin-bottom: var(--s-3); }

.io { display: block; }
.io + .io { margin-top: var(--s-3); }
.io__k { display: block; margin-bottom: 2px; }

.io__k {
  font-family: var(--mono);
  font-weight: var(--w-mono);
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  color: var(--graphite);
}

.io__v {
  font-family: var(--body);
  font-size: var(--t-body-floor);
  line-height: var(--lh-body-floor);
  color: var(--ink);
}

/* --------------------------------------------------------------------- work -
   Three decks. The cover is the button, so the whole card is one target and
   there is no separate "view" link to tab past. */

.work {
  margin-top: var(--s-6);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-5) var(--s-4);
}

.deck { min-width: 0; }

.deck__open {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.deck__cover {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--rule-on-paper);
  border-radius: var(--radius-card);
  background: var(--card);
  transition: border-color var(--dur) var(--ease);
}

.deck__open:hover .deck__cover { border-color: var(--ink); }

.deck__meta { display: block; margin-top: var(--s-3); }
.deck__name { display: block; margin-top: var(--s-1); }
.deck__count { display: block; margin-top: var(--s-2); }
.deck__line { margin-top: var(--s-3); }

/* ------------------------------------------------------------------ viewer -
   A native dialog: the browser supplies the focus trap, the backdrop and the
   Escape key. Hand-rolled modals get all three wrong. */

.viewer {
  width: min(1200px, 94vw);
  max-width: none;
  padding: 0;
  border: 0;
  border-radius: var(--radius-card-lg);
  background: var(--ink);
  color: var(--paper);
  overflow: hidden;
}

.viewer::backdrop { background: rgba(20, 32, 43, 0.86); }

.viewer__bar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--panel-border-on-ink);
}

.viewer__bar .label { color: var(--paper); margin: 0; }
.viewer__bar #viewer-count { margin-left: auto; color: var(--secondary-on-ink); }

.viewer__close {
  padding: 7px 12px;
  border: 1px solid var(--panel-border-on-ink);
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--paper);
  font-family: var(--mono);
  font-weight: var(--w-mono-strong);
  font-size: var(--t-label);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  cursor: pointer;
}

.viewer__close:hover { background: var(--paper); color: var(--ink); }

.viewer__stage { position: relative; background: var(--ink); }
.viewer__stage img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; }

.viewer__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  border: 1px solid var(--panel-border-on-ink);
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.viewer__nav:hover { background: var(--paper); color: var(--ink); }
.viewer__nav[disabled] { opacity: 0.35; cursor: default; }
.viewer__nav--prev { left: var(--s-3); }
.viewer__nav--next { right: var(--s-3); }

@media (max-width: 900px) {
  .work { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------------ pricing */

.tiers {
  margin-top: var(--s-6);
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(3, 1fr);
}

/* On an ink ground a tier is a hairline panel, never a white card. The price
   carries the hierarchy instead of a fill: it runs at heading scale, with the
   word "from" demoted to a mono label above it so the number stands alone. */
.tier {
  border: 1px solid var(--panel-border-on-ink);
  border-radius: var(--radius-card-lg);
  box-shadow: var(--lift);
  padding: var(--s-5) var(--s-4);
  display: flex;
  flex-direction: column;
}

/* The one tier worth pointing at. The border is signal: measured at 3.36:1
   against ink, which clears the 3:1 a non-text element needs. The chip stays
   paper on ink, because chip text at 12px would need 4.5:1 and red would
   give 3.36. */
.tier--flagged { border-color: var(--signal); }

.tier__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--panel-border-on-ink);
}

.tier__flag {
  flex: none;
  padding: 4px 8px;
  border-radius: var(--radius-control);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-weight: var(--w-mono-strong);
  font-size: var(--t-label);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
}

.tier__from { display: block; margin-top: var(--s-4); }

.tier__price {
  font-family: var(--display);
  font-weight: var(--w-display);
  font-size: var(--t-heading);
  line-height: var(--lh-display);
  letter-spacing: var(--track-heading);
  font-optical-sizing: auto;
  color: var(--paper);
  margin-top: var(--s-1);
}

.tier__unit { display: block; margin-top: var(--s-2); }

.tier__list {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--panel-border-on-ink);
  display: grid;
  gap: var(--s-2);
}

.tier__list li {
  font-family: var(--mono);
  font-weight: var(--w-mono);
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  color: var(--secondary-on-ink);
}

.tiers__foot {
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule-on-ink);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3) var(--s-5);
}

.tiers__foot .body { max-width: 62ch; }

/* ---------------------------------------------------------------------- faq */

.faq { margin-top: var(--s-6); max-width: var(--measure-max); }

.qa { border-top: 1px solid var(--rule-on-paper); }
.qa:last-child { border-bottom: 1px solid var(--rule-on-paper); }

.qa summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-4) 0;
  cursor: pointer;
  font-family: var(--display);
  font-weight: var(--w-display);
  font-size: var(--t-subhead);
  line-height: var(--lh-subhead);
  letter-spacing: var(--track-heading);
  font-optical-sizing: auto;
  list-style: none;
}

.qa summary::-webkit-details-marker { display: none; }

.qa summary::after {
  content: "+";
  flex: none;
  font-family: var(--mono);
  font-weight: var(--w-mono-strong);
  font-size: var(--t-control);
  color: var(--graphite);
}

.qa[open] summary::after { content: "\2212"; }

.qa__a { padding-bottom: var(--s-4); }
.qa__a .body + .body { margin-top: var(--s-3); }

/* ------------------------------------------------------------------ contact -
   The copy sits beside the fields rather than above them. Stacked, the heading
   and three lines of prose pushed the first input most of a screen down. */

.contact__grid {
  margin-top: var(--s-6);
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: var(--s-6) var(--s-7);
  align-items: start;
}

.contact__intro { position: sticky; top: calc(var(--nav-h) + var(--s-5)); }
.contact__intro .body { max-width: 40ch; }

@media (max-width: 1000px) {
  .contact__grid { grid-template-columns: 1fr; gap: var(--s-5); }
  .contact__intro { position: static; }
}

/* --------------------------------------------------------------------- form -
   Validation errors are a status, not red text and not a red border. CLAUDE.md
   is explicit: an error is "action required", so it renders as a signal square
   with a mono uppercase label. Fields in error are marked with an ink border,
   which is a weight change rather than a colour change, and with aria-invalid.
   That keeps the error legible to someone who cannot see the square at all. */

.form { max-width: none; }

.form__row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s-4);
}

.field { display: flex; flex-direction: column; gap: var(--s-2); }
.form > * + * { margin-top: var(--s-3); }

.field__label {
  font-family: var(--mono);
  font-weight: var(--w-mono);
  font-size: var(--t-control);
  line-height: var(--lh-label);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  color: var(--ink);
}

.field__input {
  width: 100%;
  padding: 12px 14px;
  background: var(--card);
  border: 1px solid var(--rule-on-paper);
  border-radius: var(--radius-control);
  font-family: var(--body);
  font-size: var(--t-body-floor);
  line-height: var(--lh-body-floor);
  color: var(--ink);
  transition: border-color var(--dur) var(--ease);
}

.field__input--area { resize: vertical; min-height: 6em; }

/* The file field. The native control keeps its own button, restyled to the
   brand's, so there is no JS standing between the user and the file dialog. */
.field__input--file { padding: 9px 10px; cursor: pointer; }

.field__input--file::file-selector-button {
  margin-right: var(--s-3);
  padding: 7px 12px;
  border: 1px solid var(--ink);
  border-radius: var(--radius-control);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-weight: var(--w-mono-strong);
  font-size: var(--t-label);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  cursor: pointer;
}

.on-ink .field__input--file::file-selector-button {
  border-color: var(--paper);
  background: var(--paper);
  color: var(--ink);
}

.filelist:empty { display: none; }
.filelist { display: grid; gap: var(--s-1); margin-top: var(--s-2); }

.filelist li {
  font-family: var(--mono);
  font-weight: var(--w-mono);
  font-size: var(--t-label);
  line-height: var(--lh-label);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  color: var(--secondary-on-ink);
}

.field__note { display: block; margin-top: var(--s-1); }

/* The native date picker. color-scheme tells the browser to draw its calendar
   and its indicator for a dark ground, otherwise both come out near-black on
   ink and the control looks broken. */
.field__input--date { cursor: pointer; }
.on-ink .field__input--date { color-scheme: dark; }
.field__input--date::-webkit-calendar-picker-indicator { cursor: pointer; opacity: 0.7; }
.field__input--date:hover::-webkit-calendar-picker-indicator { opacity: 1; }

/* The select keeps the native control but takes the field's shape. */
.field__input--select {
  appearance: none;
  padding-right: var(--s-5);
  cursor: pointer;
}
.field__input::placeholder { color: var(--graphite); }
.field__input:hover { border-color: var(--graphite); }

/* Error state. A weight change, never a colour change. */
.field__input[aria-invalid="true"] { border-color: var(--ink); border-width: 2px; padding: 11px 13px; }

/* The honeypot. Off-screen rather than display:none, because some bots skip
   what is hidden outright. */
.field--trap {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.form__foot {
  margin-top: var(--s-4) !important;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-4);
}

.form__status:empty { display: none; }

.form__status {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0;
}

.form__status::before {
  content: "";
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 1px;
  background: var(--ink);
}

/* The one status that is action required, so the one that carries signal. */
.form__status--error::before { background: var(--signal); }

.form__status span {
  font-family: var(--mono);
  font-weight: var(--w-mono-strong);
  font-size: var(--t-control);
  letter-spacing: var(--track-label);
  word-spacing: var(--word-gap-label);
  text-transform: uppercase;
  color: var(--ink);
}

/* On ink. CLAUDE.md: a hairline at 12% paper, not a lightened panel, so the
   fields are outlines rather than boxes. */
.on-ink .field__label { color: var(--paper); }

.on-ink .field__input {
  background: transparent;
  border-color: var(--panel-border-on-ink);
  color: var(--paper);
}

/* Chrome forces its own pale background on autofilled fields, which punches a
   light box through the ink ground. The inset shadow is the only way to
   override it, and text-fill-color is the only way to keep the type paper. */
.on-ink .field__input:-webkit-autofill,
.on-ink .field__input:-webkit-autofill:hover,
.on-ink .field__input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--paper);
  -webkit-box-shadow: 0 0 0 1000px var(--ink) inset;
  caret-color: var(--paper);
  transition: background-color 9999s linear;
}

.on-ink .field__input::placeholder { color: var(--secondary-on-ink); }
.on-ink .field__input:hover { border-color: var(--paper); }
.on-ink .field__input[aria-invalid="true"] { border-color: var(--paper); }
.on-ink .field__input--select option { color: var(--ink); }
.on-ink .form__status::before { background: var(--paper); }
.on-ink .form__status--error::before { background: var(--signal); }
.on-ink .form__status span { color: var(--paper); }

.btn--wide { width: 100%; justify-content: center; }

@media (max-width: 720px) {
  .form__row { grid-template-columns: 1fr; }
}

/* The confirmation that replaces the form. The square is ink, not signal:
   a message going through is not a state that needs action. */
.done {
  border: 1px solid var(--panel-border-on-ink);
  border-radius: var(--radius-card-lg);
  padding: var(--s-5) var(--s-4);
}

.done__line { margin-top: var(--s-3); }
.done .body { margin-top: var(--s-3); max-width: 44ch; }
.done__btn { margin-top: var(--s-4); }
.on-ink .done .status__square { background: var(--paper); }
.on-ink .done .status__label { color: var(--paper); }

/* ------------------------------------------------------------------- footer */

.footer {
  border-top: 1px solid var(--ink);
  padding-block: var(--s-6);
}

.footer__in {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--s-5);
}

.footer__logo { display: block; width: clamp(260px, 26vw, 320px); height: auto; }

.footer__contact { display: grid; gap: var(--s-2); }

/* -------------------------------------------------------------- thanks page */

/* Sticky footer, so the sign-off sits in the middle of the screen rather than
   above a fold that has nothing under it. */
.page-signoff { min-height: 100svh; display: flex; flex-direction: column; }
.page-signoff .footer { margin-top: auto; }

.thanks {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--s-5);
  padding-block: var(--s-7);
}

.thanks .statement { max-width: 18ch; }
.thanks__copy { max-width: 46ch; margin-inline: auto; }

/* The sign-off lockup signs the page. It is the last thing, the way a
   signature is, and it is the only place the tagline appears on this site. */
.thanks__logo {
  display: block;
  width: clamp(260px, 44vw, 420px);
  height: auto;
  margin-top: var(--s-4);
}

.footer--ink { border-top-color: var(--rule-on-ink); }
.footer--ink .footer__in { justify-content: center; text-align: center; }

/* --------------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .tiers { grid-template-columns: 1fr; }
  .deliver { grid-template-columns: repeat(2, 1fr); gap: var(--s-4) var(--s-5); }
  .hero__collage { grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(3, 1fr); }
  .hero::after {
    background: linear-gradient(to bottom, var(--ink) 0%, var(--ink) 62%, transparent 100%);
  }
  .section { padding-block: var(--s-6); }
}

@media (max-width: 720px) {
  .nav__in { flex-direction: column; align-items: flex-start; gap: var(--s-3); }
  .nav__links { justify-content: flex-start; width: 100%; }
  .qa summary { gap: var(--s-3); }
  .deliver { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  .hero__collage { grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(4, 1fr); }
}

@media (max-width: 420px) {
  .nav__links { gap: var(--s-2) var(--s-3); }
}
