/*
 * Shared foundation for every archetype and block.
 *
 * This exists because the builder was re-deriving it on every single build:
 * roughly three hundred and thirty lines of reset, type scale, spacing, button
 * and navigation CSS, written fresh from a prose description each time. Written
 * fresh means judged fresh, and one unlucky judgement is how a garage with
 * eighteen real photographs of its work ended up with a grey gradient where its
 * hero should have been.
 *
 * Solved once, by hand, and reviewed. The builder's job is their words, their
 * photographs and their colours — not re-inventing what a heading should weigh.
 *
 * The palette tokens are the seam: they are overwritten per business from the
 * colours extracted out of their own logo, so two businesses sharing this file
 * do not share a look.
 */

:root {
  /* Overwritten per build from their own brand. These are only a safe floor. */
  --color-ink: #22252a;
  --color-paper: #fbfaf7;
  --color-accent: #14733d;
  --color-primary: #d2f7e2;
  --color-secondary: #8fdaed;

  /* Derived surfaces, so a palette swap carries through the whole page. */
  --surface: color-mix(in srgb, var(--color-paper) 92%, var(--color-ink) 8%);
  --surface-2: color-mix(in srgb, var(--color-paper) 84%, var(--color-ink) 16%);
  --line: color-mix(in srgb, var(--color-paper) 78%, var(--color-ink) 22%);
  /*
   * The border of anything that is a control. --line is a hairline for cards
   * and dividers and measures about 1.5:1 against paper; WCAG 1.4.11 wants 3:1
   * for the boundary of a control, and the menu button and the carousel jump
   * links have no other visual affordance at all. 48% ink measured 2.91:1 on
   * the default paper, under the bar this comment claimed; 56% clears it with
   * room on every palette the extractor produces, and the build check now
   * recomputes this mix and refuses a palette where it does not.
   */
  --line-strong: color-mix(in srgb, var(--color-paper) 44%, var(--color-ink) 56%);
  /* Secondary text: 80% ink. At 72% it read as small light grey on every tinted band. */
  --ink-soft: color-mix(in srgb, var(--color-ink) 80%, var(--color-paper) 20%);
  --accent-soft: color-mix(in srgb, var(--color-accent) 12%, var(--color-paper) 88%);

  /*
   * Fluid type. One clamp per step means the page reads well at every width
   * without a stack of breakpoints to keep in sync, and nothing ever lands
   * between two sizes looking wrong.
   */
  /* 14px at the least: at 13px the copyright, the contact labels and the
   * notes measured as fine print on every phone. */
  --text-sm: clamp(0.875rem, 0.85rem + 0.1vw, 0.9375rem);
  --text-base: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  --text-lg: clamp(1.125rem, 1.07rem + 0.28vw, 1.25rem);
  --text-xl: clamp(1.3125rem, 1.2rem + 0.6vw, 1.625rem);
  /*
   * Display sizes sized like the sites businesses admire, not like documents.
   * Every strong local-business site surveyed (Sept 2026: Dentologie, Aspen
   * Mountain, Gibbs Landscape, Boardroom) sets its section headings nearer 40px
   * and its hero close to 70px on desk; ours topped out at 36px/60px and read
   * as a report. The phone end barely moves — small screens were never the
   * problem.
   */
  --text-2xl: clamp(1.75rem, 1.42rem + 1.65vw, 2.75rem);
  --text-3xl: clamp(2.375rem, 1.6rem + 3.9vw, 4.5rem);
  /* Display size for a hero that owns the screen. */
  --text-4xl: clamp(2.75rem, 1.6rem + 5.4vw, 6rem);

  /*
   * The spacing scale. The three small steps are fixed: they separate things
   * that belong together and must not drift apart on a big screen. The three
   * large ones are fluid, so the gaps inside a band, the band paddings and the
   * column gaps grow with the window instead of sitting fixed and then leaping
   * at a breakpoint (a 16px column gap became 64px in one step at 840px).
   */
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: clamp(1.75rem, 1.35rem + 1.6vw, 2.5rem);
  --space-5: clamp(2.5rem, 1.85rem + 2.8vw, 4rem);
  --space-6: clamp(3.5rem, 2.4rem + 4.6vw, 6rem);
  /* Space between two columns side by side: one fluid value, set whether or
   * not the columns exist yet, so nothing jumps when they appear. */
  --col-gap: clamp(1.5rem, 0.4rem + 3.4vw, 4rem);
  /* The side gutter: 20px on a small phone, 32px by a laptop. */
  --gutter: clamp(1.25rem, 0.95rem + 1.25vw, 2rem);
  /*
   * The gap between sections, fluid. A fixed 4rem everywhere gave an 8rem hole
   * between the hero and the first section on a desktop and a cramped 1rem
   * between a heading and what it heads: the largest gaps where the eye least
   * needed them. Sections now breathe with the viewport, and a heading owns a
   * clear space-3 beneath it.
   */
  --section: clamp(3.5rem, 2.5rem + 4.2vw, 7rem);
  /* Each side's share of the gap between two neighbours on one ground. */
  --section-shared: calc(var(--section) * 0.55);

  --radius: 12px;
  --radius-sm: 8px;
  /* Cards and hero media: the generous rounding that reads as current. */
  --radius-lg: 20px;
  /* Buttons and pills; moods override it (999px on the archetypes that suit pills). */
  --radius-btn: 10px;

  /*
   * One theme, on purpose, and said so. A brochure site is allowed to commit to
   * its palette; what it must not do is answer a dark-mode preference with
   * light text on the browser's dark form controls. Declaring the scheme keeps
   * controls, scrollbars and the canvas consistent with the page.
   */
  color-scheme: light;
  --max-width: 1180px;
  --measure: 34rem;   /* reading width; over-long lines are the commonest fault */
  --shadow: 0 1px 2px color-mix(in srgb, var(--color-ink) 8%, transparent),
            0 8px 24px color-mix(in srgb, var(--color-ink) 6%, transparent);
  /* The deeper lift for the one or two elements a section leads with. */
  --shadow-lift: 0 2px 4px color-mix(in srgb, var(--color-ink) 10%, transparent),
                 0 18px 44px color-mix(in srgb, var(--color-ink) 13%, transparent);
  /* The accent pushed darker, for deep bands that must stay darker than the
   * accent itself. Darkened toward black in OKLCH, which keeps the accent's own hue: mixed
   * with their blue-grey ink in sRGB an orange accent came out a muddy brown
   * band beside orange buttons (Tantallon). Black has no hue of its own to
   * pull it toward, and is darker than ink, so paper on it only gains. */
  --accent-deep: color-mix(in oklch, var(--color-accent) 70%, #000);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* An anchor jump must not hide the heading under a sticky masthead. */
  scroll-padding-top: 7rem;
}

body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body, system-ui, -apple-system, "Segoe UI", sans-serif);
  font-size: var(--text-base);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

/*
 * Weight, tracking and leading come from the typeface (fonts.ts HEADING_STYLE):
 * one tracking for every family tightened condensed faces that need air and
 * asked a one-weight inscriptional face for a bold that does not exist.
 */
h1, h2, h3, h4 {
  font-family: var(--font-heading, var(--font-body, system-ui, sans-serif));
  font-weight: var(--heading-weight, 700);
  line-height: calc(var(--heading-leading, 1.08) + 0.04);
  letter-spacing: var(--heading-tracking, -0.015em);
  margin: 0 0 var(--space-2);
  text-wrap: balance;
}
h1 { line-height: var(--heading-leading, 1.04); }
/* Small headings are read, not looked at: body-face tracking, no tightening. */
h3, h4 { letter-spacing: 0; line-height: 1.25; }
/* A section heading names what follows; it gets room to do that. */
.section > h2, .section > .wrap > h2, .section > div > h2 { margin-bottom: var(--space-3); }
/*
 * No kicker. A short accent rule above every section heading, and another on
 * every card, was the single most recognisable mark of a generated page: the
 * same ornament stamped on a funeral home and a pizza shop. Hierarchy comes
 * from size, weight and space instead.
 *
 * A section that opens with a heading and an introduction sets them side by
 * side on a wide screen, the heading on the left and the introduction on the
 * right, the way an editorial layout does, instead of a short heading and a
 * short line pinned to the left edge of a 1180px band.
 */
@media (min-width: 56rem) {
  .section:has(> h2 + .lede:not(.lede--stacked)), .section > .wrap:has(> h2 + .lede:not(.lede--stacked)) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--col-gap);
    /* First lines on one baseline: aligned to the bottom, a two-line heading
     * left the introduction floating out of line with it. */
    align-items: first baseline;
  }
  .section:has(> h2 + .lede:not(.lede--stacked)) > *, .section > .wrap:has(> h2 + .lede:not(.lede--stacked)) > * { grid-column: 1 / -1; }
  .section:has(> h2 + .lede:not(.lede--stacked)) > h2, .section > .wrap:has(> h2 + .lede:not(.lede--stacked)) > h2 { grid-column: 1; }
  .section:has(> h2 + .lede:not(.lede--stacked)) > h2 + .lede, .section > .wrap:has(> h2 + .lede:not(.lede--stacked)) > h2 + .lede {
    grid-column: 2;
    grid-row: 1;
    justify-self: start;
  }
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-base); }

/* A long email address in a narrow column ran into the next one. */
p, li, dd, h3 { overflow-wrap: break-word; }
/* A domain name or a long word in a heading or a name breaks rather than running off a phone. */
h1, h2, .masthead__brand, .colophon__brand, .trust__value, dt { overflow-wrap: anywhere; }
p { margin: 0 0 var(--space-2); max-width: var(--measure); text-wrap: pretty; }
ul, ol { margin: 0 0 var(--space-2); padding-inline-start: 1.25rem; }

