/*
 * Blinco Flooring Ltd - main.css
 * ALL site styling and ALL site colours go in this file.
 *
 * Load order:
 *   1. framework.css   layout, grid, typography, tables, navigation
 *   2. components.css  buttons, tabs, forms, cards, modals, etc.
 *   3. main.css        this file - brand identity + all website styling styling
 *
 * framework.css and components.css are brand-agnostic: every colour they
 * ship is neutral greyscale. They are a vendored library - do not edit them
 * and do not put site colours in them. This file loads last, so it wins on
 * equal specificity without needing !important.
*/

/* ==========================================================================
   1. BRAND COLOURS
   Charcoal and off-white. That is the whole palette - there is no accent
   hue, and that is deliberate: the earlier pale-blue tint was rejected,
   and rather than substitute another colour the page now works entirely
   on weight, space and type.

   This means --bf-primary is NOT a hue. The framework uses it for link
   colour, button grounds and focus rings, so it is set to charcoal; links
   are told apart by an underline rather than by colour. If an accent is
   ever wanted back, change --bf-primary and --bf-primary-rgb together and
   the whole site picks it up.

   NOTE: --bf-dark-rgb and --bf-primary-rgb must be kept in step with
   --bf-dark and --bf-primary. The framework builds every overlay, scrim and
   shadow from the -rgb channels, so if they drift the tints go wrong.
   ========================================================================== */
:root {
  /* Structure */
  --bf-dark:            #22272c;
  --bf-dark-rgb:        34 39 44;
  --bf-dark-mid:        #2d343a;   /* dropdown panels, hover on charcoal */
  --bf-dark-light:      #3a424a;

  /* "Accent" - charcoal. Link colour AND button ground. */
  --bf-primary:         #22272c;
  --bf-primary-rgb:     34 39 44;
  --bf-primary-hover:   #434c54;
  --bf-accent:          #2d343a;

  /* On-charcoal text steps, all measured against --bf-dark */
  --bf-on-dark:         #ffffff;   /* 15.1:1 */
  --bf-on-dark-soft:    #c9cbcd;   /*  9.3:1 */
  --bf-on-dark-muted:   #9aa0a5;   /*  5.7:1 */
  --bf-on-dark-line:    rgb(255 255 255 / 0.14);
  /* Surfaces - neutral, no blue cast now the accent has gone */
  --bf-body-bg:         #f4f4f2;
  --bf-surface:         #ffffff;
  --bf-surface-alt:     #ebebe8;
  --bf-surface-sunken:  #e2e2de;

  /* Text - all tested on --bf-body-bg */
  --bf-text:            #46494c;   /*  8.2:1 */
  --bf-heading:         #22272c;   /* 13.7:1 */
  --bf-text-muted:      #5e6368;   /*  5.5:1 */
  --bf-text-invert:     #ffffff;

  /* Lines */
  --bf-border:          #dcdcd9;
  --bf-border-light:    #e8e8e5;

  /* Tints the framework derives from the channels above */
  --bf-primary-soft:    rgb(var(--bf-primary-rgb) / 0.06);
  --bf-primary-ring:    rgb(var(--bf-primary-rgb) / 0.28);
  --bf-mark-bg:         #e8e8e5;
}

/* ==========================================================================
   2. BRAND TYPE + CHROME
   Jost for headings - a geometric sans, the same face the wordmark is cut
   from, so headings echo the logo. Inter for body and UI, where its larger
   x-height and tighter spacing do the real reading work.

   Both are variable woff2, latin subset, self-hosted in theme/fonts - no
   third-party request, nothing to block, no layout shift beyond the swap.
   ========================================================================== */
@font-face {
  font-family: "Jost";
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url("../fonts/jost-var.woff2") format("woff2");
}
/* Jost's real italic - a true drawn cut of the same family, not a slant.
   It exists only because the home-page strapline is set in it; nothing
   else on the site uses italic.

   THIS FILE IS WHY THE STRAPLINE IS NOT FAKED. Without a real italic,
   `font-style: italic` makes the browser shear the roman sideways, which
   at 78px shows up as uneven stroke weights and broken curves on a, e, g
   and y. With this face loaded the same declaration gets properly drawn
   italic forms - a single-storey a, a true cursive e - at a 5 degree
   angle the designer chose.

   Subset to exactly the same codepoints as jost-var.woff2 and kept
   variable on wght, so it matches the roman everywhere. */
@font-face {
  font-family: "Jost";
  font-style: italic;
  font-weight: 300 600;
  font-display: swap;
  src: url("../fonts/jost-italic-var.woff2") format("woff2");
}

/* NO THIRD FAMILY. A script (Italianno) was tried for the strapline and
   rejected as tacky - a formal script reads as a wedding invitation, not
   a flooring contractor. The site stays on Jost and Inter; the strapline
   gets its distinction from the italic cut, the light weight and the
   scale. Do not add a display or script face here again. */

:root {
  --bf-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;
  --bf-font-heading: "Jost", "Inter", -apple-system, BlinkMacSystemFont,
                     "Segoe UI", Roboto, sans-serif;
  /* The strapline face. Same family as the headings - the slogan is told
     apart by weight (300 against the headings' 500) and by scale, not by
     a different typeface. See .slogan in section 5. */
  --bf-font-slogan: var(--bf-font-heading);
  --bf-line-height: 1.65;

  /* Tighter radii than the framework default - a corporate register reads
     crisper square. */
  --bf-radius:          4px;
  --bf-radius-lg:       8px;
  --bf-radius-btn:      4px;

  --bf-nav-height:      76px;

  /* Rhythm for the brand layer, used by sections 4 and 5 below. */
  --bf-rule:            2px;
  --bf-measure:         64ch;
}

/* Headings: geometric caps want a little negative tracking at display
   sizes and none at all when small. */
h1, h2, h3, h4, h5, h6 {
  font-weight: 500;
  letter-spacing: -0.015em;
}
h1 { letter-spacing: -0.025em; }
h5, h6 { letter-spacing: 0; }

body { -webkit-font-smoothing: antialiased; }

/* Inter ships proportional figures; tabular is what you want anywhere
   numbers stack or compare. */
