/* Mizell Software — the one-page site.
 *
 * THIS IS A FILE, NOT A <style> BLOCK, and that is load-bearing. The server sends
 * `style-src 'self'` with no 'unsafe-inline'. Moving any of this into the document — a block or a
 * style attribute — makes it silently stop applying, and adding 'unsafe-inline' to get it back
 * weakens the header for the whole site. Same reasoning puts the form handler in public/form.js.
 *
 * EVERY TYPE SIZE COMES FROM THE SCALE IN fonts.css. Not one px value for type appears below: the
 * scale is --step--1 through --step-6 and a size outside it is a decision somebody made by eye at
 * 2am. tests/homepage.test.mjs fails on a font-size that is not a var(--step-*).
 *
 * COLOUR, MEASURED RATHER THAN EYEBALLED. Computed with the WCAG 2.x relative-luminance formula
 * against the ground each one actually lands on, not against white:
 *
 *   ink       #1b1c18  on paper #f6f4ef   15.58:1   body, headings, the wordmark   (needs 4.5:1)
 *   ink-quiet #55574e  on paper #f6f4ef    6.68:1   meta lines, field labels, foot (needs 4.5:1)
 *   accent    #2e5c3e  on paper #f6f4ef    7.03:1   every link on the page         (needs 4.5:1)
 *   alert     #8a2f1f  on paper #f6f4ef    7.62:1   failure messages, invalid boxes (needs 4.5:1)
 *   paper     #f6f4ef  on ink   #1b1c18   15.58:1   the submit button's label      (needs 4.5:1)
 *   rule      #8c8577  on paper #f6f4ef    3.33:1   hairlines and input borders    (needs 3:1)
 *
 * The hairline is darker than a hairline usually is on purpose: 1.4.11 asks 3:1 of any non-text
 * element carrying meaning, an input border is the only thing telling a reader where the box is,
 * and arguing about whether it carries meaning costs more than two shades of grey.
 *
 * ONE FLAT INK, ONE FLAT PAPER. No gradient anywhere — not a ground, not a hue that animates, not a
 * tinted band to break up the page. Bands are separated by a hairline and by space. A signage face
 * over a disco ground is a costume, and the sibling product's animating hue field was an owner
 * directive for a consumer music audience, not a house style to copy here.
 *
 * NO IMAGERY, GENERATED OR OTHERWISE, AND NO EMPTY SLOT WHERE ONE WOULD GO. Real photographs only
 * on this site and his have not arrived. A styled grey box advertises the gap; white space does
 * not. There is deliberately no `background-image` in this file and no rule waiting for one. */

