/* =============================================================================
   ALL AMERICAN INNOVATIONS — site.css

   THE METAPHOR, from which every rule below is deduced:

     THIS PAGE IS A CONTRACTOR'S LETTERHEAD AND ESTIMATE SHEET, SET IN ONE
     GOTHIC FACE ON GOOD PAPER. The ink sits IN the paper; every line that
     crosses the sheet is a rule doing a job; the mark is a rule, not a
     picture; and the only things permitted to be pictures are photographs
     of finished work.

   1. "the ink sits in the paper" — no drop shadow, anywhere. Depth is three
      papers (canvas / surface / sink) and 1px hairlines. The paper tooth is
      painted INTO each ground, behind the type, never over it.
   2. "the mark is a rule" — the brand is one accent rule over one dark
      hairline, under the letterhead and nowhere else. That pair IS the
      mark; a picture behind the type would be a costume.
   3. "one gothic face" — Public Sans, the face the United States designed
      for its own forms. Display at 800, prose at 400, labels at 700 in
      capitals. Every size is on a 4px lead (--lead), rounded from a 1.2
      scale, and the grid travels with the reader's own text size.
   4. "photographs of finished work" — the job book and the before/after
      comparisons are the earned flourish, and the only one.

   THE READER: a homeowner in Middle Tennessee, often older, on a phone, who
   wants to know three things: do they do my kind of job, how do I reach
   them, and what happens when I do. So: body 20px and not behind a toggle;
   nothing under 16px; every target 56px; the telephone within a thumb's
   reach on every screen; every gesture also a button.

   PALETTE — WORKWEAR. The owner's ruling (2026-09-02, reversing the navy
   and red of the sign): "i dont like the colors, looks too much like a
   political website." A company called All American, set in navy and
   campaign red, reads as a yard sign, and the work is remodeling. So the
   colours are now the ones the trade already wears: graphite so deep it
   reads as black, a warm off-white paper one step off pure so it does not
   glare on a phone in the sun, charcoal bands (the thumb bar, the terms,
   the colophon) with white type, and a burnt orange held for the accent
   rule, the telephone, the tagline and the underline of a link, and
   nowhere else.

   Every palette is a named set in tools/palette.py, which MEASURES WCAG
   contrast before it writes and refuses to apply one that fails: body text
   7:1, secondary text and links 4.5:1, non-text affordance edges 3:1.
   Workwear, against --canvas: ink 15.0, ink-70 8.5, ink-45 5.7, ink-30 3.2,
   accent 5.4, good 5.9, stop 7.4; on --band: white 13.4, --on-band-70 8.4.
   Re-measured in the browser on the rendered page (2026-09-02): 18 distinct
   colour pairs, 0 below their floor, lowest 5.4:1.
   The accent is never set as type on the band. ink-30 is NON-TEXT ONLY.
   --rule is decorative only, never an affordance edge. No colour in this
   file is a literal; everything is a token — which is what makes a palette
   a 15-line change and lets palette.py assert that it stays that way.
   ========================================================================== */


/* ==========================================================================
   1. TOKENS
   ========================================================================== */

:root {
  --canvas:      #F3F1EA;   /* the page: cool white paper                     */
  --surface:     #FFFFFF;   /* raised: the letterhead, cards, fields          */
  --sink:        #E5E2D9;   /* recessed: the pressed state of a button        */
  --rule:        #D2CEC2;   /* DECORATIVE hairlines only                      */
  --ink:         #1B1F27;   /* graphite, so deep it reads as black            */
  --ink-70:      #434A57;   /* secondary text                                 */
  --ink-45:      #5C6470;   /* captions, labels                               */
  --ink-30:      #828A96;   /* NON-TEXT: affordance borders                   */
  --band:        #2D3B4E;   /* charcoal: bars and bands                       */
  --on-band:     #FFFFFF;   /* type on the band                               */
  --on-band-70:  #C6CEDA;   /* secondary type on the band                     */
  --rule-on-band:#4E5F76;   /* a hairline on the band                         */
  --accent:      #9E3025;   /* burnt orange: the rule, the telephone, a link  */
  --good:        #35664A;   /* a thing that went right                        */
  --stop:        #93271E;   /* a thing that did not                           */

  --sans: "Public Sans", "Helvetica Neue", Arial, sans-serif;
  --wght: 400;

  /* THE LEAD IS 0.25rem, NOT 4px: with Larger print on the root is 20px, the
     lead is 5px, and every calc(N * var(--lead)) line box steps with it. */
  --lead: 0.25rem;

  --t-fine:      1rem;        /* 16 / 24  captions, fine print              */
  --t-ui:        1.125rem;    /* 18 / 28  labels, buttons                   */
  --t-body:      1.25rem;     /* 20 / 32  reading body. The floor.          */
  --t-h3:        1.5rem;      /* 24 / 32  card heading                      */
  --t-lead-para: 1.375rem;    /* 22 / 32  lead paragraph                    */
  --t-h2:        2rem;        /* 32 / 40  section                           */
  --t-h1:        2.5rem;      /* 40 / 44  the display line                  */
  --t-num:       2.5rem;      /* 40 / 44  the telephone                     */

  --s1: 0.25rem; --s2: 0.5rem; --s3: 0.75rem; --s4: 1rem; --s6: 1.5rem;
  --s8: 2rem; --s12: 3rem; --s16: 4rem; --s24: 6rem;

  --measure: 36rem;          /* about 66 characters of Public Sans at 20px  */
  --measure-lead: 30rem;
  --wrap: 43rem;
  --gutter: 1.25rem;
  --slug: 0rem;
  --slug-gap: 0rem;

  --rule-hair:  1px solid var(--rule);
  --rule-mid:   1px solid var(--ink-30);
  --rule-heavy: 2px solid var(--ink);
  --rule-accent: 3px solid var(--accent);

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --t-state: 120ms;

  /* The paper tooth: a stitched fractal-noise tile, black, multiplied into
     each ground. Mean 2%, peak 7%: felt, not seen. Set --paper: none to
     remove it in one line. SVG's default fill is black, which is why this
     carries no colour literal. */
  --paper: var(--paper-tooth);
  --paper-tooth:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256'%3E%3Cfilter id='p' x='0' y='0' width='256' height='256' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='4' stitchTiles='stitch' seed='7'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .065 .065 .065 0 -.078'/%3E%3C/filter%3E%3Crect width='256' height='256' filter='url(%23p)'/%3E%3C/svg%3E");

  color-scheme: light;
  accent-color: var(--accent);
}


/* ==========================================================================
   2. THE SHEET, AND THE TYPE
   ========================================================================== */

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

/* The one universal rule: re-resolves the weight axis from an inherited
   custom property on every element, so a <b> inside a heading keeps the
   heading's size and only changes weight. font-weight is always set beside
   --wght so the fallback stack still bolds. */
*, *::before, *::after { font-variation-settings: "wght" var(--wght); }

html {
  background-color: var(--canvas);          /* first paint is the paper      */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  background-color: var(--canvas);
  background-image: var(--paper);
  background-blend-mode: multiply;
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: calc(8 * var(--lead));
  --wght: 400;
  font-weight: 400;
  font-kerning: normal;
  hanging-punctuation: first last;
  overflow-x: auto;                          /* overflow scrolls, never hides */
}

::selection { background: var(--rule); color: var(--ink); }
img { max-width: 100%; height: auto; }

