/* Busy Season Local. The component library: everything the templated
   pages draw with. The shell it sits on is assets/chrome.css, which
   carries the tokens, the type and the chrome, and which the thirteen
   standalone pages load on their own. */

/* ---------- the two registers ----------
   The sign is cut; the paperwork under it is typed. One family carries both,
   and the width axis is what separates them: cut lettering runs condensed and
   heavy, prose runs normal width at 400. That gap is wider than most two-font
   pairings and it is honest to a world where a shop owns one alphabet. */
h1,h2,h3,h4{margin:0;font-family:var(--sign);color:var(--ink);text-transform:uppercase;text-wrap:balance}
h1{font-size:var(--h1);font-weight:800;font-stretch:80%;line-height:.94;letter-spacing:-.005em;margin-bottom:18px}
h2{font-size:var(--h2);font-weight:800;font-stretch:82%;line-height:.98;letter-spacing:0;margin-bottom:16px}
h3{font-size:var(--h3);font-weight:700;font-stretch:92%;line-height:1.16;letter-spacing:.01em;margin-bottom:8px}
p{margin:0 0 14px}
p:last-child{margin-bottom:0}

.lede{font-size:var(--lede);color:var(--ink-70);max-width:68ch;margin:0 0 26px;font-stretch:100%}
.src{margin-top:22px;font-size:var(--fs-note);color:var(--ink-70);max-width:72ch;font-stretch:100%;line-height:1.5}
.src a{color:var(--ink-70);text-underline-offset:3px}

.wrap{max-width:var(--wrap);margin:0 auto;padding-inline:var(--pad);width:100%}
section{padding-block:var(--gap)}
/* One field, so a section is told apart by a rule and by space. */
section + section,.band + section,section + .band{border-top:1px solid var(--hair)}
/* THE PANELS PAINT AGAIN. They were flattened to background:transparent when the
   page became "one continuous paper field", and the tones were left setting an
   accent variable and nothing else. Measured 2026-09-16: every band of every
   page rendered rgb(231,234,230), one colour, 100 percent of the area, header
   and footer included. Michael: "why do we have pages so bland with that colour
   ... it all looks like one page ... dont we have a palette? like a navy blue
   somewhere". The navy is slate #27587A and it had stopped appearing anywhere
   but a bar chart.
   The Tone Contract Rule from DESIGN.md is what makes this safe: a panel sets
   four properties and every component inside reads them, so a tint that is
   quiet on ink is never left illegible on brick. */
.panel{position:relative;background:transparent;
  --key:var(--ochre-deep);--fg:var(--ink);--body:var(--ink-70);--quiet:var(--ink-45);
  --edge:var(--hair);--bad:var(--red-deep);color:var(--fg)}
/* The three dark grounds share one foreground contract: paper type, paper tints,
   a paper hairline, and ochre as the key that survives all three. */