.stat-value, .tabular { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   3. ICONS
   Lucide 1.62.0 via the local sprite at theme/svg/icons.svg.
   Notices: theme/svg/LICENSES. Rebuild: python3 theme/svg/build-icons.py

   MARKUP
     Decorative (the overwhelming majority - the icon repeats a visible
     label, so screen readers should skip it):

       <svg class="icon" aria-hidden="true">
         <use href="/theme/svg/icons.svg#phone"></use>
       </svg>

     Meaningful (icon carries information no nearby text does, e.g. an
     icon-only button) - give it a role and an accessible name:

       <svg class="icon icon-lg" role="img" aria-label="Call us">
         <use href="/theme/svg/icons.svg#phone"></use>
       </svg>

   COLOUR
     Icons inherit currentColor, so they pick up the colour of whatever
     they sit in. Do NOT add fill or stroke to .icon: the sprite sets
     those per symbol (strokes are fill:none/stroke:currentColor, the
     brand-* marks are the exact opposite), and a CSS declaration here
     would beat the attribute and flatten the solid marks into nothing.
   ========================================================================== */
:root {
  /* Default size follows the text the icon sits in. */
  --bf-icon-size:   1em;
  --bf-icon-stroke: 2;
  --bf-icon-gap:    0.5em;
}

.icon {
  display: inline-block;
  flex: none;                 /* never let a flex row squash an icon */
  height: var(--bf-icon-size);
  width: var(--bf-icon-size);
  stroke-width: var(--bf-icon-stroke);
  /* Lucide centres its artwork in a 24 grid, so nudging up by this much
     sits the optical centre on the text's x-height rather than the
     baseline. */
  vertical-align: -0.15em;
  /* Clicks land on the enclosing link or button instead of the <use>
     shadow tree. Add .icon-interactive if an icon must be its own
     event target. */
  pointer-events: none;
}
.icon-interactive { pointer-events: auto; }

/* --- Sizes ---------------------------------------------------------------
   Stroke width tapers as the box grows. A 24-unit path scaled to 48px
   renders its 2-unit stroke at 4 real pixels, which reads as a much
   heavier icon than the same artwork at 20px - so the larger steps pull
   the stroke back to keep the apparent weight even across the scale. */
.icon-xs  { --bf-icon-size: 0.875rem; --bf-icon-stroke: 2.25; }
.icon-sm  { --bf-icon-size: 1rem;     --bf-icon-stroke: 2.25; }
.icon-md  { --bf-icon-size: 1.25rem;  --bf-icon-stroke: 2; }
.icon-lg  { --bf-icon-size: 1.5rem;   --bf-icon-stroke: 2; }
.icon-xl  { --bf-icon-size: 2rem;     --bf-icon-stroke: 1.75; }
.icon-2xl { --bf-icon-size: 2.5rem;   --bf-icon-stroke: 1.6; }
.icon-3xl { --bf-icon-size: 3rem;     --bf-icon-stroke: 1.5; }

/* Deliberate weight override, independent of size. */
.icon-thin { --bf-icon-stroke: 1.5; }
.icon-bold { --bf-icon-stroke: 2.5; }

/* The brand marks are solid shapes with no stroke, so the taper above is
   a no-op on them - nothing to do here, noted so it isn't mistaken for an
   omission. */

/* --- Colour helpers ------------------------------------------------------
   Only for icons that need to break from their parent's colour; otherwise
   let currentColor do the work. */
.icon-primary { color: var(--bf-primary); }
.icon-muted   { color: var(--bf-text-muted); }
.icon-success { color: var(--bf-success); }
.icon-warning { color: var(--bf-warning); }
.icon-danger  { color: var(--bf-danger); }
.icon-invert  { color: var(--bf-text-invert); }

/* --- Icon + text pair ----------------------------------------------------
   Contact lines, meta rows, inline links. Keeps the icon optically
   aligned with the first line when the text wraps.

     <span class="icon-text">
       <svg class="icon" aria-hidden="true">
         <use href="/theme/svg/icons.svg#map-pin"></use>
       </svg>
       47 Example Road, Townsville
     </span>
*/
.icon-text {
  align-items: start;
  display: inline-flex;
  gap: var(--bf-icon-gap);
}
.icon-text > .icon {
  /* Push the icon down onto the first line's optical centre. Pairs with
     align-items:start so multi-line text stays top-aligned. */
  margin-top: calc((1em * var(--bf-line-height) - var(--bf-icon-size)) / 2);
  vertical-align: baseline;
}
/* Single-line variant - cheaper and perfectly centred when it cannot wrap. */
.icon-text-center {
  align-items: center;
  display: inline-flex;
  gap: var(--bf-icon-gap);
}
.icon-text-center > .icon { margin-top: 0; vertical-align: baseline; }

/* --- Icon list -----------------------------------------------------------
   Replaces bullets with icons - spec lists, "what's included", USPs.

     <ul class="icon-list">
       <li>
         <svg class="icon icon-success" aria-hidden="true">
           <use href="/theme/svg/icons.svg#circle-check"></use>
         </svg>
         Free measure and quote
       </li>
     </ul>
*/
.icon-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.icon-list > li {
  align-items: start;
  display: flex;
  gap: var(--bf-icon-gap);
  padding: 0.25rem 0;
}
.icon-list > li > .icon {
  margin-top: calc((1em * var(--bf-line-height) - var(--bf-icon-size)) / 2);
}
/* Tighter rhythm where the list is dense. */
.icon-list-tight > li { padding: 0.0625rem 0; }

/* --- Icon badge ----------------------------------------------------------
   An icon on its own tile - service cards, feature rows, step markers.
   Shape defaults to a rounded square; add .icon-badge-circle for a disc.

     <span class="icon-badge icon-badge-soft">
       <svg class="icon icon-lg" aria-hidden="true">
         <use href="/theme/svg/icons.svg#ruler"></use>
       </svg>
     </span>
*/
.icon-badge {
  align-items: center;
  background: var(--bf-surface-sunken);
  border-radius: var(--bf-radius);
  color: var(--bf-text);
  display: inline-flex;
  flex: none;
  height: var(--bf-icon-badge-size, 3rem);
  justify-content: center;
  transition: background var(--bf-transition), color var(--bf-transition);
  width: var(--bf-icon-badge-size, 3rem);
}
.icon-badge-circle  { border-radius: 100px; }

.icon-badge-soft    { background: var(--bf-primary-soft); color: var(--bf-primary); }
.icon-badge-solid   { background: var(--bf-primary); color: var(--bf-text-invert); }
.icon-badge-outline {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--bf-border);
  color: var(--bf-primary);
}
.icon-badge-invert  {
  background: var(--bf-overlay-light);
  color: var(--bf-text-invert);
}

.icon-badge-sm { --bf-icon-badge-size: 2.25rem; }
.icon-badge-lg { --bf-icon-badge-size: 4rem; }
.icon-badge-xl { --bf-icon-badge-size: 5rem; }