:root {
  --paper: #f6f4ef;
  --ink: #1b1c18;
  --ink-quiet: #55574e;
  --accent: #2e5c3e;
  --alert: #8a2f1f;
  --rule: #8c8577;

  /* ────────────────────────────────────────────────────────────────────────────────────────────
     ALIGNMENT. THE OWNER'S RULING GOES HERE AND NOWHERE ELSE — ticket-1786067055076-lextp7.
     Centred-versus-left-set is an open owner fork. This ships LEFT because the research names the
     centred hero as the default this page falls into, and every alignment decision on the page
     reads this ONE token: `text-align` takes it directly, and the flex columns take it as
     `align-items`, which is why the value is the logical keyword `start` rather than `left`.
     Change `start` to `center` on the next line and the whole page flips. Nothing else to touch.
     ──────────────────────────────────────────────────────────────────────────────────────────── */
  --align: start;

  /* SPACING, not type — the scale in fonts.css governs font sizes and these are the gaps between
     things. Same idea though: a short ratio-based list, so nothing lands on a number chosen by eye. */
  --space-xs: 0.4rem;
  --space-s: 0.75rem;
  --space-m: 1.5rem;
  --space-l: 3rem;
  --space-xl: 5.5rem;

  /* THE TAP TARGET, AND IT IS THE ONE RAW PIXEL VALUE IN THIS FILE THAT IS SUPPOSED TO BE ONE —
     ticket-1786091236984-msbilc. Everything else here is type (the scale in fonts.css) or space
     between things (the five above), and both of those scale with the screen. This is a finger. A
     finger is the same size on a 375px phone and a 1440px laptop, so a relative unit here would be
     the wrong answer rather than the tidier one. 44px is the figure the platform guidance has used
     for a decade and it clears WCAG 2.5.5's 44px comfortably; 2.5.8's floor of 24px is the one these
     links technically already met, because they sit isolated enough for the spacing exception. The
     buyer this page is written for is tapping in a van with a work glove half off. */
  --tap: 44px;

  /* The page runs 4–6 screens at 1280 rather than one. A one-screen landing page was an
     owner-rejected failure on the sibling product, and all five measured references ran 7–17. */
  --band-width: 46rem;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--paper);
  /* The hero link and the header address both jump to anchors on this same page; without this the
     target lands flush against the viewport edge with its heading half off screen. */
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  /* There is no animation on this page at all — this is here because `scroll-behavior: smooth` IS
     motion, and it is the one piece of it that would otherwise slip past the preference. */
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--leading-body);
  -webkit-text-size-adjust: 100%;

  /* A SHORT PAGE STILL ENDS AT THE BOTTOM OF THE WINDOW — ticket-1786112469951-mg6xp1.
     WRITTEN FOR ANY SHORT PAGE, NOT FOR THE ONE THAT FOUND IT. Measured on /contact at 1440x900:
     the footer's hairline landed at y=547 with 232px of empty paper beneath it, which does not read
     as broken — the ground is continuous paper, so there is no white band — it reads as UNFINISHED,
     on the one page whose entire job is to stop a prospect concluding this company cannot keep a
     website up. The homepage never needed this because it is 6,077px tall at that width.

     `svh`, NOT `vh`. On a phone `vh` is the LARGE viewport — the height the window has only once the
     browser chrome has retracted — so a `vh` rule makes a short page taller than the screen it is on
     and puts a scrollbar on a page that fits. The 404 happens to fit exactly at 375 today, which is
     precisely the condition that would let the wrong unit look right until the copy changed by one
     line. A browser too old for `svh` drops this declaration and gets today's behaviour back, which
     is the correct thing to degrade to. */
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

/* The region that takes the slack, so the footer is pushed rather than stretched. `main`, not the
   404 page's own band class: this is the first short page the site has had and it will not be the
   last, and a rule keyed on that page's markup has to be rediscovered by whoever writes the second.
   `flex-grow: 1` distributes FREE space and there is none on a tall page, so the homepage does not
   move; `flex-shrink: 0` is what stops a long page being squeezed to fit the window instead. */
main {
  flex: 1 0 auto;
}

/* ── Skip link ─────────────────────────────────────────────────────────────────────────────────
   Off screen until it is focused, then it is the first thing a keyboard reaches. Moved with a
   position offset rather than `display: none`, which would take it out of the focus order and make
   it decorative. */
.skip-link {
  position: absolute;
  top: var(--space-s);
  left: var(--space-s);
  transform: translateY(-400%);
  z-index: 2;
  padding: var(--space-xs) var(--space-s);
  background: var(--ink);
  color: var(--paper);
  font-size: var(--step--1);
}

.skip-link:focus {
  transform: translateY(0);
}

/* ── Bands ─────────────────────────────────────────────────────────────────────────────────────
   Seven of them, separated by space and a hairline. Never by a second background colour. */
.band {
  padding: var(--space-xl) var(--space-m);
  border-top: 1px solid var(--rule);
}

.band--header,
.band--hero {
  border-top: 0;
}

.band--header {
  padding-top: var(--space-l);
  padding-bottom: 0;
}

.band--hero {
  padding-top: var(--space-l);
}

.band--footer {
  padding-top: var(--space-l);
  padding-bottom: var(--space-l);
}