h1, h2, h3 { margin: 0 0 var(--s6); text-wrap: balance; --wght: 800; font-weight: 800; }
/* The display line's longest word, "construction.", MEASURED at wght 800
   with this tracking: 6.1641em. On a 320 screen in Larger print the column is
   270px, so the size may not pass 43.8px there; 13vw is 41.6px. Everywhere
   else the reader's own scale (--t-h1) is the size. */
h1 { font-size: min(var(--t-h1), 13vw); line-height: calc(11 * var(--lead)); letter-spacing: -0.02em; max-width: 16ch; }
h2 { font-size: var(--t-h2); line-height: calc(10 * var(--lead)); letter-spacing: -0.015em; max-width: 18ch; }
h3 { font-size: var(--t-h3); line-height: calc(8 * var(--lead)); letter-spacing: -0.01em; margin-bottom: var(--s3); }

p { margin: 0 0 var(--s6); max-width: var(--measure); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }
li, dt, dd, summary, figcaption, .hint, .actions-note { text-wrap: pretty; }

.lead {
  font-size: var(--t-lead-para);
  line-height: calc(8 * var(--lead));
  max-width: var(--measure-lead);
  color: var(--ink-70);
}

strong, b { --wght: 700; font-weight: 700; }

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.12em;
  text-decoration-color: var(--accent);
  text-decoration-skip-ink: auto;
}
a:visited { color: var(--ink-70); text-decoration-color: var(--ink-70); }
@media (hover: hover) and (pointer: fine) { a:hover { text-decoration-thickness: 2px; } }
a:active { color: var(--ink); text-decoration-color: var(--ink); }

:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 0; }
:focus:not(:focus-visible) { outline: none; }

/* The gothic label: capitals, tracked .08em, in ink-45. Three words or
   fewer, never a sentence. */
.place, .callbar-lbl {
  font-family: var(--sans); --wght: 700; font-weight: 700;
  font-size: var(--t-fine); line-height: calc(6 * var(--lead));
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-45);
  margin: 0 0 var(--s3); max-width: none;
}

.vh {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}


/* ==========================================================================
   3. MATERIAL — the paper tooth into the grounds that have one
   ========================================================================== */

.masthead, .work, .terms, .colophon, .estimate, .house .post {
  background-image: var(--paper);
  background-blend-mode: multiply;
  background-repeat: repeat;
}
/* Raised stock is where the reader ACTS: the letterhead that carries the
   number, and the sheet they fill in. One impression of the tooth takes white
   to a mean of 249.9 against a page ground of ~240, so a raised ground still
   reads as raised; a second impression would take it to 244.9 and spend half
   that raise, which is why there is only ever one. */
.masthead  { background-color: var(--surface); }
.estimate  { background-color: var(--surface); }
.work      { background-color: var(--canvas); }
.terms     { background-color: var(--band); color: var(--on-band); }
.colophon  { background-color: var(--band); color: var(--on-band); }


/* ==========================================================================
   4. PAGE FRAME AND RHYTHM
   ========================================================================== */

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline:
    calc(var(--gutter) + env(safe-area-inset-left, 0px))
    calc(var(--gutter) + env(safe-area-inset-right, 0px));
}
.wrap > * { min-width: 0; }

section { padding-block: var(--s12); }
/* A hairline separates two sections ONLY where the ground does not change.
   Where the paper changes, the paper has done the separating. */
/* A hairline separates two sections ONLY where the ground does not change;
   where the paper changes, the paper has already done the separating. These
   four sit on the canvas below another canvas section. .estimate (raised) and
   .house (below the charcoal band) change ground and take none. The rule's own
   pixel comes out of the padding, so the section stays on the lead. */
.work, .path, .jobs { border-top: var(--rule-hair); padding-top: calc(var(--s12) - 1px); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ink); color: var(--on-band);
  --wght: 700; font-weight: 700; font-size: var(--t-ui);
  padding: var(--s3) var(--s4); text-decoration: none;
}
.skip:focus-visible { left: var(--s4); top: var(--s2); }

.mount:empty { display: none; }
.noscript-note { padding-block: var(--s4); }
.noscript-note p { color: var(--ink-70); font-size: var(--t-ui); line-height: calc(7 * var(--lead)); }


/* ==========================================================================
   5. THE LETTERHEAD

   The band closes with the accent rule: 3px of burnt orange, 3px of paper,
   1px of graphite. It is the only ornament on the page that is not a
   photograph, and it appears once. Measured at 390: the band is 216px in normal print.
   ========================================================================== */

.masthead {
  position: relative;
  /* THE FLAG, REDUCED TO A RULE, AND IT IS THREE RULES. The company is called
     ALL AMERICAN; the sign it came from is red, white and blue. Painting the
     page in campaign navy and crimson made it a yard sign (D18), but taking
     the flag out altogether took the company's name off its own letterhead —
     which is the fault this restores. So: 3px of red, 2px of the paper, 1px of
     the band's indigo, closing the letterhead and appearing exactly once.
     That pair of colours with the bone paper between them IS the flag. A
     stars-and-stripes background would be a costume; this is a rule doing a
     job, and it is the only ornament on the page that is not a photograph.
     Drawn with a gradient so it is one border and the band's height is
     unchanged — three stacked borders would have cost three more pixels and
     the grid with them. */
  border-bottom: 6px solid transparent;
  border-image: linear-gradient(to bottom,
                  var(--accent) 0 3px,
                  var(--canvas) 3px 5px,
                  var(--band)   5px 6px) 6 / 6px;
  /* The 6px rule is subtracted from the padding beneath, so the band's height
     is a whole number of leads and everything under it stays on the grid. */
  padding-block: max(var(--s4), env(safe-area-inset-top, 0px)) calc(var(--s4) - 6px);
}
.masthead::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -7px; height: 0;
  border-top: 1px solid var(--ink); z-index: 1; pointer-events: none;
}
.masthead-in { display: grid; row-gap: 0; }
.masthead-in > * { min-width: 0; }

/* THE MARK. A builder's mark is a house: the same elevation the seven steps
   draw, cut to what still reads at 40px. Drawn in the ink, one stroke weight,
   no fill and no badge around it. It sits above the tagline on a phone and
   beside the name on a sheet, and it is the shape that carries over into the
   running head, the tab and the homescreen. */
.mark {
  /* The mark carries the WHOLE drawing now, so it is drawn at twice the size
     it was: at 68px the forty-eight paths were grey mush. The three weights are
     still compressed rather than scaled, because a hairline at this size would
     sit under one device pixel. */
  --e-heavy: 2.2; --e-mid: 1.5; --e-fine: 0.9;
  /* HEIGHT IN LEADS, NEVER auto — the same trap the elevation fell into: a
     viewBox scaled to a width gives a fractional height, and every heading on
     the page below it comes off the 4px grid by a fifth of a pixel. */
  display: block; width: calc(30 * var(--lead)); height: calc(20 * var(--lead));
  margin: 0 0 var(--s2);
  fill: none; stroke: var(--ink); stroke-width: 2;
  stroke-linecap: butt; stroke-linejoin: miter; vector-effect: non-scaling-stroke;
}
/* The structure carries the weight; the ground and the openings are lighter,
   the way an elevation is drawn. The door takes the accent, exactly as the tab
   icon and the homescreen icon do, so the three are one shape. */
.mark .st-1 path, .mark .e-fine { stroke: var(--ink-45); }
.mark .mk-door { stroke: var(--accent); }