/* Lift the badge when the card around it is hovered. Opt in by putting
   .icon-badge-hover on the card, not on the badge. */
.icon-badge-hover:hover .icon-badge-soft { background: var(--bf-primary); color: var(--bf-text-invert); }

/* --- Component slot bridges ---------------------------------------------
   framework.css and components.css predate the sprite and size their icon
   slots for a font glyph (`font-size` on an <i>/<span>). An <svg> ignores
   font-size, so these give the same slots a real box.

   Note .button > i in components.css paints a round chip behind the glyph.
   An <svg class="icon"> placed directly in a .button is not matched by
   that rule, so it stays chip-free; wrap it in <span class="btn-icon"> if
   the chip is wanted. */
.button > .icon { --bf-icon-size: 1.1em; }
.button > .btn-icon > .icon { --bf-icon-size: 0.9em; }

/* .timeline-icon is a 40px disc that components.css grows to 60px at
   min-width:1141px - matching that breakpoint exactly, not the max-width
   cascade the rest of the framework uses, because that is the query the
   component itself is written against.

   The colour matters too: components.css whitens `.timeline-icon i` and
   `.timeline-icon span`, and an <svg> is neither, so without this the icon
   inherits body text and renders charcoal-on-primary. */
.timeline-icon > .icon {
  --bf-icon-size: 1.25rem;
  color: var(--bf-text-invert);
}
@media only screen and (min-width: 1141px) {
  .timeline-icon > .icon { --bf-icon-size: 1.75rem; }
}


/* ==========================================================================
   4. SITE-WIDE CHROME
   Header, logo, footer. Header and footer are the same charcoal, so the
   page is bookended in the brand colour and everything between them is
   light. The logo is white in both.
   ========================================================================== */

/* ANCHORS THAT ARE NOT LINK-COLOURED
   framework.css sets `a, a:link, a:visited, a:hover, a:active { color:
   var(--bf-primary) }`. A pseudo-class counts as a class, so `a:link` is
   (0,1,1) and quietly beats any single-class rule you write for a
   block-level link - the anchor keeps the link colour and every child
   inherits it. Any component built on an <a> must restate the full chain
   to win. This is the cascade collision to watch for in this codebase. */
.skip-link, .skip-link:link, .skip-link:visited,
.skip-link:hover, .skip-link:active {
  background: var(--bf-dark);
  border-radius: 0 0 var(--bf-radius) 0;
  color: var(--bf-on-dark);
  left: 0;
  padding: 0.75rem 1.25rem;
  position: absolute;
  text-decoration: none;
  top: 0;
  transform: translateY(-120%);
  z-index: 200;
}
.skip-link:focus { transform: translateY(0); }

/* --- Logo ----------------------------------------------------------------
   The wordmark is inlined in the page as a hidden symbol and referenced
   same-document, because a cross-document <use> does not resolve over
   file:// - see the comment above it in index.html.

   Each .site-logo element carries the matching viewBox in its markup, so
   `width: auto` derives the width from the real aspect ratio. Do not swap
   that for a CSS aspect-ratio: an <svg> with no viewBox has an intrinsic
   300x150 default, and the two fight. */
.site-logo {
  color: var(--bf-on-dark);
  display: block;
  height: 44px;
  width: auto;
}
.logo-link { display: inline-block; }
.logo-link:hover .site-logo { color: var(--bf-on-dark-soft); }
.site-logo-footer { height: 40px; }

/* --- Header, upper tier --------------------------------------------------
   Charcoal, matching the footer. Logo left, Portal right. */
.site-header, .header-top { background: var(--bf-dark); }
.header-top-inner {
  align-items: center;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  min-height: var(--bf-nav-height);
  padding: 0.75rem var(--bf-gutter);
}

/* Portal. Outlined on the charcoal, filling white on hover - the only
   button in the header, so it should read as a control without shouting. */
.button.portal-btn,
a.button.portal-btn, a.button.portal-btn:link,
a.button.portal-btn:active, a.button.portal-btn:visited {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.35);
  color: var(--bf-on-dark);
  flex: none;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  padding: 0.7rem 1.5rem;
  text-transform: uppercase;
}
.button.portal-btn:hover {
  background: var(--bf-on-dark);
  box-shadow: inset 0 0 0 1px var(--bf-on-dark);
  color: var(--bf-dark);
  filter: none;
}

/* --- Header, lower tier -------------------------------------------------- */
.header-nav {
  background: var(--bf-dark);
  border-top: 1px solid var(--bf-on-dark-line);
}

@media screen and (min-width: 769px) {
  /* The portal link is duplicated into the nav list for the mobile panel;
     on desktop the button in the upper tier is the live one. */
  .portal-row { display: none; }

  /* NOTE the `> ul >` child combinators throughout this block. A
     descendant `ul` would also match the dropdown lists, and
     `... ul > li:first-child > a` (0,3,3) outranks the dropdown rule
     (0,2,4) - which silently strips the left padding from the first item
     in every dropdown. */
  .header-nav .top-nav > ul > li > a,
  .header-nav .top-nav > ul > li > .sub-label {
    color: var(--bf-on-dark-soft);
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    padding: 1.05rem 0.95rem;
    text-transform: uppercase;
  }
  .header-nav .top-nav > ul > li > a:hover,
  .header-nav .top-nav > ul > li > .sub-label:hover,
  .header-nav .top-nav > ul > li:focus-within > .sub-label {
    background: transparent;
    box-shadow: inset 0 -2px 0 var(--bf-on-dark);
    color: var(--bf-on-dark);
  }
  /* First nav item optically aligned with the logo above it. 0.6rem is
     measured, not derived: the link's padding box and the glyph's left
     sidebearing do not coincide, so the full 0.95rem padding pushes HOME
     ~7px right of the logo while removing it entirely pulls it ~12px left.
     Verified against the rendered pixels. */
  .header-nav .top-nav > ul > li:first-child > a { padding-left: 0.6rem; }
  .header-nav .top-nav > ul > li.active-item > a {
    background: transparent;
    color: var(--bf-on-dark);
  }

  /* Dropdowns: a step lighter than the bar so they read as a panel sitting
     on top of it rather than a continuation of it. */
  .header-nav .top-nav ul ul {
    background: var(--bf-dark-mid);
    border: 1px solid var(--bf-on-dark-line);
    border-top: 0;
    box-shadow: 0 16px 32px rgb(0 0 0 / 0.28);
    min-width: 14rem;
    padding: 0.35rem 0;
  }
  .header-nav .top-nav ul ul li a {
    background: transparent;
    color: var(--bf-on-dark-soft);
    font-size: 0.85rem;
    letter-spacing: 0;
    padding: 0.6rem 1.25rem;
    text-transform: none;
    white-space: nowrap;
  }
  .header-nav .top-nav ul ul li a:hover {
    background: var(--bf-dark-light);
    box-shadow: none;
    color: var(--bf-on-dark);
  }
}