.panel.ink,.panel.slate,.panel.brick{color:var(--fg);
  --fg:var(--paper);--body:var(--on-dark);--quiet:var(--on-dark-quiet);
  --edge:var(--on-dark-hair);--key:var(--ochre);--bad:#FF7A6B}
.panel.ink{background:var(--ink)}
/* paper on slate is 6.26:1, so the body tint has room; on brick it is 4.87:1
   at full strength and has almost none, which is why brick's tints are near
   solid and its panel fill is the deeper mix. */
.panel.slate{background:var(--slate);--quiet:rgba(231,234,230,.82)}
.panel.brick{background:var(--brick-deep);--body:rgba(231,234,230,.93);--quiet:rgba(231,234,230,.82)}
/* Ochre is a LIGHT ground, so it keeps the dark contract rather than the paper
   one: paper on it measures 1.69:1 (the amber, since 2026-09-28) and would be
   unreadable. */
/* Ochre was the tightest ground on the site. Solid ink on the old #B8891F
   measured 5.09:1 and an alpha of .86 measured 4.20, so the usual body and
   quiet tints both failed AA
   here: seven strings on three pages, caught by contrast_check and not by the
   painted-contrast sweep. DESIGN.md already records that brick's tints "run near
   solid" for this reason; ochre is the same problem further along. So on ochre
   the hierarchy is carried by weight and size, not by alpha, and every tint is
   near solid. The alternative was repainting the section, which would have cost
   the three-group colour coding to save a tint nobody can see anyway. */
.panel.ochre{background:var(--ochre);--fg:var(--ink);--body:rgba(26,34,41,.95);
  --quiet:rgba(26,34,41,.95);--edge:rgba(26,34,41,.34);--key:var(--ink)}

.panel h1,.panel h2,.panel h3,.panel h4,.panel strong{color:var(--fg)}
.panel p,.panel li,.panel dd,.panel td,.panel .lede{color:var(--body)}
.panel .src,.panel .lab,.panel .ticket,.panel .cnt{color:var(--quiet)}
.panel a{color:var(--fg)}
/* The trim rule under a band that hands over to the next one. */

/* A quiet band on the paper ground, ruled top and bottom rather than tinted. */
.tint{background:transparent}.hero{padding-block:clamp(34px,3.6vw,52px) clamp(26px,2.8vw,40px)}
.hero-grid{display:grid;grid-template-columns:minmax(0,1.02fr) minmax(420px,.98fr);gap:clamp(26px,3vw,48px);align-items:start}
/* The lead column is the short one. Measured at 1440: 290px of headline, lede
   and meta rule against a 514px panel beside it, so the fold carried 272px of
   bare slate under the phone number on the home page, 213 on contact and 232
   on how we score. Michael, 2026-09-22, about the home page: "too much empty
   space". Two answers, because the two pages have different things to give.
   Where there is nothing true to put in the hole, the meta rule is set to the
   foot of the row and lands level with the panel's bottom edge, which is a
   baseline rather than a gap. Where there is (the home page holds four
   measured figures), the figures move up into it. */
.hero-grid>.lead{display:flex;flex-direction:column;align-self:stretch}
.hero-grid>.lead>.lede{margin-bottom:clamp(24px,2.6vw,36px)}
.hero-grid>.lead>.strip{margin-top:auto}
/* The home page: the four readings take the lead column's second row, so they
   are read beside the check rather than under it, and the column bottoms out
   with the panel. The band was full width and four across; in half the width
   it is two across, the shape it already takes on a phone.

   Written up, not down, and every declaration of the layout lives in the
   query, names included: a bare grid-area on an item whose grid declares no
   such area is auto-placed, and a .hero-grid.stacked>.readings rule outranks
   the .readings one at 760 and would have held the band at two columns down
   to 320. */
@media (min-width:1081px){
  .hero-grid.stacked{grid-template-areas:"lead card" "under card"}
  .hero-grid.stacked>.lead{grid-area:lead;align-self:start}
  .hero-grid.stacked>.checkside{grid-area:card}
  /* The row gap already separates the two, and the band's own margin was
     stacking on top of it for 83px between the meta rule and the first
     figure. */
  .hero-grid.stacked>.readings{grid-area:under;grid-template-columns:1fr 1fr;
    margin-top:0}
  .hero-grid.stacked>.readings>div{border-bottom:1px solid var(--edge,var(--hair))}
  .hero-grid.stacked>.readings>div:nth-child(2n){border-right:0;
    padding-left:clamp(14px,1.8vw,26px)}
  .hero-grid.stacked>.readings>div:nth-last-child(-n+2){border-bottom:0}
}
.hero h1{font-size:var(--h1);font-stretch:62%;line-height:.9;letter-spacing:-.01em;max-width:none;margin-bottom:22px}
.hero .lede{font-size:var(--lede);max-width:44ch;margin-bottom:0}
/* The rocker strip: where we work and the number to call, cut small and
   tracked the way it runs along the bottom of a van door. */
.strip{display:flex;flex-wrap:wrap;align-items:center;margin-top:clamp(24px,2.6vw,36px);padding-top:16px;border-top:1px solid var(--edge,var(--hair));gap:8px 20px;font-family:var(--sign);font-weight:700;font-stretch:86%;font-size:var(--fs-label);letter-spacing:.14em;text-transform:uppercase;color:var(--body,var(--ink-70))}

.strip a{color:var(--fg,var(--ink));text-decoration:none;font-variant-numeric:tabular-nums}
.strip a:hover{color:var(--key,var(--red-deep))}

/* ---------- the check plate ----------
   A paper plate on a painted panel, with a nameplate tab cut into its top edge.
   Hard border, zero radius: it is screwed on, not floating. */
/* The plate is paper, and it sits inside a painted panel. Without this reset
   the panel's tone properties reach into it and set near-white labels on a
   near-white ground: the three form labels and the state line measured 1.00:1
   on both the homepage and the contact page. A light island inside a dark
   region has to restate the light region's colours. */
.checkcard{--fg:var(--paper);--body:rgba(231,234,230,.84);--quiet:rgba(231,234,230,.66);--edge:rgba(231,234,230,.22);--bad:var(--red-lift);color:var(--paper);background:var(--ink);border:0;display:flex;flex-direction:column;gap:clamp(14px,1.5vw,18px);padding:clamp(22px,2.4vw,34px);position:relative}
.checkcard::before{content:attr(data-plate);position:absolute;top:0;left:0;background:var(--ochre);color:var(--ink);font-family:var(--sign);font-weight:800;font-stretch:84%;font-size:var(--fs-micro);letter-spacing:.16em;text-transform:uppercase;padding:6px 14px}
.checkcard>*:first-child{margin-top:clamp(16px,1.6vw,20px)}

.entry{display:flex;flex-direction:column;gap:12px}
.entry label{display:block;margin-bottom:7px}
.entry .f{display:flex;flex-direction:column;min-width:0}
.entry .row{display:flex;gap:10px}
.entry .row input{flex:1;min-width:0}
.entry .row .go{flex:none}
.fields{display:grid;grid-template-columns:1.35fr .95fr auto;gap:10px;align-items:end}
.fields .f{display:flex;flex-direction:column;min-width:0}
.fields input{width:100%}
.fields .go{align-self:end;height:50px;min-height:50px;padding-inline:22px}
.entry input{width:100%;font-family:var(--sign);font-weight:600;font-stretch:94%;font-size:var(--fs-small);padding:0 13px;height:50px;border:2px solid var(--ink);background:var(--white);color:var(--ink);min-width:0;border-radius:0}
.entry input::placeholder{color:var(--ink-45);font-weight:400}
.entry input:focus-visible{outline:3px solid var(--red);outline-offset:2px}

/* The readings. A label at the left, a measurement at the right, a rule
   between: the shape a reading has wherever one is written down. */
/* The row owns its line box, not the value inside it. A value that resolves
   to a word rather than a number renders at --fs-note instead of --lede, and
   because the row took its height from the value, the two rows that land on a
   word shrank 4px each the moment the demo filled them in. That was the whole
   of the homepage's layout shift: the fold moving under the reader while the
   check ran. Fixing the line box here keeps every state the same height. */
.card{border-top:2px solid var(--edge)}.card .r{display:flex;justify-content:space-between;align-items:baseline;gap:12px;border-bottom:1px solid var(--edge);opacity:.34;transition:opacity .3s ease;padding:13px 0;line-height:calc(var(--lede) * 1.55)}.card .k{font-family:var(--sign);text-transform:uppercase;font-size:var(--fs-micro);font-weight:700;font-stretch:88%;letter-spacing:.13em;color:var(--quiet);align-self:center}.card .v{font-family:var(--sign);font-variant-numeric:tabular-nums;text-align:right;font-size:var(--lede);font-weight:800;font-stretch:84%;color:var(--fg);letter-spacing:0}
.card .v.bad{color:var(--bad);font-weight:800}.card .v.none{color:var(--quiet);font-size:var(--fs-note);font-weight:700;font-stretch:92%}
/* Two lines are reserved because the demo writes into this line on load.
   The opening sentence fits on one, the result sentence needs two, and the
   growth pushed the readings band down 13px: the only layout shift left on
   the homepage, in the element the fold is spent on. min-height is a floor,
   so a narrower column that needs three lines still gets them. */
.samp{font-size:var(--fs-note);line-height:1.5;color:var(--body);font-stretch:100%;margin:0;min-height:calc(2 * 1.5 * var(--fs-note))}

/* ---------- readings band ----------
   Four measured figures, each carrying the run it came from. The ticket is the
   point: a figure without a source is the thing this business exists to refuse. */
.readings{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:0;margin-top:clamp(30px,3.4vw,52px);border-top:1px solid var(--hair)}
.readings>div{padding:20px clamp(14px,1.8vw,26px) 4px 0;border-right:1px solid var(--hair);display:grid;grid-template-rows:auto 1fr auto}
.readings>div:last-child{border-right:0}
.readings b{display:block;font-family:var(--sign);font-weight:800;font-stretch:72%;font-size:var(--fig);line-height:.84;letter-spacing:-.015em;font-variant-numeric:tabular-nums;color:var(--fg)}
.readings span{display:block;margin-top:8px;font-size:var(--fs-note);color:var(--body);max-width:20ch;line-height:1.35}.readings .ticket{display:block;margin-top:9px;font-family:var(--sign);font-weight:700;font-stretch:86%;font-size:var(--fs-micro);letter-spacing:.13em;text-transform:uppercase;color:var(--quiet);align-self:end}

/* ---------- the three keys, as panels ----------
   Three painted signs in a row, edge to edge, each one the colour its group
   keeps for the rest of the site. Not three identical cards with an icon on top. */
.keys{display:grid;grid-template-columns:repeat(3,1fr);gap:0 clamp(24px,3vw,52px);max-width:var(--wrap);margin-inline:auto;padding-inline:var(--pad);width:100%}
.key{display:flex;flex-direction:column;gap:10px;text-decoration:none;padding:22px 0 clamp(24px,2.6vw,38px);border-top:3px solid var(--ink);color:var(--ink);transition:background .16s linear}.key:last-child{border-right:0}
/* Same tone properties as the panels, for the same reason: a 12px label at
   .78 alpha measured 4.25:1 on brick and 2.99:1 on ochre. */
.key.brick{border-top-color:var(--brick)}
.key.ochre{border-top-color:var(--ochre-deep)}
.key.slate{border-top-color:var(--slate)}
.key.brick:hover{background:rgba(26,34,41,.045)}
.key.ochre:hover{background:rgba(26,34,41,.045)}
.key.slate:hover{background:rgba(26,34,41,.045)}
.key h3{font-size:var(--h3);font-weight:800;font-stretch:82%;line-height:1.02;color:inherit;margin:0}
.key p{margin:0;font-size:var(--fs-small);line-height:1.45;color:var(--ink-70)}.key .more{margin-top:auto;padding-top:16px;font-family:var(--sign);font-weight:800;font-stretch:86%;font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-45);display:inline-block;transition:transform .18s cubic-bezier(.16,1,.3,1),color .16s linear}