/* The underline at half strength until it is pointed at: an editorial link,
 * not a browser default. */
a { color: var(--color-accent); text-underline-offset: 0.18em; text-decoration-thickness: 1px; text-decoration-color: color-mix(in srgb, var(--color-accent) 45%, transparent); }
a:hover { text-decoration-color: currentColor; }
/*
 * A link that leaves the site says so. The arrow is generated content with an
 * empty alternative so a screen reader does not read "north east arrow"; the
 * rel attribute is what tells assistive tech it is external.
 */
/* Not on a picture that is a link: the arrow landed alone under the map. */
/* A no-break space before it: with a plain one the arrow wrapped onto a line
 * of its own after an address (Cold Crank, Mike Herman, Pro Experts). */
main a[href^="http"]:not(.btn):not(:has(img))::after { content: "\00a0\2197" / ""; font-size: 0.85em; }

/*
 * One focus style, everywhere, and never removed, and visible on any ground.
 *
 * A single accent-coloured outline disappeared entirely on the call-to-action
 * band, which is painted in that same accent: measured 1.00:1 against its own
 * background, and offset outwards so it sat on the band rather than the button.
 * Two rings of opposite lightness cannot both vanish, whatever is behind them.
 */
:where(a, button, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--color-paper);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--color-ink);
  border-radius: var(--radius-sm);
}

img, video { display: block; max-width: 100%; height: auto; }
/* No grade over their photographs. A gentle desaturation meant to make phone
 * snaps sit together dulled every photograph a little, and it outranked the
 * greyscale on the membership badges (25 Oct); photographs are shown as the
 * business took them. */
/* The wrapper is invisible to layout: block CSS sizes `.x img` and must keep
 * working whether or not the image gained a <picture> around it. */
picture { display: contents; }

/* The first thing in the tab order: a way past the navigation. */
.skip-link {
  position: absolute;
  left: var(--space-2);
  top: -4rem;
  z-index: 100;
  padding: var(--space-1) var(--space-2);
  background: var(--color-accent);
  color: var(--color-paper);
  border-radius: var(--radius-sm);
  font-weight: 700;
  text-decoration: none;
  transition: top 140ms ease;
}
.skip-link:focus { top: var(--space-2); }

.wrap {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/*
 * The gap between sections.
 *
 * Every section carries its full padding where its ground changes, so a
 * colour boundary always has air on both sides. Where two neighbours share a
 * ground, the two paddings used to read as one oversized empty band (190px
 * between Do It Rite's hero and its work at tablet width), so there each
 * gives up part of its padding and the pair share one gap of about the same
 * size as the padding inside a band. Which sections paint a ground of their
 * own is decided per archetype, so the sharing rules live in moods.css, next
 * to the list of bands.
 */
.section { padding-block: var(--section); }
.section--tight { padding-block: var(--space-4); }
.section--tinted { background: var(--surface); border-block: 1px solid var(--line); }
/* Two tinted bands in a row share one rule, not two. */
.section--tinted + .section--tinted, .trust + .section--tinted { border-top: 0; }
/* A heading and its introduction sit a step closer than the introduction and
 * what it introduces, at every width (it used to grow 16 to 24px at 900px). */
.section h2 + .lede, .section > .wrap > h2 + .lede { margin-bottom: var(--space-3); }

/* One button, two weights, sized for a thumb. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: 48px;
  /* A one-word label ("Call") still gets a button worth pressing. */
  min-width: 9rem;
  padding: 0 var(--space-3);
  border: 2px solid var(--color-accent);
  border-radius: var(--radius-btn);
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: transform 120ms ease, background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;
}
.btn:hover { transform: translateY(-1px); }
.btn--primary {
  background: var(--color-accent);
  color: var(--color-paper);
}
.btn--primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.btn--ghost { background: transparent; color: var(--color-accent); }
.btn--ghost:hover { background: var(--accent-soft); }

.actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* Sentence case, the business's own colour, no shouting: tracked capitals on
 * every label is a template's voice. */
.eyebrow {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-accent);
  margin: 0 0 var(--space-1);
}

.lede { font-size: var(--text-lg); color: var(--ink-soft); }

/* The way from a home page section to the page that goes further into it. */
.section-more { margin: var(--space-4) 0 0; }
.section-more a {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  font-weight: 700;
  color: var(--color-accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
}
.section-more a:hover { text-decoration-thickness: 2px; }

/*
 * Per-archetype adjustments. The page carries its shape as a class on <body>,
 * so a shape can ask for a different canvas without a stylesheet of its own.
 *
 * single-column-story promises one narrow column of strong typography and no
 * grids, and until this it got the same 1180px canvas and the same three-column
 * service grid as everything else. The promise is now kept in the one place
 * that renders it.
 */
.archetype-single-column-story { --max-width: 46rem; --measure: 38rem; }
.archetype-single-column-story .steps,
.archetype-single-column-story .quotes,
.archetype-single-column-story .cards { grid-template-columns: minmax(0, 1fr); }
.archetype-single-column-story .hours { max-width: none; }
/* The 46rem canvas has no spare width to side-head into; the hours and story
 * blocks' two-column desk layouts would cram both columns, so they stay
 * stacked here. */
.archetype-single-column-story [data-section="hours"],
.archetype-single-column-story [data-section="questions"] { display: block; }
/* The column is the story; the header and the footer are the page's frame.
 * Held to the 46rem column, five links and the phone wrapped under the logo
 * at every desktop width, and the footer's three columns broke an email
 * address in two. */
.archetype-single-column-story :is(.masthead, .colophon) { --max-width: 1180px; }
.archetype-single-column-story [data-section="story"] .about { display: block; max-width: var(--measure); }
/* Stacked over the story, its heading scrolls with it (see blocks/facts.ts). */
.archetype-single-column-story [data-section="story"] .about__lead { position: static; }
.archetype-single-column-story .faq { max-width: none; }
/*
 * The story shape's hero is centred. Left-aligned in a narrow column on a
 * wide screen, the band's empty right side read as a missing picture; centred,
 * the column is the design.
 */
.archetype-single-column-story .hero-plain .wrap { text-align: center; }
.archetype-single-column-story .hero-plain h1,
.archetype-single-column-story .hero-plain__promise { margin-inline: auto; }
.archetype-single-column-story .hero-plain .actions { justify-content: center; }

/* Line icons sit on the text's baseline at the text's size. */
.icon { width: 1.15em; height: 1.15em; vertical-align: -0.2em; flex-shrink: 0; }
.btn .icon { width: 1.1em; height: 1.1em; }
.stars { display: inline-flex; gap: 0.1em; color: var(--color-accent); vertical-align: -0.15em; }
.stars__half { clip-path: inset(0 50% 0 0); }

/*
 * The call bar: on a phone, the two actions that matter stay in reach at the
 * bottom of the screen. Fixed, so it needs its own room under the page, and
 * it honours the home-indicator inset.
 */
.callbar { display: none; }
@media (max-width: 47.99rem) {
  /* The room is the footer's own, in its colour: as the body's padding it
   * showed as a paper-coloured strip under the dark footer on every phone. */
  .colophon:has(+ .callbar) { padding-bottom: calc(var(--space-3) + 4.25rem + env(safe-area-inset-bottom, 0px)); }
  /* A link Tab scrolls to stops above the bar, not underneath it (WCAG 2.4.11). */
  html:has(.callbar) { scroll-padding-bottom: calc(5rem + env(safe-area-inset-bottom, 0px)); }
  .callbar {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 30;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 1px;
    background: var(--line-strong);
    border-top: 1px solid var(--line-strong);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .callbar a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 3.25rem;
    background: var(--color-paper);
    color: var(--color-ink);
    font-weight: 700;
    text-decoration: none;
  }
  .callbar a:first-child { background: var(--color-accent); color: var(--color-paper); }
  /* Away while the hero's own buttons are on screen (motion.js). Hidden, not
   * just moved, so Tab does not land on a bar nobody can see. */
  .callbar { transition: transform .25s ease, visibility 0s; }
  .callbar.callbar--away { transform: translateY(110%); visibility: hidden; transition: transform .25s ease, visibility 0s .25s; }
}

/* Present to a screen reader, absent to everyone else. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * Print. People still print hours, directions and menus. The chrome goes, the
 * bands lose their fill so they do not drain a cartridge, and the phone and
 * address are on page one because the masthead and contact block are.
 */
/* Print rules are in print.css, the last stylesheet on the page, so no block's own colours outrank them. */

/*
 * Motion is js/motion.js (GSAP): each section's content comes in, in reading
 * order, with its picture a beat after the words. The stylesheet only holds
 * content back for the moment before the script runs, so nothing appears and
 * then vanishes: never the hero's heading or picture, and if the script never
 * arrives the failsafe shows everything after a second.
 */
@media (prefers-reduced-motion: no-preference) {
  .motion-pending main > section:first-child:not(:focus-within) :is(p, li, .actions, .btn):not(h1 *, [class*="__media"] *),
  .motion-pending main > section:not(:first-child, :focus-within) :is(h2, h3, p, li, figure, picture, blockquote, dl, address, table, iframe, .actions, .btn, [class*="__media"], [class*="card"]) {
    opacity: 0;
    animation: motion-failsafe 0s linear 1.2s forwards;
  }
  @keyframes motion-failsafe { to { opacity: 1; } }
}

/*
 * Motion, off.
 *
 * No priority overrides (the standards check forbids them), and this is not
 * the last rule on the page: every block's CSS follows base.css. The catch-all
 * at zero specificity covers whatever sets no timing of its own; the named
 * rules beat the ones that do, wherever they sit. Every block animation is
 * already inside a no-preference query of its own.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
  }
  html .btn, html .skip-link, html .callbar, html .callbar.callbar--away, html .faq__item > summary .icon { transition: none; }
  .btn:hover { transform: none; }
}

/*
 * Links a thumb is meant to hit, a comfortable target on a phone: the
 * contact rows and the footer's links measured under 32px tall on every
 * build (WCAG 2.5.8 asks 24; a finger wants more).
 */
@media (max-width: 47.99rem) {
  .contact__dl dd a, .colophon__nav a, .colophon__row a { display: inline-block; padding-block: 0.45rem; }
}

/* --- this business --- */
:root {
  --max-width: 1180px;
  --color-ink: #18232a;
  --color-paper: #f6f8fa;
  --color-accent: #1d5c8a;
  --color-primary: #1d5c8a;
  --color-secondary: #c9772a;
  --font-heading: "Nunito Sans", Georgia, serif;
  --font-body: "Nunito Sans", system-ui, sans-serif;
}

/* --- masthead --- */
/*
 * The masthead is opaque wherever there is no blur behind it. The 88% mix
 * was meant as a nicety, but without the blur it is just a bar the page
 * scrolls through: on every phone the hero headline ghosted behind the brand
 * text. Translucency only exists where backdrop-filter frosts what shows
 * through, and even there 94% keeps the nav legible over busy sections.
 */
.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--color-paper);
  border-bottom: 1px solid var(--line);
}
@media (min-width: 52rem) {
  @supports (backdrop-filter: blur(1px)) {
    .masthead {
      background: color-mix(in srgb, var(--color-paper) 94%, transparent);
      backdrop-filter: blur(10px);
    }
  }
}
.masthead__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding-block: var(--space-2);
}
.masthead__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
  font-family: var(--font-heading, inherit);
  font-size: var(--text-lg);
  font-weight: var(--heading-weight, 700);
  line-height: 1.15;
  letter-spacing: var(--heading-tracking, -0.02em);
  margin: 0;
  text-decoration: none;
  color: inherit;
}
/* Their mark is the one thing on the page that is unmistakably theirs; at
 * 2.6rem on a desk it read as an afterthought beside the links. It grows to
 * 3.4rem with the window rather than in one step at 832px. */