@media screen and (max-width: 768px) {
  /* Below 768px framework.css turns the <ul> into a fixed full-screen
     panel and pins the hamburger to the corner, both position:fixed. The
     lower tier therefore has nothing left to lay out - collapse it to
     nothing but leave it in the DOM, because the fixed children are its
     descendants and `display: none` would take them with it. */
  .header-nav { border: 0; height: 0; padding: 0; }
  .header-nav .line { padding: 0; }

  /* The hamburger occupies the top-right corner, which is where the portal
     button sits. Swap the button out for a row inside the panel.

     The full link chain is restated because components.css declares
     `display: inline-flex` on `a.button:link` (0,2,1) - a bare
     `.portal-btn { display: none }` (0,1,0) loses to it, and the button
     stays on screen sitting underneath the hamburger. */
  .portal-btn,
  a.button.portal-btn, a.button.portal-btn:link,
  a.button.portal-btn:visited, a.button.portal-btn:active { display: none; }
  .portal-row > a {
    box-shadow: inset 0 1px 0 var(--bf-on-dark-line);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  /* Header is charcoal now, so the hamburger bars stay white throughout. */
  .site-header .nav-text { background: transparent; }
}

/* --- Grid row gap --------------------------------------------------------
   framework.css gives .grid.margin a grid-column-gap and nothing else, so
   the moment a grid wraps onto a second row the rows sit flush and the
   content collides. Add .row-gap to any grid that can wrap. */
.grid.row-gap { row-gap: 2.75rem; }

/* --- Footer --------------------------------------------------------------
   The footer mark is #wordmark-strap, which carries the signature line as
   part of the logo artwork. It used to be a separate styled <p>, which on
   wide screens outgrew its grid column and ran under the Sectors list -
   inside the mark it scales with the logo and cannot overflow anything.
   No telephone or email here by design - both live on /contact/. */
.site-footer { padding: clamp(3rem, 5.5vw, 4.25rem) 0 2rem; }

.footer-head {
  color: var(--bf-on-dark);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  margin: 0.5rem 0 1.1rem;
  text-transform: uppercase;
}
.footer-nav { list-style: none; margin: 0; padding: 0; }
.footer-nav li { margin-bottom: 0.6rem; }
.footer-nav a, .footer-nav a:link, .footer-nav a:visited {
  color: var(--bf-on-dark-muted);
  font-size: 0.9rem;
  text-decoration: none;
}
.footer-nav a:hover { color: var(--bf-on-dark); }
.footer-base {
  border-top: 1px solid var(--bf-on-dark-line);
  margin: 3rem var(--bf-gutter) 0;
  padding-top: 1.5rem;
}
.footer-base p {
  color: var(--bf-on-dark-muted);
  font-size: 0.78rem;
  margin: 0;
}

/* ==========================================================================
   5. PAGE FURNITURE
   Everything in this section is page-agnostic and is meant to be used on
   EVERY page of the site. A new inner page needs nothing from section 6.

   The shape of a page is always the same three parts:

     <main id="main">
       <section class="page-hero weave">   <- the only per-page change
       <section class="band">              <- content bands, repeat as needed
       <section class="band band-alt">
     </main>

   Bands alternate ground so the page reads as a stack of distinct
   statements rather than one long scroll:

     .band            off-white (the body ground - no background painted)
     .band-alt        white, for the band that must feel like a surface
     .band-sunken     one step down from off-white, for quiet rows
     .band-dark       charcoal, bookending with the header and footer

   THE MOTIF
   There is no photography on this site and no accent hue (see section 1),
   so the one piece of decoration is .weave - a parquet lattice drawn in
   CSS at ~5% white and faded out downwards. It carries the flooring idea
   without a single image request, and because it is a gradient it scales
   to any viewport and costs nothing. Use it on charcoal only; on light
   grounds it reads as dirt.
   ========================================================================== */
:root {
  /* Vertical rhythm for bands. One token so every band on every page
     breathes identically - change it here, not per section. */
  --bf-band-y:       clamp(3.5rem, 6.5vw, 6rem);
  --bf-band-y-tight: clamp(2.25rem, 4vw, 3.25rem);

  /* The parquet lattice. Two periods, not one: equal periods give a
     square diamond grid, and the 1:2 ratio below stretches it into the
     elongated block a parquet floor is actually laid in. */
  --bf-weave-line: rgb(255 255 255 / 0.055);
  --bf-weave-a:    34px;
  --bf-weave-b:    68px;
}

/* --- Bands ---------------------------------------------------------------
   `position: relative` on every band so .weave and any other absolutely
   positioned decoration has something to anchor to. */
.band {
  padding: var(--bf-band-y) 0;
  position: relative;
}

/* ONE LEFT EDGE.
   A direct child of .line starts at the .line padding - one gutter in.
   Anything inside a grid column starts a gutter further in again, because
   framework.css pads every column. Left alone, a section heading sits one
   gutter to the left of the cards underneath it, which is small enough to
   look like a mistake rather than a decision.

   So every block that sits directly in a .line carries the extra gutter
   and the whole page lines up. Grids are excluded - they pad their own
   columns, and padding the grid as well would push them a gutter too far. */
.band-head,
.divided-row,
.page-hero > .line > :not(.grid) {
  padding-left: var(--bf-gutter);
  padding-right: var(--bf-gutter);
}
.band-tight { padding: var(--bf-band-y-tight) 0; }
.band-flush-top    { padding-top: 0; }
.band-flush-bottom { padding-bottom: 0; }

.band-alt    { background: var(--bf-surface); }
.band-sunken { background: var(--bf-surface-alt); }
.band-dark   { background: var(--bf-dark); color: var(--bf-on-dark-soft); }
.band-dark h1, .band-dark h2, .band-dark h3,
.band-dark h4, .band-dark h5, .band-dark h6 { color: var(--bf-on-dark); }

/* Hairline between two bands that share the same ground, so the rhythm
   does not collapse into one slab. */
.band-rule { border-top: 1px solid var(--bf-border); }

/* The global focus ring is charcoal (framework.css section 2), which is
   invisible on a charcoal band. Flip it for anything on dark. */
.band-dark :focus-visible,
.page-hero :focus-visible { outline-color: var(--bf-on-dark); }

/* --- The parquet lattice -------------------------------------------------
   Drawn on ::before rather than on the element's own background so a band
   can keep a flat background colour underneath and so the mask only ever
   fades the pattern, never the content.

   `isolation: isolate` is load-bearing: it creates a stacking context, so
   `z-index: -1` on the pseudo-element sits above the element's own
   background but below its children. Without it the -1 escapes to the
   nearest ancestor context and the pattern disappears behind the page. */
.weave { isolation: isolate; overflow: hidden; position: relative; }
.weave::before {
  background-image:
    repeating-linear-gradient(45deg,
      var(--bf-weave-line) 0 1px, transparent 1px var(--bf-weave-a)),
    repeating-linear-gradient(-45deg,
      var(--bf-weave-line) 0 1px, transparent 1px var(--bf-weave-b));
  content: "";
  inset: 0;
  -webkit-mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0.95), transparent 88%);
  mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0.95), transparent 88%);
  position: absolute;
  z-index: -1;
}
/* Lattice running the other way - use on a second dark band so the two do
   not look like the same tile repeated. */