.tagline {
  margin: 0; --wght: 700; font-weight: 700; font-size: var(--t-fine);
  line-height: calc(5 * var(--lead)); letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent);
}
.wordmark {
  margin: var(--s1) 0 0; --wght: 800; font-weight: 800;
  font-size: var(--t-h2); line-height: calc(8 * var(--lead));
  letter-spacing: -0.015em; text-transform: uppercase; color: var(--ink);
}
.wordmark-b { color: var(--ink); }
/* No letter-spacing here: the case figure is .08em, set once above. It had
   been overridden back to .1em, so the page carried three tracking figures
   for one case. .tagline keeps .14em as the wordmark lockup's own line, which
   is the single declared exception. */
.place { margin: var(--s2) 0 0; line-height: calc(5 * var(--lead)); }
.masthead .tel-line { margin-top: var(--s3); }
.masthead .reach { margin-top: var(--s2); display: none; }   /* the thumb bar has these on a phone */
/* And so is the letterhead's telephone, for the same reason: the charcoal bar
   under the thumb carries the number on every phone screen, in real markup,
   script off included. Two orange numerals in the first 570px meant the hero's
   — the one a reader is meant to press — had to compete with a smaller copy of
   itself 300px above it. Restored beside the wordmark at 840. */
.masthead .tel-line { display: none; }

/* ---- The telephone: a display numeral, tabular, in the accent ---------- */
.tel, .tel-big {
  display: block; width: max-content; max-width: 100%; --wght: 800; font-weight: 800;
  /* PROPORTIONAL, not tabular. Public Sans pads every tabular digit to a flat
     1400/2000 em, and the "1" of (615) is drawn at 876 — 0.262em of
     manufactured air, 10.5px at the hero's 40px. The numeral is a display
     line here, not a column. The one real column of figures on the page,
     the seven step numbers, keeps tabular-nums where it is set. */
  font-variant-numeric: proportional-nums lining-nums;
  color: var(--accent); text-decoration: none;
  min-height: calc(14 * var(--lead)); line-height: calc(11 * var(--lead));
  letter-spacing: -0.01em;
  /* The reader's preference sets the size (rem); the glass caps it (vw);
     a floor keeps it a display numeral on the narrowest phone. MEASURED in
     the browser: "(615) 390-0634" at wght 800, tabular, is 8.1736em wide.
     The narrowest column is a 320 screen in Larger print, 270px, so the size
     may not pass 33.0px there: 10vw is 32px, and at 390 it is 39px (319px of
     type in a 340px column). 12vw, the first guess, overflowed both. */
  font-size: max(1.5rem, min(var(--t-num), 10vw));
}
/* The letterhead's own number is a contact line, not the display numeral: the
   display numeral is the hero's, once. Three copies of a 39px number on one
   phone screen was the sign's loudness, not the letterhead's (the taste
   verifier, 2026-09-02). Above 840 the letterhead is a full sheet and the
   number takes its display size back. */
.masthead .tel { font-size: var(--t-h3); line-height: calc(8 * var(--lead)); min-height: calc(14 * var(--lead)); padding-block: var(--s3); }
.tel:hover, .tel-big:hover { text-decoration: underline; }
.tel:visited, .tel-big:visited { color: var(--accent); }
.tel-line { margin: 0; }

/* ---- Three ways to reach the company: call, text, email ----------------- */
.reach { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0; padding: 0; list-style: none; }
.reach li { margin: 0; }
.reach a, .do, .aai-btn, .bk-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  /* 14 leads: the 56px touch floor at the default root, 70px in Larger
     print, so the box is on the grid in both modes (56 is not a multiple
     of 5). */
  /* The 2px rules are inside the padding, so a label that wraps to two
     lines still makes a box of whole leads: 56 + 16 + 4 = 76 in normal print,
     70 + 16 + 4 = 90 in Larger print. "Next step" on a 320 screen in Larger
     print measured 88 before this. */
  min-height: calc(14 * var(--lead)); padding: calc(var(--s2) - 2px) var(--s6);
  --wght: 700; font-weight: 700; font-size: var(--t-ui); line-height: calc(7 * var(--lead));
  color: var(--ink); background: var(--surface); border: 2px solid var(--ink);
  text-decoration: none; cursor: pointer; border-radius: 0; font-family: var(--sans);
}
/* The doubled class beats the sheet reading.js injects after this one, which
   sets its own padding and a six-lead line for every .aai-btn; the page's own
   buttons (Back, Next step, Print it, Take it off) take the site's measure. */
.aai-btn.aai-btn { font-size: var(--t-ui); line-height: calc(7 * var(--lead)); padding: calc(var(--s2) - 2px) var(--s6); min-height: calc(14 * var(--lead)); }
.reach a:visited { color: var(--ink); }
.reach a:hover, .aai-btn:hover { background: var(--canvas); }
.reach .is-lead, .do { background: var(--ink); color: var(--on-band); border-color: var(--ink); }
.reach .is-lead:visited { color: var(--on-band); }
.reach .is-lead:hover, .do:hover { background: var(--accent); border-color: var(--accent); color: var(--on-band); }
.reach a:focus-visible, .do:focus-visible, .aai-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.aai-btn[disabled], .do[disabled] { opacity: .4; cursor: default; }


/* ==========================================================================
   6. THE OPENING
   ========================================================================== */

.hero { padding-block: var(--s8) var(--s16); }
/* THE GROUND OF THE FIRST SCREEN. Drawn at a size the section cannot contain
   and cropped by it, in --rule — the token this sheet keeps for a line that is
   decoration and nothing else — and thrown out of focus so it can be large
   without ever asking to be looked at. The blur is what makes the size safe:
   in focus at this scale it would be a picture competing with the headline; at
   3px it is weather. */
.ghost {
  /* ON EVERY SCREEN, NOT ONLY THE PAPER ONES (owner, 2026-09-02). At z-index
     -1 it sat under the section grounds, so the charcoal band, the estimate's
     white stock and the job book hid it completely — it was absent for most of
     the page. It is above the grounds now and blended: `overlay` darkens a
     light ground and lightens a dark one — except that it does not: overlay
     maps a mid grey to no change at all, which is exactly what a watermark
     wants to be drawn in, and the drawing simply disappeared on every ground
     at once. So no blend. One mid tone, --ink-30, is LIGHTER than the indigo
     band and DARKER than the bone paper and the white stock, which is all a
     watermark has to be; at 38 per cent it is weather on all three. The words
     are lifted above it, so it is a ground under the type and never a film
     over it. */
  position: fixed; z-index: 1; pointer-events: none; opacity: .38;
  --e-heavy: 2.6; --e-mid: 1.6; --e-fine: 0.9;
  right: -8%; bottom: -6%; width: 118%; max-width: none; height: auto;
  fill: none; stroke: var(--ink-30); stroke-width: 2.5;
  stroke-linecap: butt; stroke-linejoin: miter; vector-effect: non-scaling-stroke;
  filter: blur(3px);
}
@media (min-width: 52.5rem) {
  /* On a sheet it sits off to the side of the column the words are set in, and
     it can be softer still because it is larger. */
  .ghost { right: -3%; bottom: -8%; width: 62%; stroke-width: 3; filter: blur(4px); }
}
@media print { .ghost { display: none; } }
/* The words sit ABOVE the ground. Without this the fixed drawing would be a
   film over the type rather than a watermark under it. */
