/* ==========================================================================
   CalcMule — concrete calculator page mockup
   Brand tokens from the approved v10 brand system:
     cream #faf7f1 · ink #20242b · plum #5c3a52 (brand / site-wide action)
     The accent tokens are set per page by the build — see the block in :root below
   ========================================================================== */

:root{
  color-scheme: light;

  /* surfaces */
  --paper:#faf7f1;
  --surface:#ffffff;
  --surface-2:#f4efe5;
  --surface-3:#ece5d7;

  /* text */
  --ink:#20242b;
  --ink-2:#4c525b;
  --ink-3:#63676f;

  /* lines */
  --line:#e4ddcf;
  --line-2:#cfc7b4;

  /* brand */
  --plum:#5c3a52;
  --plum-2:#734b67;
  /* ---- the accent tokens -------------------------------------------------
     Three tokens, set per page by the build (calcmule.py's accent-theme section) and
     re-emitted in each page's own <style> block. The fallbacks here are concrete's, and
     only matter for a page rendered without that block.

     --accent      the hero band background, and the fill under filled controls. Fixed:
                   the accent comes from the mascot and never changes with the theme.
     --accent-ink  the text and icons that sit ON the accent — computed per page as cream or
                   ink, whichever actually reads on that accent. A light accent (gravel,
                   drywall) flips to ink, which is what stops those bands being unreadable.
                   Also used for filled controls, for the same reason.
     --accent-lead the lead line's opacity on the band. The design's de-emphasis is 88%; the
                   build raises it per page only as far as 4.5:1 requires.
     --accent-ui   links, focus rings, selected-state borders — anything that sits on a
                   *surface* rather than on the accent. Lifts in dark mode. */
  --accent:#56677d;
  --accent-ink:#f2f4f7;
  --accent-lead:88%;
  --accent-ui:var(--accent);

  /* notes / trust */
  --sand:#f0e8d9;
  --sand-line:#ded2ba;

  /* semantic */
  --ok:#3f7a5c;
  --warn:#9c5a20;

  /* elevation */
  --sh-1:0 1px 2px rgba(32,36,43,.06), 0 2px 8px rgba(32,36,43,.05);
  --sh-2:0 2px 5px rgba(32,36,43,.07), 0 14px 30px rgba(32,36,43,.09);

  /* shape */
  --r-sm:7px; --r-md:11px; --r-lg:16px;

  /* type */
  --sans:"Work Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --disp:"Space Grotesk", "Work Sans", ui-sans-serif, system-ui, sans-serif;

  /* metrics */
  --maxw:1080px;
  --gut:20px;
}

@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    color-scheme: dark;
    --paper:#16181d;
    --surface:#1e2127;
    --surface-2:#242830;
    --surface-3:#2c313a;
    --ink:#f1ede4;
    --ink-2:#c2bdb2;
    --ink-3:#9b988f;
    --line:#333842;
    --line-2:#454c58;
    --plum:#6f4663;
    --plum-2:#845a76;
    --sand:#2a2620;
    --sand-line:#433c30;
    --ok:#6aa88a;
    --warn:#d69a5c;
    --sh-1:0 1px 2px rgba(0,0,0,.4);
    --sh-2:0 2px 6px rgba(0,0,0,.4), 0 16px 32px rgba(0,0,0,.45);
  }
}
:root[data-theme="dark"]{
  color-scheme: dark;
  --paper:#16181d;
  --surface:#1e2127;
  --surface-2:#242830;
  --surface-3:#2c313a;
  --ink:#f1ede4;
  --ink-2:#c2bdb2;
  --ink-3:#9b988f;
  --line:#333842;
  --line-2:#454c58;
  --plum:#6f4663;
  --plum-2:#845a76;
  --sand:#2a2620;
  --sand-line:#433c30;
  --ok:#6aa88a;
  --warn:#d69a5c;
  --sh-1:0 1px 2px rgba(0,0,0,.4);
  --sh-2:0 2px 6px rgba(0,0,0,.4), 0 16px 32px rgba(0,0,0,.45);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--sans);
  font-size:16px;
  line-height:1.55;
  padding-block:0;
}
img,svg{max-width:100%}
[hidden]{display:none !important}
a{color:inherit}
button{font:inherit;color:inherit}
h1,h2,h3,h4{font-family:var(--disp);font-weight:600;line-height:1.15;text-wrap:balance;margin:0}
p{margin:0}
ul{margin:0;padding:0;list-style:none}

.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gut)}
.num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
.eyebrow{
  font-family:var(--sans);font-size:11px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);
}
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
:where(a,button,input,select,[tabindex]):focus-visible{
  outline:2px solid var(--accent-ui);outline-offset:2px;border-radius:4px;
}

/* ---------- header ---------- */
.sitehead{
  position:sticky;top:0;z-index:40;
  background:color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter:saturate(1.4) blur(8px);
  border-bottom:1px solid var(--line);
}
.sitehead .wrap{display:flex;align-items:center;gap:14px;min-height:60px}
.brand{display:flex;align-items:center;gap:9px;text-decoration:none;font-family:var(--disp);
  font-weight:700;font-size:19px;letter-spacing:-.015em}
.brand .mark{width:30px;height:30px;flex:0 0 auto;display:block}
.brand .mark svg{display:block;width:100%;height:100%}
.nav{margin-left:auto;display:flex;align-items:center;gap:4px}
.nav a{
  text-decoration:none;font-size:14.5px;font-weight:500;color:var(--ink-2);
  padding:8px 11px;border-radius:var(--r-sm);
}
.nav a:hover{color:var(--ink);background:var(--surface-2)}
@media (max-width:620px){ .nav a.opt{display:none} }

/* ---------- hero ---------- */
.hero{
  background-color:var(--accent);color:var(--accent-ink);
  position:relative;overflow:hidden;
  /* A page whose accent is a gradient (paint) supplies --band-image and paints it here, over the
     flat --accent underneath; every other page gets `none` as an inert first layer, which keeps
     the background-size list aligned either way. Below it, the aggregate speckle that grounds the
     band in the material. */
  background-image:var(--band-image,none),
    radial-gradient(circle at 12% 22%, color-mix(in srgb, var(--accent-ink) 7%, transparent) 0 1.5px, transparent 2px),
    radial-gradient(circle at 62% 71%, color-mix(in srgb, var(--accent-ink) 10%, transparent) 0 1.5px, transparent 2px),
    radial-gradient(circle at 38% 88%, color-mix(in srgb, var(--accent-ink) 5%, transparent) 0 1px, transparent 1.5px),
    radial-gradient(circle at 84% 34%, color-mix(in srgb, var(--accent-ink) 8%, transparent) 0 1px, transparent 1.5px);
  background-size:auto, 26px 26px, 34px 34px, 19px 19px, 29px 29px;
}
.hero .wrap{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:28px;align-items:center;
  padding-block:36px 34px;
}
.crumb{font-size:13px;opacity:.85;margin-bottom:12px}
.crumb a{text-decoration:none}
.crumb a:hover{text-decoration:underline}
.hero h1{font-size:clamp(30px,5.6vw,46px);font-weight:700;letter-spacing:-.025em}
.hero .lead{margin-top:12px;max-width:52ch;font-size:16.5px;
  color:color-mix(in srgb, var(--accent-ink) var(--accent-lead), transparent)}