.weave-flip::before { transform: scaleX(-1); }

/* --- Page hero -----------------------------------------------------------
   THIS IS THE PER-PAGE BLOCK. Every inner page is this hero with a
   different eyebrow, h1 and lede, followed by its own bands. Nothing else
   about the template needs touching.

     <section class="page-hero weave">
       <div class="line">
         <p class="eyebrow">Sectors</p>
         <h1>Commercial flooring</h1>
         <p class="hero-lede">One sentence of orientation.</p>
       </div>
     </section>

   Add .page-hero-home for the taller home-page proportions. */
.page-hero {
  background: var(--bf-dark);
  color: var(--bf-on-dark-soft);
  padding: clamp(3.25rem, 6vw, 5.5rem) 0 clamp(2.75rem, 5vw, 4.5rem);
}
.page-hero h1 {
  color: var(--bf-on-dark);
  font-size: clamp(2rem, 4.2vw, 3.1rem);
  font-weight: 400;
  line-height: 1.1;
  margin: 0 0 1.15rem;
  max-width: 24ch;
}
.hero-lede {
  color: var(--bf-on-dark-soft);
  font-size: clamp(1rem, 1.15vw, 1.1rem);
  margin: 0;
  max-width: 58ch;
}

/* Home page only: more height, and the strapline runs the full measure
   with the memo beneath it. */
.page-hero-home {
  padding: clamp(3.5rem, 6.5vw, 6rem) 0 clamp(3.5rem, 6.5vw, 6rem);
}
/* Sans fallback for a home hero that is NOT the strapline. .slogan below
   overrides all of this. */
.page-hero-home h1 {
  font-size: clamp(2.35rem, 4.6vw, 3.9rem);
  letter-spacing: -0.03em;
}

/* --- The strapline -------------------------------------------------------
   Jost Light Italic, full width, with the memo under it. Three things
   separate it from the h2s below, which are the same family: the italic
   cut, the light weight against their 500, and twice the size. At this
   scale a 500 would shout; 300 across the whole measure reads confident
   rather than loud.

   SIZING IS MEASURED, NOT GUESSED. In the ITALIC at weight 300 with
   -0.02em tracking this line is 15.76em wide whole, and 9.21em on its
   longer half once it wraps - so there are two regimes, not one curve:

   MIND THE MEASURE. The usable width is NOT the .line's 1260px: this h1
   is a direct child of .line, so it also carries the one-gutter-each-side
   alignment padding from the top of this section. Its content box is
   1280 - 20 (.line) - 20 (that padding) = 1240px. Sizing against 1260
   overflows by 8px and the line silently wraps.

     >= 769px   one line.   size = measure / 15.76
                  1240px box -> 78.7px, capped at 4.85rem (77.6px)
                   729px box -> 46.3px, 5.9vw gives 45.4px
     <= 768px   two lines.  size = measure / 9.21
                   728px box -> 79.1px, 9.3vw gives 71.4px
                   320px box -> 34.8px, floor of 2rem (32px)

   The italic runs 0.9% narrower than the roman, so these are the tighter
   of the two sets of numbers - the clamp holds either way.

   A single clamp cannot express that, because the wrap is a
   discontinuity: the size that fits one line at 1280 would need a
   viewport 1300 wide at 769.

   The 9.27em figure is the GREEDY split ("Designed, planned and" /
   "beautifully fitted."). `text-wrap: balance` will usually pick the
   evener 7.52 / 8.12 split instead, but sizing against the greedy number
   means it still fits in browsers that ignore balance.

   IF THE WORDING EVER CHANGES, re-measure both numbers - fontTools
   against theme/fonts/jost-var.woff2 instanced at wght 300.

   Specificity note: `.page-hero-home h1` above is (0,1,1), so `.slogan`
   alone (0,1,0) would lose. Scoping it to `.page-hero .slogan` (0,2,0)
   wins without !important. */
.page-hero .slogan {
  color: var(--bf-on-dark);
  font-family: var(--bf-font-slogan);
  font-size: clamp(2.8rem, 5.9vw, 4.85rem);
  font-style: italic;
  /* If jost-italic-var.woff2 ever fails to load, fall back to upright
     Jost rather than letting the browser shear the roman - a synthetic
     oblique at this size is worse than no italic at all. */
  font-synthesis: none;
  font-weight: 300;
  /* The measured width above assumes exactly this tracking. */
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0 0 clamp(2.25rem, 4vw, 3.25rem);
  max-width: none;
  /* Keeps the two mobile lines near-equal instead of orphaning a word. */
  text-wrap: balance;
}
/* The two-line regime. Below 769px the line cannot fit whole at any size
   worth setting it at, so it is sized to its longer half instead. */
@media screen and (max-width: 768px) {
  .page-hero .slogan { font-size: clamp(2rem, 9.3vw, 4.8rem); }
}

/* --- The memo ------------------------------------------------------------
   Two columns under the strapline. Body weight and a real measure: it has
   to read as something a person wrote, not as strapline support text. */