main > section > .wrap, .colophon > .wrap, .masthead > .wrap, .callbar, .aai-bar {
  position: relative; z-index: 2;
}
.hero-in { display: grid; gap: var(--s6); }
.hero .lead { color: var(--ink-70); }
.hero .tel-line { margin-top: var(--s2); }
.hero .reach { margin-top: var(--s4); }


/* ==========================================================================
   7. THE WORK — three kinds, as three lines of an estimate sheet

   The sign's own words are the headings. They had been 16px eyebrows under a
   copywriter's line in a boxed card with a coupon rule; the taste verifier
   (2026-09-02) called it the web's feature card, and it was. A sheet lists
   its items: the word, one sentence, three lines, a hairline, the next.
   ========================================================================== */

/* Each hairline is subtracted from the padding beside it, so a row's pitch
   stays a multiple of the lead: the rule is part of the lead, not extra to
   it. Left alone, every rule pushed everything below it one pixel off. */
/* THE LINE WEIGHTS OF AN ELEVATION. Heavy for the profile, medium for the
   planes inside it, a hairline for trim, siding and glazing bars. One weight
   throughout is exactly what makes a drawing read as an icon rather than as a
   rendering (owner, 2026-09-02). The numbers are set per drawing, because a
   40px mark and a 400px elevation cannot share a stroke. tools/make-elevation.py
   emits all three from one geometry. */
.mark .e-heavy, .build .e-heavy, .ghost .e-heavy { stroke-width: var(--e-heavy, 2); }
.mark .e-mid,   .build .e-mid,   .ghost .e-mid   { stroke-width: var(--e-mid, 1.4); }
.mark .e-fine,  .build .e-fine,  .ghost .e-fine  { stroke-width: var(--e-fine, 0.8); }

/* THE THREE KINDS. Reworked 2026-09-02 ("this section looks terrible", and it
   did): it was a heading, a grey line and three ruled rows, three times over —
   the same shape a word processor makes by itself, at one volume, with nothing
   for the eye to land on.

   What it is now is a numbered sheet. The index is the largest thing in the
   section by a factor of two and the only thing on this page bigger than a
   paragraph outside the hero, so the section has a spine and a reading order
   you can see from across the room. It is TYPE, not ornament: no box, no card,
   no shadow — the taste review killed the boxed-card family (D16) and the
   depth here is the same hairline the rest of the sheet already uses.

   The list is an <ol>, so the numbers are the list's own; `counter(kind)` only
   draws what the markup already means, and the "/ """ alt text keeps a screen
   reader from reading "zero one" before the word it belongs to. */
.kind-grid { list-style: none; margin: calc(var(--s8) - 1px) 0 0; padding: 0; max-width: var(--measure);
             border-top: var(--rule-hair); counter-reset: kind; }
.kind {
  counter-increment: kind; margin: 0; padding: var(--s6) 0 calc(var(--s8) - 1px);
  border-bottom: var(--rule-hair);
  display: grid; grid-template-columns: 3.5rem minmax(0, 1fr); column-gap: var(--s4);
}
.kind::before {
  content: counter(kind, decimal-leading-zero);
  grid-column: 1; grid-row: 1 / -1;
  font-size: var(--t-h1); line-height: calc(9 * var(--lead));
  --wght: 800; font-weight: 800; letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums lining-nums;   /* a real column of three */
  color: var(--ink-45);
}
@supports (content: "a" / "b") { .kind::before { content: counter(kind, decimal-leading-zero) / ""; } }
.kind > * { grid-column: 2; }
.kind h3 { margin: 0 0 var(--s2); max-width: none; }
.kind-say { color: var(--ink-70); }
.plain-list { list-style: none; margin: var(--s3) 0 0; padding: 0; max-width: var(--measure); }
/* The dash sits in a 12px gutter, not a 24px one. At 24 the items stood 36px
   in from the prose they belong to and read as a third column; at 12 they read
   as what they are, the same paragraph continued in short lines. */
.plain-list li {
  display: grid; grid-template-columns: var(--s3) minmax(0, 1fr); column-gap: var(--s2);
  /* NO HAIRLINE HERE. The en-dash below already marks the item, and a dash
     AND a rule is the same boundary drawn twice — which is what turned nine
     two-word phrases into a ledger and the section into a Word document
     (owner, 2026-09-02). The group rule on .kind can only read as a group
     rule once the items inside it stop drawing one each. */
  align-items: baseline; padding: var(--s2) 0;
  line-height: calc(7 * var(--lead));
}
.plain-list li::before { content: "\2013"; text-align: right; color: var(--ink-45); --wght: 500; font-weight: 500; }
@supports (content: "a" / "b") { .plain-list li::before { content: "\2013" / ""; } }


/* ==========================================================================
   8. HOW A JOB GOES — stations (path.js), or a numbered list without script
   ========================================================================== */

.steps { margin: var(--s8) 0 0; padding: 0 0 0 1.6em; max-width: var(--measure); }
.steps li { padding: var(--s3) 0 calc(var(--s3) - 1px); border-bottom: var(--rule-hair); }
.steps li:last-child { border-bottom: 0; }
.steps li::marker { --wght: 700; font-weight: 700; color: var(--ink-45); font-variant-numeric: tabular-nums; }
.steps b { --wght: 700; font-weight: 700; }

/* THE ELEVATION. A drawing, not a picture: nothing here stands in for a
   photograph of Aaron's work, and a line elevation cannot be mistaken for one.
   Every stroke is the ink, every join is mitred, and there is no fill — the
   same instrument the rest of the sheet is drawn with. It is fully drawn by
   default; motion.css §8 is the only thing that ever hides a stroke, and only
   where the scroll can put it back. */
.build {
  /* THE SECTION'S GROUND. A sibling of .wrap, which carries z-index 2, so this
     is painted above the section's paper and beneath every word on it — it
     cannot cover the lettering at any size, which is what a sticky plate above
     the list could never promise.

     Sticky, with a negative bottom margin equal to its own height: it holds
     still for the whole of the seven steps and occupies nothing in the flow,
     so the steps sit exactly where they sat before it existed.

     One rule at every width. The phone-only plate and the sheet-only slug
     drawing were two versions of one idea and they drifted within a day. */
  --e-heavy: 2.4; --e-mid: 1.5; --e-fine: 0.8;
  position: sticky; top: calc(var(--mo-strip, 56px) + var(--s6)); z-index: 0;
  display: block; width: 100%; max-width: none;
  height: calc(62 * var(--lead));
  margin: 0 0 calc(-62 * var(--lead));
  /* 0.38, and the number is derived, not chosen. The worst case is a word
     sitting on the heaviest stroke: the ground there is the stroke composited
     over the paper, and the tier that has to survive it is the lead paragraph
     at --ink-70, whose floor is 4.5:1. Solved: 0.39 is the last opacity that
     holds it (4.64:1 at 0.38, 4.52 at 0.40, 4.38 at 0.42 — which is what
     shipped for an hour). Body text is 8.59:1 there, well over its 7:1. */
  fill: none; stroke: var(--ink-45); opacity: .38;
  stroke-linecap: butt; stroke-linejoin: miter; vector-effect: non-scaling-stroke;
}
/* THE PINNED SECTION. path.js sets .is-pinned and writes --path-track only
   where the hold can be done well: script on, motion allowed, and a viewport
   tall enough to hold the stage. Everywhere else these rules do not exist and
   the section is the plain one. The track is seven steps of scroll plus one
   screen; the stage inside it is one screen, stuck to the top. */