.hero .gets{display:flex;flex-wrap:wrap;gap:8px;margin-top:20px}
.hero .gets li{
  font-size:13px;font-weight:500;padding:5px 11px;border-radius:999px;
  background:color-mix(in srgb, var(--accent-ink) 13%, transparent);
  border:1px solid color-mix(in srgb, var(--accent-ink) 22%, transparent);
}
.hero .mascot{width:clamp(120px,17vw,186px);margin:0}
.hero .mascot svg{display:block;width:100%;height:auto}
@media (max-width:720px){
  .hero .wrap{grid-template-columns:1fr;gap:18px;padding-block:26px 28px}
  .hero .mascot{width:104px;order:-1;margin:0}
}

/* ---------- workspace ---------- */
.work{
  display:grid;grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  gap:26px;align-items:start;padding-block:34px 8px;
}
@media (max-width:900px){ .work{grid-template-columns:1fr;gap:20px} }

.card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--sh-1);
}
.calc{padding:22px}

/* mode tabs */
.modes{display:flex;gap:4px;padding:4px;background:var(--surface-2);border-radius:var(--r-md);margin-bottom:20px}
.modes button{
  flex:1;border:0;background:none;cursor:pointer;border-radius:var(--r-sm);
  padding:9px 10px;font-size:14px;font-weight:500;color:var(--ink-2);
}
.modes button[aria-selected="true"]{background:var(--surface);color:var(--ink);font-weight:600;box-shadow:var(--sh-1)}
.modes button:hover:not([aria-selected="true"]){color:var(--ink)}

/* step */
.step{display:flex;gap:12px;align-items:baseline;margin-bottom:12px}
.step .n{
  flex:0 0 auto;width:21px;height:21px;border-radius:50%;
  background:var(--accent);color:var(--accent-ink);box-shadow:0 0 0 1px var(--accent-ui);
  font-family:var(--disp);font-size:12px;font-weight:600;
  display:grid;place-items:center;transform:translateY(2px);
}
.step h2{font-size:16.5px;font-weight:600;letter-spacing:-.01em}
.step .sub{font-size:13.5px;color:var(--ink-3);font-weight:400;font-family:var(--sans)}
/* `~` rather than `+`: the dimensions readout sits between two blocks, so the step that follows
   it is no longer an adjacent sibling and would lose its divider. Every .block is a sibling
   inside one calculator panel, so the two selectors agree wherever the blocks are contiguous. */
.block ~ .block{margin-top:26px;padding-top:24px;border-top:1px solid var(--line)}

/* shape picker */
.shapes{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:8px}
.shapes button{
  display:flex;flex-direction:column;align-items:center;gap:7px;cursor:pointer;
  padding:12px 6px 10px;background:var(--surface);color:var(--ink-2);
  border:1px solid var(--line);border-radius:var(--r-md);font-size:12.5px;font-weight:500;
  text-align:center;line-height:1.25;
}
.shapes button svg{width:30px;height:30px;color:var(--ink-3)}
.shapes button:hover{border-color:var(--line-2);color:var(--ink)}
.shapes button[aria-pressed="true"]{
  border-color:var(--accent-ui);background:color-mix(in srgb, var(--accent-ui) 8%, var(--surface));
  color:var(--ink);font-weight:600;box-shadow:inset 0 0 0 1px var(--accent-ui);
}
.shapes button[aria-pressed="true"] svg{color:var(--accent-ui)}
.shapehint{margin-top:11px;font-size:13.5px;color:var(--ink-3)}
/* The labeled diagram for a shape whose three numbers are easy to mix up (the trapezoid's two
   parallel sides and its perpendicular height). It carries the same names the fields use, on
   arrows pointing at the parts, so the two agree without a paragraph of explanation. Text gets a
   surface-colored halo so a label can sit on a dimension line the way drafting labels do. */
.shapediag{
  margin-top:13px;padding:13px 10px 7px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-md);
}
.shapediag svg{display:block;width:100%;max-width:360px;height:auto;margin:0 auto;color:var(--ink-2)}
.shapediag svg .dlab text{
  fill:var(--ink-2);font-family:var(--disp);font-size:12px;
  paint-order:stroke;stroke:var(--surface-2);stroke-width:3px;stroke-linejoin:round;
}
.morebtn{
  margin-top:9px;cursor:pointer;font:inherit;font-size:12.5px;font-weight:500;
  padding:5px 11px;border-radius:999px;border:1px dashed var(--line-2);
  background:none;color:var(--ink-2);
}
.morebtn:hover{border-color:var(--accent-ui);color:var(--accent-ui)}

/* the handoff — the hub's routing block. Deliberately styled close to .rel (the related grid at
   the foot of every page) but given its own names, because its job is different: these links
   carry the measurement with them, so it sits inside the ticket next to the result. */
.handoff{margin-top:16px;border-top:1px solid var(--line);padding-top:14px}
.handoff h3{font-size:14px;font-weight:600;letter-spacing:-.01em}
.handoff .hsub{margin-top:5px;font-size:12.5px;color:var(--ink-3)}
.hgrid{margin-top:11px;display:grid;grid-template-columns:1fr 1fr;gap:8px}
@media (max-width:480px){ .hgrid{grid-template-columns:1fr} }
.hitem{
  display:flex;align-items:center;gap:8px;text-decoration:none;color:inherit;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:7px 9px;
}
.hitem:hover{border-color:var(--accent-ui);background:color-mix(in srgb, var(--accent-ui) 7%, var(--surface-2))}
.hitem img{width:21px;height:21px;flex:0 0 auto;display:block}
.hitem .ht{font-family:var(--disp);font-weight:600;font-size:12.5px;display:block;line-height:1.2}
.hitem .hd{font-size:11px;color:var(--ink-3);display:block;line-height:1.25}

/* the room list — repeating dimension rows, unique to the square-footage hub */
.rooms{margin-top:14px;display:flex;flex-direction:column;gap:10px}
.room{display:grid;grid-template-columns:26px 1fr 18px 1fr 30px;gap:8px;align-items:center}
.room .rnum{
  font-family:var(--disp);font-size:12px;font-weight:600;color:var(--ink-3);
  display:grid;place-items:center;width:22px;height:22px;border-radius:50%;
  background:var(--surface-2);border:1px solid var(--line);
}
.room .rx{text-align:center;color:var(--ink-3);font-size:13px}
.room .rm{
  cursor:pointer;border:1px solid var(--line);background:var(--surface);color:var(--ink-3);
  width:26px;height:26px;border-radius:50%;font:inherit;font-size:14px;line-height:1;
  /* Centres the bin. The button carries an SVG now rather than a text glyph, and an inline SVG on
     the baseline of a 26px circle sits low and off-centre. */
  display:grid;place-items:center;
}
.room .rm svg{width:15px;height:15px;display:block}
.room .rm:hover{border-color:var(--warn);color:var(--warn)}
.room .inwrap{border:1px solid var(--line-2);border-radius:var(--r-sm);background:var(--surface);
  display:flex;align-items:stretch;overflow:hidden}
.room .inwrap input{border:0;background:none;font:inherit;font-size:14px;color:var(--ink);
  padding:8px 9px;width:100%;min-width:0}