.wrap {
  display: flex;
  flex-direction: column;
  align-items: var(--align);
  max-width: var(--band-width);
  margin: 0 auto;
  text-align: var(--align);
}

.wrap--split {
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-m);
  /* The header is a row and stays a row: the wordmark and the address are not a stack. Below the
     narrowest phone they wrap, which is what `flex-wrap` is for. */
  flex-wrap: wrap;
  text-align: start;
}

/* Prose never runs wider than the measure in fonts.css, whatever the band does. */
.wrap > p,
.wrap > .receipt > p,
.wrap > .work-row > p,
.wrap > dl,
.wrap > form,
.wrap > noscript {
  max-width: var(--measure);
}

/* ── Type ──────────────────────────────────────────────────────────────────────────────────────── */
h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

h1 {
  margin: 0;
  font-size: var(--step-5);
  line-height: var(--leading-display);
  max-width: 20ch;
}

h2 {
  margin: 0 0 var(--space-m);
  font-size: var(--step-3);
  line-height: var(--leading-heading);
  max-width: 26ch;
}

h3 {
  margin: 0 0 var(--space-s);
  font-size: var(--step-1);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-tight);
  max-width: 34ch;
}

p {
  margin: 0 0 var(--space-m);
}

p:last-child {
  margin-bottom: 0;
}

/* ── Links. Underlined, never colour-only: colour on its own is not a link affordance for a reader
   who cannot separate these two hues, and both of ours are dark. ───────────────────────────────── */
a {
  color: var(--accent);
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-thickness: 2px;
}

/* ── The links a thumb has to hit — ticket-1786091236984-msbilc ─────────────────────────────────
   MEASURED AT 375, BEFORE: every one of these came back 15.00px tall. Both receipt links and every
   hello@mizellsoftware.com on the site. The receipts are the second most important click on this
   page after the submit button — they are the proof, and the whole trust position is that a prospect
   can go and check the two live things — and the addresses are the way out of every failure branch
   the form has. AFTER: 44.00px, with the widths and the line positions unchanged.

   NOT A BUTTON, AND THAT IS THE POINT OF DOING IT THIS WAY. The reflex fix is a pill with a ground
   and a radius, which is the exact slop the design brief names and would break the page's argument —
   prose rows, not cards. There is no background, no border, no radius and no change of type size
   here: the link looks identical and the box around it got bigger.

   VERTICAL PADDING PLUS A CANCELLING NEGATIVE MARGIN. `(--tap - 1lh) / 2` grows the border box to
   exactly 44px whatever size the text is, and `(1lh - --tap) / 2` takes the same amount back off the
   margin box — so the MARGIN box stays exactly one line tall and the surrounding rhythm cannot move.
   Dropping the margin half is the likeliest thing a later pass does, and it shoves every line on the
   page apart. Measured against `1lh` rather than against the step these six happen to share, so a
   link set at another size still lands on 44 rather than on whatever that arithmetic produced.

   AND THE MAX-WIDTH IS NOT DECORATION. `inline-block` makes the link ATOMIC — it stops breaking
   across lines — and two of these addresses sit mid-sentence rather than isolated (the line beside
   the form and the footer colophon). Without a clamp, a container narrower than the address puts a
   horizontal scrollbar on a phone; with it, the box wraps inside itself the way the text used to.
   Checked at 375: the widest of them is 224.61px in a 327px column, so nothing wraps today — this is
   the guard for the day the copy or the container changes, not a fix for something visible now.

   The attribute selector is deliberate: it catches every address on the site, including the line
   form.js removes when it loads (visible only when the script never arrives, which is the one branch
   where the address is the ONLY way to reach him) and both on the 404 page, which reuses these
   classes and has no stylesheet of its own. */
.receipt-link a,
a[href^='mailto:'] {
  display: inline-block;
  max-width: 100%;
  overflow-wrap: break-word;
  padding-block: calc((var(--tap) - 1lh) / 2);
  margin-block: calc((1lh - var(--tap)) / 2);
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* ── Band 0, header ────────────────────────────────────────────────────────────────────────────── */
.wordmark {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-1);
  letter-spacing: var(--tracking-tight);
}

