/* =========================================================================
   AUTHORITATIVE HEADER / LOGO SIZING — loaded LAST on every page.
   Ported from the Georgia design system's header-final.css, adapted
   red-forward for CT. Loads after every other stylesheet and after the
   inline <style> blocks are parsed, so it wins on cascade order without
   editing five files.

   The mark is a near-square badge (1391x1175, ~1.184:1), not a wide wordmark
   — it needs real height to read.

   DELTAS FROM GEORGIA (beyond mechanical class-for-class porting):

   1. Georgia's header surface is --off-white (light) — see that file's own
      "HEADER OUTLINE BUTTON" comment. CT's header surface is dark by design
      (tokens.css --color-bg-header-glass/-solid — originally --red-deep,
      repointed to --gray-ink in the 2026-08-04 gray-glass restyle; either
      way, dark). Because of that, two Georgia overrides were NOT ported:
        - ".site-header .btn--outline { color: var(--navy); ... }" — this
          existed only to fix navy-on-off-white contrast on Georgia's light
          header. CT's header is dark, so the base dark-context
          .btn--outline from components.css (white border/text, hover
          fills white / text flips to --color-btn-outline-hover-text)
          already reads correctly with no override needed.
        - ".site-header__phone { color: var(--navy); }" inside the
          @media (max-width: 1023px) mobile-meta block — same reason;
          removing it lets the phone number fall back to
          --color-text-inverse (white), correct against the dark header
          surface at every width, not just desktop.
      Per the porting brief, .btn--outline stays a DARK-CONTEXT-ONLY
      component in this system; no light-context variant is introduced.
   2. Georgia's "OUTLINE BUTTONS ON LIGHT BACKGROUNDS" section
      (.ig-feed__placeholder .btn--outline, navy-on-light) was dropped
      entirely for the same reason: it is a second light-context
      .btn--outline override, and the porting brief is explicit that
      .btn--outline must remain dark-context-only with no light variant
      created. If a CT page ever needs an outline button on a light
      section (e.g. an Instagram-feed placeholder), it should get its own
      class rather than reopening this exact selector — reopening it is
      what caused Georgia's "invisible button" bug in the first place.
   3. Several raw literals were replaced with token references:
        - rgba(255, 255, 255, N) → color-mix(in srgb, var(--white) N%, transparent)
        - rgba(13, 13, 13, N) → rgba(var(--near-black-rgb), N)   [hero photo scrim]
        - #F5A623 (star color)  → var(--color-rating-star)        [CT's verified gold]
        - var(--navy) / var(--white) on the "CL" review monogram →
          var(--color-bg-accent-navy) / var(--color-text-inverse) (semantic
          tokens, same visual role: navy fill, white text)
   4. The nav-sub dropdown panel and the mobile <details> nav group carried
      several var(--token, #hexFallback) pairs where the token name did not
      match any token in this design system (e.g. var(--color-border, ...),
      var(--color-heading, ...), var(--color-surface-alt, ...),
      var(--blue, ...), var(--weight-normal, ...)). Fixed to the real
      semantic token names that exist in tokens.css:
        --color-border        → --color-border-default
        --color-heading       → --color-text-heading
        --color-surface-alt   → --color-bg-page-alt
        --blue (hover accent) → --color-text-link-hover (CT's red-hover —
                                 red-forward accent for the dropdown's
                                 hover state, in place of Georgia's blue)
        --weight-normal       → --weight-regular
      All other var(--token, fallback) fallbacks were stripped since every
      referenced token now resolves for real; none of these were
      color-related design decisions, just broken references.
   ========================================================================= */

/* The base reset sets `img { max-width: 100% }` but never `height: auto`, so a
   constrained image kept its declared pixel height while its width shrank to
   the container — the blog rendered 1800x1013 photos as 260x1013 slivers.
   Element-selector specificity (0,0,0,1) means any component that genuinely
   needs a fixed height (.hero__photo, .video-thumb__img) still wins. */
img { height: auto; }