.hero-memo p {
  color: var(--bf-on-dark-soft);
  font-size: 1.0625rem;
  margin-bottom: 1.15rem;
}
.hero-memo p:last-child { margin-bottom: 0; }
.hero-memo strong {
  color: var(--bf-on-dark);
  font-weight: 500;
}
/* Stacked, the two columns are just consecutive paragraphs, so they want
   paragraph spacing - not the 2.75rem .row-gap gives a wrapping card row.
   Three classes so it beats .grid.row-gap on specificity, not on order. */
.grid.hero-memo.row-gap { row-gap: 1.15rem; }

/* A row of .link-arrow links under a block of copy - in the hero, under
   prose, anywhere. Wraps to a second line rather than squashing. */
.link-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem 2.25rem;
  margin-top: 2.25rem;
}

/* Bordered panel for the hero's second column. Barely-there white lift so
   it reads as a panel on the charcoal without introducing a new colour. */
.hero-panel {
  background: rgb(255 255 255 / 0.03);
  border: 1px solid var(--bf-on-dark-line);
  padding: clamp(1.4rem, 2.2vw, 2rem);
}
.hero-panel-head {
  color: var(--bf-on-dark-muted);
  font-family: var(--bf-font);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  margin: 0 0 0.35rem;
  text-transform: uppercase;
}

/* A row of links inside the panel: label, one line of detail, arrow.
   Built on an <a>, so the full link-colour chain is restated - see the
   note at the top of section 4.

   All three children get an explicit grid-area. Do not leave any of them
   to auto-placement: the arrow has a definite row, and the auto-placement
   algorithm places definite-row items BEFORE auto ones, so it claims
   column 1 and shunts the label into the narrow second column - which
   right-aligns the label against the arrow. */
.panel-link,
.panel-link:link, .panel-link:visited,
.panel-link:hover, .panel-link:active {
  border-top: 1px solid var(--bf-on-dark-line);
  color: var(--bf-on-dark);
  display: grid;
  gap: 0.1rem 1rem;
  grid-template-columns: 1fr auto;
  padding: 1rem 0 0.95rem;
  text-decoration: none;
}
.panel-link:last-child { padding-bottom: 0.25rem; }
.panel-link strong {
  font-family: var(--bf-font-heading);
  font-size: 1.05rem;
  font-weight: 500;
  grid-area: 1 / 1 / 2 / 2;
  letter-spacing: -0.01em;
}
.panel-link span {
  color: var(--bf-on-dark-muted);
  font-size: 0.85rem;
  grid-area: 2 / 1 / 3 / 2;
}
.panel-link .icon {
  align-self: center;
  color: var(--bf-on-dark-muted);
  grid-area: 1 / 2 / 3 / 3;
  transition: transform 0.25s var(--bf-ease), color var(--bf-transition);
}
.panel-link:hover .icon { color: var(--bf-on-dark); transform: translateX(4px); }

/* --- Section heads -------------------------------------------------------
   Eyebrow + rule, then the heading, then an optional lede. The rule is the
   only ornament the brand has, so it does the work the accent colour would
   normally do: it marks every section opening identically. */
.eyebrow {
  align-items: center;
  color: var(--bf-text-muted);
  display: flex;
  font-family: var(--bf-font);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  margin: 0 0 1.1rem;
  text-transform: uppercase;
}
.eyebrow::before {
  background: currentColor;
  content: "";
  display: block;
  flex: none;
  height: var(--bf-rule);
  margin-right: 0.9rem;
  opacity: 0.6;
  width: 2rem;
}
.page-hero .eyebrow,
.band-dark .eyebrow { color: var(--bf-on-dark-muted); }

.band-head { margin-bottom: clamp(2rem, 3.5vw, 3rem); }
.band-head h2 {
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  line-height: 1.14;
  margin: 0;
  max-width: 26ch;
}
.band-head p {
  color: var(--bf-text-muted);
  font-size: 1.0625rem;
  margin: 1rem 0 0;
  max-width: 62ch;
}
.band-dark .band-head p { color: var(--bf-on-dark-soft); }

/* Heading left, a link parked on the baseline to its right. Collapses to a
   stack below 769px, where a floated link beside a wrapped heading looks
   like a mistake. */
.band-head-split {
  align-items: end;
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem 2rem;
  justify-content: space-between;
}
.band-head-split > div { flex: 1 1 28rem; }

/* --- Prose ---------------------------------------------------------------
   For any run of body copy. --bf-measure caps the line length at a
   readable 64 characters however wide the column is. */
.prose { max-width: var(--bf-measure); }
.prose p { margin-bottom: 1.1rem; }
.prose p:last-child { margin-bottom: 0; }
.prose-lead {
  color: var(--bf-heading);
  font-size: 1.1875rem;
  line-height: 1.5;
}

/* --- Arrow link ----------------------------------------------------------
   The site's one call-to-action style. Deliberately a link and not a
   button: the register here is "go to the right page", not "convert".

     <a href="sectors/domestic/" class="link-arrow">
       <span>Domestic flooring</span>
       <svg class="icon icon-sm" aria-hidden="true">
         <use href="/theme/svg/icons.svg#arrow-right"></use>
       </svg>
     </a>

   The <span> is required - it carries the hover underline so the arrow
   itself does not get underlined with it. */
.link-arrow,
.link-arrow:link, .link-arrow:visited,
.link-arrow:hover, .link-arrow:active {
  align-items: center;
  color: var(--bf-heading);
  display: inline-flex;
  font-size: 0.78rem;
  font-weight: 600;
  gap: 0.55rem;
  letter-spacing: 0.1em;
  text-decoration: none;
  text-transform: uppercase;
}
.link-arrow > span {
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: border-color var(--bf-transition);
}
.link-arrow:hover > span { border-bottom-color: currentColor; }
.link-arrow .icon { transition: transform 0.25s var(--bf-ease); }
.link-arrow:hover .icon { transform: translateX(4px); }

.page-hero .link-arrow,
.page-hero .link-arrow:link, .page-hero .link-arrow:visited,
.page-hero .link-arrow:hover, .page-hero .link-arrow:active,
.band-dark .link-arrow,
.band-dark .link-arrow:link, .band-dark .link-arrow:visited,
.band-dark .link-arrow:hover, .band-dark .link-arrow:active {
  color: var(--bf-on-dark);
}

/* Makes a whole card clickable from the arrow link inside it. The anchor
   stays a real inline link - so it keeps its own focus ring and its text
   is what a screen reader announces - and the ::after simply covers the
   card. Requires `position: relative` on the card. */
.stretch-link::after {
  content: "";
  inset: 0;
  position: absolute;
}