.room .inwrap input:focus{outline:2px solid var(--accent-ui);outline-offset:-2px}
.room .inwrap input[data-sub]{border-left:1px solid var(--line);max-width:52px}
/* A room row gives each dimension 193px. A normal .field gives the same three controls 380px, and
   the fraction bar costs a fixed ~190px in both, because five segments at a 38px tap-target floor
   is already the floor. Side by side the bar took the entire column: the feet box measured 18px
   wide with 0px of content, so a single digit was clipped, and the inches box was no better. That
   is the failure mode to watch for here — `min-width:0` on a flex item next to a fixed-width
   sibling, which is a collapse rather than a squeeze and so looks like a rendering accident.
   The bar cannot be shrunk to fit, so it takes its own line, which reads better anyway: the numbers
   on top, the fraction picker beneath them. The number boxes get a real floor so they cannot
   collapse again, and `data-main` grows to fill whatever the inches box does not take. */
.room .inwrap{flex-wrap:wrap}
.room .inwrap .box-main{flex:1 1 84px;min-width:84px}
.room .inwrap .box-sub{flex:0 0 52px;min-width:52px}
.room .inwrap .fracbar{flex:1 1 100%}

/* ---------- prose ----------
   For the pages that are text rather than a tool: about, contact, the legal set. Needed because
   the global reset zeroes paragraph margins and strips list markers, so an article inside the
   existing stylesheet would run together as one block. */
.prose{max-width:70ch}
.prose>p{margin-top:15px;color:var(--ink-2)}
.prose>p:first-child{margin-top:0}
.prose h3{margin-top:30px;font-size:16.5px;font-weight:600;letter-spacing:-.01em}
.prose h4{margin-top:22px;font-size:14.5px;font-weight:600;color:var(--ink)}
.prose ul,.prose ol{margin-top:12px;padding-left:20px;list-style:disc}
.prose ol{list-style:decimal}
.prose li{margin-top:7px;color:var(--ink-2)}
.prose a{color:var(--accent-ui);text-decoration:underline;text-underline-offset:2px}
.prose a:hover{text-decoration-thickness:2px}
.prose code{
  font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  font-size:.9em;background:var(--surface-2);border:1px solid var(--line);
  border-radius:4px;padding:1px 5px;
}
.prose strong{color:var(--ink);font-weight:600}
.prose .lede{font-size:17px;color:var(--ink);margin-top:0}
.prose .meta{margin-top:22px;font-size:12.5px;color:var(--ink-3)}

/* ---------- the crew ----------
   The homepage composition: the mule centred with the ten crew badges flanking it five a side,
   staggered, so it reads as the mule carrying the crew rather than eleven equal icons. Eleven
   items will not sit on one line at phone width, so the row wraps and the mule leads. */
.crew{display:flex;align-items:center;justify-content:center;gap:9px;flex-wrap:wrap;margin:0}
.crew .crewb{width:54px;height:54px;flex:none;display:block;border-radius:50%}
/* The badge is a link to its calculator, so the image fills the anchor and carries the rounding. */
.crew .crewb img{display:block;width:100%;height:100%;border-radius:50%}
.crew .crewb:hover img{opacity:.82}
.crew .crewb:focus-visible{outline:2px solid var(--accent-ui);outline-offset:3px}
.crew .muleb{width:124px;height:124px;flex:none;display:block;border-radius:50%;margin:0 10px}
/* The stagger: every other badge sits a little higher. */
.crew .crewb:nth-child(odd){transform:translateY(-8px)}
.crew .crewb:nth-child(even){transform:translateY(8px)}
@media (max-width:640px){
  .crew .muleb{width:96px;height:96px;order:-1;width:100%;margin:0 auto 14px}
  .crew .crewb{width:44px;height:44px}
  .crew .crewb:nth-child(odd),.crew .crewb:nth-child(even){transform:none}
  /* Constrained so the ten badges wrap five and five rather than six and four — the composition is
     meant to read as five a side, and that should survive the stack. */
  .crew{max-width:264px;margin-inline:auto}
}

/* ---------- homepage ---------- */
/* No colour here: .hero .lead already sets the band's own text colour, and this rule comes later
   in the file, so specifying one would override it and put dark ink on the plum band. */
.homelead{max-width:60ch;margin:0 auto;text-align:center}
.homelead h1{font-size:clamp(28px,4.6vw,40px)}
.homelead .lead{margin-top:14px;font-size:17px;max-width:none}
.crewwrap{padding-block:34px 30px}

/* ---------- tape entry ----------
   The inch fraction as a bar of four denominator families, ascending in precision left to right.
   A segment shows its denominator when idle and the live value when its family owns the fraction,
   so the bar is the picker and the readout at once. Sized for a gloved thumb: a tap lands in a
   family, another moves up inside it, and holding scrubs — which matters for the sixteenths,
   where a family holds eight. */
.fracbar{display:flex;align-self:stretch;flex:0 0 auto}
.fracbar [data-fracbtn]{
  cursor:pointer;border:0;border-left:1px solid var(--line);background:var(--surface-2);
  color:var(--ink-3);font-family:var(--disp);font-size:13px;font-weight:600;line-height:1;
  min-width:38px;padding:0 6px;touch-action:manipulation;user-select:none;-webkit-user-select:none;
}
.fracbar [data-fracbtn]:hover{background:var(--surface-3);color:var(--ink)}
.fracbar [data-fracbtn][aria-pressed="true"]{
  background:var(--accent);color:var(--accent-ink);font-size:16px;
}
.fracbar [data-fracbtn]:focus-visible{outline:2px solid var(--accent-ui);outline-offset:-2px}
/* The zero segment clears the fraction. It is narrower than the families because it holds one
   glyph where they hold "1/16", and the bar has nothing to spare at phone sizes. It reads apart
   from them on its own: idle it says "0" while the others say their denominators.
   `[data-tape]` here is not decoration: both the preset-row rule and the small-screen rule below
   also set a min-width on segments, at equal specificity but later in the file, so this one has to
   outrank them rather than rely on source order. */
.field[data-tape] .fracbar [data-fracbtn][data-fraczero]{min-width:20px;padding:0 2px}
/* The zero costs about 20px the bar did not have, and below 430px the number boxes are already at
   their floor — so it is paid for out of the segments rather than out of the digits, since a
   segment is not the primary input on a phone and the row is the thing that has to fit. */
@media (max-width:430px){
  .field .fracbar [data-fracbtn]{min-width:30px;padding:0 3px}
  .field[data-presets] .fracbar [data-fracbtn]{min-width:26px;padding:0 3px}
}
.field .inwrap input[data-sub]{border-left:1px solid var(--line);max-width:62px}
/* A field carrying the bar needs the width for it — four segments do not fit a grid column, so it
   takes the row. The group is capped rather than stretched: a bare input running the full width of
   a desktop panel reads as a mistake, and the bar belongs next to the number it modifies. */
.field[data-tape]{grid-column:1/-1}
.field[data-tape] .inwrap{max-width:380px}

/* Tape or decimals. Two icon buttons rather than one flipping control, so the mode you are in is
   visible without tapping to find out. */
/* The toggle carries its own gap rather than inheriting one. It is `inline-flex`, so its vertical
   margins are an atomic inline-level box's and do not collapse with anything: nine of the ten
   calculators put it immediately after the step heading, whose own 12px bottom margin was doing
   the work, and roofing puts it after the measurement-basis note, which has no bottom margin at
   all — so on roofing the note and the toggle were flush. Measured: 12px on the nine, 0px on
   roofing. Stating the gap here and cancelling it after a step keeps every page at 12 and makes a
   future page spacing correctly by default instead of by luck. */