/* =========================================================================
   GLASS HEADER (2026-08-04 gray-glass restyle)

   The header markup now renders a real crest (.site-header__logo-mark,
   /assets/logo-header.png, 124x112) before the text lockup. Its height is
   kept in sync with --header-h (rather than a fixed 92/76/58px step) so it
   never overflows the compact fixed bar at any width.

   .site-header itself is sticky + solid in components.css (base layer);
   this file loads last and overrides it to fixed + translucent, per the
   file's own "AUTHORITATIVE HEADER" cascade-ordering role documented above.
   ========================================================================= */
:root {
  --header-h: 64px;
}
@media (max-width: 768px) {
  :root { --header-h: 56px; }
}

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--header-h);
  background: var(--color-bg-header-glass);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
/* Browsers without backdrop-filter support would otherwise show hero
   photo/text bleeding through an under-saturated 72%-alpha panel with no
   blur to soften it — fall back to the fully opaque surface instead. */
@supports not (backdrop-filter: blur(1px)) {
  .site-header {
    background: var(--color-bg-header-solid);
  }
}

/* Keep the bar's own box height pinned to --header-h: reduce its vertical
   padding so the 44px touch-target buttons/hamburger inside it (the
   tallest content) plus padding stay under --header-h at every width,
   rather than min-height being overridden by taller content. */
.site-header__bar {
  min-height: var(--header-h);
  padding-block: var(--space-1);
}

/* components.css stacks .site-header__meta (phone number over the "Free
   Estimate" button) in a column at >=1024px — 26px phone line + a 44px
   touch-target button + column gap comes to ~76px, well past the amount
   of vertical room --header-h leaves once .site-header__bar's padding is
   subtracted. min-height on the bar can't stop it: a taller child simply
   overrides it, which is exactly what pushed the bar to ~84px and let
   content spill out below the fixed 64px glass band. Below 1024px this is
   already a row (see the max-width:1023px meta block further down) — this
   just carries the same row treatment up to desktop so the phone and the
   button sit side by side (~44px tall) instead of stacked, fitting the
   fixed header at every width the meta block appears.

   Trade-off: a row is ~2x wider than the column it replaces (phone AND
   button side by side instead of stacked), and .site-nav is the only
   flexible sibling in the bar (flex:1, min-width:0) — so that width has to
   come out of the nav's box. .site-nav ul is nowrap, so if the bar can't
   spare enough room the nav text overflows its box and visibly overlaps
   this meta block instead of wrapping. .site-header__cta is shrunk here
   (not full .btn size) and the HEADER NAV FIT block below gives back the
   rest, specifically to buy back that room — see the comment there. */
@media (min-width: 1024px) {
  .site-header__meta {
    flex-direction: row;
    align-items: center;
    gap: var(--space-2);
  }
  .site-header__meta .site-header__cta {
    padding: 0.5rem 0.85rem;
    font-size: var(--text-sm);
    min-height: var(--touch-target-min);
  }
}

.site-header__logo-mark {
  height: calc(var(--header-h) - 12px);
  width: auto;
  display: block;
  flex: none;
}

/* Three header markup variants shipped: 8 city pages render the crest alone,
   5 render crest + tagline, and 20 render crest + wordmark + tagline. Hiding
   both the wordmark and the tagline collapses all three to one identical
   lockup, and frees the bar space that was making "About" collide with the
   phone number. Nothing is lost: "EST. 2001" appears in the hero proof chips
   ("25 Years in Business") and the footer NAP block. */
.site-header__logo-tag { display: none; }

/* Real gutter between the last nav item and the phone block. */
.site-header__bar {
  column-gap: var(--space-8);
}