/* --- Cards ---------------------------------------------------------------
   Square, bordered, flat. The hover is the only movement on the page: the
   rule across the top draws itself in. With no accent colour available
   that gesture is what tells you the card is live.

   .card-tile is the small variant (product ranges, link tiles);
   .card-feature is the large one (sectors, anything with a body and a
   list inside it). Both are generic enough for any page. */
.card-feature, .card-tile {
  background: var(--bf-surface);
  border: 1px solid var(--bf-border);
  display: flex;
  flex-direction: column;
  height: 100%;
  position: relative;
  transition: border-color var(--bf-transition),
              box-shadow 0.25s var(--bf-ease),
              transform 0.25s var(--bf-ease);
}
.card-feature { padding: clamp(1.5rem, 2.4vw, 2.25rem); }
.card-tile    { --bf-tile-pad: clamp(1.25rem, 1.9vw, 1.75rem); padding: var(--bf-tile-pad); }

.card-feature::before, .card-tile::before {
  background: var(--bf-dark);
  content: "";
  height: var(--bf-rule);
  left: -1px;
  position: absolute;
  top: -1px;
  transition: width 0.4s var(--bf-ease);
  width: 0;
}
.card-feature:hover, .card-tile:hover,
.card-feature:focus-within, .card-tile:focus-within {
  border-color: var(--bf-dark-light);
  box-shadow: var(--bf-shadow-lg);
  transform: translateY(-2px);
}
.card-feature:hover::before, .card-tile:hover::before,
.card-feature:focus-within::before, .card-tile:focus-within::before {
  width: calc(100% + 2px);
}

/* On a charcoal band the card inverts: no white ground, hairline border,
   and the top rule draws in white. */
.band-dark .card-feature, .band-dark .card-tile {
  background: rgb(255 255 255 / 0.03);
  border-color: var(--bf-on-dark-line);
}
.band-dark .card-feature::before, .band-dark .card-tile::before {
  background: var(--bf-on-dark);
}
.band-dark .card-feature:hover, .band-dark .card-tile:hover,
.band-dark .card-feature:focus-within, .band-dark .card-tile:focus-within {
  border-color: rgb(255 255 255 / 0.3);
  box-shadow: none;
}

/* Index numeral above a card title - 01 / 02 / 03. Jost, tracked out, so
   it reads as a label and not as a price. */
.card-index {
  color: var(--bf-text-muted);
  display: block;
  font-family: var(--bf-font-heading);
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  margin-bottom: 1.5rem;
}
.band-dark .card-index { color: var(--bf-on-dark-muted); }

.card-feature h3 {
  font-size: 1.5rem;
  margin: 0 0 0.75rem;
}
/* `flex: 1` is doing layout work, not sizing: it lets the intro paragraph
   absorb the leftover height in a card that is shorter than its
   neighbours, so the hairline above .card-list lands on the same line in
   every card of the row however ragged the copy is. */
.card-feature > p {
  color: var(--bf-text);
  flex: 1 1 auto;
  margin: 0 0 1.35rem;
}
.band-dark .card-feature > p { color: var(--bf-on-dark-soft); }

/* A card may open with an icon instead of an index numeral. */
.card-feature > .icon { color: var(--bf-heading); margin-bottom: 1.25rem; }
.band-dark .card-feature > .icon { color: var(--bf-on-dark); }

/* Pushes the arrow link to the bottom of a card so a row of cards with
   unequal copy still lines its links up. */
.card-foot { margin-top: auto; padding-top: 1.5rem; }

.card-tile h3 {
  font-size: 1.1875rem;
  margin: 1rem 0 0.35rem;
}
.card-tile p {
  color: var(--bf-text-muted);
  font-size: 0.9rem;
  margin: 0;
}
.card-tile .icon { color: var(--bf-heading); }

/* A tile that is itself the link. The whole chain is restated because
   framework.css colours `a:link` (0,1,1) - without it the tile's heading
   and line inherit the link colour. */
a.card-tile, a.card-tile:link, a.card-tile:visited,
a.card-tile:hover, a.card-tile:active {
  color: var(--bf-text);
  text-decoration: none;
}

/* The arrow sits in the top-right corner, opposite the icon, rather than
   on a footer row - with one line of text under the name there is no
   footer worth having. */
.tile-go {
  color: var(--bf-text-muted);
  line-height: 1;
  position: absolute;
  right: var(--bf-tile-pad);
  top: var(--bf-tile-pad);
  transition: transform 0.25s var(--bf-ease), color var(--bf-transition);
}
.card-tile:hover .tile-go,
.card-tile:focus-within .tile-go {
  color: var(--bf-heading);
  transform: translateX(4px);
}

/* Spec list inside a card. The icon replaces the bullet and the whole
   thing sits on a hairline so the list reads as evidence under the copy
   rather than as more prose. */
.card-list {
  border-top: 1px solid var(--bf-border-light);
  list-style: none;
  margin: 0;
  padding: 1.25rem 0 0;
}
.card-list li {
  align-items: start;
  color: var(--bf-text-muted);
  display: flex;
  font-size: 0.9rem;
  gap: 0.6rem;
  line-height: 1.5;
  padding: 0.3rem 0;
}
.card-list li > .icon {
  color: var(--bf-heading);
  margin-top: 0.28em;
}
.band-dark .card-list { border-top-color: var(--bf-on-dark-line); }
.band-dark .card-list li { color: var(--bf-on-dark-soft); }
.band-dark .card-list li > .icon { color: var(--bf-on-dark); }

/* --- Divided row --------------------------------------------------------
   A run of short items separated by hairlines rather than sat in boxes -
   fact strips, "what's included", footnote rows. Its own grid, not the
   framework's: the framework grid carries a column gap, and a gap puts
   daylight either side of a divider, which breaks the ruled-column look.
   ----------------------------------------------------------------------- */
.divided-row {
  display: grid;
  gap: 0;
  grid-template-columns: repeat(var(--bf-divided-cols, 4), 1fr);
}
.divided-row > * {
  padding: clamp(1.25rem, 2vw, 1.75rem) clamp(1.1rem, 1.8vw, 1.85rem);
}
.divided-row > * + * { border-left: 1px solid var(--bf-border); }
.divided-row > *:first-child { padding-left: 0; }
.divided-row > *:last-child  { padding-right: 0; }
.band-dark .divided-row > * + * { border-left-color: var(--bf-on-dark-line); }

.divided-row-3 { --bf-divided-cols: 3; }
.divided-row-2 { --bf-divided-cols: 2; }