.header-contact {
  margin: 0;
  font-size: var(--step--1);
}

/* ── Band 1, hero ──────────────────────────────────────────────────────────────────────────────── */
.hero-body {
  margin-top: var(--space-m);
  font-size: var(--step-1);
  letter-spacing: var(--tracking-tight);
}

.hero-action {
  margin-top: var(--space-m);
}

/* ONE link, not two buttons. It is a thing you click with a noun in it, not a verb in a pill. */
.hero-link {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-1);
  letter-spacing: var(--tracking-tight);
}

/* ── Band 2, receipts ──────────────────────────────────────────────────────────────────────────── */
.receipt {
  margin-bottom: var(--space-xl);
  max-width: var(--measure);
}

.receipt-meta {
  color: var(--ink-quiet);
  margin-bottom: var(--space-xs);
}

/* The one hard number in each row, in the mono face. `data-numeric` in fonts.css already sets the
   family and tabular figures; this only makes it sit up in the sentence. */
.figure {
  color: var(--ink);
  font-weight: 700;
}

.receipt-link {
  font-size: var(--step--1);
}

.receipt-honesty {
  padding-top: var(--space-m);
  border-top: 1px solid var(--rule);
  max-width: var(--measure);
}

/* ── Band 3, what I actually do ────────────────────────────────────────────────────────────────
   THREE UNEQUAL ROWS, and the inequality is the argument. Three cards of the same size imply that
   discovery, build and care are parallel and interchangeable; they are not, they are a dependency
   chain, and the first is where nearly all of the value is. The rows get narrower and quieter as
   they go, so the shape of the section says that before the words do. */
.work-row {
  margin-bottom: var(--space-l);
}

.work-row--first {
  max-width: var(--measure);
}

.work-row--second {
  max-width: 56ch;
}

.work-row--third {
  max-width: 44ch;
  color: var(--ink-quiet);
}

.work-row--third h3 {
  color: var(--ink);
}

/* ── Band 4, the audit ─────────────────────────────────────────────────────────────────────────── */
.price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-s);
  margin-bottom: var(--space-l);
}

.price-figure {
  font-size: var(--step-4);
  font-weight: 700;
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
}

.price-note {
  color: var(--ink-quiet);
  font-size: var(--step--1);
}

.terms {
  margin: 0;
  max-width: var(--measure);
}

.terms dt {
  margin-bottom: var(--space-xs);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-0);
  letter-spacing: var(--tracking-tight);
}

.terms dd {
  margin: 0 0 var(--space-m);
}

.terms dd:last-child {
  margin-bottom: 0;
}

/* ── Band 5, what it costs after that ──────────────────────────────────────────────────────────── */
.ladder {
  padding-top: var(--space-m);
  border-top: 1px solid var(--rule);
}

/* ── The two sentences that carry a reader to the form ───────────────────────────────────────────
   One closes band 4, one closes band 5 — ticket-1786102545722-7py4ig. Body size, body colour, body
   face: it is prose with a link in it and it has to read as the next thing he says, not as a control
   somebody bolted onto the end of the section. There is deliberately NO rule here (that would be a
   third hairline in the pricing band alone), no ground, no border, no padding and no width of its
   own — space above it is the entire treatment.

   THE THINGS THIS MUST NEVER BECOME are named because the reflex is strong: a sticky bar, a button
   bar, a card. `position: fixed` and `position: sticky` appear nowhere in this file and
   tests/intake-escape-hatches.test.mjs is what keeps it that way. */
.onward {
  margin-top: var(--space-l);
  margin-bottom: 0;
}

/* ── Band 6, intake ────────────────────────────────────────────────────────────────────────────── */
.intake-lede {
  color: var(--ink-quiet);
  margin-bottom: var(--space-l);
}