/* Some page groups render a "The Hardwood Guys" text wordmark beside the crest
   and some don't, so the header lockup was a different width depending on which
   page you landed on. The crest already contains the wordmark graphically, so
   the text is redundant and costs ~180px of bar. Hidden visually, kept in the
   accessibility tree. NOTE: the built markup's visible wordmark span carries
   the class .site-header__logo-text (components.css "text-only logo
   fallback"), not .site-header__logo-word — this selector currently matches
   nothing live. Left in place rather than renamed/removed: renaming it to
   .site-header__logo-text would visually hide the wordmark, which the header
   spec requires to stay visible and on one line (see the rule below). */
.site-header__logo-word {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* WRAP FIX: .site-header__logo's max-width:260px (components.css) predates
   the crest image. The crest (52px tall + 8px gap at desktop) now eats a
   third of that budget, leaving ~200px for "The Hardwood Guys" at
   --text-lg/700 Playfair Display — which needs ~252px unwrapped — so it
   broke after "The" onto a second line and taller-than-intended the whole
   .site-header__logo row (52.8px vs. the crest's 52px), which is what fed
   into the bar-height overflow above. .site-header__logo is flex:none, so
   it never grows past its own content — the cap was only ever a ceiling,
   not something else depends on it — so it's safe to lift. nowrap on the
   text itself is kept as insurance if this box ever gets narrower again. */
.site-header__logo { max-width: none; }
.site-header__logo-text { white-space: nowrap; }

/* Nav stays on one row at desktop — wrapping to two lines pushed the bar to
   130px tall and looked broken. */
.site-nav ul {
  flex-wrap: nowrap;
  white-space: nowrap;
}

@media (max-width: 1100px) {
  .site-nav ul { gap: var(--space-4); }
}

/* components.css turns the full desktop nav on at 1024px, but the bar needs
   roughly 1200px to fit crest + 6 nav items + phone + hours + CTA. In the
   1024-1199 band the nav overflowed its flex item and printed straight over
   the phone number ("About"/"Blog" on top of the phone number).

   Below 1200px the NAV collapses to the hamburger — but the phone block and
   the Free Estimate button stay visible, because the phone is the primary
   conversion element on this site and tablet users should not lose it. */
@media (max-width: 1199px) {
  .site-nav { display: none; }
  .hamburger { display: inline-flex; }
  /* .site-nav was the flex:1 spacer. With it hidden, the phone and CTA
     collapse back against the logo, so push them to the right instead.
     .hamburger already carries margin-left:auto — leaving both auto margins
     in play splits the free space and strands a hole mid-bar, so the
     hamburger's is zeroed and the meta block owns the push. */
  .site-header__meta { margin-left: auto; }
  .hamburger { margin-left: 0; }
}

/* Last-resort guard: even if something else re-enables the nav early, never
   let it paint over a sibling. */
.site-nav { min-width: 0; }

/* ---------------------------------------------------------------------
   MOBILE ALIGNMENT — hero proof chips and CTAs

   On a narrow screen the fix is to stop letting content size itself: one
   column, every element the full width of the container. Chips, buttons and
   the form card then share both edges, which is what makes a stack look
   deliberate rather than accidental.

   Kept to <=560px. Above that the chips SHOULD flow inline -- full-width
   pills on a desktop hero would look absurd.
   --------------------------------------------------------------------- */
@media (max-width: 560px) {
  .proof-chips {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-2);
  }
  .proof-chip {
    width: 100%;
    justify-content: flex-start;
  }
  .hero__actions {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-3);
  }
  .hero__actions .btn {
    width: 100%;
    justify-content: center;
  }

  /* The CTA band reuses .hero__actions but nests it in a narrower centred
     .cta-band__inner, so width:100% resolved to a narrower box than the
     hero's buttons. Two button widths on one page reads as a mistake even
     when each pair is individually centred. Let the band's inner container
     run full width on mobile so every button on every page shares the same
     two edges. */
  .cta-band__inner { max-width: none; width: 100%; }
  .cta-band .hero__actions { width: 100%; }
}