.entrytog{display:inline-flex;margin-top:12px;border:1px solid var(--line-2);border-radius:var(--r-sm);overflow:hidden;background:var(--surface)}
.step + .entrytog{margin-top:0}
.entrytog button{
  display:flex;align-items:center;gap:7px;cursor:pointer;font:inherit;font-size:13px;
  font-weight:600;padding:9px 13px;border:0;background:none;color:var(--ink-2);
  touch-action:manipulation;
}
.entrytog button+button{border-left:1px solid var(--line-2)}
.entrytog button svg{width:21px;height:21px;flex:none}
.entrytog button[aria-pressed="true"]{background:var(--accent);color:var(--accent-ink)}
.entrytog button[aria-pressed="false"]:hover{color:var(--ink);background:var(--surface-2)}
@media (max-width:400px){ .entrytog button span{display:none} .entrytog button{padding:10px 14px} }
/* At phone width a room row stacks: the number, then each dimension on its own line with the full
   width for its three controls, then the remove button. Both dimensions are pinned to column 2 —
   without that the second one auto-places into the 20px number column and collapses. */
@media (max-width:560px){
  .room{grid-template-columns:20px 1fr;row-gap:6px}
  .room .rx{display:none}
  .room .inwrap{grid-column:2}
  .room .rm{grid-column:1/-1;width:auto;border-radius:var(--r-sm);margin-top:2px}
}
.addroom{
  margin-top:11px;cursor:pointer;font:inherit;font-size:13px;font-weight:600;
  padding:8px 13px;border-radius:var(--r-sm);border:1px solid var(--line-2);
  background:var(--surface);color:var(--ink-2);
}
.addroom:hover{border-color:var(--accent-ui);color:var(--accent-ui)}

/* fields */
.fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:14px}
.field label,.flabel{display:block;font-size:12px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3);margin-bottom:6px}
.flabel{margin-top:0}
.fields{margin-top:14px}
/* A field with a preset row needs the full width: its buttons do not fit a third of the grid. */
.field[data-presets]{grid-column:1/-1}
/* A preset field is a small number, so the row is capped to keep the box from stretching across the
   full column. The cap has to fit the fraction bar plus a real entry, though: at 190px the bar took
   152 and left the box 36px, which holds exactly one digit, so a 12 in or 24 in thickness showed
   only its first character. Thickness also steps in quarters, so the box has to hold "12.25". The
   cap is set from that sum rather than picked. */
.field[data-presets] .inwrap{max-width:240px}
/* A preset row is the cramped one: it holds the fraction bar plus up to two number boxes, and
   fencing's height and flooring's roll width are feet fields, so they carry both. Two things are
   reclaimed here, and only here — a plain dimension field keeps the full 38px segments and the
   12px side padding, because it has the room and the bar there is the primary input.
   The boxes hold one to three characters, so 12px a side was slack; the segments come down from
   38px to 30px, which is still a tap target and is what stops the number being squeezed to a
   single digit. Together they leave each box ~42px, enough for "12" and "12.25". */
.field[data-presets] .inwrap input{padding-left:9px;padding-right:9px}
.field[data-presets] .fracbar [data-fracbtn]{min-width:30px;padding:0 4px}
.inwrap{display:flex;align-items:stretch;border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--surface);overflow:hidden}
.inwrap:focus-within{border-color:var(--accent-ui);box-shadow:0 0 0 3px color-mix(in srgb, var(--accent-ui) 22%, transparent)}
.inwrap input{
  flex:1;min-width:0;border:0;background:none;padding:11px 12px;font-size:17px;font-family:var(--disp);
  font-weight:500;font-variant-numeric:tabular-nums;color:var(--ink);
}
.inwrap input:focus{outline:none}
/* Each number box is wrapped so its unit can be shown inside it. The wrapper takes the flex slot
   the input used to hold, and the label overlays the input's right edge — an overlay, not a
   reserved column, because the tape rows at phone width have no column to give. The runtime hides
   the label when the field's unit button is already saying the unit, and when a long value would
   run underneath it. `[hidden]` needs the explicit rule: the display:flex below is authored CSS
   and would otherwise beat the browser's own hidden rule. */
.inwrap .box{position:relative;display:flex;flex:1;min-width:0}
/* The inches box sizes exactly the way the bare input did before the wrapper existed: flex:1, so
   it shares free space, capped at 62px. `flex:0 0 auto`, its first form, sized the box to the
   input's min-content instead; Firefox computes that as ~37px for a number input, the box
   collapsed there, the in-box label no longer fit its own measurement, and the runtime hid it.
   That is how "in" went missing on the concrete page in Firefox while Chromium looked right. */
.inwrap .box-sub{flex:1;min-width:0;max-width:62px}
.inwrap .box[hidden]{display:none}
.usuf{
  position:absolute;right:7px;top:50%;transform:translateY(-50%);pointer-events:none;
  font-family:var(--disp);font-size:11.5px;font-weight:600;letter-spacing:.02em;color:var(--ink-3);
}
.usuf[data-tight="true"]{display:none}
.inwrap .unit{
  border:0;border-left:1px solid var(--line);background:var(--surface-2);cursor:pointer;
  padding:0 11px;font-size:13px;font-weight:600;color:var(--ink-2);min-width:44px;
}
.inwrap .unit:hover{background:var(--surface-3);color:var(--ink)}
/* The dash placeholder, now only the never-written state of a box: every inch box settles to a
   real number (zero included) on change, so this shows mid-edit and not at rest. Quieter than a
   typed number, so a box that has nothing in it does not read as having something. */
.inwrap input::placeholder{color:var(--ink-3);opacity:1}
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
input[type=number]{-moz-appearance:textfield;appearance:textfield}

/* Spin the inches without a keyboard, at phone and tablet widths only — that is where typing a
   number is the slowest way to change one, and where the fraction bar beside it is already sized
   for a thumb. The site hides the native number spinner everywhere, and iOS Safari never draws
   one for type=number at all, so these are real buttons rather than a styled affordance, and they
   behave the same on every phone. Above 1024px they are off: the row is roomier there and the
   arrows would crowd the fraction bar.

   A SECOND stepper on the feet box was asked for and measured, then dropped for want of room.
   The numbers, so nobody has to derive them again: a second stepper costs 27px, and a feet row is
   main + sub + bar(140) + steppers. At 390px that leaves 47px per box, which fits; at 360px it
   leaves 32px, which clips. Worse, a PRESET row is capped at 254px whatever the viewport, so
   fencing's height and flooring's roll width would be left 30px either way. Both of those are
   under the ~45px a box needs to show "12", which is the one-digit clipping this file already
   fixed once. Making it fit everywhere means taking the 27px from the fraction bar — segments
   are already down from 38px to 30px — and the bar is the primary tape input where the feet box
   is a plain number that is usually quicker to type. */