.masthead__logo { height: clamp(2.6rem, 2.1rem + 1.3vw, 3.4rem); width: auto; }
/*
 * One height does not suit every shape of mark. A 9:1 wordmark at 2.6rem ran
 * wider than a phone has room for beside the menu, and the flex row squeezed
 * it out of proportion; a tall emblem at the same height was a 40px smudge.
 * Wide marks are set a little lower and tall ones taller, so each carries the
 * same visual weight, and a mark that still runs out of room is scaled down
 * whole (contain) rather than squashed.
 */
.masthead__logo, .colophon__logo { max-width: 100%; object-fit: contain; object-position: left center; }
.masthead__logo--wide { height: clamp(2.1rem, 1.7rem + 1.05vw, 2.75rem); }
.masthead__logo--tall { height: clamp(3rem, 2.35rem + 1.7vw, 4rem); }
/*
 * The inverse masthead: for a light-on-transparent logo, the bar itself goes
 * dark — a brand-tinted near-ink, not the raw accent, because paper text on
 * the accent is only guaranteed 3:1 while paper on this mix tracks the ink's
 * own 7:1. Everything inside flips to paper; the phone pill inverts so it
 * still reads as the one action.
 */
.masthead--inverse {
  background: color-mix(in srgb, var(--color-ink) 74%, var(--color-accent));
  border-bottom-color: transparent;
  color: var(--color-paper);
}
.masthead--inverse .masthead__phone { background: var(--color-paper); color: var(--color-accent); }
/* A seam between the dark bar and a dark hero, which otherwise merge. */
.masthead--inverse { box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--color-paper) 14%, transparent); }
.masthead--inverse .navmenu > summary { border-color: color-mix(in srgb, var(--color-paper) 55%, transparent); color: var(--color-paper); }
@media (min-width: 40rem) {
  .masthead--inverse .masthead__phone { background: none; color: var(--color-paper); padding-inline: var(--space-2); }
}
@media (min-width: 64rem) {
  @supports (backdrop-filter: blur(1px)) {
    .masthead--inverse {
      background: color-mix(in srgb, var(--color-ink) 74%, var(--color-accent));
      backdrop-filter: none;
    }
  }
  /* The inline desktop links sit directly on the dark bar. */
  .masthead--inverse .navmenu__list a { color: var(--color-paper); }
  .masthead--inverse .navmenu__list a:hover { background: color-mix(in srgb, var(--color-paper) 16%, transparent); }
}
.masthead__right { display: flex; align-items: center; gap: var(--space-1); flex-shrink: 0; }
/*
 * The phone number is the one action in the header, so on a phone it is a
 * button rather than a line of digits: the full number does not fit beside
 * the name and the menu at 390px, and what a thumb wants there is "Call".
 */
.masthead__phone {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 44px;
  padding-inline: var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--color-accent);
  color: var(--color-paper);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.masthead__phone-full { display: none; }
/*
 * One row, always. With the name held to one line the right side used to
 * drop below it, and between 832 and 1100px a third of the builds carried a
 * two-storey sticky header (Mike Herman's at every width). A long name now
 * breaks onto a second, balanced line instead; on a small phone it is set a
 * step smaller and the menu button keeps its icon alone, so the name gets
 * two lines rather than three.
 */
.masthead__brand { text-wrap: balance; }
.masthead__name { display: block; min-width: 0; }
@media (max-width: 29.99rem) {
  /* Beside an emblem on a small phone the name steps down, so the mark, the
   * name, the menu and the call button still share one row. */
  .masthead__logo + .masthead__name { font-size: var(--text-base); line-height: 1.15; }
}
@media (max-width: 29.99rem) {
  .masthead__brand--long { font-size: var(--text-base); }
  /* A name set beside an emblem shares the row with it, so even a short one
   * needs the room the menu's word takes: WCH Builders' "Builders" broke
   * mid-word in the 51px left beside the mark. */
  .masthead:is(:has(.masthead__brand--long), :has(.masthead__name)) .navmenu > summary { padding-inline: 0.7rem; }
  .masthead:is(:has(.masthead__brand--long), :has(.masthead__name)) .navmenu > summary span {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}
/*
 * The narrowest phones (320 to 359px). Beside a mark, the menu's word and the
 * call button's word left the name 30 to 50px, and it broke inside its words:
 * "Gra / ves Ba / rns & Buil / ding / s" in a 165px header (Graves), "Builde
 * / rs" (WCH), "Dartmou / th" (Talay Thai), and a wide wordmark squeezed to a
 * smudge (Victor, Atmosphere). Both buttons keep their icon alone and their
 * 44px target; each is still named (the summary's word stays for a screen
 * reader, the call link carries its own label), and the call bar at the foot
 * of the screen says "Call" in full.
 */
@media (max-width: 22.49rem) {
  .masthead .navmenu > summary { padding-inline: 0.7rem; }
  .masthead .navmenu > summary span {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .masthead__phone { padding-inline: 0.75rem; min-width: 44px; justify-content: center; }
  .masthead__phone-short { display: none; }
}
@media (min-width: 40rem) {
  .masthead__brand { font-size: var(--text-xl); }
  .masthead__right { gap: var(--space-2); }
  /* Still a button: reduced to bare accent text, the page's one standing
   * action vanished into the links on every desktop. */
  .masthead__phone { background: var(--color-accent); color: var(--color-paper); padding-inline: var(--space-2); border-radius: var(--radius-btn); }
  .masthead__phone-full { display: inline; }
  .masthead__phone-short { display: none; }
}

/* The menu: a disclosure, not a script. */
/* On a phone the menu is anchored to the bar itself (the sticky masthead is
 * the containing block), so it sits exactly under it whatever its height. */
.navmenu { position: static; }
.navmenu > summary {
  list-style: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  /* A 44px target both ways: with only its icon showing it measured 43 wide. */
  min-height: 44px;
  min-width: 44px;
  padding-inline: var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-weight: 600;
  cursor: pointer;
}
.navmenu > summary::-webkit-details-marker { display: none; }
.navmenu > summary .icon--close, .navmenu[open] > summary .icon--menu { display: none; }
.navmenu[open] > summary .icon--close { display: inline-block; }
/*
 * On a phone the menu is a sheet under the bar, the full width of the screen,
 * with links a thumb can hit and the phone number at the bottom. It used to
 * be a 12rem popover hanging off the right edge, which is a desktop idea.
 */
.navmenu__list {
  position: absolute;
  inset-inline: 0;
  top: 100%;
  margin: 0;
  padding: var(--space-2) var(--space-3) var(--space-4);
  list-style: none;
  background: var(--color-paper);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-lift);
  max-height: calc(100vh - 5rem);
  overflow-y: auto;
}
.navmenu__list a {
  display: block;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  color: inherit;
  font-family: var(--font-heading, inherit);
  font-weight: var(--heading-weight, 700);
  font-size: var(--text-xl);
}
.navmenu__list li:last-child a { border-bottom: 0; }

/*
 * On a wide screen the links sit inline and the disclosure disappears.
 *
 * The ::details-content rule is not decoration. Current browsers hide a
 * closed disclosure's contents with content-visibility on that pseudo-element,
 * which no amount of styling the list itself can override; so hiding the
 * summary on desktop removed the only control that could open it, and the
 * navigation became invisible, unclickable, untabbable and absent from the
 * accessibility tree on every wide screen. Styling the list covers the
 * browsers that predate the pseudo-element; this covers the rest.
 */
@media (min-width: 64rem) {
  .masthead:not(:has(.navmenu__list > :nth-child(5))) .navmenu > summary { display: none; }
  .masthead:not(:has(.navmenu__list > :nth-child(5))) .navmenu::details-content {
    content-visibility: visible;
    block-size: auto;
    opacity: 1;
  }
  .masthead:not(:has(.navmenu__list > :nth-child(5))) .navmenu__list {
    position: static;
    display: flex;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    min-width: 0;
    background: none;
    border: 0;
    box-shadow: none;
    max-height: none;
    overflow: visible;
  }
  /* Inline links, not the phone sheet's rows. */
  .masthead:not(:has(.navmenu__list > :nth-child(5))) .navmenu__list a { padding: 0.5rem 0.25rem; border-bottom: 0; font-family: inherit; font-weight: 600; font-size: var(--text-base); }
}
/*
 * Five or six links beside a long name squeezed the name to four lines and
 * a 150px sticky header at 1024px: such a menu stays behind its button until
 * there is room for it.
 */
@media (min-width: 80rem) {
  .navmenu > summary { display: none; }
  .navmenu::details-content {
    content-visibility: visible;
    block-size: auto;
    opacity: 1;
  }
  .navmenu__list {
    position: static;
    display: flex;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    min-width: 0;
    background: none;
    border: 0;
    box-shadow: none;
    max-height: none;
    overflow: visible;
  }
  /* Inline links, not the phone sheet's rows. */
  .navmenu__list a { padding: 0.5rem 0.25rem; border-bottom: 0; font-family: inherit; font-weight: 600; font-size: var(--text-base); }
}
/* The page being read, in the menu of a site of several pages. */
.navmenu__list a[aria-current="page"] { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 0.4em; }

/* --- hero-split --- */
.hero-kicker {
  display: block;
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.4;
  margin-bottom: var(--space-2);
  opacity: 0.92;
  text-wrap: balance;
}
/* Display type over 20px at the body's 1.65 leading ran 43px between lines of
 * a 26px promise, and every promise read as loose and floating. */
.hero-photo__promise, .hero-split .lede, .hero-plain__promise { line-height: 1.4; }

/*
 * The hero is the headline, one line of promise, the two ways to act and the
 * facts that decide a phone call, set like a magazine opener: type on the
 * left, their own photograph large on the right.
 *
 * It used to put the actions in a tinted, shadowed card with a ticked
 * checklist under the headline, on a soft radial wash. Every one of those is
 * a generated-page signature (the boxed CTA panel, the green ticks, the blob
 * gradient) and together they made four different businesses' first screens
 * the same picture. The facts are now a quiet row of plain statements under a
 * hairline, and the buttons are buttons.
 */
.hero-split { padding-block: var(--space-5) var(--section); background: var(--color-paper); }
/* One fluid gap between words and picture, stacked or side by side: it used
 * to step 40 to 64px at 1024px. */
.hero-split__grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--col-gap); align-items: center; }
/* The picture settles from a touch larger: never wide enough to scroll a phone sideways. */
.hero-split { overflow-x: clip; }
.hero-split .lede { font-size: var(--text-xl); max-width: 38ch; margin-bottom: 0; color: var(--ink-soft); }
.hero-split .actions { margin-top: var(--space-4); }
/*
 * Half a tablet is narrow for display type, so the split hero's headline is
 * set a size below the page's h1, on one fluid curve at every width. It used
 * to switch scales at 768px and drop from 55 to 39px in one step.
 */