/* ---------------------------------------------------------------------
   KEN BURNS — slow drift on hero photography

   A 20s scale from 1.00 to 1.08 with a slight pan. Long and shallow on
   purpose: a hero that visibly moves competes with the headline and the
   phone number, which are the only two things on this page that matter.
   The intent is that a visitor feels the image is alive without being able
   to point at the motion.

   transform only -- it runs on the compositor, so it costs no layout or
   paint work and will not fight the LCP image for main-thread time.

   Disabled entirely under prefers-reduced-motion. Vestibular disorders are
   real and a slow zoom is a common trigger; the site loses nothing without
   it.
   --------------------------------------------------------------------- */
/* NOTE (2026-08-04 gray-glass restyle): the built site's real homepage hero
   photo is <img class="hero__media">, not a `.hero__photo img` wrapper —
   .hero__photo and .hero--lite never appear in site/*.html (checked against
   all 40 pages), so this thg-kenburns rule no longer touches .hero__media.
   The live Ken Burns effect for .hero__media now lives in components.css as
   @keyframes hwg-kenburns (new timing/scale requested by the owner), so it
   isn't fought over by two animations on the same element — header.css
   loads last and would otherwise silently win the cascade. thg-kenburns is
   left in place for the unused .hero__photo/.hero--lite selectors in case
   a future template variant reintroduces them. */
@keyframes thg-kenburns {
  from { transform: scale(1) translate3d(0, 0, 0); }
  to   { transform: scale(1.08) translate3d(-1.5%, -1%, 0); }
}
.hero__photo img,
.hero--lite .hero__media {
  will-change: transform;
  animation: thg-kenburns 20s ease-out forwards;
  transform-origin: center center;
}
/* The scrim must not drift with the photo or the edges would show. */
.hero__scrim { animation: none; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .hero__photo img,
  .hero--lite .hero__media {
    animation: none;
    transform: none;
    will-change: auto;
  }
}

/* ---------------------------------------------------------------------
   MOBILE HEADER — stop wasting the bar

   components.css sets .site-header__meta{display:none} at base and only
   restores it at min-width:1024px, so on a phone the header was a logo, a
   hamburger, and a wide strip of dead space. The most valuable strip of a
   contractor's site was showing nothing actionable.

   Below 1024px the phone number and a tap-to-text button move into that gap.
   Hours and the Free Estimate button stay hidden -- they do not fit, and the
   sticky bottom bar already carries Free Estimate.

   Yes, this duplicates the bottom bar. That is deliberate: the bar is fixed
   to the bottom of the viewport and a first-time visitor's eye starts at the
   top. Tap-to-call in the header converts people who never scroll.
   --------------------------------------------------------------------- */
@media (max-width: 1023px) {
  .site-header__meta {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
    margin-left: auto;
    margin-right: var(--space-2);
  }
  .site-header__meta .site-header__hours { display: none; }
  .site-header__meta .btn--outline {
    padding: 0.45rem 0.7rem;
    font-size: var(--text-sm);
    min-height: 44px;
  }
  /* The label is too wide next to a phone number on a narrow screen; the
     speech-bubble icon carries the meaning and the link is still labelled
     for screen readers. */
  .site-header__meta .btn--outline .btn__label { display: none; }
  .site-header__phone {
    font-size: var(--text-base);
    font-weight: var(--weight-bold);
    /* No color override here (Georgia set --navy for its light header).
       CT's header is a dark surface (--color-bg-header-glass/-solid,
       --gray-ink) at every width, so the base .site-header__phone rule in
       components.css — which already sets color: var(--color-text-inverse)
       — is correct as-is. */
    white-space: nowrap;
  }
}
@media (max-width: 359px) {
  /* Only the very narrowest phones drop the text button. At normal phone
     widths the logo, number, icon button and hamburger all fit, and
     click-to-text is worth more than the breathing room. */
  .site-header__meta .btn--outline { display: none; }
}


/* =========================================================================
   HEADER DROPDOWNS

   No JavaScript. Desktop opens on :hover and :focus-within; mobile is a
   native <details>.
   ========================================================================= */

.site-nav .has-sub { position: relative; }

.site-nav .has-sub > a {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
}