.path.is-pinned { min-height: var(--path-track, auto); }
.path.is-pinned > .wrap {
  /* --path-stage is the viewport height floored to the lead; path.js writes it
     because only script can measure the lead. The content is TOP-aligned, not
     centred: centring splits the leftover space in two and a half-pixel offset
     puts every heading below this section off the grid. */
  position: sticky; top: 0; min-height: var(--path-stage, 100svh);
  display: flex; flex-direction: column; justify-content: flex-start;
  padding-block: var(--s8);
}
@media (min-width: 52.5rem) {
  .path.is-pinned > .wrap { display: grid; align-content: start; }
}
@media print { .path.is-pinned { min-height: 0; } .path.is-pinned > .wrap { position: static; min-height: 0; } }

/* The section is the containing block it travels inside, and it clips it: the
   negative margin puts the drawing's border box below the section's own bottom,
   so without this it paints on into "Finished work". `clip` rather than
   `hidden` — hidden would make the section a scroll container and sticky would
   stop working entirely. */
.path { position: relative; overflow: clip; }
@media print { .build { display: none; } }
/* ON A SHEET IT KEEPS OUT OF THE MARGIN. The section's running label is the one
   piece of 16px --ink-45 text on this page, and NO ground dark enough to be
   worth drawing keeps a 16px caption at 4.5:1 — the only token light enough is
   --sink, which is invisible. So the drawing does not go there: at 840 and up
   it starts where the prose starts, and every word over it is --ink or
   --ink-70, both of which it clears. */
@media (min-width: 52.5rem) {
  /* It has to be inset from where the PROSE starts, not from the section's own
     edge — .wrap is centred inside the section with gutters, so a plain
     margin-left put the drawing straight back under the label. Matching the
     wrap's own box and then padding by the slug is the only way to land on the
     same column the words are in. */
  .build { max-width: var(--wrap); margin-inline: auto;
           padding-left: calc(var(--slug) + var(--slug-gap)); }
}

/* ON A PHONE THE DRAWING STAYS PUT WHILE THE STEPS GO PAST (owner, 2026-09-02:
   "on mobile the building of the house is on the screen for only a few seconds
   when scrolling"). In flow above the list it was gone in a second and the
   whole point of it — that the house goes up in the order the job does — went
   with it. Stuck to the top of its own section, it is on screen for the entire
   seven steps and leaves when they do.

   It carries the section's own ground and a hairline under it, because the
   steps now pass BEHIND it and type showing through a drawing is a mess. The
   ground is what makes it a plate rather than a smear.

   It is 34 leads instead of 48: on an 844px screen a 192px plate plus the
   73px call bar is a third of the page given to one drawing. 136 plus its rule
   is 36 leads, so the grid is untouched. Above 840 the sheet's own sticky slug
   takes over and this block does not apply. */


.stations { position: relative; margin: var(--s8) 0 0; padding: 0; list-style: none; max-width: var(--measure); }
.stations::before { content: ""; position: absolute; left: 1.125rem; top: 1.5rem; bottom: 1.5rem; border-left: var(--rule-mid); }
.station { position: relative; margin: 0; }
.station-btn {
  display: grid; grid-template-columns: 2.25rem minmax(0, 1fr); column-gap: var(--s4);
  align-items: baseline; width: 100%; text-align: left; cursor: pointer;
  background: none; border: 0; padding: var(--s3) 0; color: var(--ink);
  font: inherit; line-height: calc(8 * var(--lead)); min-height: 56px;
}
.station-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.station-no {
  /* 2.25rem = 36px, so a row is 12 + 36 + 12 = 60 and the page stays on
     the lead below it; at 2.3rem every row was 60.8px and every hairline
     after "How a job goes" landed on a half pixel. */
  position: relative; z-index: 1; width: 2.25rem; height: 2.25rem; border-radius: 50%;
  display: grid; place-items: center; align-self: center;
  font-variant-numeric: tabular-nums; --wght: 700; font-weight: 700;
  background: var(--canvas); border: var(--rule-mid); color: var(--ink-70);
  transition: background var(--t-state), color var(--t-state), border-color var(--t-state);
}
.station-label { --wght: 700; font-weight: 700; }
.station-say { margin: 0 0 var(--s3) calc(2.25rem + var(--s4)); padding: 0 0 calc(var(--s3) - 1px);
               color: var(--ink-70); border-bottom: var(--rule-hair); max-width: var(--measure); }
.station:not(.is-open) .station-say { display: none; }
.station.is-open .station-no { background: var(--ink); color: var(--on-band); border-color: var(--ink); }
.station.is-done .station-no { background: var(--sink); }
.station-nav { display: flex; gap: var(--s3); margin-top: var(--s4); }
@media (prefers-reduced-motion: reduce) { .station-no { transition: none; } }


/* ==========================================================================
   9. FINISHED WORK — the job book (book.js) and the comparisons (compare.js)

   The section ships hidden and is shown by book.js when the manifest lists
   at least one job; tools/add-project.py removes the attribute for good
   once photographs exist. Nothing here is a placeholder.
   ========================================================================== */

.jobs { border-top: var(--rule-hair); }
.jobs .lead { margin-bottom: var(--s6); }
.pages-fallback { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s8); }
.pages-fallback figure { margin: 0; }
.pages-fallback img { display: block; width: 100%; height: auto; border: var(--rule-mid); background: var(--surface); }
.pages-fallback figcaption { font-size: var(--t-fine); line-height: calc(6 * var(--lead)); color: var(--ink-70); margin-top: var(--s2); }
.pages-fallback .pg { --wght: 700; font-weight: 700; color: var(--ink); }

.compare-list { margin: var(--s12) 0 0; display: grid; gap: var(--s12); }
.compare { margin: 0 auto; max-width: 46rem; }
.compare-stage {
  position: relative; overflow: hidden; background: var(--surface); border: var(--rule-mid);
  touch-action: pan-y; -webkit-user-select: none; user-select: none; cursor: ew-resize;
}
.compare-stage img { display: block; width: 100%; height: auto; pointer-events: none; }
.compare-after { position: absolute; inset: 0; overflow: hidden; }
.compare-after img { width: var(--cmp-w, 100%); max-width: none; }
.compare-bar {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px;
  background: var(--on-band); pointer-events: none;
}
.compare-bar::before { content: ""; position: absolute; inset: 0; background: var(--ink); opacity: .35; margin: 0 -1px; }
.compare-handle {
  position: absolute; top: 50%; left: 50%; width: 56px; height: 56px; margin: -28px 0 0 -28px;
  border-radius: 50%; background: var(--surface); border: 2px solid var(--ink); cursor: grab;
  display: grid; place-items: center; --wght: 800; font-weight: 800; color: var(--ink);
  font-size: var(--t-ui); letter-spacing: -.05em; padding: 0; font-family: var(--sans);
}
.compare-handle:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.compare.is-dragging .compare-handle { cursor: grabbing; }
.compare-tag {
  position: absolute; top: var(--s3); padding: var(--s1) var(--s3);
  --wght: 700; font-weight: 700; font-size: var(--t-fine); line-height: calc(5 * var(--lead));
  letter-spacing: .08em; text-transform: uppercase; background: var(--ink); color: var(--on-band);
  pointer-events: none;
}
/* Left is how the room was, right is how it was left: the order every
   homeowner has already seen. compare.js clips the after picture from the
   left edge of the divider to match, and switches off whichever tag has no
   picture left under it, so neither ever names the one across the divider. */