.hero-split__grid--two h1 { font-size: clamp(2.375rem, 1.3rem + 2.7vw, 3.75rem); }
/* Capped where the column stops growing (about 1300px): on a viewport curve
 * alone the same headline was four lines at 1280 and five at 1920. */
.hero-split.hero--long .hero-split__grid--two h1 { font-size: clamp(2.125rem, 1.25rem + 2.2vw, 3.25rem); }
.hero-split--brand.hero--long h1 { font-size: clamp(2.375rem, 1.3rem + 3.4vw, 4.25rem); }
/*
 * Facts in even rows: two across, so four make a clean 2x2 and three a row of
 * two over one that reads as a list. An auto-fit grid wrapped four facts into
 * a ragged three-and-one at tablet width.
 */
.hero-facts {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-2) var(--space-3);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.hero-facts li { padding-top: var(--space-1); border-top: 1px solid var(--line-strong); font-size: var(--text-base); line-height: 1.4; color: var(--color-ink); }
/* An odd count in two columns orphans the last; three or five read as a list. */
.hero-facts:has(> li:last-child:nth-child(odd)) { grid-template-columns: minmax(0, 1fr); gap: 0; }
.hero-facts:has(> li:last-child:nth-child(odd)) li { padding-block: 0.55rem; }
.hero-split__media img { border-radius: var(--radius-lg); width: 100%; aspect-ratio: 4 / 3; max-height: 26rem; object-fit: cover; }
/* Side by side from tablet width: stacked at 768px, the words took the left
 * half of the screen and the photograph started at the fold. */
@media (min-width: 48rem) {
  /* The photograph takes the height of the words beside it, never more: a tall
   * fixed-ratio photo next to a short headline left an empty band under the
   * words that every critic called unfinished. */
  .hero-split__grid--two { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); align-items: stretch; }
  /* ...but never taller than 4:5 of its column. Beside a long headline at
   * tablet width the column ran to twice its width, and a 6:5 photograph of
   * two huskies was cropped to a strip showing half of each. Shorter than the
   * words, it sits centred beside them. */
  .hero-split__media { display: flex; align-items: center; --hs-col: calc((100vw - 88px) / 2.1); }
  .hero-split__media img { aspect-ratio: auto; height: 100%; min-height: min(26rem, var(--hs-col) * 1.25); max-height: min(78vh, 44rem, var(--hs-col) * 1.25); }
}
@media (min-width: 64rem) {
  .hero-split__media { --hs-col: calc((100vw - 112px) / 2.1); }
  .hero-split__grid--two > div:first-child { align-self: center; }
}
@media (min-width: 1180px) { .hero-split__media { --hs-col: 509px; } }
/* The wide and narrow measures (treatment.ts) move the cap: the column stops
 * growing where the page does, at 1320px and 1040px as at 1180px above. */
.t-measure-wide .hero-split__media { --hs-col: calc((min(100vw, 1320px) - 112px) / 2.1); }
.t-measure-narrow .hero-split__media { --hs-col: calc((min(100vw, 1040px) - 112px) / 2.1); }
/*
 * Two photographs the column cannot stretch to the height of the words: a
 * panorama (cropped to a fifth of itself and zoomed past its pixels) and one
 * narrower than the column (a 300px picture drawn 500px wide and 600px tall).
 * Both keep a 4:3 frame centred beside the words; the small one at its own
 * width, so it is crisp rather than large.
 */
@media (min-width: 48rem) {
  .hero-split__media--fit, .hero-split__media--natural { align-self: center; justify-content: center; }
  .hero-split__media--fit img, .hero-split__media--natural img { height: auto; min-height: 0; max-height: none; aspect-ratio: 4 / 3; }
}
/* Its own shape, too: squeezed below its own width on a small phone it was
 * forced to 4:3 and grew 48px taller the moment it fitted (at 340px). */
.hero-split__media--natural img { width: auto; margin-inline: auto; aspect-ratio: auto; }
/*
 * On a phone the photograph comes first, edge to edge under the masthead.
 * Stacked after the words it landed below two buttons and a row of facts, so
 * the first screen on a phone was all text: the note the critic left on
 * Graves, Belle Kennel, Talay Thai and Pro Experts alike.
 */
@media (max-width: 47.99rem) {
  .hero-split:has(.hero-split__media:not(.hero-split__media--natural)) { padding-top: 0; }
  .hero-split__media:not(.hero-split__media--natural) { order: -1; margin-inline: calc(-1 * var(--gutter, 1.5rem)); }
  .hero-split__media:not(.hero-split__media--natural) img { border-radius: 0; aspect-ratio: 3 / 2; max-height: 60vh; }
}
/*
 * With no photograph the hero commits to the brand instead: the whole first
 * screen in their colour, words in paper. The accent is already corrected to
 * carry paper-coloured text at 4.5:1, and this ground is darker still.
 */
.hero-split--brand {
  position: relative;
  overflow: hidden;
  background: var(--accent-deep);
  color: var(--color-paper);
  padding-block: var(--space-6);
}
.hero-split--brand > .wrap { position: relative; z-index: 1; }

.hero-split--brand h1 { color: var(--color-paper); max-width: 20ch; font-size: clamp(2.6rem, 1.3rem + 4.2vw, 5.5rem); }
.hero-split--brand .lede { max-width: 52ch; color: color-mix(in srgb, var(--color-paper) 90%, var(--accent-deep)); }
/*
 * One wide column: the headline spans the field and the facts run beneath it
 * as a ruled row. Every attempt to give the facts the right half of the field
 * (centred, top-aligned, bottom-aligned, set large in the display face) was
 * marked down for floating in empty colour, and long facts like "Cape Breton
 * Regional Municipality" broke mid-word in a narrow column. Set in the body
 * face, across the width, they wrap at word boundaries and fill the band.
 */