.nav-caret {
  width: 0.7em;
  height: 0.7em;
  flex: none;
  transition: transform var(--motion-base) var(--motion-ease);
}

.site-nav .has-sub:hover > a .nav-caret,
.site-nav .has-sub:focus-within > a .nav-caret { transform: rotate(180deg); }

.site-nav .nav-sub {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 60;
  display: block;
  min-width: 16rem;
  margin: 0;
  padding: var(--space-2);
  background: var(--color-bg-header-solid);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-base);
  box-shadow: var(--shadow-lg);

  /* Hidden with opacity+visibility rather than display:none so the caret
     rotation and fade can animate, and so the submenu keeps its box for
     :focus-within to land on. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity .16s ease, transform .16s ease, visibility .16s;
}

/* The 8px gap between the nav link and the panel would close the menu the
   moment the pointer crossed it. This invisible bridge spans the gap. */
.site-nav .nav-sub::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -10px;
  height: 10px;
}

.site-nav .has-sub:hover > .nav-sub,
.site-nav .has-sub:focus-within > .nav-sub {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* The parent <ul> is a nowrap flex row; the submenu must not inherit that. */
.site-nav .nav-sub { white-space: normal; }
.site-nav .nav-sub li { display: block; }

/* Panel background is now --color-bg-header-solid (--gray-ink), so link
   color must be the dark-surface pairing (--color-nav-link, white,
   16.64:1) rather than --color-text-heading — since --color-text-heading
   is now --navy, which is nearly unreadable against gray-ink (1.51:1-class
   dark-on-dark; navy is a light-surface heading color, see tokens.css). */
.site-nav .nav-sub a {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1.35;
  color: var(--color-nav-link);
  text-decoration: none;
}

/* No colour swap on the dark panel (blue/red-hover both fail against
   gray-ink — see tokens.css traps) — hover/focus is underline only, same
   rule as every other dark-surface nav link in this system. */
.site-nav .nav-sub a:hover,
.site-nav .nav-sub a:focus-visible {
  color: var(--color-nav-link-hover);
  text-decoration: underline;
}

/* Right-hand dropdowns would otherwise overflow the viewport on a 1024px
   screen where the nav sits far right. */
@media (max-width: 1200px) {
  .site-nav .has-sub:nth-last-child(n + 5) .nav-sub { left: auto; right: 0; }
}

/* ---- mobile: native <details> ---------------------------------------- */

.site-header__mobile-nav .mnav-group details { border: 0; }

/* Match .site-header__mobile-nav a exactly. A <summary> is not an <a>, so it
   would otherwise inherit the body's dark text colour and render invisible
   against the dark red panel -- the row would be there and tappable, just
   unreadable. */
.site-header__mobile-nav .mnav-group summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--color-text-inverse);
  border-bottom: 1px solid color-mix(in srgb, var(--white) 18%, transparent);
  cursor: pointer;
  list-style: none;
}

/* An open group owns its children, so the divider under the summary would
   read as a separator between the label and its own list. */
.site-header__mobile-nav .mnav-group details[open] > summary { border-bottom-color: transparent; }

/* Safari draws its own disclosure triangle unless this is killed. */
.site-header__mobile-nav .mnav-group summary::-webkit-details-marker { display: none; }

.site-header__mobile-nav .mnav-group details[open] > summary .nav-caret {
  transform: rotate(180deg);
}

.site-header__mobile-nav .mnav-group summary .nav-caret {
  width: 0.7em;
  height: 0.7em;
  opacity: .75;
}

/* The list's last link and the meta block's top rule would otherwise draw
   two horizontal lines close together with nothing between them, which
   reads as an empty menu row rather than a section break. One line is the
   separator. */
.site-header__mobile-nav > ul > li:last-child > a,
.site-header__mobile-nav > ul > li:last-child > details:not([open]) > summary {
  border-bottom-color: transparent;
}