@media screen and (max-width: 1140px) {
  .divided-row { grid-template-columns: repeat(2, 1fr); }
  /* Once it is two-up the dividers have to be recomputed: every odd child
     starts a new row and must lose its left border, and every child except
     those on the last row needs a top border instead. */
  .divided-row > * { padding-left: clamp(1.1rem, 1.8vw, 1.85rem); padding-right: clamp(1.1rem, 1.8vw, 1.85rem); }
  .divided-row > *:nth-child(odd)  { border-left: 0; padding-left: 0; }
  .divided-row > *:nth-child(even) { padding-right: 0; }
  .divided-row > *:nth-child(n + 3) { border-top: 1px solid var(--bf-border); }
  .band-dark .divided-row > *:nth-child(n + 3) { border-top-color: var(--bf-on-dark-line); }
  .divided-row-3 { grid-template-columns: 1fr; }
}
@media screen and (max-width: 600px) {
  .divided-row { grid-template-columns: 1fr; }
  .divided-row > * {
    border-left: 0 !important;
    padding: 1.15rem 0;
  }
  .divided-row > * + * { border-top: 1px solid var(--bf-border); }
  .band-dark .divided-row > * + * { border-top-color: var(--bf-on-dark-line); }
}

/* --- Step row -----------------------------------------------------------
   Numbered process steps, each under its own 2px rule so the row reads as
   a sequence. Works at any count - 3, 4 or 5 - because it is the framework
   grid doing the columns. */
.step { border-top: var(--bf-rule) solid var(--bf-dark); padding-top: 1.25rem; }
.band-dark .step { border-top-color: var(--bf-on-dark); }
.step-index {
  color: var(--bf-text-muted);
  display: block;
  font-family: var(--bf-font-heading);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  margin-bottom: 0.9rem;
}
.step h3 {
  font-size: 1.1875rem;
  margin: 0 0 0.5rem;
}
.step p {
  color: var(--bf-text-muted);
  font-size: 0.925rem;
  margin: 0;
}
.band-dark .step-index { color: var(--bf-on-dark-muted); }
.band-dark .step p { color: var(--bf-on-dark-soft); }

/* --- Footnote -----------------------------------------------------------
   Small print that still matters - standards, scope, caveats. Left rule
   instead of a tinted box, because a tinted box on this palette would be
   indistinguishable from a card. */
.footnote {
  border-left: var(--bf-rule) solid var(--bf-border);
  color: var(--bf-text-muted);
  font-size: 0.875rem;
  /* Margin, not padding, for the gutter: the padding is already spoken
     for by the gap between the left rule and the text. */
  margin: clamp(2rem, 3.5vw, 3rem) var(--bf-gutter) 0;
  max-width: 76ch;
  padding-left: 1.25rem;
}
.footnote strong { color: var(--bf-heading); }
.band-dark .footnote {
  border-left-color: var(--bf-on-dark-line);
  color: var(--bf-on-dark-muted);
}

/* --- Closing row --------------------------------------------------------
   Goes on a .divided-row to close a page: eyebrow, small heading, a line
   of copy, an arrow link. Sized down from a card deliberately - it is a
   sign-post row, not another offer.
   ----------------------------------------------------------------------- */
.endnote .eyebrow { margin-bottom: 0.85rem; }
.endnote h3 {
  font-size: 1.25rem;
  margin: 0 0 0.5rem;
}
.endnote p {
  color: var(--bf-text-muted);
  font-size: 0.925rem;
  margin: 0 0 1.35rem;
}


/* ==========================================================================
   6. HOME PAGE
   The two blocks below are the only styling unique to index.html.
   Everything else on that page is section 5 furniture.

   If either gets reused on an inner page, promote it up into section 5
   rather than copying it.
   ========================================================================== */

/* --- Fact strip ---------------------------------------------------------
   Sits directly beneath the hero and continues the charcoal before the
   page turns light, so the top of the site is one solid charcoal mass:
   header, hero, strip. One step lighter than the hero so the join reads.

   Note this is NOT a stat row - no figures, no counters. Four plain
   statements of fact, which is the register the rest of the site keeps. */
.fact-strip {
  background: var(--bf-dark-mid);
  border-top: 1px solid var(--bf-on-dark-line);
  color: var(--bf-on-dark-soft);
}
.fact-strip .divided-row > * + * { border-left-color: var(--bf-on-dark-line); }
.fact {
  align-items: start;
  display: flex;
  gap: 0.85rem;
}
.fact .icon {
  color: var(--bf-on-dark);
  margin-top: 0.15em;
}
.fact strong {
  color: var(--bf-on-dark);
  display: block;
  font-family: var(--bf-font-heading);
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: -0.005em;
}
.fact span {
  color: var(--bf-on-dark-muted);
  font-size: 0.85rem;
  line-height: 1.5;
}
@media screen and (max-width: 1140px) {
  .fact-strip .divided-row > *:nth-child(n + 3) { border-top-color: var(--bf-on-dark-line); }
}
@media screen and (max-width: 600px) {
  .fact-strip .divided-row > * + * { border-top-color: var(--bf-on-dark-line); }
}

/* --- Pull quote ---------------------------------------------------------
   The founder's line in the About band. Jost at display size with a
   hanging quote mark cut into the left margin - the one place on the site
   where type is allowed to be the whole design. */
/* framework.css styles a bare <blockquote> as a tinted, left-ruled callout.
   This is a display quote, not a callout, so all three of those are reset -
   including the background, which is the one people forget. */
.pull {
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
}
.pull p {
  color: var(--bf-heading);
  font-family: var(--bf-font-heading);
  font-size: clamp(1.35rem, 2.2vw, 1.85rem);
  font-weight: 400;
  letter-spacing: -0.015em;
  line-height: 1.32;
  margin: 0;
  position: relative;
  text-indent: -0.42em;   /* hangs the opening quote into the margin */
}
/* Standalone variant - when the quote is the whole band rather than a
   sidebar beside prose, it has to carry the band on its own. */
.pull-lg p { font-size: clamp(1.5rem, 2.9vw, 2.4rem); }

.pull footer {
  color: var(--bf-text-muted);
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  margin-top: 1.5rem;
  text-transform: uppercase;
}
/* The supporting list beneath the quote. */
.pull + .card-list { margin-top: clamp(1.75rem, 2.5vw, 2.25rem); }

/* No text-indent on narrow screens: the hung quote mark would be clipped
   by the viewport edge rather than sitting in a margin. */
@media screen and (max-width: 768px) {
  .pull p { text-indent: 0; }
}