.hero-split--brand .hero-facts { grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); max-width: none; margin-top: var(--space-5); }
.hero-split--brand .hero-facts:has(> li:last-child:nth-child(odd)) { grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); gap: var(--space-2) var(--space-3); }
.hero-split--brand .hero-facts li { color: color-mix(in srgb, var(--color-paper) 90%, var(--accent-deep)); border-top-color: color-mix(in srgb, var(--color-paper) 35%, transparent); font-size: var(--text-lg); hyphens: manual; overflow-wrap: normal; }
.hero-split--brand .btn--primary { background: var(--color-paper); color: var(--accent-deep); border-color: var(--color-paper); }
.hero-split--brand .btn--primary:hover { background: color-mix(in srgb, var(--color-paper) 88%, var(--accent-deep)); border-color: transparent; }
.hero-split--brand .btn--ghost { color: var(--color-paper); border-color: var(--color-paper); }
.hero-split--brand .btn--ghost:hover { background: color-mix(in srgb, var(--color-paper) 16%, transparent); }
/*
 * With no photograph, their Google rating set large balances the field. The
 * words sat in the left 60% of the band over an empty right side, and every
 * critic called the first screen unfinished. A real figure, not decoration:
 * only when the rating is a fact with enough reviews behind it.
 */
.hero-score { margin: var(--space-5) 0 0; padding-top: var(--space-2); border-top: 1px solid color-mix(in srgb, var(--color-paper) 35%, transparent); }
.hero-score__value { display: block; font-family: var(--font-heading, inherit); font-weight: var(--heading-weight, 700); letter-spacing: var(--heading-tracking, -0.02em); font-size: clamp(4rem, 2.4rem + 6vw, 8.5rem); line-height: 0.95; color: var(--color-paper); }
.hero-score .stars { color: var(--color-paper); margin-block: var(--space-1); }
.hero-score .stars svg { width: 1.5rem; height: 1.5rem; }
.hero-score__label { display: block; font-size: var(--text-lg); color: color-mix(in srgb, var(--color-paper) 90%, var(--accent-deep)); }
/* A phone number is fourteen characters, not three: set a size down so it
 * holds one line in the column, and keep it a working link. */
.hero-score--phone .hero-score__value { font-size: clamp(2rem, 1rem + 1.6vw, 3rem); line-height: 1.1; margin-block: var(--space-1); text-decoration: none; white-space: nowrap; }
.hero-score--phone a.hero-score__value:hover { text-decoration: underline; text-decoration-thickness: 2px; }
.hero-score--phone .hero-score__value--hours { font-size: var(--text-xl); line-height: 1.35; white-space: normal; }
.hero-score__value--hours > span { display: block; }
/* On a phone the call bar and the hero's buttons already carry it. */
@media (max-width: 63.99rem) { .hero-score--phone { display: none; } }
@media (min-width: 64rem) {
  /* Centred on the words: hung from the bottom, the rating sat under empty
   * colour and read as detached from the headline. */
  .hero-split__grid--score { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); align-items: center; }
  .hero-score { margin: 0; }
  /* The facts share the narrower column in one row: at 13rem three wrapped
   * two over one, an orphan under the buttons. */
  .hero-split__grid--score .hero-facts,
  .hero-split__grid--score .hero-facts:has(> li:last-child:nth-child(odd)) { grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr)); }
}

/* --- services-cards --- */
/*
 * Services as an editorial index: each one under a rule, its name in the
 * display face, a sentence beneath. No box, no shadow, no hover lift.
 *
 * These were bordered, shadowed cards that rose 3px under the cursor and led
 * with an accent rule: a uniform card grid that looks clickable and is not,
 * which is the most generated-looking component on the web. Open columns on a
 * rule let an odd count end a row without a hole, so nothing needs stretching.
 */
.cards { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-top: var(--space-2);
  border-top: 1px solid var(--line-strong);
}
.card h3 { margin: 0; font-size: var(--text-xl); letter-spacing: var(--heading-tracking, -0.01em); }
.card p { margin: 0; max-width: 40ch; color: var(--ink-soft); font-size: var(--text-lg); }
.card__photo { margin: 0 0 var(--space-1); overflow: hidden; border-radius: var(--radius); }
.card__photo img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
/* With photographs the cards are tiles: the picture leads, the rule goes. */
.cards--photos .card { border-top: 0; padding-top: 0; }
/* Through .card: the bare class lost to `.card p { margin: 0 }` and the price
 * never reached the bottom, so prices in one row sat at different heights
 * (Talay Thai). */
.card .card__price { margin-top: auto; padding-top: var(--space-1); font-weight: 700; color: var(--color-accent); }
.cards-wrap { container-type: inline-size; }
/* By the container: the same cards in a narrow column stay stacked however wide the screen. */
@container (min-width: 40rem) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-4); }
  /* An odd count in two columns: the last takes the row rather than standing
   * alone beside nothing ("Window cleaning", "Snowplowing services"). */
  .cards > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}
/* Beside one of their photographs: the picture large, the services a ruled list next to it. */
.services-split { display: grid; gap: var(--space-4); }
.services-split__photo { margin: 0; overflow: hidden; border-radius: var(--radius-lg); }
.services-split__photo img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4 / 3; }
@media (min-width: 56rem) {
  .services-split { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); column-gap: var(--col-gap); align-items: center; }
  .services-split__photo img { aspect-ratio: 5 / 4; }
}
.services-split .cards { gap: var(--space-2); }
@container (min-width: 64rem) {
  .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cards > :last-child:nth-child(odd) { grid-column: auto; }
  /* Four, seven or ten in rows of three leave one alone on the last row;
   * rows of four do not. */
  .cards:has(> :last-child:nth-child(3n+1)) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* Five, or eight: rows of three end with two beside an empty cell. On a
   * six-column grid the last two take three columns each and fill the row. */
  .cards:has(> :last-child:nth-child(3n+2)) { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .cards:has(> :last-child:nth-child(3n+2)) > * { grid-column: span 2; }
  .cards:has(> :last-child:nth-child(3n+2)) > :nth-last-child(-n+2) { grid-column: span 3; }
}

/* --- trust-strip --- */
/*
 * Never --color-primary, the raw dominant colour sampled from the logo, which
 * nothing corrects: ink on it measured 2.16:1. The figures are the band that changes key mid-page: the deep brand colour,
 * paper type. That was already every style but refined (which sets them on
 * paper between ink rules, in styles.css); as the block's own default too, a
 * page with no style no longer puts the figures on the same grey as the
 * services band beside them, two grey paddings reading as one hole.
 */
.trust { background: var(--accent-deep); color: var(--color-paper); border-block: 1px solid var(--accent-deep); }
.trust__list { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); margin: 0; padding-block: var(--space-5); padding-inline: 0; list-style: none; }
/* The numbers ARE the section, at display size in paper on the deep brand
 * colour (corrected against paper at palette time). Left-aligned and ruled
 * apart, like the figures in a printed report, rather than centred under a
 * 3px accent stripe with tracked capitals beneath. */
.trust__value { display: block; font-family: var(--font-heading, inherit); font-size: clamp(1.6rem, 0.9rem + 2.4vw, 3.5rem); font-weight: var(--heading-weight, 700); letter-spacing: var(--heading-tracking, -0.015em); line-height: 1.1; color: var(--color-paper); }
.trust__label { display: block; margin-top: 0.35rem; font-size: var(--text-base); color: color-mix(in srgb, var(--color-paper) 82%, var(--accent-deep)); max-width: 32ch; text-wrap: balance; }
.trust--words .trust__value { font-size: var(--text-xl); line-height: 1.2; text-wrap: balance; }
@media (min-width: 40rem) {
  .trust__list { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 0; }
  .trust__item { padding-inline: var(--space-3); border-inline-start: 1px solid color-mix(in srgb, var(--color-paper) 28%, transparent); }
  .trust__item:first-child { padding-inline-start: 0; border-inline-start: 0; }
  /* Four go two by two until there is room for four: three across left the
   * fourth alone under a stray rule at 768 (Lowry's). */
  .trust__list:has(> :last-child:nth-child(4)) { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--space-3); }
  .trust__list:has(> :last-child:nth-child(4)) > :nth-child(3) { padding-inline-start: 0; border-inline-start: 0; }
}
@media (min-width: 64rem) {
  .trust__list:has(> :last-child:nth-child(4)) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .trust__list:has(> :last-child:nth-child(4)) > :nth-child(3) { padding-inline-start: var(--space-3); border-inline-start: 1px solid color-mix(in srgb, var(--color-paper) 28%, transparent); }
}

/* --- steps --- */
/*
 * The number is set as type, in the display face and the brand colour, above
 * the step: the order is the point of the block, so it gets size, not a filled
 * accent circle, which every generated "how it works" carries.
 */
.steps { list-style: none; counter-reset: step; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
.steps li { counter-increment: step; display: grid; gap: var(--space-1); align-content: start; padding-top: var(--space-2); border-top: 1px solid var(--line-strong); }
.steps li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-heading, inherit);
  font-size: var(--text-2xl);
  font-weight: var(--heading-weight, 700);
  line-height: 1;
  color: var(--color-accent);
  /* Lining figures: a face whose default figures are old-style (Cormorant)
   * set "01" as a small "o" and a roman "I" (Jay Wells). */
  font-variant-numeric: lining-nums tabular-nums;
}
.steps h3 { margin: 0 0 0.15rem; }
.steps p { margin: 0; color: var(--ink-soft); font-size: var(--text-lg); }
/* Columns from the count, not a fixed three: four steps in a 3-up grid left
 * step four alone beside two empty cells. auto-fit gives 4 steps four-up on a
 * wide screen and 2x2 in between; 3 steps land three-up as before. */