.site-header__mobile-nav .mnav-group ul {
  margin: var(--space-2) 0 var(--space-3);
  padding-left: var(--space-4);
  border-left: 2px solid color-mix(in srgb, var(--white) 22%, transparent);
}

.site-header__mobile-nav .mnav-group ul a {
  font-size: var(--text-sm);
  font-weight: var(--weight-regular);
  opacity: .92;
}

/* =========================================================================
   HEADER NAV FIT

   The desktop nav needs to fit crest + 6 nav items + phone + hours + CTA
   inside a .container capped at var(--container-max). Four changes,
   scoped to min-width:1024px and up (the 1024-1199 band is already handled
   above, where the nav collapses to the hamburger):

   1. Nav sizing: 16px items, 10px item gap (was 14px — see #4), and a caret
      that costs 0.62em instead of 0.7em + 0.3em of gap.
   2. Header bar gap drops from --space-6 to --space-5, handing the nav back
      the room it needs. Padding is left alone on purpose so the logo stays
      flush with the page content below it.
   3. column-gap tightens further, from --space-5 to --space-3, on top of #2.
   4. Item gap above tightens from 14px to 10px (6 gaps × 4px = 24px back).

   #3 and #4 exist for a reason not on this list: the "row-conversion" fix in
   the .site-header__meta block near the top of this file (search "components
   .css stacks .site-header__meta") made that block ~135px wider at this same
   breakpoint (phone + button side by side instead of stacked), and .site-nav
   is the only flexible sibling in the bar (flex:1, min-width:0) — so that
   width has to come out of the nav. .site-nav ul is forced nowrap (see "Nav
   stays on one row at desktop" above), so any shortfall doesn't wrap, it
   overlaps the meta block instead — confirmed live: at 1280px, with only the
   meta fix and none of this section's savings, .site-nav needed 484px but
   was only given 467.5px, and "Contact" visibly overlapped the phone number.
   #3 + #4 hand back 16px (column-gap) + 24px (item gap) = 40px, clearing
   that 16.5px shortfall with a real margin (checked live: ~23px to spare at
   1280px, the narrowest width the full desktop nav renders at).
   ========================================================================= */

@media (min-width: 1024px) {
  .site-header__bar { gap: var(--space-5); column-gap: var(--space-3); }

  .site-nav a { font-size: 1rem; }
  .site-nav ul { gap: 10px; }
  .site-nav .has-sub > a { gap: 0.15em; }
  .nav-caret { width: 0.62em; height: 0.62em; }
}

/* =========================================================================
   HEADER LOGO — belt and braces

   Sizing by CLASS alone assumes every page remembers the class. This rule
   targets the image by its position in the header instead, so a forgotten
   class costs a few pixels of accuracy rather than the entire layout. The
   .site-header__logo-mark rules above still win where they apply -- they are
   more specific -- so nothing that works today changes.
   ========================================================================= */
.site-header__logo img { height: calc(var(--header-h) - 12px); width: auto; display: block; }

/* =========================================================================
   HERO PHOTO LAYER

   Markup pattern <div class="hero__media"><img></div>. The ::after carries
   the scrim. It has to be a pseudo-element rather than a background on
   .hero__media, because an absolutely positioned <img> covers its parent's
   background but not a later-painted pseudo-element.
   ========================================================================= */

.hero--photo .hero__media { background: var(--color-bg-header-solid); }

.hero--photo .hero__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.hero--photo .hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(var(--near-black-rgb), .66), rgba(var(--near-black-rgb), .5));
}


/* =========================================================================
   STICKY BAR — 24/7 flag on Text

   Texts are answered round the clock by the scheduling assistant; calls are
   not. The bar is what a visitor sees at 9pm on a phone, so the one channel
   that is actually open at 9pm says so. Kept to a sub-label rather than a
   badge, because the bar has three targets across a narrow viewport and
   anything larger starts eating the tap area.
   ========================================================================= */