/* ---------- ruled lists ----------
   A rate sheet, a parts list, a run of questions. Everything that pairs a name
   with a reading is ruled, because that is what the paperwork under a sign
   looks like and it is denser and more honest than a grid of boxes. */
/* The way out of the four to the nine. Sits on the sweep's own bottom rule so
   it reads as the last row of the list rather than as a button under it. */
.more-trades{display:block;padding:18px 0 0;font-family:var(--sign);font-weight:800;
  font-stretch:86%;font-size:var(--fs-note);letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink);text-decoration:none}
.more-trades:hover{color:var(--red-deep)}
.rows{border-top:2px solid var(--ink);margin-top:clamp(24px,2.6vw,36px)}
.row-item{display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.3fr);gap:8px clamp(20px,3vw,56px);align-items:start;padding:clamp(18px,2vw,26px) 0;border-bottom:1px solid var(--hair);text-decoration:none}.row-item h3{margin:0;font-size:var(--h3);font-weight:800;font-stretch:86%;line-height:1.06;transition:transform .18s cubic-bezier(.16,1,.3,1)}
.row-item p{margin:0;color:var(--ink-70);font-size:var(--fs-small)}
.row-item .lab{align-self:start}
a.row-item{transition:background .14s linear}
a.row-item:hover{background:rgba(26,34,41,.045)}a.row-item:hover h3{color:var(--red-deep);transform:translateX(4px)}
.panel .rows{border-top-color:var(--edge)}
.panel .row-item{border-bottom-color:var(--edge)}
.panel a.row-item:hover{background:rgba(255,255,255,.07)}