@media (min-width: 40rem) { .steps { grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); } }

/* --- cta-band --- */
/* On the tinted ink, flat, so the page has two darks: the brand's deep colour
 * for the hero and the figures, and its near-black here. Three slabs of one
 * colour read as a template; two colours read as a decision. (A gradient and
 * glow were a template tell every critic noticed.) */
.cta {
  background: var(--color-ink);
  color: var(--color-paper);
}
.cta__inner { padding-block: var(--space-6); display: grid; gap: var(--space-2) var(--col-gap); justify-items: start; }
.cta h2 { font-size: var(--text-2xl); }
/* The band spans the whole container; a 20ch headline pinned to its left edge
 * leaves the right two-thirds empty blue at desk widths. Words left, buttons
 * right, centred on each other, so the band reads as one composed row. */
@media (min-width: 52rem) {
  .cta__inner { grid-template-columns: 1fr auto; align-items: center; }
  /* Spanning the headline and the line under it. `1 / -1` ends at the last
   * explicit line, which with no explicit rows is the first, so the buttons
   * centred on the headline and the body hung below them. */
  .cta__inner .actions { grid-column: 2; grid-row: 1; }
  .cta__inner:has(> p) .actions { grid-row: 1 / span 2; }
}
.cta h2 { color: var(--color-paper); margin: 0; max-width: 20ch; }
.cta p { color: color-mix(in srgb, var(--color-paper) 82%, var(--color-ink)); margin: 0; }
.cta .btn--primary { background: var(--color-paper); color: var(--color-ink); border-color: var(--color-paper); }
.cta .btn--ghost { color: var(--color-paper); border-color: var(--color-paper); }
.cta .btn--ghost:hover { background: color-mix(in srgb, var(--color-paper) 18%, transparent); }

/* --- contact --- */
.contact { background: var(--surface); border-top: 1px solid var(--line); }
.contact__grid { display: grid; gap: var(--space-4) var(--col-gap); padding-block: var(--section); }
.contact__dl { margin: 0; display: grid; gap: 0; }
.contact__dl > div { display: grid; gap: 0.1rem; padding-block: var(--space-2); border-bottom: 1px solid var(--line); }
/*
 * Three rows of hairlines spanning 1180px with the right two thirds empty is
 * not a layout. With room, each fact becomes a column under its own rule.
 */
.contact__facts { container-type: inline-size; }
/*
 * Sized by the column it sits in, not the viewport: beside a map the facts
 * have half the width they have alone, and a viewport rule cannot know which.
 */
@container (min-width: 36rem) {
  .contact__dl { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--space-3); }
  /* Counts that do not fill rows of four go in threes: five in an auto-fit row
   * of four left the last one alone under the rest. */
  .contact__dl:has(> div:last-child:nth-child(5)), .contact__dl:has(> div:last-child:nth-child(6)) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .contact__dl:has(> div:last-child:nth-child(4)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Three in two columns left the third alone under a hole (Cold Crank at
   * 768); stacked until there is room for three across. Seven, in fours, with
   * the first taking two cells, so no row ends early (Graves). */
  .contact__dl:has(> div:last-child:nth-child(3)) { grid-template-columns: minmax(0, 1fr); }
  .contact__dl:has(> div:last-child:nth-child(7)) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .contact__dl:has(> div:last-child:nth-child(7)) > div:first-child { grid-column: span 2; }
  .contact__dl > div { border-bottom: 0; border-top: 2px solid var(--line-strong); padding-top: var(--space-2); }
  /* Each fact's label and value on shared rows, so values start on one line
   * across the columns however the labels wrap. */
  .contact__dl > div { display: grid; grid-row: span 2; grid-template-rows: subgrid; row-gap: 0.25rem; align-content: start; }
}
@container (min-width: 46rem) {
  .contact__dl:has(> div:last-child:nth-child(3)) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.contact__dl dt { display: flex; align-items: center; gap: 0.4rem; font-size: var(--text-sm); font-weight: 600; color: var(--ink-soft); }
.contact__areas { display: flex; gap: 0.6rem; align-items: baseline; margin: 0 0 var(--space-3); color: var(--ink-soft); max-width: 60ch; }
/* Break an email address at the @ (a <wbr> the render puts there), never mid-word:
 * "customerservice@del / uxedrycleaners.ca" read as two broken words. */
.contact__dl dd { margin: 0; font-size: var(--text-lg); overflow-wrap: break-word; }
/* One fluid height, stacked or beside the details: as a 4:3 frame it was
 * 587px tall on a tablet and 288px a step later, beside them. */
.contact__map img { width: 100%; border-radius: var(--radius-lg); height: clamp(14rem, 8rem + 16vw, 24rem); object-fit: cover; }
/*
 * Two columns only when there is something to put in the second one. Nothing in
 * the pipeline harvests a map picture yet, so the grid was splitting every real
 * page in half and leaving the right side blank on any desktop screen.
 */
@media (min-width: 52rem) {
  .contact__grid--map { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
}
/* The contact page: the words left, the ways to reach them right, as a list
 * of large lines a thumb can hit. */
.contact__page { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4) var(--col-gap); padding-block: var(--section); }
.contact__intro > :last-child { margin-bottom: 0; }
.contact__where { margin-top: var(--space-3); }
.contact__where h3 { font-size: var(--text-lg); margin: 0 0 0.25rem; }
.contact__where p { margin: 0; max-width: 40ch; }
.contact__dl--list { display: grid; gap: 0; border-top: 2px solid var(--line-strong); }
.contact__dl--list > div { padding-block: var(--space-2); border-bottom: 1px solid var(--line); }
.contact__dl--list dd { font-size: var(--text-xl); }
@media (min-width: 52rem) {
  .contact__page { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); align-items: start; }
  .contact__page--stack { grid-template-columns: minmax(0, 1fr); }
  .contact__page--stack .contact__dl--list { grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); column-gap: var(--col-gap); }
}

/* --- colophon --- */
/*
 * A real footer: the name and what they do, how to reach them, when they are
 * open and where they work, and the page's sections again, on a dark band
 * with a rule across the bottom. The old colophon was three lines of small
 * grey text, which is the surest sign of an unfinished site.
 */
.colophon {
  background: var(--color-ink);
  color: color-mix(in srgb, var(--color-paper) 82%, var(--color-ink));
  padding-block: var(--space-5) var(--space-3);
  font-size: var(--text-base);
}
.colophon a { color: var(--color-paper); text-decoration-color: color-mix(in srgb, var(--color-paper) 40%, transparent); }
.colophon a:hover { text-decoration-color: currentColor; }
.colophon__grid { display: grid; gap: var(--space-4); }
.colophon__brand { font-family: var(--font-heading, inherit); font-weight: var(--heading-weight, 700); letter-spacing: var(--heading-tracking, -0.01em); font-size: var(--text-xl); color: var(--color-paper); margin-bottom: var(--space-1); }
/* Their light mark on the dark footer, the way the masthead carries it. */
.colophon__logo { height: 3rem; width: auto; margin-bottom: var(--space-1); }
.colophon__logo--wide { height: 2.5rem; }
.colophon__logo--tall { height: 4rem; }
.colophon__col p { margin: 0 0 0.35rem; max-width: 36ch; }
.colophon__label { display: block; font-size: var(--text-sm); font-weight: 600; color: color-mix(in srgb, var(--color-paper) 60%, var(--color-ink)); margin-bottom: 0.5rem; }
.colophon__row { display: flex; gap: 0.6rem; align-items: baseline; margin: 0 0 0.45rem; min-width: 0; }
.colophon__row a, .colophon__row span { overflow-wrap: anywhere; }
.colophon__hours > span { display: block; }
.colophon__row .icon { position: relative; top: 0.05em; flex-shrink: 0; color: color-mix(in srgb, var(--color-paper) 60%, var(--color-ink)); }
.colophon__nav { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.colophon__nav a { text-decoration: none; }
.colophon__nav a:hover { text-decoration: underline; }
.colophon__bottom {
  margin-top: var(--space-4);
  padding-top: var(--space-2);
  border-top: 1px solid color-mix(in srgb, var(--color-paper) 18%, transparent);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  color: color-mix(in srgb, var(--color-paper) 62%, var(--color-ink));
}
.colophon__bottom p { margin: 0; }
/* Two columns on a tablet, with the brand across the top; three only when
 * each column has room for an email address on one line. */
@media (min-width: 48rem) {
  .colophon__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-4) var(--space-5); }
  .colophon__col:first-child { grid-column: 1 / -1; }
}
@media (min-width: 64rem) {
  .colophon__grid { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-5); }
  .colophon__col:first-child { grid-column: auto; }
  /* Two columns when the contact block above said everything: three left the
   * right third of the footer empty (Graves, Do It Rite, Boutiliers, WCH). */
  .colophon__grid:not(:has(> :nth-child(3))) { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}