.compare-tag-before { left: var(--s3); }
.compare-tag-after { right: var(--s3); }
.compare-tag.is-off { visibility: hidden; }
.compare-cap { font-size: var(--t-fine); line-height: calc(6 * var(--lead)); color: var(--ink-70); margin-top: var(--s2); }
.compare-cap b { color: var(--ink); }


/* ==========================================================================
   10. THE ESTIMATE — one form, sent from here or printed
   ========================================================================== */

.letter { max-width: var(--measure); margin: var(--s8) 0 0; }
.letter label, .field-lab { display: block; margin: 0 0 var(--s4); --wght: 700; font-weight: 700;
                            font-size: var(--t-ui); line-height: calc(7 * var(--lead)); letter-spacing: 0; text-transform: none; color: var(--ink); }
.letter input[type="text"], .letter input[type="tel"], .letter input[type="email"], .letter textarea {
  display: block; width: 100%; margin-top: var(--s1);
  font: inherit; font-size: var(--t-body); line-height: calc(8 * var(--lead));
  color: var(--ink); background: var(--surface); border: 0; border-bottom: 2px solid var(--ink); border-radius: 0;
  padding: var(--s2) var(--s2); min-height: calc(14 * var(--lead));
}
/* A sheet's fields are rules to write on, not boxes; the one box is the
   writing area for the message. */
.letter textarea { min-height: 9rem; resize: vertical; border: 1px solid var(--ink-30); padding: calc(var(--s3) - 1px) var(--s3); }
.letter input:focus-visible, .letter textarea:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.letter .hint { display: block; --wght: 400; font-weight: 400; font-size: var(--t-fine); line-height: calc(6 * var(--lead)); color: var(--ink-45); }

.kinds { margin: 0 0 var(--s4); padding: 0; border: 0; }
.kinds legend { padding: 0; --wght: 700; font-weight: 700; font-size: var(--t-ui); line-height: calc(7 * var(--lead)); margin-bottom: var(--s2); }
.kinds label { display: grid; grid-template-columns: 1.75rem 1fr; gap: var(--s3); align-items: center;
               min-height: calc(14 * var(--lead)); margin: 0; --wght: 400; font-weight: 400; font-size: var(--t-body); cursor: pointer;
               border-top: var(--rule-hair); }
.kinds label:last-child { border-bottom: var(--rule-hair); }
.kinds { margin: 0 0 var(--s4); }
.kinds input { width: 1.75rem; height: 1.75rem; margin: 0; accent-color: var(--ink); }

.photos { margin: 0 0 var(--s4); }
.photos input[type="file"] { display: block; width: 100%; margin-top: var(--s1); font: inherit; font-size: var(--t-ui);
  /* 14 leads tall with the dashed rule inside, like every other field; its
     own line-height is on the lead so the box does not grow past the floor
     (it was 61px in Larger print). */
  line-height: calc(7 * var(--lead)); padding: 0 var(--s3); background: var(--canvas); border: 2px dashed var(--ink-30);
  border-radius: 0; min-height: calc(14 * var(--lead)); }
.photo-tray { list-style: none; margin: var(--s3) 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); gap: var(--s3); }
.photo-tray:empty { display: none; }
.photo-tray li { margin: 0; }
.photo-tray img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border: var(--rule-mid); background: var(--surface); }
.photo-tray span { display: block; font-size: var(--t-fine); line-height: calc(6 * var(--lead)); color: var(--ink-45); }
.photo-tray .aai-btn.aai-btn { min-height: calc(14 * var(--lead)); width: 100%; padding: 0 var(--s2); font-size: var(--t-fine); margin-top: var(--s1); }

.actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s6); }
.actions-note { margin: var(--s2) 0 0; font-size: var(--t-fine); line-height: calc(6 * var(--lead)); color: var(--ink-45); }
.form-say { margin: var(--s3) 0 0; color: var(--ink-70); min-height: calc(8 * var(--lead)); }
.form-say a { --wght: 700; font-weight: 700; position: relative; }
.form-say a::after { content: ""; position: absolute; inset: -16px -6px; }
.estimate .or { margin-top: var(--s8); padding-top: calc(var(--s6) - 1px); border-top: var(--rule-hair); color: var(--ink-70); }
.estimate .or a, .house .post a { --wght: 700; font-weight: 700; position: relative; }
.estimate .or a::after, .house .post a::after { content: ""; position: absolute; inset: -16px -6px; }


/* ==========================================================================
   11. HOW WE WORK — six lines, the reason on touch
   ========================================================================== */

.terms h2, .terms .lead { color: var(--on-band); }
.nots { margin: calc(var(--s8) - 1px) 0 0; max-width: var(--measure); border-top: 1px solid var(--rule-on-band); }
.no { border-bottom: 1px solid var(--rule-on-band); }
/* The row is 14 leads INCLUDING the hairline under it: the summary's floor is
   one pixel short so the rule completes it. A 56px floor plus a 1px rule made
   every closed row 57px and put the whole band below off the lead. */
.no summary { list-style: none; cursor: pointer; padding: var(--s3) 0 calc(var(--s3) - 1px); min-height: calc(14 * var(--lead) - 1px);
              display: grid; grid-template-columns: var(--s6) minmax(0, 1fr); column-gap: var(--s3);
              align-items: baseline; line-height: calc(8 * var(--lead)); --wght: 700; font-weight: 700; }
.no summary::-webkit-details-marker { display: none; }
.no summary::before { content: "\2013"; text-align: right; color: var(--on-band-70); --wght: 500; font-weight: 500; }
.no[open] summary::before { content: "\2014"; color: var(--on-band); }
.no summary:focus-visible { outline: 3px solid var(--on-band); outline-offset: 3px; }
.no p { margin: 0 0 var(--s4) calc(var(--s6) + var(--s3)); color: var(--on-band-70); }


/* ==========================================================================
   12. ABOUT THE COMPANY, AND THE COLOPHON
   ========================================================================== */

.house .post { font-style: normal; font-size: var(--t-ui); line-height: calc(7 * var(--lead)); background-color: var(--surface);
               border: var(--rule-mid); padding: calc(var(--s4) - 1px) var(--s6); display: inline-block; margin-top: var(--s4); }
.house .post { display: block; max-width: var(--measure); }
.house .post a { --wght: 700; font-weight: 700; }
a[href^="mailto:"] { overflow-wrap: anywhere; }

.colophon { padding-block: var(--s12); padding-bottom: max(var(--s12), env(safe-area-inset-bottom)); }
.wordmark-small { margin: 0 0 var(--s3); --wght: 700; font-weight: 700; font-size: var(--t-fine); line-height: calc(6 * var(--lead));
                  letter-spacing: .08em; text-transform: uppercase; color: var(--on-band-70); }
.colophon .tel, .colophon .tel:visited { color: var(--on-band); }
.colophon .reach { margin-top: var(--s4); }
.colophon .reach a { color: var(--on-band); border-color: var(--on-band); background: transparent; }
.colophon .reach a:visited { color: var(--on-band); }
.colophon .reach a:hover { background: var(--rule-on-band); }
.colophon .reach .is-lead { background: var(--on-band); color: var(--band); }
.colophon .reach .is-lead:visited { color: var(--band); }
.colophon .reach .is-lead:hover { background: var(--accent); border-color: var(--accent); color: var(--on-band); }
.colophon .reach a:focus-visible { outline-color: var(--on-band); }
.fine { font-size: var(--t-fine); line-height: calc(6 * var(--lead)); color: var(--on-band-70); max-width: var(--measure); margin-top: var(--s6); }