.mobile-cta-bar .mcb-stack {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.1;
}
.mobile-cta-bar .mcb-stack small {
  font-size: 0.62rem;
  font-weight: var(--weight-semibold);
  opacity: .82;
  letter-spacing: .02em;
}


/* =========================================================================
   REVIEW BADGE — logos instead of a list of names

   The marks are inline SVG, not images from a third-party CDN, so this adds
   zero third-party requests to the homepage.
   ========================================================================= */

.review-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: nowrap;
  background: transparent;
  border: 0;
  padding: 0;
  box-shadow: none;
}

.review-badge__stars {
  /* Georgia hardcoded #F5A623 here, bypassing the token system. CT's star
     color is --color-rating-star (--gold, contrast-verified against both
     white and --red-deep in tokens.css); reusing it here keeps the review
     badge and the hero proof chips visually and semantically consistent. */
  color: var(--color-rating-star);
  font-size: 1.35rem;
  letter-spacing: .08em;
  line-height: 1;
}

.review-badge__logos {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding-left: var(--space-3);
  border-left: 1px solid var(--color-border-default);
}

.rb-logo { display: block; height: 24px; width: auto; }
.rb-logo--yelp { height: 20px; }

/* No usable third-party mark exists for this one, so it is set as a
   monogram so it reads as a logo beside two real ones rather than as a
   stray abbreviation. Navy (the supporting accent) rather than red, so it
   doesn't compete with the red CTAs elsewhere on the page. */
.rb-cl {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 24px;
  padding: 0 .35rem;
  border-radius: 5px;
  background: var(--color-bg-accent-navy);
  color: var(--color-text-inverse);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .06em;
}

/* One line on a phone too -- stars and three small logos fit inside a
   narrow viewport, so it wraps under the heading as a single row rather
   than stacking. */
@media (max-width: 560px) {
  .review-badge { gap: var(--space-2); }
  .review-badge__stars { font-size: 1.15rem; }
  .review-badge__logos { gap: var(--space-2); padding-left: var(--space-2); }
  .rb-logo { height: 21px; }
  .rb-logo--yelp { height: 21px; }
}

/* ---------------------------------------------------------------------
   NARROW-SCREEN HEADER FIT — CT addition, not in Georgia.
   The text logo ("The Hardwood Guys" in the heading serif) next to the
   crest, plus the header phone plus the 44px hamburger, overflows a
   375px viewport. Overflow here is not cosmetic: it widens the layout
   viewport, the page zooms out, and the fixed mobile CTA bar lands
   below the visual viewport (found live 2026-08-04). Shrink, then drop
   the wordmark entirely below 480px — the crest alone is already an
   established lockup in this system (8 city pages render crest-alone,
   see components.css), so losing the redundant text at the narrowest
   widths costs nothing and is what buys back the room the hamburger
   needs (confirmed live 2026-08-05: with the crest image present, the
   hamburger's rect ran to x=427 on a 375px viewport — fully off-canvas
   and untappable — until the wordmark was dropped here).
   --------------------------------------------------------------------- */
@media (max-width: 599px) {
  .site-header__logo-text { font-size: var(--text-md); }
  .site-header__logo-tag { display: none; }
  .site-header__meta .site-header__phone { font-size: var(--text-sm); }
}
@media (max-width: 479px) {
  .site-header__logo-text { display: none; }
  .site-header__bar { gap: var(--space-2); }
  .site-header__meta .site-header__phone { font-size: var(--text-xs); }
}

/* ---------------------------------------------------------------------
   HERO PHOTO AS <img> — CT addition. components.css styles .hero__media
   as a background DIV (gradient + texture fallback); the build emits a
   real <img class="hero__media"> plus a .hero__scrim overlay, exactly
   like Georgia's built pages (which patched this inline per page).
   Without these rules the image doesn't cover the hero box and the
   title runs onto white (found live on mobile 2026-08-04).
   --------------------------------------------------------------------- */
img.hero__media {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(rgba(var(--near-black-rgb), 0.55),
                              rgba(var(--near-black-rgb), 0.45));
}