/*
 * Per-archetype moods: the industry design languages, in one layer.
 *
 * Distilled from a survey of standout local-business sites (Sept 2026):
 * Edwards Plumbing and Aspen Mountain Plumbing for the trades; Dentologie and
 * Grand Street Dental for clinics; Juniper & Ivy and Mei Mei for food;
 * Boardroom for salons; Christian Brothers for auto; Gibbs Landscape for
 * landscaping. What they share is commitment: a tinted ground instead of white,
 * a page that reads as alternating bands, the brand colour used as surfaces
 * rather than only on buttons, and one decisive hero.
 *
 * This file loads after every block's own CSS, so it can restyle a block for
 * one archetype without touching what the block ships everywhere else. Nothing
 * here changes markup, content, or the contrast guarantees: every band is a
 * token the palette step already corrected the accent against, and the one
 * derived colour that could drift (--ink-soft on a tinted band) is re-derived
 * darker wherever such a band appears.
 *
 * The bands themselves (which sections sit on a tint, and the shared gap
 * between two neighbours on one ground) moved to grounds.ts and
 * treatments.css, where the renderer stamps them per section.
 */

/*
 * The grounds.
 *
 * Which sections sit on a tint of their own is decided per archetype in
 * grounds.ts (the table is BANDS) and stamped on each section by the
 * renderer as a class: ground-tint, ground-soft, and the half-gap classes
 * where two neighbours share one ground. The paint and the gaps are in
 * treatments.css. This file keeps what is left of each archetype's mood:
 * the brand stripe, the corners of its buttons.
 */

/* --- gallery-led: trades and landscapers with photographs ----------------- */
/* Aspen Mountain / Gibbs: a brand stripe at the very top, work on clean paper
 * so the photographs pop, then a tinted band for the services and a soft
 * brand-coloured moment for the story. */
body.archetype-gallery-led { border-top: 4px solid var(--color-accent); }

/* --- card-grid-map: trades and auto shops without a portfolio ------------- */
/* Edwards / Christian Brothers: the same brand stripe, and the story and the
 * reviews on a quiet tinted band. Services stay on paper: tinted too, with
 * the trust strip and the steps band tinted below them, the page was one flat
 * grey from the hero to the footer and its sections blurred together. */
body.archetype-card-grid-map { border-top: 4px solid var(--color-accent); }

/* --- split-hero-booking: clinics and appointment businesses --------------- */
/* Dentologie: pill buttons and generous air. Calm is the brand. Hours stay
 * on paper: the steps band above and the contact footer below are both
 * tinted, and three grey bands in a row read as one flat slab. */
.archetype-split-hero-booking { --radius-btn: 999px; }

/* --- single-column-story: the no-photograph tier -------------------------- */
/* Grand Street Dental: the decorated typographic hero carries the top, pills
 * keep it friendly, and the services sit on one full-width tinted band so the
 * narrow column still reads as a designed page, not a document. */
.archetype-single-column-story { --radius-btn: 999px; }

/* --- hero-over-photo: restaurants, salons, look-first businesses ---------- */
/* Juniper & Ivy / Boardroom: the photograph owns the whole first screen (the
 * hero block itself commits to that), then services on a tinted band and the
 * story on a soft brand moment. */
.archetype-hero-over-photo { --radius-btn: 999px; }

/* --- menu-forward: cafes and restaurants where the menu is the page ------- */
/* Mei Mei: warmth early — the hours arrive on a soft brand band right under
 * the hero — and the menu itself stays on clean paper where it is easiest to
 * read. */
.archetype-menu-forward { --radius-btn: 999px; }

/*
 * Tinted bands that meet the blocks that tint themselves (the trust strip,
 * the steps band, the contact footer) would double a hairline where they
 * touch. The border belongs to the self-tinting block; nothing to do here —
 * the bleed bands deliberately carry no borders at all.
 */

/*
 * Design styles: the visual language of a kind of business (styles.ts).
 *
 * Loaded after the per-archetype moods, so a style decides corners, rules,
 * spacing and how colour is used, while the archetype still decides which
 * sections a page has and which bands are tinted. Every colour here is a token
 * the palette step has already corrected: paper on ink and paper on the deep
 * accent are guaranteed, and nothing sets accent-coloured text on a dark band.
 */

/* --- sturdy: trades, builders, garages, landscapers ---------------------- */
/* Square corners, heavy rules, a dark band: the vocabulary of a van decal, a
 * hard hat and a printed quote, not of an app. */
.style-sturdy {
  --radius: 3px;
  --radius-sm: 2px;
  --radius-lg: 4px;
  --radius-btn: 3px;
  --rule-weight: 3px;
}
/*
 * The heavy rule is the style's signature, so it marks one thing: the
 * services, which is what a trade page is for. Stamped on the cards, steps,
 * reviews and hero facts alike it ran to about thirty-five identical black
 * bars on one page (Pro Experts) and every trade page read as one template.
 */
.style-sturdy .card { border-top-width: var(--rule-weight); border-top-color: var(--color-ink); }
.style-sturdy .services-list li:first-child { border-top-color: var(--color-ink); }
.style-sturdy .steps li { border-top-color: var(--line-strong); }
/* On the dark hero an ink rule is invisible; there the rules are paper. */
.style-sturdy .hero-split--brand .hero-facts li { border-top-color: color-mix(in srgb, var(--color-paper) 45%, transparent); }
/* Buttons at body size in sentence case, as the masthead's own: tracked
 * capitals at 13px looked feeble under a 70px headline (Tantallon, Green
 * Arbor, WCH), and base.css keeps tracked capitals off the page for that. */
.style-sturdy .btn { font-weight: 700; letter-spacing: 0.01em; }

/* --- warm: pets, food, families ------------------------------------------ */
/* Soft corners, pill buttons, photographs with generous rounding, and the
 * brand's own tint carrying whole bands, so the page feels like a place. */
/* Warm's one tint is its own colour: every tinted band uses it, so the page
 * has paper, the brand tint and the deep brand colour, and nothing else. */
.style-warm {
  --surface: var(--accent-soft);
  --radius: 18px;
  --radius-sm: 12px;
  --radius-lg: 28px;
  --radius-btn: 999px;
}
.style-warm .section--tinted { background: var(--accent-soft); border-block-color: transparent; }
.style-warm .section--tinted p { --ink-soft: color-mix(in srgb, var(--color-ink) 82%, var(--color-paper)); }

/* --- refined: salons, spas, professionals, funeral homes ----------------- */
/* Hairlines, air and restraint. Square buttons, no tinted bands, larger
 * spacing between sections, and type left to carry the weight. */
.style-refined {
  --radius: 2px;
  --radius-sm: 0px;
  --radius-lg: 2px;
  --radius-btn: 0px;
  --section: clamp(3.5rem, 2.4rem + 4.5vw, 7rem);
}
/* Its tinted bands become plain paper sections, spaced like one: a paper
 * band that kept a band's inner padding sat between paper neighbours as a
 * doubled gap. */
.style-refined .section--tinted { background: none; border-block: 0; }
.style-refined .trust { background: var(--color-paper); color: var(--color-ink); }
.style-refined .trust__value { color: var(--color-accent); }
.style-refined .trust__label { color: var(--ink-soft); }
.style-refined .trust__item { border-inline-start-color: var(--line-strong); }
.style-refined .card, .style-refined .steps li, .style-refined .quote { border-top-color: var(--line); }
.style-refined .btn { font-weight: 600; letter-spacing: 0.02em; }
.style-refined h2 { font-size: clamp(1.9rem, 1.4rem + 2.2vw, 3.25rem); }

/* Reviews in italic; set large only when every one of them is short. A
 * hundred-word review at 2rem was a column of display type a screen long
 * (Atmosphere Day Spa), and beside it the short ones looked lost. */
.style-refined [data-section="reviews"] .quote p { font-style: italic; font-weight: 400; }
.style-refined [data-section="reviews"] .quotes:not(:has(> .quote:not(.quote--short))) .quote p { font-size: clamp(1.4rem, 1.1rem + 1.2vw, 2rem); line-height: 1.4; }
/*
 * The mid-page beat. After a strong hero the page used to settle into paper,
 * grey, paper, grey, and the middle read flat. The figures are the one band
 * that changes key: the deep brand colour for sturdy, clean and warm (the
 * trust block's own default), and for refined the same paper with the figures set very large
 * between heavier rules, restraint being its own kind of emphasis.
 */
.style-refined .trust { border-block: 1px solid var(--color-ink); }
.style-refined .trust__value { font-size: clamp(2.4rem, 1.4rem + 3.6vw, 4.5rem); font-weight: 400; }

/* --- clean: clinics, services, everything else --------------------------- */
/* The base system as it stands: medium corners, hairlines, quiet tints. */
.style-clean { --radius-btn: 10px; }

/*
 * The design treatment (treatment.ts): the knobs a designer turns for one
 * page, as classes on <body>, and the grounds the renderer stamps on each
 * section (grounds.ts). Loaded last, after the moods and the styles, so a
 * treatment can override either without a single.
 */

/* --- grounds -------------------------------------------------------------- */
/*
 * The full-bleed trick: a section whose class is "section wrap" is
 * width-capped, so a background on it would be a boxed stripe. The 100vmax
 * box-shadow paints the same colour to both edges of the viewport and the
 * clip-path stops it bleeding vertically; shadows do not affect layout, so no
 * horizontal scrollbar can appear.
 */
.ground-tint { --band: var(--surface); }
.ground-soft { --band: var(--accent-soft); --ink-soft: color-mix(in srgb, var(--color-ink) 82%, var(--color-paper)); }
main > .ground-tint.wrap, main > .ground-soft.wrap {
  background: var(--band);
  box-shadow: 0 0 0 100vmax var(--band);
  clip-path: inset(0 -100vmax);
}
/* Two neighbours on one ground share one gap. */
main > .gap-below { padding-bottom: var(--section-shared); }
main > .gap-above { padding-top: var(--section-shared); }
/* The statement carries its own rules and air; beside a neighbour on the
 * same ground it sits close, so the empty space between them stays under
 * the spacing gate's limit. */