.inchstep{display:none}
@media (max-width:1023px){
  .field[data-tape] .inchstep{
    display:flex;flex-direction:column;flex:0 0 auto;align-self:stretch;
    border-left:1px solid var(--line);background:var(--surface-2);
  }
  .field[data-tape] .inchstep button{
    flex:1;width:26px;border:0;background:none;cursor:pointer;padding:0;margin:0;
    color:var(--ink-2);font-size:13px;font-weight:600;line-height:1;font-family:var(--disp);
  }
  .field[data-tape] .inchstep button+button{border-top:1px solid var(--line)}
  .field[data-tape] .inchstep button:hover{background:var(--surface-3);color:var(--ink)}
  .field[data-tape] .inchstep button:active{background:var(--accent-ui);color:var(--accent-ink)}
  /* The stepper is hidden where it would change a box nobody can see (a feet field in decimal
     mode). The attribute alone loses to the display rule above, which is more specific. */
  .field[data-tape] .inchstep[hidden]{display:none}
  /* Same sum as the desktop cap, plus the stepper's 26px. */
  .field[data-presets] .inwrap{max-width:254px}
  /* These rows carry the stepper as well as the bar, and the feet ones hold two boxes, so the boxes
     give back a little side padding to keep a three-character entry whole — "6.5" in a fence
     height, where the step is a half. */
  .field[data-presets] .inwrap input{padding-left:6px;padding-right:6px}
}
/* The live dimensions readout in the Measurements block. Boxed and a step up in size from the
   field labels so it reads as an instrument rather than as prose, and quiet enough that it does
   not compete with the ticket below. Empty until the first render, so it takes no space before
   then — and `:empty` is what stops a blank bar flashing on load. */
/* The status panel: the caption, the quantity and the cost in one place, pinned under the header.
   It replaces two surfaces that each showed part of this. The dimensions band used to sit between
   the steps and *mirrored the ticket's caption verbatim*, so on desktop the same sentence appeared
   twice on screen at once, 153px apart, pinned at top:61 in the left column and top:80 in the
   right. The live bar put a third copy of a subset at the foot of the screen on phones. One panel,
   shown only while the Order Estimate is out of view, so it never duplicates it.

   Fixed rather than sticky. A sticky bar would have to occupy flow space to pin, and it appears and
   disappears as you scroll, so inserting that space would shift the page under the reader each time
   the condition flipped.

   Pinned at the top rather than the bottom on the user's call, and the reason turned out to be more
   than preference: on iOS the on-screen keyboard resizes only the *visual* viewport, so a fixed
   element at the bottom ends up underneath the keyboard, and Safari still does not support
   interactive-widget=resizes-content, which is the declaration that fixes it properly. At the top
   the question does not arise.

   The shadow is the same token the ticket carries, which is what makes it read as a layer above the
   page rather than as another band of it. `--sh-1` is invisible at this size; that was measured on
   the old band and the finding holds. */
.statusbar{
  position:fixed;left:0;right:0;top:61px;z-index:35;
  background:var(--surface);border-bottom:1px solid var(--line-2);
  box-shadow:var(--sh-2);
}
.statusbar[hidden]{display:none}
.statusbar .wrap{
  display:flex;align-items:center;gap:11px;
  padding-block:9px;
}
.statusbar .sb-m{width:26px;height:26px;flex:0 0 auto;display:block;border-radius:50%}
/* The caption wraps and is never truncated, on the user's call: the panel is the only place some
   of these numbers appear once the ticket's own headline has scrolled away, so a shorter panel is a
   worse panel. It had `text-overflow:ellipsis` and, below 560px, `display:none` — which hid the
   measurements precisely at the widths where screen space is scarcest and the panel is most
   useful. Nothing in this panel is ever hidden. */
.statusbar .sb-cap{
  flex:1 1 auto;min-width:0;
  font-size:13.5px;color:var(--ink-2);
}
/* The figures never truncate: they are the reason the panel exists. */
.statusbar .sb-fig{flex:0 0 auto;display:flex;align-items:baseline;gap:4px}
.statusbar .sb-n{
  font-family:var(--disp);font-weight:700;font-size:19px;
  font-variant-numeric:tabular-nums;color:var(--ink);
}
.statusbar .sb-u,.statusbar .sb-c{font-size:13px;color:var(--ink-2)}
.statusbar .sb-c{font-variant-numeric:tabular-nums}
.statusbar #sb-go{
  flex:0 0 auto;margin-left:4px;border:0;background:none;cursor:pointer;
  font:inherit;font-size:13px;font-weight:600;color:var(--accent-ui);white-space:nowrap;
  padding:4px 2px;touch-action:manipulation;
}
/* Narrower than tablet the panel takes two lines rather than dropping anything: the mascot, the
   figures and the way back to the ticket on the first, the full caption wrapped underneath. */
@media (max-width:700px){
  .statusbar .wrap{flex-wrap:wrap;row-gap:3px}
  .statusbar .sb-m{order:1}
  .statusbar .sb-fig{order:2}
  .statusbar #sb-go{order:3;margin-left:auto}
  /* A full-width basis forces the caption onto its own line whatever the others measure. */
  .statusbar .sb-cap{order:4;flex:1 1 100%}
}
.presets{display:flex;gap:6px;margin-top:8px;flex-wrap:wrap}
.presets button{
  cursor:pointer;padding:5px 10px;border-radius:999px;font-size:12.5px;font-weight:500;
  border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
}
.presets button:hover{border-color:var(--line-2);color:var(--ink)}
.presets button[aria-pressed="true"]{background:var(--accent);border-color:var(--accent-ui);color:var(--accent-ink);font-weight:600}
.helper{margin-top:9px;font-size:13px;color:var(--ink-2)}
.helper b{font-weight:600;font-family:var(--disp)}

/* option groups — the waste allowance, the measurement basis, the shingle line */
/* An option row carries its own gap above it, the same way the entry toggle does, because
   otherwise it lives on whatever happens to precede it. Measured across the site: after a step
   heading a row sits 12px down (the heading's own margin-bottom does the work), after a .flabel
   6px (the label's margin-bottom), and after another option row **0px**, which is the bug: roofing
   ran the material picker straight into the shingle lines, and paint ran one row into the next.
   A .wastenote between two rows had the same problem from the other side, since that note has a
   top margin and no bottom one. Cancelling after a step and a label keeps the existing spacing
   everywhere it already worked and fixes only where it did not. */
.opts{display:flex;gap:8px;flex-wrap:wrap}
.opts + .opts,
.wastenote + .opts{margin-top:12px}
.opts button{
  cursor:pointer;padding:8px 13px;border-radius:var(--r-sm);border:1px solid var(--line);
  background:var(--surface);font-size:14px;font-weight:500;color:var(--ink-2);
}
.opts button:hover{border-color:var(--line-2)}
.opts button[aria-pressed="true"]{background:var(--accent);border-color:var(--accent-ui);color:var(--accent-ink);font-weight:600}
/* Option rows whose choice is a shape rather than a word: the icon sits above the label, the same
   arrangement as the shape grid, so the two pickers read as the same kind of control. Only the
   options that declare an icon take the taller card; waste, tile type and the rest stay pills. */
.opts button.hasicon{
  display:flex;flex-direction:column;align-items:center;gap:6px;
  min-width:88px;padding:11px 12px 9px;text-align:center;
}
.opts button.hasicon svg{width:30px;height:30px}
.opts button.hasicon[aria-pressed="false"] svg{color:var(--ink-3)}
.wastenote{margin-top:11px;font-size:13px;color:var(--ink-2);background:var(--sand);
  border:1px solid var(--sand-line);border-radius:var(--r-sm);padding:10px 12px}