.intake {
  width: 100%;
  max-width: var(--measure);
}

.field {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-xs);
  margin-bottom: var(--space-m);
  text-align: start;
}

.field label {
  color: var(--ink-quiet);
  font-size: var(--step--1);
}

.field input,
.field textarea {
  width: 100%;
  padding: var(--space-s);
  border: 1px solid var(--rule);
  border-radius: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  /* Inputs do not inherit the document's font size, and anything under 16px makes iOS Safari zoom
     the whole page on focus — which reads as the layout breaking when a field is tapped. */
  font-size: var(--step-0);
  line-height: var(--leading-body);
}

.field textarea {
  resize: vertical;
}

.field input:focus-visible,
.field textarea:focus-visible {
  border-color: var(--ink);
}

/* ── The invalid state, AND IT IS NOT COLOUR ALONE ──────────────────────────────────────────────
   1.4.1 is the rule and a red border is the classic way to break it: a reader who cannot separate
   these two dark hues sees an unchanged box. So the box also gains a THICK LEFT RULE — a change in
   shape, visible with no colour perception at all — and the sentence saying what is wrong sits
   directly underneath it. Three signals: shape, colour, words.

   Driven by [aria-invalid='true'] rather than a class, so the visible state and the state a screen
   reader announces cannot come apart — there is only one attribute, and setting it does both. */
.field input[aria-invalid='true'],
.field textarea[aria-invalid='true'] {
  border-color: var(--alert);
  border-left-width: 4px;
}

/* The message for one box, beside that box. 7.62:1 on paper. */
.field-error {
  margin: 0;
  color: var(--alert);
  font-size: var(--step--1);
}

.submit-row {
  margin-bottom: var(--space-m);
}

/* The one dark clickable piece on the page. Ink ground, paper label — 15.58:1. */
button[type='submit'] {
  padding: var(--space-s) var(--space-m);
  border: 1px solid var(--ink);
  border-radius: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-0);
  letter-spacing: var(--tracking-tight);
  cursor: pointer;
}

button[type='submit'][disabled] {
  /* Not a faded ghost: a disabled control still has to be legible, because the reason it is
     disabled is explained right underneath it and has to be readable too. */
  background: var(--paper);
  color: var(--ink-quiet);
  border-color: var(--rule);

  /* AND THE SHAPE HAS TO SAY IT, WHICH IS THE HALF THAT WAS MISSING — ticket-1786091236695-w7ugp3.
     Paper ground, quiet ink and a hairline rule is what an ordinary outlined button looks like on
     this page, so a visitor whose script never arrived read a dead control as a pressable one, typed
     all three boxes and pressed it. A dashed rule is not a button anybody has ever pressed, and it
     is legible with no colour perception at all — the same argument as the thick left rule on an
     invalid box. Not opacity and not a faded label: the sentence saying WHY it is disabled sits
     directly underneath, and dimming the control to prove a point costs the reader that sentence. */
  border-style: dashed;
  cursor: not-allowed;
}

.feedback {
  margin-bottom: 0;
  /* Empty until the form says something. `role="status"` on an element with no text announces
     nothing, which is the correct resting state. */
  max-width: var(--measure);
}

.feedback--error {
  color: var(--alert);
}

.feedback--sent {
  padding-top: var(--space-s);
  border-top: 1px solid var(--rule);
}

/* The three quiet notes around the form. `.intake-fallback` is the one form.js removes when it
   arrives, so it is only ever on screen next to a button that cannot be pressed — it is set like the
   other two rather than as an alarm, because it is a way out, not a warning. */
.noscript-note,
.intake-fallback,
.intake-alt {
  margin-top: var(--space-m);
  color: var(--ink-quiet);
  font-size: var(--step--1);
  max-width: var(--measure);
}

/* ── Band 7, footer ────────────────────────────────────────────────────────────────────────────── */
.colophon {
  margin: 0;
  color: var(--ink-quiet);
  font-size: var(--step--1);
}