/* ==========================================================================
   13. THE THUMB BAR — call and text, always within reach on a phone
   ========================================================================== */

.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: var(--band); border-top: 1px solid var(--rule-on-band);
  padding: var(--s2) var(--gutter);
  padding-bottom: max(var(--s2), env(safe-area-inset-bottom));
  /* Call carries the number and takes the wider column; Text is a word.
     Both numbers under both words put 8.17em of digits into a 169px column
     at 390 and the last digits left the bar. */
  display: grid; grid-template-columns: 1.55fr 1fr; gap: var(--s3);
  align-items: center;
}
/* THE MARK ON THE BAR. It takes a column of its own and the width is paid for
   out of the bar's own padding and gap, not out of the telephone: the gutter
   drops from --gutter to --s3 and the gap from --s3 to --s2, which returns
   24px against the 40px the mark and its gap cost. Measured at 390 in Larger
   print the number's column still clears the 8.17em of digits; at 320 it does
   not, so below 360 the mark is not there. The digits win. */
.callbar-mark { display: none; }
@media (min-width: 22.5rem) {
  .callbar { padding-inline: var(--s3); }
  .callbar { grid-template-columns: calc(8 * var(--lead)) 1.55fr 1fr; gap: var(--s2); }
  .callbar-mark {
    display: block; width: calc(8 * var(--lead)); height: calc(6 * var(--lead)); margin: 0;
    stroke: var(--on-band); stroke-width: 2.5;
  }
  .callbar-mark .mk-set path:first-child { stroke: var(--on-band-70); }
  .callbar-mark .mk-door { stroke: var(--accent); }
}
.callbar a {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 56px; text-decoration: none; color: var(--on-band);
  line-height: calc(5 * var(--lead));
}
.callbar a:visited { color: var(--on-band); }
.callbar-lbl { margin: 0; color: var(--on-band); opacity: .8; letter-spacing: .08em; line-height: calc(5 * var(--lead)); }
.callbar-num { --wght: 800; font-weight: 800; font-variant-numeric: proportional-nums lining-nums;
               /* 8.1736em of digits in a column that is 152px on a 320 screen
                  in Larger print: 5.8vw is 18.6px there and 22px at 390. */
               font-size: min(1.375rem, 5.8vw); line-height: calc(7 * var(--lead)); }
.callbar-word { --wght: 800; font-weight: 800; font-size: var(--t-h3); line-height: calc(7 * var(--lead)); }
.callbar a:focus-visible { outline: 3px solid var(--on-band); outline-offset: -3px; }
body { padding-bottom: 6.5rem; }
@media (min-width: 52.5rem) {
  .callbar { display: none; }
  body { padding-bottom: 0; }
}


/* ==========================================================================
   14. THE READING BAR (reading.js) — two utilities, quiet
   ========================================================================== */

.aai-bar { border-bottom: var(--rule-hair); }
.aai-bar.aai-bar { padding-block: var(--s2) calc(var(--s2) - 1px); }
.aai-btnrow { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; margin: 0; }
/* The toolbar is not the mark. In the accent these two read as a caption
   beside the tagline that is also in the accent; in the ink they read as what
   they are — controls — and the accent is left to the telephone. The rule
   under them still says they are pressable, and the hover below is the accent. */
.aai-bar .aai-btnrow:first-child .aai-btn {
  background: none; border: 0; border-bottom: var(--rule-mid); padding-inline: var(--s1); color: var(--ink);
}
/* The doubled class beats the sheet reading.js injects after this one. The
   label may wrap to two lines on a 320 screen; with the 1px rule inside the
   bottom padding the box is 56 on one line and 64 on two (70 and 80 in
   Larger print), all of them whole leads. Measured before: 65 and 81. */
.aai-bar .aai-btnrow:first-child .aai-btn.aai-btn {
  box-sizing: border-box; min-height: calc(14 * var(--lead)); line-height: calc(7 * var(--lead));
  padding-block: var(--s1) calc(var(--s1) - 1px);
}
.aai-bar .aai-btnrow:first-child .aai-btn:hover { border-bottom-color: var(--accent); }
.aai-barnote.aai-barnote { margin: var(--s1) 0 0; font-size: var(--t-fine); line-height: calc(6 * var(--lead)); color: var(--ink-45); max-width: var(--measure); }
.aai-count { font-size: var(--t-fine); color: var(--ink-45); }


/* ---- The space the reading bar will take, held before it exists ----------
   reading.js is deferred and builds the bar as main's first child, so on any
   real connection the page is painted, read, and then dropped by the height
   of the bar. early.js marks the root "js" before first paint and reading.js
   marks it "aai-bar-on" the moment the bar is in; between the two, this holds
   exactly the bar's own height, written on the lead so it steps with Larger
   print rather than being two magic numbers.

   MEASURED on the shipped folder, both print modes, eighteen widths, and the
   two boundaries bisected: the row is nowrap, so below a certain width the
   label "Read this page aloud" takes two lines and the bar grows one line
   taller. 14 leads is one line, 16 is two; the padding is 2 x --s2 less the
   rule, plus the rule. That gives 72 and 80 at a 16px root, 90 and 100 at
   20px — every one of the four heights the bar actually measures.

   The boundaries are in px, not rem, ON PURPOSE: a rem media query resolves
   against the initial font size, so it cannot follow a label that is 25%
   wider in Larger print. They are 343/344 and 438/439, bisected. IF THE TWO
   LABELS EVER CHANGE, THESE MOVE — re-bisect them.

   With script off there is no "js" class and so no space at all, which is
   right: nothing is coming. */
html.js:not(.aai-bar-on) main::before {
  content: ""; display: block;
  height: calc(2 * var(--s2) + 14 * var(--lead));          /* 72 normal, 90 large */
}
@media (max-width: 343px) {
  html.js:not(.aai-bar-on) main::before { height: calc(2 * var(--s2) + 16 * var(--lead)); }        /* 80 */
}
@media (max-width: 438px) {
  html.js.large:not(.aai-bar-on) main::before { height: calc(2 * var(--s2) + 16 * var(--lead)); }  /* 100 */
}
@media print { html.js main::before { display: none; } }

/* THE BAR MUST BE ONE HEIGHT WHETHER IT HOLDS ONE CONTROL OR TWO. Larger
   print is built at once; read-aloud waits for the browser's voice list,
   which arrives on an event (and at 700ms and 2000ms fallbacks). The row is
   nowrap, so the second button shrinks both, and below the widths bisected
   above the label then wraps and the bar grows a line — measured at 320
   normal as two shifts, hero up 8px at 251ms and down 8px at 263ms, a
   layout shift of 0.1841 landing after the reader has begun. No ordering
   can fix it; the button is held at the two-line height instead, in exactly
   the zones the reservation already assumes two lines. The doubled class
   beats the sheet reading.js injects after this one. */
@media (max-width: 343px) {
  .aai-bar .aai-btnrow:first-child .aai-btn.aai-btn { min-height: calc(16 * var(--lead)); }
}
@media (max-width: 438px) {
  html.large .aai-bar .aai-btnrow:first-child .aai-btn.aai-btn { min-height: calc(16 * var(--lead)); }
}


/* ==========================================================================
   15. >= 840px IS A DIFFERENT MACHINE
   ========================================================================== */