/* The callout — something the calculator has to tell you that you did not ask for: roofing's
   pitch rule, paint's primer trigger. A finding, not fine print. */
.callout{margin-top:12px;font-size:13.5px;color:var(--ink-2);background:var(--sand);
  border:1px solid var(--sand-line);border-left:3px solid var(--warn);
  border-radius:var(--r-sm);padding:11px 13px}
.callout b{color:var(--ink)}
.callout.ok{border-left-color:var(--ok)}
.callout.stop{background:var(--warn);color:var(--surface);border-color:var(--warn)}
.callout.stop b{color:var(--surface)}
/* a sub-question inside a block — a picker that is not its own numbered step */
.subpick{margin-top:20px}
/* a second line inside a table cell, for the arithmetic behind a row */
.bagtable td .use{display:block;margin-top:2px;font-size:11.5px;font-weight:400;color:var(--ink-3)}

/* trace */
.tracewrap{border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;background:var(--surface-2)}
#trace{display:block;width:100%;height:auto;touch-action:none;cursor:crosshair;background:#cfd8cd}
.tracebar{
  display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between;
  padding:10px 12px;border-top:1px solid var(--line);background:var(--surface);
  font-size:13px;color:var(--ink-2);
}
.tracebar .read{font-family:var(--disp);font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
.mocknote{margin-top:10px;font-size:12.5px;color:var(--ink-3);
  border-left:2px solid var(--line-2);padding-left:10px}

/* ---------- the delivery ticket ---------- */
.ticket{
  position:sticky;top:80px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--sh-2);padding:20px;
}
@media (max-width:900px){ .ticket{position:static;top:auto} }

.ticket .thead{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding-bottom:12px;border-bottom:2px solid var(--ink);
}
.ticket .thead .t{font-family:var(--disp);font-weight:700;font-size:12px;letter-spacing:.16em;text-transform:uppercase}
.ticket .thead .m{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--ink-3)}
.ticket .thead .m svg{width:17px;height:17px;display:block}

.verdict{padding:18px 0 6px}
.verdict .qn{font-family:var(--disp);font-size:clamp(46px,8vw,58px);font-weight:700;line-height:.92;letter-spacing:-.035em}
.verdict .qu{font-family:var(--disp);font-size:19px;font-weight:500;color:var(--ink-2);margin-left:7px}
.verdict .cap{margin-top:8px;font-size:14.5px;color:var(--ink-2)}
.verdict .raw{margin-top:4px;font-size:13px;color:var(--ink-3)}

.perf{position:relative;height:0;border-top:1px dashed var(--line-2);margin:20px 0 18px}
.perf::before,.perf::after{
  content:"";position:absolute;top:-9px;width:18px;height:18px;border-radius:50%;background:var(--paper);
}
.perf::before{left:-29px}
.perf::after{right:-29px}

.path + .path{margin-top:16px;padding-top:16px;border-top:1px solid var(--line)}
/* The finishing list is a .path too, but it is rendered from a different template block and so
   sits *outside* #paths — which meant `.path + .path` never reached it and it had no space above at
   all, while the table's own margin put 10px below. Measured: 0px above the title, 10px below,
   which is the wrong way round for a heading, since a heading should sit closer to what it
   introduces than to what precedes it. Matching the sibling rule gives it the same 32px above that
   every other .path gets from its margin and padding together. */
.path.bill{margin-top:16px;padding-top:16px;border-top:1px solid var(--line)}
.path .ph{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.path .pn{font-family:var(--disp);font-weight:700;font-size:12px;letter-spacing:.14em;text-transform:uppercase}
.path .badge{
  font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  border-radius:999px;padding:2px 9px;background:color-mix(in srgb, var(--ok) 8%, var(--surface));
  color:var(--ok);border:1px solid color-mix(in srgb, var(--ok) 35%, transparent);
}
.path .amount{margin-left:auto;font-family:var(--disp);font-weight:600;font-size:19px;font-variant-numeric:tabular-nums}
.path .qty{font-family:var(--disp);font-weight:600;font-size:17px;margin-top:2px}
.path .note{margin-top:3px;font-size:13px;color:var(--ink-2)}

.bagtable{width:100%;border-collapse:collapse;margin-top:10px}
.bagtable th,.bagtable td{text-align:left;padding:7px 0;border-bottom:1px solid var(--line);font-size:14px}
/* A gutter between columns. `padding:7px 0` gave none, so a long value butted straight against the
   next column with nothing to separate them. */
.bagtable th + th,.bagtable td + td{padding-left:14px}
/* The Qty header was left while its values were right: the `.n` class right-aligned `td` and had no
   `th` counterpart, so every numeric column wore its heading on the wrong side. */
.bagtable th.n{text-align:right}
.bagtable th{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);font-weight:600}
.bagtable td.n{text-align:right;font-family:var(--disp);font-variant-numeric:tabular-nums;font-weight:500}
/* The Basis column is prose rather than a figure, so it is left in both its heading and its cells,
   and it says so in the markup. This was first written positionally as `nth-child(3)` and that was
   wrong: the third column is numeric on paint (Gallons), concrete (Cost) and roofing (Coverage), so
   the rule would have left-aligned their headings over right-aligned figures and reintroduced the
   very mismatch it was fixing. A column's alignment is a property of what it holds, not of where it
   sits, so it has to come from the markup. */
.bagtable th.basis,.bagtable td.basis{text-align:left}
.bagtable tr:last-child td{border-bottom:0}
.bagtable .hi td{background:color-mix(in srgb, var(--accent-ui) 7%, transparent)}
.bagtable .hi td:first-child{
  border-left:3px solid var(--accent-ui);padding-left:9px;
  border-top-left-radius:4px;border-bottom-left-radius:4px;
}

.facts{display:grid;grid-template-columns:auto 1fr;gap:7px 14px;margin-top:16px;
  padding-top:14px;border-top:1px solid var(--line);font-size:13.5px}
.facts dt{color:var(--ink-3)}
.facts dd{margin:0;font-family:var(--disp);font-variant-numeric:tabular-nums;font-weight:500}

.tfoot{margin-top:16px;padding-top:12px;border-top:1px solid var(--line);
  font-size:12px;color:var(--ink-3);display:flex;gap:10px;flex-wrap:wrap;justify-content:space-between}

/* ---------- trust / method ---------- */
.section{padding-block:38px}
.section > .wrap > .head{max-width:62ch}
.section h2{font-size:clamp(21px,3vw,26px);letter-spacing:-.02em}
.section .head p{margin-top:9px;color:var(--ink-2);font-size:15.5px}
.methods{display:grid;grid-template-columns:repeat(auto-fit,minmax(232px,1fr));gap:14px;margin-top:22px}
.rule{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);padding:16px;
}
.rule h3{font-size:14.5px;font-weight:600;letter-spacing:-.005em}
.rule p{margin-top:6px;font-size:14px;color:var(--ink-2)}
.rule code{
  font-family:var(--disp);font-size:13px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:5px;padding:1px 5px;
}
/* The published sources. Quieter than the method rules above it: this is the backing, not the
   argument, so it reads as fine print you can check rather than as another claim. */