/* Numbered steps. The ordinal is cut large because the sequence is the
   information: this is what happens first, and this is what it costs. */
.steps{list-style:none;margin:clamp(24px,2.6vw,36px) 0 0;padding:0;border-top:2px solid var(--ink)}
.steps li{border-bottom:1px solid var(--hair);padding:clamp(18px,2vw,28px) 0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:4px clamp(18px,2.4vw,36px);align-items:start}
.steps .num{grid-row:span 2;font-family:var(--sign);font-weight:800;font-stretch:76%;font-size:var(--price);line-height:.82;font-variant-numeric:tabular-nums;color:var(--ochre-deep);min-width:1.4ch;text-align:right}
.steps h3{font-size:var(--h3);font-weight:800;font-stretch:86%;line-height:1.06;margin:0}
.steps p{margin:7px 0 0;color:var(--ink-70);font-size:var(--fs-small);max-width:64ch}.faq{margin:clamp(24px,2.6vw,36px) 0 0;display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));column-gap:clamp(28px,3.4vw,64px);max-width:none;border-top:2px solid var(--ink)}.faq dt{font-family:var(--sign);font-weight:800;font-stretch:86%;font-size:var(--h3);line-height:1.1;text-transform:uppercase;padding:clamp(18px,2vw,26px) 0 0;padding-top:clamp(16px,1.8vw,24px)}.faq dd{color:var(--ink-70);font-size:var(--fs-small);line-height:1.6;margin:10px 0 0;padding:0;border-bottom:0;max-width:none}

.facts-list{list-style:none;margin:clamp(24px,2.6vw,36px) 0 0;padding:0;border-top:2px solid var(--ink);max-width:78ch}
.facts-list li{border-bottom:1px solid var(--hair);padding:14px 0;font-size:var(--fs-small);color:var(--ink-70);padding-left:26px;position:relative}
.facts-list li::before{content:"";position:absolute;left:0;top:.72em;width:12px;height:2px;background:var(--ochre-deep)}

/* ---------- tables ----------
   A rate sheet. The price is cut lettering because the price is the thing the
   reader came to find. */
.tw{overflow-x:auto;margin-top:clamp(24px,2.6vw,36px)}
table{width:100%;border-collapse:collapse;font-size:var(--fs-small)}
th,td{text-align:left;padding:14px 18px 14px 0;border-bottom:1px solid var(--hair);vertical-align:baseline}
thead th{font-family:var(--sign);font-weight:800;font-stretch:86%;font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-70);border-bottom:2px solid var(--ink);padding-bottom:11px}
td{color:var(--ink-70)}
td:first-child{color:var(--ink);font-weight:600;font-stretch:94%}
td.n,td.p{font-variant-numeric:tabular-nums;font-family:var(--sign);font-weight:800;font-stretch:80%;color:var(--ink);white-space:nowrap}
td.p{font-size:var(--reading);line-height:1}
td.n{font-size:var(--h3)}
/* The rate sheet's last column is prose and must be allowed to wrap. It was
   nowrap by inheritance and clipped mid-word on the homepage at 1440. */
td.w{white-space:normal;color:var(--ink-70)}
.rates td.w{font-size:var(--fs-small)}
tr.lead td{border-bottom:2px solid var(--ink)}
.panel table th,.panel table td{border-bottom-color:var(--edge)}
.panel td.w{color:var(--body)}
.panel thead th{color:var(--quiet);border-bottom-color:var(--edge)}
.panel td:first-child,.panel td.n,.panel td.p{color:var(--fg)}

/* ---------- findings ----------
   A count, what it is out of, and what it means. The denominator sits beside
   the figure, because "104" is a number and "104 of 166" is a finding. */
.findings{display:grid;grid-template-columns:repeat(auto-fit,minmax(255px,1fr));gap:clamp(22px,2.6vw,40px);margin-top:clamp(26px,2.8vw,40px)}
/* Four findings need a track twelve pixels narrower than six do, or the fourth
   wraps alone. Dropping the track globally to buy that fixed the one page with
   four and broke the thirteen with six, which went from a balanced 3+3 to a
   ragged 4+2. So the narrower track is scoped to the grids that hold exactly
   four. Where :has is unsupported the four-item page falls back to 3+1, which
   is what it did before, and the six-item pages are untouched either way. */