@media (min-width: 52.5rem) {
  :root {
    --wrap: 62rem; --gutter: 2rem; --slug: 12rem; --slug-gap: 3.5rem;
    --t-h1: 3.25rem;                                   /* 52 / 56 */
  }
  h1 { line-height: calc(14 * var(--lead)); max-width: 18ch; }

  .masthead { padding-block: max(var(--s6), env(safe-area-inset-top, 0px)) calc(var(--s4) - 6px); }
  .masthead-in { grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--s6); align-items: end; }
  /* The mark takes its own column and spans the name's three lines, so the
     letterhead reads mark / name / number across the sheet — and the running
     head below carries the same mark once the letterhead has compressed, which
     is what keeps it on screen without pinning anything new. */
  .masthead-in > .mark      { grid-column: 1; grid-row: 1 / 4; align-self: center;
                              width: calc(36 * var(--lead)); height: calc(24 * var(--lead));
                              margin: 0 var(--s4) 0 0; }
  .masthead-in > .tagline   { grid-column: 2; grid-row: 1; }
  .masthead-in > .wordmark  { grid-column: 2; grid-row: 2; font-size: var(--t-h1); line-height: calc(12 * var(--lead)); }
  .masthead-in > .place     { grid-column: 2; grid-row: 3; }
  .masthead-in > .tel-line  { display: block; grid-column: 3; grid-row: 1 / 3; align-self: end; justify-self: end; margin: 0; }
  .masthead .tel { font-size: var(--t-num); line-height: calc(11 * var(--lead)); padding-block: 0; }
  .masthead-in > .reach     { grid-column: 3; grid-row: 3; justify-self: end; margin: 0; display: flex; }

  .hero { padding-block: var(--s16) var(--s24); }
  .hero-in { grid-template-columns: var(--slug) minmax(0, 1fr); column-gap: var(--slug-gap); }
  .hero-in > h1 { grid-column: 1 / -1; }
  .hero-in > * { grid-column: 2; }

  .work > .wrap, .path > .wrap, .jobs > .wrap, .estimate > .wrap, .terms > .wrap, .house > .wrap {
    display: grid; grid-template-columns: var(--slug) minmax(0, 1fr);
    column-gap: var(--slug-gap); align-items: start;
  }
  .work > .wrap > *, .path > .wrap > *, .jobs > .wrap > *, .estimate > .wrap > *, .terms > .wrap > *, .house > .wrap > * { grid-column: 2; }
  /* THE SLUG IS A MARGIN, NOT A COLUMN OF HEADINGS. Set at --t-h3 it was
     24px/800 — the same size and weight as the item heads beside it, so the
     section title and the things inside it argued at one volume and the page
     read as a document rather than as a sheet. A contractor's estimate sheet
     puts a LABEL in the left column and the description to its right, so the
     section title is set in the tier this file already owns and never used
     inside <main>: tracked capitals, --t-fine, --ink-45. It differs from the
     prose in KIND, not in degree. The box height is unchanged, so nothing
     below it moves. */
  .work > .wrap > h2, .path > .wrap > h2, .jobs > .wrap > h2, .estimate > .wrap > h2, .terms > .wrap > h2, .house > .wrap > h2 {
    grid-column: 1; grid-row: 1 / -1; align-self: start; margin-bottom: 0; max-width: none;
    font-size: var(--t-fine); line-height: calc(8 * var(--lead));
    --wght: 700; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
    color: var(--ink-45);
    /* It rides its section, so the empty column reads as a margin holding a
       running label rather than as a hole. Held clear of the reading bar. */
    position: sticky; top: calc(var(--mo-strip, 56px) + var(--s6));
  }
  .terms > .wrap > h2 { color: var(--on-band-70); }
  @media print { .work > .wrap > h2, .path > .wrap > h2, .jobs > .wrap > h2,
                 .estimate > .wrap > h2, .terms > .wrap > h2, .house > .wrap > h2 { position: static; } }
  .work > .wrap > .kind-grid { grid-column: 2; max-width: var(--measure); }

  /* THE RULE RUNS THE WHOLE SHEET. Every hairline in the work section used to
     begin at the prose and float in the middle of the page; on a sheet a rule
     crosses it. subgrid puts each kind's name in the slug beside its own
     text — which is what an estimate sheet actually looks like — and the rule
     it carries now spans both columns. The hairline stays a hairline: it gets
     longer, not heavier, so the lead is untouched. Without subgrid the
     browser renders exactly what it rendered before. */
  @supports (grid-template-columns: subgrid) {
    .work > .wrap > .kind-grid { grid-column: 1 / -1; max-width: none;
                                 display: grid; grid-template-columns: subgrid; }
    /* The index and the name ride the margin together, one under the other,
       and the prose keeps the sheet. The hairline crosses both. */
    /* The rows are pinned to whole leads. Left to content, row 1 measured 50px
       (the index, set at --t-h1, which is 52px here) and row 2 measured 38px,
       so every heading below the section sat two or three pixels off the 4px
       grid — the one thing this whole file is built to keep. Pinned, they step
       with Larger print like everything else. */
    .kind { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid;
            grid-template-rows: calc(13 * var(--lead)) calc(10 * var(--lead)) auto;
            column-gap: var(--slug-gap); }
    .kind::before { grid-column: 1; grid-row: 1; line-height: calc(11 * var(--lead)); }
    .kind h3 { grid-column: 1; grid-row: 2; margin: 0; }
    .kind-say { grid-column: 2; grid-row: 1 / 3; }
    .kind .plain-list { grid-column: 2; grid-row: 3; margin-top: var(--s4); }
  }
  .jobs > .wrap > .mount, .jobs > .wrap > .compare-list { grid-column: 1 / -1; }
  /* The hero's slug column was a hole under the display line; the three
     doors stand in it, beside the number and the lead. */
  .hero-in > .reach { grid-column: 1; grid-row: 2 / span 2; flex-direction: column; align-items: stretch; align-content: start; margin: 0; }
  .hero-in > .tel-line { grid-row: 2; }
  .hero-in > .lead { grid-row: 3; }

  .colophon > .wrap { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: var(--s16); align-items: start; }
  .colophon .fine { grid-column: 2; margin-top: 0; }
}
@media (min-width: 64rem) { :root { --slug: 14rem; } }
@media (min-width: 80rem) { :root { --gutter: 3rem; --slug: 16rem; } }


/* ==========================================================================
   16. REDUCED MOTION AND PRINT
   ========================================================================== */

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

@media print {
  html, body, .masthead, .work, .terms, .colophon, .estimate, .house .post, section { background-color: transparent; background-image: none; }
  body { font-size: 1rem; line-height: 1.5; padding-bottom: 0; }
  .masthead { border-bottom: 1px solid var(--ink); padding-block: 0 12pt; }
  .masthead::after { display: none; }
  section { padding-block: 16pt; }
  .callbar, .aai-bar, .reach, .actions, .station-nav, .photos, .skip { display: none; }
  h1, h2, h3 { break-after: avoid; }
  .kind, .no, .station { break-inside: avoid; }
  a, .tel, .tel-big { color: var(--ink); }
  .house .post a::after { content: " (" attr(href) ")"; position: static; inset: auto; font-size: .9em; }
  /* "Print it" on the estimate form prints the sheet alone, with the
     letterhead, so what comes off the printer is one page to fill in and
     post or bring to the walk-through. */
  body.print-estimate main > section:not(.estimate), body.print-estimate .colophon,
  body.print-estimate noscript { display: none; }
  body.print-estimate .estimate { border-top: 0; }
  body.print-estimate .estimate .or { display: none; }
  .letter input, .letter textarea { background: transparent; }
}