.srcs{margin-top:18px;padding-top:15px;border-top:1px solid var(--line)}
.srcs h3{font-size:14.5px;font-weight:600;letter-spacing:-.005em}
.srcs ul{list-style:none;margin:9px 0 0;padding:0;display:grid;gap:7px}
.srcs li{position:relative;padding-left:14px;font-size:13px;line-height:1.5;color:var(--ink-2)}
.srcs li::before{
  content:"";position:absolute;left:1px;top:.58em;width:5px;height:5px;border-radius:50%;
  background:var(--accent-ui);opacity:.5;
}
.ref{margin-top:14px;background:var(--sand);border:1px solid var(--sand-line);
  border-radius:var(--r-md);padding:16px 18px}
.ref h3{font-size:14.5px}
.ref table{width:100%;border-collapse:collapse;margin-top:10px}
.ref th,.ref td{text-align:left;padding:6px 0;font-size:14px;border-bottom:1px solid var(--sand-line)}
.ref th{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.ref td.n{text-align:right;font-family:var(--disp);font-variant-numeric:tabular-nums;padding-right:18px}
.ref th:nth-child(2){text-align:right;padding-right:18px}
.ref tr:last-child td{border-bottom:0}
.ref .fine{margin-top:10px;font-size:12.5px;color:var(--ink-3)}
.xlink{margin-top:16px;font-size:14.5px;color:var(--ink-2)}
.xlink a{color:var(--accent-ui);font-weight:500;text-underline-offset:2px}

/* ---------- lead-gen band ---------- */
.quoteband{background:var(--plum);color:#f7f0f4;margin-top:8px}
.quoteband .wrap{
  display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);
  gap:26px;align-items:center;padding-block:34px;
}
@media (max-width:820px){ .quoteband .wrap{grid-template-columns:1fr;gap:20px;padding-block:28px} }
.quoteband h2{font-size:clamp(20px,3vw,25px);letter-spacing:-.02em}
.quoteband .sub{margin-top:9px;font-size:15px;color:rgba(255,255,255,.83);max-width:48ch}
.zipform{display:flex;gap:8px;margin-top:18px;flex-wrap:wrap}
.zipform input{
  flex:1 1 156px;min-width:0;padding:12px 13px;font-size:16px;font-family:var(--disp);
  border-radius:var(--r-sm);border:1px solid rgba(255,255,255,.34);
  background:rgba(255,255,255,.1);color:#fff;
}
.zipform input::placeholder{color:rgba(255,255,255,.72)}
.zipform input:focus{outline:2px solid #fff;outline-offset:1px}
.zipform button{
  cursor:pointer;border:0;border-radius:var(--r-sm);padding:12px 19px;font-size:15px;font-weight:600;
  background:#faf7f1;color:#5c3a52;
}
.zipform button:hover{background:#fff}
.consent{margin-top:10px;font-size:12.5px;color:rgba(255,255,255,.72);max-width:52ch}
.msg{margin-top:11px;font-size:13.5px;font-weight:500}
.msg.err{color:#ffd9c9}
.msg.ok{color:#e6f6ec}
.callbox{
  background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.2);
  border-radius:var(--r-md);padding:18px;
}
.callbox .cl{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.72)}
.callbox a{display:block;font-family:var(--disp);font-size:23px;font-weight:600;
  color:#fff;text-decoration:none;margin-top:5px;text-underline-offset:3px}
.callbox a:hover{text-decoration:underline}
.callbox p{margin-top:8px;font-size:13px;color:rgba(255,255,255,.72)}

/* ---------- amazon module ---------- */
.shop{display:grid;grid-template-columns:1fr;gap:12px;margin-top:20px}
@media (min-width:620px){ .shop{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (min-width:1000px){ .shop{grid-template-columns:repeat(3,minmax(0,1fr))} }
.item{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  padding:14px 15px;display:flex;flex-direction:column;gap:5px;
}
.item .t{font-weight:600;font-size:14.5px;text-decoration:none;text-underline-offset:2px}
.item .t:hover{color:var(--accent-ui);text-decoration:underline}
.item .w{font-size:13.5px;color:var(--ink-2)}
.item .tag{margin-top:auto;padding-top:7px;font-size:11.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3)}
.disclosure{margin-top:14px;font-size:12.5px;color:var(--ink-3);max-width:70ch}

/* ---------- related calculators ---------- */
.related{background:var(--surface-2);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));gap:10px;margin-top:22px}
.rel{
  display:flex;align-items:center;gap:11px;text-decoration:none;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  padding:11px 13px;
}
.rel:hover{border-color:var(--line-2);box-shadow:var(--sh-1);transform:translateY(-1px)}
.rel .m{width:34px;height:34px;flex:0 0 auto}
.rel .m svg{display:block;width:100%;height:100%}
.rel .nm{font-family:var(--disp);font-weight:600;font-size:14.5px;letter-spacing:-.01em}
.rel .ds{font-size:12.5px;color:var(--ink-3)}
.rel.self{border-color:var(--accent-ui);background:color-mix(in srgb, var(--accent-ui) 7%, var(--surface))}
.rel.self .ds{color:var(--accent-ui);font-weight:600}
/* A map pin beside "You're here", so the card for the page you are on reads as a place on the grid
   rather than only as different wording. Sized in em so it tracks the descriptor's 12.5px, and it
   inherits the accent from .self through currentColor. The negative vertical-align is what puts a
   1.15em glyph on the text's baseline rather than its descender. */
.rel .ds .pin{width:1.15em;height:1.15em;vertical-align:-.2em;margin-right:3px;overflow:visible}

/* ---------- ad slots (placeholders, not real ad code) ---------- */
.adband{padding-block:18px 0}
.adfoot{margin-top:22px}
.adslot{
  position:relative;border:1.5px dashed var(--line-2);border-radius:var(--r-md);
  min-height:90px;display:grid;place-content:center;gap:5px;text-align:center;
  padding:14px 18px;
  background:repeating-linear-gradient(135deg,transparent 0 9px,
    color-mix(in srgb, var(--ink-3) 6%, transparent) 9px 18px);
}
.adslot .adchip{
  position:absolute;top:7px;left:9px;font-size:9.5px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3);border:1px solid var(--line-2);
  border-radius:3px;padding:0 4px;
}
.adslot .adsize{font-family:var(--disp);font-weight:600;font-size:14.5px;color:var(--ink-2)}
.adslot .adwhere{font-size:12.5px;color:var(--ink-3)}
.adslot .narrow{display:none}
@media (max-width:620px){
  .adslot{min-height:100px}
  .adslot .wide{display:none}
  .adslot .narrow{display:inline}
}

/* ---------- footer ---------- */
.sitefoot{padding-block:34px 40px}
.sitefoot .fgrid{display:grid;grid-template-columns:minmax(0,1.2fr) repeat(2,minmax(0,1fr));gap:24px}
/* Narrow screens: the blurb takes its own full row, and Site and Legal flow beneath it. auto-fit
   puts them side by side while two columns of 150px fit, and gives each its own row once they do
   not. The old rule was two fixed columns, which left the blurb and Site sharing a row with Legal
   wrapped under the blurb. */
@media (max-width:720px){
  .sitefoot .fgrid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:22px}
  .sitefoot .fgrid>div:first-child{grid-column:1/-1}
}
.sitefoot .brand{margin-bottom:10px}
.sitefoot p{font-size:13.5px;color:var(--ink-2);max-width:38ch}
.fcol h4{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);margin-bottom:10px}
.fcol li{margin-bottom:6px}
.fcol a{font-size:14px;color:var(--ink-2);text-decoration:none}
.fcol a:hover{color:var(--ink);text-decoration:underline}
.fineprint{margin-top:30px;padding-top:18px;border-top:1px solid var(--line);
  font-size:12.5px;color:var(--ink-3);display:flex;gap:16px;flex-wrap:wrap;justify-content:space-between}