.findings:has(> :nth-child(4):last-child){grid-template-columns:repeat(auto-fit,minmax(238px,1fr))}
.find{padding-top:16px;border-top:3px solid var(--ink)}
.find .n{color:var(--fg);font-family:var(--sign);font-weight:800;font-stretch:78%;font-size:var(--fig);line-height:.86;font-variant-numeric:tabular-nums;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.find .n .of{font-size:var(--fs-note);font-weight:700;font-stretch:86%;letter-spacing:.13em;text-transform:uppercase;color:var(--quiet)}
.find p{margin:10px 0 0;font-size:var(--fs-small);color:var(--body);max-width:34ch}
.panel .find{border-top-color:var(--edge)}
.panel .find .n{color:var(--fg)}
.panel .find .n .of{color:var(--quiet)}/* Four services per group, so the column count may never be three. auto-fit
   with a 290px floor resolved to three columns from 1100 to 1440 and left the
   fourth card alone beside two empty columns, three times down the page. Any
   floor that fixes 1440 breaks at 960: with four items the only counts that
   fill the last row are four, two and one, and that is a decision about the
   set, not about a minimum width. Measured: 254px columns at 1440, 245px at
   1201, 492px at 1200. */
.groups{display:grid;gap:clamp(22px,2.6vw,40px);margin-top:clamp(26px,2.8vw,38px);grid-template-columns:repeat(4,minmax(0,1fr))}
/* Four columns is right for the twelve services, which come in groups of four.
   Every OTHER use of this grid holds six, and six into four is 4+2: two filled
   cells and two empty ones, on seventeen pages. Six gets three columns and
   sits 3+3. Measured 2026-09-16; the same shape the findings grid had.
   Inside a min-width query, because :has() carries the specificity of its own
   argument and outranked the two max-width rules below it: unscoped, it held
   six columns at three across a 390px phone and gave each cell 102px, which
   broke a word out of its own heading. A specificity that ignores a breakpoint
   is how a desktop fix reaches a phone. */
@media (min-width:861px){
  .groups:has(> :nth-child(6):last-child){grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (max-width:1200px){.groups{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:620px){.groups{grid-template-columns:minmax(0,1fr)}}
.group{border-top:3px solid var(--ink);padding-top:16px;display:flex;flex-direction:column}
.grouped.brick .group{border-top-color:var(--brick)}
.grouped.ochre .group{border-top-color:var(--ochre-deep)}
.grouped.slate .group{border-top-color:var(--slate)}
.group h3{margin-bottom:7px}
/* A service card that leads somewhere behaves like a hub row: the whole card is
   the target, the heading takes the accent and steps right. Nothing changes at
   rest, so twelve cards do not turn into twelve underlined blocks. */
a.group{color:inherit;text-decoration:none;transition:background .14s linear}
a.group:hover,a.group:focus-visible{background:rgba(26,34,41,.05)}
a.group h3{transition:transform .18s cubic-bezier(.16,1,.3,1)}
a.group:hover h3,a.group:focus-visible h3{color:var(--key,var(--red-deep));transform:translateX(4px)}
.panel a.group:hover,.panel a.group:focus-visible{background:rgba(231,234,230,.09)}
.group p{margin:0 0 10px;color:var(--ink-70);font-size:var(--fs-small)}
.group .src{margin-top:0}
.group .cnt{margin-top:auto;font-family:var(--sign);font-weight:700;font-stretch:86%;font-size:var(--fs-micro);letter-spacing:.13em;text-transform:uppercase;color:var(--ink-45)}
.panel .group{border-top-color:var(--edge)}
.panel .group p,.panel .group .src,.panel .group .cnt{color:var(--body)}

/* ---------- prices ---------- */
.prices{display:grid;grid-template-columns:repeat(auto-fit,minmax(238px,1fr));gap:clamp(22px,2.6vw,40px);margin-top:clamp(26px,2.8vw,38px)}
.price{padding-top:16px;border-top:3px solid var(--ink);display:flex;flex-direction:column;align-items:flex-start}
.price .p{font-family:var(--sign);font-weight:800;font-stretch:78%;font-size:var(--price);line-height:.9;font-variant-numeric:tabular-nums}
.price h3{margin:10px 0 6px}
.price .w{font-size:var(--fs-small);color:var(--ink-70);max-width:32ch;margin-bottom:18px}
/* Every fixed price is buyable from where it is read (2026-09-27: all sixteen went to
   /contact and nothing on the site could be bought). The button sits under the words. */
/* The buttons in a row of price cards line up: a card is a column and its
   button takes the space left over, so a longer description no longer pushes
   its button below its neighbours' (Michael, 2026-09-28: "the buttons arent
   alligned"). */
.price .price-go{margin-top:auto}
/* The price and its checkout, under the lede of every page that sells one
   thing. The red button is the one click; the free check is a quiet link. */
/* A grid, not a wrapping row: with two prices of different widths a row put
   the second button further right than the first, the misalignment Michael
   had just reported on /pricing. Prices share a column, buttons share one. */
.buybar{display:grid;grid-template-columns:auto minmax(0,max-content);justify-content:start;align-items:center;gap:12px 18px;margin-top:clamp(18px,2vw,26px)}
.buyitem{display:contents}
/* .go is nowrap everywhere (a header button must stay one line), and a nowrap
   label is as wide as its text. Measured 2026-10-16 at 320 to 407: "Start the
   receptionist" ran 71px past the edge beside the price, 21 pages had the same
   cut, and "Run the free visibility check" on /links ran 22px past. Inside a
   page section the button may now wrap and never be wider than its column; the
   price grid's second column may shrink to what is left. Wide enough, nothing
   changes. */
section :is(a,button).go{max-width:100%;white-space:normal;text-align:center;line-height:1.2;padding-block:10px}
.buyalt{grid-column:1/-1;font-size:var(--fs-small);font-weight:600;text-underline-offset:3px}
.buyitem .bp{font-family:var(--sign);font-weight:800;font-stretch:78%;font-size:var(--reading);line-height:1;font-variant-numeric:tabular-nums;color:var(--ink)}
td.b{text-align:right;white-space:nowrap}
a.buyl{font-weight:700;text-underline-offset:3px}

/* ---------- masthead ----------
   A subpage announces itself the way a shop front does: the name cut large
   over a stripe that runs to the trim, not a small heading floating on a
   column of prose. */
.masthead{padding-block:clamp(36px,4vw,64px) clamp(24px,2.6vw,38px);border-bottom:var(--stripe) solid var(--ochre)}
.masthead h1{font-size:var(--h1-page);font-stretch:72%;max-width:18ch;margin-bottom:0}
.masthead .lede{margin-bottom:0}

/* ---------- marquee ----------
   The painted sign above the shelf. The dense service copy reads on the paper
   ground below it rather than on the saturated field, because plenty of these
   readers are in their sixties. */
.marquee{padding-block:clamp(26px,3vw,44px);border-top:3px solid var(--key,var(--ink))}
.marquee .wrap{display:flex;align-items:center;justify-content:space-between;gap:20px clamp(24px,3vw,48px)}
.marquee h2{margin-bottom:8px}
.marquee p{margin:0;font-size:var(--lede);max-width:56ch}
.marquee .count{flex:none;text-align:right;font-family:var(--sign);font-weight:800;font-stretch:76%;font-size:var(--fig);line-height:.84;font-variant-numeric:tabular-nums}
.marquee .count em{display:block;font-style:normal;font-size:var(--fs-micro);font-weight:700;font-stretch:86%;letter-spacing:.16em;text-transform:uppercase;margin-top:8px;color:var(--quiet)}

/* ---------- prose bits inside a panel ---------- */
.panel ul{margin:0;padding-left:22px;max-width:68ch}
.panel li{margin-bottom:10px;font-size:var(--fs-small)}
hr.rule{border:0;border-top:2px solid var(--hair);margin:clamp(20px,2.4vw,30px) 0}
.panel hr.rule{border-top-color:var(--edge)}

/* A row whose whole content is its heading spans the sheet rather than
   leaving the second column empty. */
.row-item>h3:only-child{grid-column:1 / -1}
/* A row you can buy carries its price at cut size, where the eye lands. */
.row-item .price-line{font-family:var(--sign);font-weight:800;font-stretch:78%;font-size:var(--reading);line-height:1;margin:0 0 6px;font-variant-numeric:tabular-nums;color:var(--ink)}
a.buy:hover .price-line{color:var(--red-deep)}

/* Every component that sets an ink tint for prose restates it from the tone
   properties when it lands on a painted panel. Without this the component is
   only correct on the ground it was first drawn on, which is how a paragraph
   about who runs your audit ended up at 3.44:1 on the ochre rate sheet. */
.panel :is(.row-item p,.steps p,.faq dd,.facts-list li,.price .w,.aside p,
  .samp,.marquee p,.card .v){color:var(--body)}
.panel :is(.find .n .of,.group .cnt,.card .k,.steps .num){color:var(--quiet)}
/* Specificity, not order: the catch-all above is (0,3,0) and so is
   .card .v.bad, so the later one won and the bad state was dead.

   These two named a colour for the ground the card was drawn on when the panel
   was painted light. C1 inverted the card to an ink plate and left them: the
   "none" value became ink at 66% on ink, which is 1.00:1, the same colour as
   the plate, and the "bad" value sat at 2.42:1. Two rows of the check on the
   homepage fold were unreadable, one of them invisible. Both read from the
   tone now, which is what the note four rules above tells every component to
   do. The contrast gate could not see it because both states are applied by
   the demo after load, and the gate measured the page before it ran. */
.panel .card .v.bad{color:var(--bad)}
.panel .card .v.none{color:var(--quiet)}
.panel :is(.rows,.steps,.faq,.facts-list,.aside,.prices,.findings){border-top-color:var(--edge)}
.panel :is(.steps li,.faq dd,.facts-list li,.price,.find){border-bottom-color:var(--edge);border-right-color:var(--edge)}

/* ---------- the aside ---------- */
.aside{margin-top:clamp(30px,3vw,46px);padding-top:clamp(22px,2.2vw,30px);border-top:2px solid var(--ink);max-width:68ch}
.aside h3{margin-bottom:8px}
.aside p{margin:0;color:var(--ink-70)}
.panel .aside{border-top-color:var(--edge)}
/* ---------- the one moment ----------
   The plate runs a labelled sample of itself when the page loads, one reading
   at a time, then clears and hands the visitor the controls. It is the only
   motion on the site: a page that fades and slides every section on scroll is
   the thing that reads as generated, and the check is the only element here
   that is about the visitor. No API call is spent on it. */
.card .r.on{opacity:1}
.card .r .v{display:inline-block;transition:transform .3s cubic-bezier(.16,1,.3,1),opacity .3s ease}
.card .r:not(.on) .v{transform:translateY(3px)}
.checkcard .capture{border-top:2px solid var(--edge);padding-top:16px;animation:settle .32s cubic-bezier(.16,1,.3,1)}
@keyframes settle{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}
.checkcard.is-sample{position:relative}
.checkcard.is-sample::after{content:"Sample";position:absolute;top:0;right:0;background:var(--paper);color:var(--ink);font-family:var(--sign);font-weight:800;font-stretch:84%;font-size:var(--fs-micro);letter-spacing:.16em;text-transform:uppercase;padding:6px 12px}

@media (prefers-reduced-motion:reduce){
  .card .r,.card .r .v{transition:none}
  .card .r:not(.on) .v{transform:none}
  .checkcard .capture{animation:none}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
}
/* ---------- responsive ---------- */
@media (max-width:1080px){
  .hero-grid{grid-template-columns:1fr;gap:34px}
  /* One column, so the grid has no areas at all and the source order stands:
     lead, then the check, then the readings. The panel is the thing this page
     is for and a phone should not scroll past four figures to reach it. */
  .hero-grid>.lead{align-self:start}
  /* The strip takes its own margin back here, so the lede must give its one
     up or the two stack and the phone gains 24px of nothing. */
  .hero-grid>.lead>.lede{margin-bottom:0}
  .hero-grid>.lead>.strip{margin-top:clamp(24px,2.6vw,36px)}
  
  
  
}
@media (max-width:1000px){
  
  
  .key:last-child{border-bottom:0}
}
@media (max-width:760px){
  .row-item{grid-template-columns:1fr;gap:7px}
  .steps li{grid-template-columns:auto 1fr;gap:2px 16px}
  .readings{grid-template-columns:1fr 1fr}
  .readings>div{border-right:1px solid var(--edge);border-bottom:1px solid var(--edge);padding-right:16px;padding-left:0}
  .readings>div:nth-child(2n){border-right:0;padding-left:16px}
  .findings,.prices{grid-template-columns:1fr}
  .fields{grid-template-columns:1fr}
  .fields .go{width:100%;height:54px;min-height:54px}
  /* Stacked, the rule between strip items reads as a stray tick beside the
     lines that happen not to be first. */
  
}
@media (max-width:520px){
  .readings{grid-template-columns:1fr}
  .readings>div{border-right:0;padding-left:0}
  .readings>div:nth-child(2n){padding-left:0}
  .readings>div:last-child{border-bottom:0}
  .steps li{grid-template-columns:1fr}
  .steps .num{grid-row:auto;text-align:left}
}


/* ---------- the right half of the page ----------
   Measured on 2026-09-14, and it was the one thing both buyers named first:
   every section put its heading and its lede in one left-hugging column and
   left 358 to 653 pixels empty down the right, on the same side, every time.
   The content was not too little; it never crossed the page.

   The heading and its lede now sit side by side on the grid with no template
   change: a section's wrap is a two-column grid, everything spans both columns
   by default, and only a leading heading plus the lede that follows it take one
   column each. */
section > .wrap{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);column-gap:clamp(28px,3.4vw,64px);align-content:start}
section > .wrap > *{grid-column:1 / -1;min-width:0}
/* A grid item stretches to its column by default, which turned the one action
   at the foot of every page into a 1128px slab. An action is the size of its
   own words. */
section > .wrap > .go{justify-self:start;grid-column:1}
section > .wrap > :is(h1,h2):first-child{grid-column:1;margin-bottom:0}
section > .wrap > :is(h1,h2):first-child + .lede{grid-column:2;align-self:end;margin-bottom:0;max-width:52ch}
/* The pair sits on one baseline, so whatever follows clears both of them. */
section > .wrap > :is(h1,h2):first-child + .lede + *{margin-top:clamp(26px,3vw,44px)}
@media (max-width:860px){
  section > .wrap{grid-template-columns:minmax(0,1fr)}
  section > .wrap > :is(h1,h2):first-child{grid-column:1 / -1;margin-bottom:14px}
  section > .wrap > :is(h1,h2):first-child + .lede{grid-column:1 / -1;margin-bottom:0;max-width:64ch}
}
.faq .qa{border-bottom:1px solid var(--hair);padding-bottom:clamp(16px,1.8vw,24px)}
.panel .faq{border-top-color:var(--edge)}
.panel .faq .qa{border-bottom-color:var(--edge)}

/* The keys STOPPED being full-bleed when .keys took max-width:var(--wrap) and
   padding-inline:var(--pad). The compensation for the old layout survived the
   new one and became a double inset: measured at 1440, "Get found" began at
   312px against a rule that began at 156px, and the first card had 191px of
   text column where the other two had 348px. The grid already lands the type
   on the content grid; nothing here has to help it. */

/* The four source tickets sat at two different heights, 1117 and 1096, because
   whichever caption above them wrapped to two lines pushed its ticket down. In
   the one band meant to read like instruments, the labels were ragged. */
.readings > div{display:grid;grid-template-rows:auto 1fr auto}

/* A full sentence set in condensed capitals is slow to read and, stacked four
   deep on a saturated band, it shouts so evenly that nothing is loud. The caps
   stay on names and numbers, where they came from. */
.row-item > h3:only-child{text-transform:none;font-stretch:96%;font-weight:700;font-size:var(--lede);line-height:1.32;letter-spacing:0}
section + section, .band + section{padding-top:clamp(36px,3.4vw,58px)}
.key .more{display:inline-block;transition:transform .18s cubic-bezier(.16,1,.3,1),color .16s linear}
.key:hover .more{color:var(--red-deep);transform:translateX(6px)}
@media (prefers-reduced-motion:reduce){
  .key .more,.row-item h3{transition:none}
  .key:hover .more{transform:none}
  a.row-item:hover h3{transform:none}
}

/* A rate sheet is the one table on this site a phone cannot scroll sideways
   through without losing the price. Below 620 it stops being a table and
   becomes stacked rows, so the NOTE column stops slicing itself into "in tota"
   and "and yo" down the screen edge. */
@media (max-width:820px){
  .faq{grid-template-columns:1fr}
}
@media (max-width:620px){
  .tw table,.tw tbody,.tw tr,.tw td{display:block;width:100%}
  .rates,.rates tbody,.rates tr,.rates td{display:block;width:100%}
  .tw thead,.rates thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .tw tr{border-bottom:1px solid var(--hair);padding-block:16px}
  .tw td{border:0;padding:0 0 4px}
  .tw td.n{font-size:var(--reading)}
  /* stacked on a phone, the buy link sits on the same left edge as the rest */
  .tw td.b{text-align:left;padding-top:6px}
  /* The header is hidden once rows stack, so a cell carries its own column name.
     Without it the town table read '249' then '79' with nothing saying what either was. */
  .tw td[data-label]::before{content:attr(data-label);display:block;font-family:var(--sign);font-weight:700;
    font-stretch:86%;font-size:var(--fs-micro);letter-spacing:.12em;text-transform:uppercase;color:var(--quiet);margin-top:6px}
  .rates tr{border-bottom:1px solid var(--hair);padding-block:16px}
  .rates tr.lead{border-bottom-width:2px;border-bottom-color:var(--ink)}
  .rates td{border:0;padding:0 0 4px}
  .rates td.p{font-size:var(--price);padding-bottom:2px}
  .tw{overflow-x:visible}
}

/* ---------- the sweep, drawn ----------
   The only picture on the site, and it is a measurement rather than a
   decoration: each bar's width is a claim token rendered from the ledger at
   build time, so it goes stale with the data and cannot drift from the figure
   printed beside it.

   Two buyers in a row said the absence of any visual material was the single
   thing stopping them believing a company that sells web design. Client work
   is not ours to show, so the site shows its own. */
.sweep{border-top:2px solid var(--ink);margin-top:clamp(24px,2.6vw,36px)}
.sweep-row{display:grid;grid-template-columns:minmax(120px,.7fr) minmax(0,2fr) minmax(94px,auto);align-items:center;gap:6px clamp(18px,2.4vw,36px);padding:clamp(16px,1.9vw,24px) 0;border-bottom:1px solid var(--hair);text-decoration:none;transition:background .14s linear}
.sweep-row:hover{background:rgba(26,34,41,.045)}
.sweep-row h3{margin:0;font-size:var(--h3)}
.sweep-row .track{position:relative;height:22px;background:rgba(26,34,41,.09)}
.sweep-row .fill{position:absolute;inset:0 auto 0 0;background:var(--ink)}
.sweep-row.brick .fill{background:var(--brick-deep)}
.sweep-row.ochre .fill{background:var(--ochre-deep)}
.sweep-row.slate .fill{background:var(--slate)}
.sweep-row .count{text-align:right;white-space:nowrap}
.sweep-row .count b{font-weight:800;font-stretch:78%;font-size:var(--reading);line-height:1;font-variant-numeric:tabular-nums}
.sweep-row .count span{display:block;margin-top:3px;font-size:var(--fs-micro);font-weight:700;font-stretch:86%;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-70)}
.sweep-row p{grid-column:1 / -1;margin:2px 0 0;font-size:var(--fs-note);color:var(--ink-70);max-width:70ch}
.sweep-row:hover h3{color:var(--red-deep)}
@media (max-width:720px){
  .sweep-row{grid-template-columns:1fr auto;gap:10px 16px}
  .sweep-row .track{grid-column:1 / -1;order:3}
  .sweep-row p{order:4}
}



/* The fixed action bar covers the foot of the first viewport, which on the
   contact page hid the YOUR EMAIL label completely and a third of its field at
   scroll 0. Body padding clears the bar at the end of a page, not inside it. */
@media (max-width:760px){
  .hero 
  .entry .f input,.entry .f label{scroll-margin-bottom:88px}
}


/* Prose measure. A bare <p> inside a section had no cap, so the longest lines
   on the site ran to 155 characters at 1440 while the FAQ answers beside them
   sat at a correct 65. Anything that is a sentence gets a measure. */
section > .wrap > p,
section > .wrap > .aside p,
.marquee p{max-width:72ch}


/* The opening runs wider than the reading measure. The comp's headline column
   is 649px of a 1536 frame; inside --wrap it was 549px and the three cut lines
   became five. Only the hero widens: prose below it keeps 1240 so the measure
   stays readable. */
.hero > .wrap{max-width:1432px}


/* Three keys across is a desktop shape. Below 820 they stack, and each keeps
   its own coloured rule so the group it names is still readable. */
@media (max-width:820px){
  .keys{grid-template-columns:1fr;gap:0}
  .key{padding-block:20px 24px}
}


/* A marquee already announces its group, so the passage under it starts close.
   Section padding was stacking with the marquee's and leaving a dead band
   between a heading and the things it heads. */
.marquee + section{padding-top:clamp(18px,2vw,30px);border-top:0}

/* /links, the page the Instagram bio opens: each post's picture beside its line, the
   way the feed showed it, on the same ruled row every list here uses. The picture
   stays beside the words at phone width, where .row-item otherwise stacks. */
.masthead .go{margin-top:clamp(20px,2.4vw,28px)}
.row-item.post{grid-template-columns:clamp(84px,10vw,120px) minmax(0,1fr);gap:clamp(14px,2vw,28px);align-items:center}
.row-item.post img{display:block;width:100%;height:auto;aspect-ratio:4/5;object-fit:cover;background:var(--ink)}
.row-item.post .said{display:block;min-width:0}
.row-item.post .lab{display:block;margin:0 0 8px}
.row-item.post h3{margin:0 0 8px}
.posts .src{margin-top:18px}