main > .statement.gap-above { padding-top: var(--space-2); }
main > .statement.gap-below { padding-bottom: var(--space-2); }
/* The steps band's hairlines only where the ground changes. */
.section--tinted.gap-below { border-bottom: 0; }
.section--tinted.gap-above { border-top: 0; }

/* --- rhythm --------------------------------------------------------------- */
/* Paper: no tints at all, and a hairline where two paper sections meet. */
.t-rhythm-paper .section--tinted, .t-rhythm-airy .section--tinted { background: none; border-block: 0; }
.t-rhythm-paper main > .gap-above:not(.statement) { border-top: 1px solid var(--line); }
/* Airy: more room between sections and the section headings a size up. The
 * shared gap is half a section each side, so two paper neighbours sit 132px
 * apart at most, inside the spacing gate's 140. */
.t-rhythm-airy { --section: clamp(4rem, 2.8rem + 5vw, 8.25rem); --section-shared: calc(var(--section) * 0.5); }
.t-rhythm-airy h2 { font-size: clamp(1.9rem, 1.4rem + 2.2vw, 3.25rem); }

/* --- measure -------------------------------------------------------------- */
/* The column itself is a token the renderer sets (--max-width); the story
 * shape keeps its own narrow canvas whatever the treatment. */
.t-measure-narrow { --measure: 36rem; }

/* --- accent --------------------------------------------------------------- */
/* The brand colour on the section headings where they sit on paper (the
 * accent is corrected to read on paper), a brand stripe at the top of the
 * page, and the closing band in the deep brand colour rather than near-black. */
.t-accent-bold { border-top: 4px solid var(--color-accent); }
/* Every section heading on a light ground, not only those on paper: with the
 * tinted sections left near-black the critic read one blue heading as a
 * mistake. The accent is corrected to read on paper and on every tint
 * derived from it; the dark and brand grounds keep paper type. */
.t-accent-bold main > :is(.ground-paper, .ground-tint, .ground-soft) > h2,
.t-accent-bold main > :is(.ground-paper, .ground-tint, .ground-soft) > .wrap > h2,
.t-accent-bold main > :is(.ground-paper, .ground-tint, .ground-soft) > div > h2,
.t-accent-bold main > :is(.ground-paper, .ground-tint, .ground-soft) .about__lead > h2 { color: var(--color-accent); }
.t-accent-bold .cta { background: var(--accent-deep); }
.t-accent-bold .cta p { color: color-mix(in srgb, var(--color-paper) 88%, var(--accent-deep)); }
.t-accent-bold .cta .btn--primary { color: var(--accent-deep); }
.t-accent-bold .hero-split:not(.hero-split--brand) .hero-facts li { border-top-color: var(--color-accent); }

/* --- photos --------------------------------------------------------------- */
/* Edge to edge, square: the galleries drop the column and the corners. */
.t-photos-bleed :is(.work-grid__item, .strip figure, .carousel__slide, .photo-band img, .split__media img, .about__photo img, .hero-split__media img) { border-radius: 0; }
.t-photos-bleed main > .work-grid--bleed, .t-photos-bleed main > .strip--bleed { max-width: none; padding-inline: 0; }
.t-photos-bleed .work-grid--bleed > h2, .t-photos-bleed .work-grid--bleed > .lede { margin-inline: auto; max-width: var(--max-width); padding-inline: var(--gutter); }
.t-photos-bleed .work-grid--bleed > .lede { max-width: var(--max-width); }
/* The link to the work page goes back on the column with the heading: left
 * on the bled section it sat against the window's edge (Green Arbor). */
.t-photos-bleed .work-grid--bleed > .section-more { justify-self: stretch; width: 100%; margin-inline: auto; max-width: var(--max-width); padding-inline: var(--gutter); }
.t-photos-bleed .work-grid--bleed .work-grid__items { gap: 4px; }
.t-photos-bleed .strip--bleed { gap: 4px; padding-block: 0; }
/* A bleed strip is a band of photographs; its neighbours keep their own gaps. */
.t-photos-bleed main > .strip--bleed.gap-above { padding-top: 0; }
.t-photos-bleed main > .strip--bleed.gap-below { padding-bottom: 0; }

/* --- hero ----------------------------------------------------------------- */
/* Display: the headline a size up. Only set where the words are short enough
 * (the block decides and adds the modifier). */
.hero-split--display h1 { font-size: var(--text-4xl); max-width: 14ch; }
.hero-split--display .lede { font-size: var(--text-xl); max-width: 36ch; }
.hero-plain--display h1 { font-size: var(--text-4xl); }
.hero-photo--display .hero-photo__inner h1 { font-size: clamp(3rem, 1.6rem + 6.4vw, 7rem); max-width: 18ch; }
/* Stacked: the words centred across the measure, compact, the picture full
 * width beneath them and the facts beneath that. The words are held to a
 * short block (the split hero's own headline size, a two-line measure, the
 * promise at body size) so the picture starts inside the first screen on a
 * desk: set larger, it began at 640px and the critic saw a first screen with
 * no photograph. */
.hero-split--stacked { padding-block: var(--space-4) var(--section); }
.hero-split--stacked .hero-split__grid { grid-template-columns: minmax(0, 1fr); text-align: center; justify-items: center; row-gap: var(--space-3); }
.hero-split--stacked .hero-split__grid > div:first-child { display: grid; justify-items: center; max-width: 60ch; }
.hero-split--stacked h1 { max-width: 18ch; font-size: clamp(2.375rem, 1.3rem + 2.7vw, 4.25rem); }
.hero-split--stacked .lede { margin-inline: auto; font-size: var(--text-lg); max-width: 48ch; }
.hero-split--stacked .actions { justify-content: center; margin-top: var(--space-3); }
.hero-split--stacked .hero-split__grid > .hero-facts { width: 100%; max-width: none; margin-top: 0; text-align: start; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: var(--space-2) var(--space-3); }
.hero-split--stacked .hero-split__grid > .hero-facts:has(> li:last-child:nth-child(odd)) { grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: var(--space-2) var(--space-3); }
.hero-split--stacked .hero-split__media { width: 100%; max-width: none; }
.hero-split--stacked .hero-split__media img { width: 100%; max-height: none; aspect-ratio: 2 / 1; min-height: 0; }
.hero-split--stacked .hero-score { margin: var(--space-3) auto 0; text-align: center; }
.hero-split--stacked .hero-score .stars { justify-content: center; }
@media (min-width: 48rem) {
  .hero-split--stacked .hero-split__grid--two { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .hero-split--stacked .hero-split__media { display: block; }
  .hero-split--stacked .hero-split__media img { height: auto; min-height: 0; max-height: 62vh; aspect-ratio: 2 / 1; }
  .hero-split--stacked .hero-split__grid--score { grid-template-columns: minmax(0, 1fr); }
  .hero-split--stacked .hero-score--phone { display: block; }
}
/* Display beside the rating: centred on each other, not hung from the
 * bottom, where the rating sat detached from a taller headline. */
.hero-split--display .hero-split__grid--score { align-items: center; }
@media (max-width: 47.99rem) {
  /* Stacked already puts the picture under the words; the phone keeps it first. */
  .hero-split--stacked .hero-split__media:not(.hero-split__media--natural) img { aspect-ratio: 3 / 2; }
}

/*
 * On paper.
 *
 * Last of every stylesheet on the page, so these win over any block's own
 * colours at the same strength: browsers print no backgrounds by default, so
 * paper-coloured type on a coloured band would print white on white. The
 * bands are named once, and their type is matched at a strength (html body
 * plus a class) that outranks a block's two-class rules.
 */
@media print {
  .masthead, .cta, .skip-link, .navmenu { display: none; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .section, .hero-split, .hero-plain, .hero-photo__inner { padding-block: 1rem; }
  .section--tinted, .contact { background: none; border: 0; }

  /* The bands that set paper type on a colour: no ground, ink type. */
  html body :is(.hero-photo, .hero-plain, .hero-split--brand, .hero-score, .trust, .page-header, .colophon) { background: none; color: #000; border: 0; }
  html body :is(.hero-photo, .hero-plain, .hero-split--brand, .hero-score, .trust, .page-header, .crumbs, .colophon) :is(*, a) { color: #000; }
  /* Three-class rules in the brand hero that the line above does not outrank. */
  .hero-split--brand .lede, .hero-split--brand .hero-facts li { color: #000; }
  .hero-photo::after { display: none; }
  html body .hero-photo__media { position: static; max-height: 30vh; overflow: hidden; }
  html body .hero-photo__words { background: none; color: #000; padding: 0; }
  /* Buttons and small labels are drawn on colours print drops: outlined in ink instead. */
  html body a.btn.btn.btn { color: #000; background: none; border: 1px solid #000; box-shadow: none; }
  html body .hero-kicker.hero-kicker.hero-kicker { color: #000; background: none; }

  a { color: #000; }
  a[href^="tel:"]::after, a[href^="mailto:"]::after { content: none; }
  /* No colour eases in on paper: the page is printed as it is at that instant. */
  html body :is(a, .btn, [class]) { transition: none; }

  /* Rows, not the scroller's columns: with the flow left as columns every
   * slide took an implicit column of its own and ran off the paper (IMQ
   * printed 1881px wide on a 794px page, Alnabelsi 1577px, 28 Oct). */
  html body .carousel__track { overflow: visible; display: grid; grid-auto-flow: row; grid-auto-columns: auto; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  html body .carousel__track > * { grid-column: auto; }
  details, .faq__item, .card, .quote { break-inside: avoid; }
}