/* ---------- mobile live bar ---------- */

}

/* ---------- toast ---------- */
.toast{
  position:fixed;left:50%;transform:translateX(-50%);bottom:22px;z-index:60;
  background:var(--ink);color:var(--paper);font-size:13.5px;
  padding:10px 15px;border-radius:999px;box-shadow:var(--sh-2);max-width:calc(100vw - 32px);
}
@media (max-width:900px){ .toast{bottom:84px} }

@media (prefers-reduced-motion:no-preference){
  .rel{transition:transform .12s ease, box-shadow .12s ease, border-color .12s ease}
}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms !important;transition-duration:.01ms !important}
}

/* ---------- the print sheet ----------
   The one output of this site that leaves the screen. It goes to a yard counter or a truck, so it
   carries the answers and nothing that is an affordance: the estimate, the shopping list, and the
   method with its sources. Nav, the input card, the satellite trace, the quote band, the product
   module, the related grid, the site footer and the mobile bar all go.

   It is also built for a mono laser rather than a screen: every background forced white, every
   text black, every shadow off, so the plum hero band cannot eat a cartridge. Hierarchy survives
   on size and weight alone, which is what a printed sheet actually needs. */
.printonly{display:none}

@media print{
  *,*::before,*::after{
    background:#fff !important;color:#000 !important;
    box-shadow:none !important;text-shadow:none !important;
  }
  @page{margin:14mm}
  body{font-size:11pt;line-height:1.45}

  .sitehead,.hero,.card.calc,#panel-trace,.tracewrap,.statusbar,.quoteband,#supplies,
  .related,.sitefoot,.adband,.adfoot,.tprint,.consent{display:none !important}
  .printonly{display:block}

  /* The two-column grid is a screen layout; the sheet is one column of answers. */
  .work{display:block;padding:0}
  .ticket{position:static;border:0;border-radius:0;padding:0;background:#fff}
  .section,.wrap{max-width:none;padding-inline:0}

  /* The sheet's one conversion surface. It is the only place the paper asks for anything: the QR
     carries utm_source=print so GA4 can attribute a return, and the clean address beside it is what
     a person would actually type. 24mm keeps the code comfortably scannable at print size. */
  .printhead{
    display:flex;justify-content:space-between;align-items:flex-start;gap:14pt;
    border-bottom:1.5pt solid #000;padding-bottom:7pt;margin-bottom:12pt;
  }
  .printhead .pn{font-family:var(--disp,inherit);font-weight:700;font-size:15pt;letter-spacing:-.02em}
  .printhead .pc{margin:3pt 0 0;font-size:10pt;max-width:36ch}
  .printhead .pr{display:flex;flex-direction:column;align-items:center;gap:3pt;flex:none;text-align:center}
  .printhead .pr svg{width:24mm;height:24mm;display:block}
  .printhead .pu{font-size:8.5pt}

  /* The estimate leads the sheet, so it gets the space the hero band would have taken. */
  .verdict{padding:0 0 8pt}
  .verdict .qn{font-size:32pt;line-height:1}
  .verdict .qu{font-size:12pt}
  .thead .m img{display:none}

  /* Nothing in a list of things to buy should be split across a page break. */
  .path,.rule,.ref tr,.tfoot{break-inside:avoid}
  /* ON THE TICKET'S LAST ROW, which was investigated and deliberately left as it is. The kind of
     break here is the reason break-inside was never going to help: the disclaimer is one line, so
     it can never break *inside*. What happens is a break *before* it, and on drywall the ticket
     fills page 1 exactly, so the disclaimer lands alone at the top of page 2 above the method
     heading. Every one of these three was measured on a rendered PDF rather than reasoned about:

       - `break-before:avoid` on `.tfoot` does fix it, and costs a blank fourth sheet on drywall
         (3 pages -> 4, and page 4 extracts no text at all). Moving one line cascades the whole
         pagination. Not worth a sheet of paper.
       - Loosening `@page{margin}` to 13mm and 12mm does nothing: drywall stays at 3 pages with the
         same break. The margin is not the lever.
       - `break-before:avoid` on `.facts` does not work at all: the browser cannot honour it when
         the block genuinely does not fit, so flooring, roofing and tile still start page 2 with a
         facts line.

     So the stranded line stands. Read on paper it is fine print at the top of a continuation page,
     right-aligned against its own date, which reads as the sheet's running fine print rather than
     as a fragment of the ticket. A blank sheet reads as a mistake.
     Deliberately NOT break-inside:avoid on the whole ticket either: it runs longer than one page on
     several calculators, and forcing it whole would push the entire estimate to page 2 and leave
     page 1 nearly empty. */
  .rule,.ref{margin-bottom:9pt}
  .methods{display:block;margin-top:12pt}
  .rule{border:0;border-radius:0;padding:0}
  .rule h3{font-size:11pt;margin-bottom:1pt}
  .ref,.methods{break-inside:auto}
  #method{break-before:auto}
  #paths{break-inside:auto}
  .srcs li{break-inside:avoid}

  /* Printed links are not clickable, so the underline is noise and the href has to be spelled out.
     With the domain attached: a relative path on a sheet of paper tells the reader nothing. */
  a{text-decoration:none;color:#000 !important}
  .xlink a::after{content:" (calcmule.com" attr(href) ")";font-size:9pt}
}
/* The button that reveals the feature. Screen only — on paper it would be a picture of a button. */
.tprint{margin-top:14px}
.tprint button{
  cursor:pointer;width:100%;padding:9px 13px;font:inherit;font-size:13.5px;font-weight:600;
  border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--surface);color:var(--ink-2);
}
.tprint button:hover{background:var(--surface-2);color:var(--ink);border-color:var(--accent-ui)}

/* ---------- analytics consent ----------
   Present only when site.json holds a Cloudflare token. A bar at the foot of the screen rather
   than the top, because the calculator's status panel is pinned under the header. */
.consent{
  position:fixed;left:12px;right:12px;bottom:12px;z-index:60;
  max-width:680px;margin-inline:auto;padding:14px 16px;
  display:flex;flex-wrap:wrap;gap:12px 16px;align-items:center;justify-content:space-between;
  background:var(--surface);color:var(--ink);
  border:1px solid var(--line-2);border-radius:12px;box-shadow:var(--sh-2);
}
.consent[hidden]{display:none}
.consent p{margin:0;font-size:14px;line-height:1.45;flex:1 1 320px}
.consent a{color:var(--accent-ui);text-decoration:underline}
.consent .cbtns{display:flex;gap:8px;flex:0 0 auto}
.consent button{
  min-height:40px;padding:8px 18px;border-radius:8px;font-weight:600;cursor:pointer;
  border:1px solid var(--line-2);background:var(--surface);color:var(--ink);
}
.consent button.cyes{background:var(--plum);border-color:var(--plum);color:#f7f0f4}
.linkbtn{
  background:none;border:0;padding:0;font:inherit;color:inherit;
  text-decoration:underline;cursor:pointer;text-align:left;
}
