/* ─── Real Deal Builders / Site CSS ───────────────────────────────────────────
   Everything except the 3D hero (that lives in hero.css). Base reset, shared
   type, buttons, the homepage marketing sections, the interior page shell
   (header, breadcrumb, page hero, service and city content), and the footer.
   No border-radius anywhere: the brand is square and industrial. */

/* ─── Reset ─────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
/* scroll-padding-top keeps an anchor target clear of the sticky header. Without
   it every in page jump link, and there are ten on each page in the region and
   FAQ navigation, lands with its heading hidden underneath the header. The
   --rdb-nav-h token already carries the header height. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
       scroll-padding-top: calc(var(--rdb-nav-h) + 16px); }
body {
    margin: 0;
    font-family: var(--rdb-body);
    color: var(--rdb-text);
    background: var(--rdb-light);
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: clip;
}
img, video, canvas { display: block; max-width: 100%; height: auto; }
/* .18s ease is the house duration for colour, already retyped in 14 other
   rules. Putting it on the base `a` brings the ~20 link hover rules that
   currently snap (nav, breadcrumbs, footer, panel rows, hero trust chips) into
   the convention the rest of the file already follows. */
a { color: var(--rdb-accent); text-decoration: none; transition: color .18s ease; }
a:hover { color: var(--rdb-accent-hover); }

/* ─── Shared shell and type ───────────────────────────────────────────────────── */
.rdb-section { padding: var(--rdb-pad-y) var(--rdb-pad-x); }
.rdb-wrap { max-width: var(--rdb-col); margin: 0 auto; }

.rdb-eyebrow {
    font: 600 12px/1 var(--rdb-body);
    letter-spacing: .26em;
    text-transform: uppercase;
    color: var(--rdb-accent);
    margin: 0 0 18px;
    display: inline-flex; align-items: center; gap: 12px;
}
.rdb-eyebrow::before {
    content: ""; width: 16px; height: 10px; background: var(--rdb-accent);
    /* roofline peak, pulled from the logo mark */
    clip-path: polygon(50% 0, 100% 100%, 50% 66%, 0 100%);
}
/* The floor is 30px, not 38px. Both h1 rules floor at 34px and only overtake
   4.8vw where 5vw = 38px, i.e. at 760px wide, so a 38px floor here made every
   h2 on the site 12% LARGER than the h1 above it from 320px to 760px. Measured
   at 360: h1 34px, h2 38px. The clamp is unchanged from 792px up, so desktop
   is untouched. */
.rdb-h2 {
    margin: 0;
    font-family: var(--rdb-serif);
    font-weight: 800;
    font-size: clamp(30px, 4.8vw, 68px);
    line-height: 1.0;
    letter-spacing: -.01em;
    text-transform: uppercase;
    text-wrap: balance;
    color: var(--rdb-dark);
}
/* .rdb-h3 removed 2026-08-25: defined here but applied by no page and no
   build.py template. The live card headings are .rdb-tile__t and
   .rdb-promise__t below. */
.rdb-lead {
    font-size: clamp(17px, 1.7vw, 20px);
    color: var(--rdb-text);
    max-width: 60ch;
}
.rdb-p { margin: 0 0 1em; max-width: 68ch; }
.rdb-p:last-child { margin-bottom: 0; }

/* ─── Buttons ─────────────────────────────────────────────────────────────────── */
.rdb-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    padding: 16px 26px;
    font: 700 13px/1 var(--rdb-body);
    letter-spacing: .16em; text-transform: uppercase;
    border: 2px solid transparent; cursor: pointer;
    transition: background .18s ease, color .18s ease, border-color .18s ease,
                transform .16s ease-out;
}
/* Press feedback. The quote and call buttons were the only frequently pressed
   controls on the site with no response to a press, while the hamburger
   (.site-header__menu:active), the drawer close and the mobile action bar all
   had one. transform only, so it composites and cannot shift layout. */
.rdb-btn:active { transform: scale(.97); }
.rdb-btn { -webkit-tap-highlight-color: transparent; }

/* Press feedback for the full-surface tappable cards.
   .rdb-btn, the drawer links and both .rdb-mbar buttons already had an :active;
   the tiles, cards, chips and location cells did not, so on a phone the only
   press response was the browser's default blue-grey tap flash. These are the
   primary mobile navigation targets: 16 tiles on the homepage, 48 location
   cells, and a card grid on every service, city and money page.
   Shallower and faster than the button's .97/.16s on purpose. This tier is
   tapped tens of times a session, where motion has to sit near the threshold
   of perception or it starts to feel slow. */
a.rdb-tile:active,
a.rdb-card:active,
a.rdb-chip:active,
a.rdb-loc:active,
a.rdb-notice__card:active { transform: scale(.98); }
.rdb-tile, .rdb-card, .rdb-chip, .rdb-loc, .rdb-notice__card {
    -webkit-tap-highlight-color: transparent;
}
.rdb-btn--accent { background: var(--rdb-accent); color: #fff; }
.rdb-btn--accent:hover { background: var(--rdb-accent-hover); color: #fff; }
.rdb-btn--dark { background: var(--rdb-dark); color: #fff; }
.rdb-btn--dark:hover { background: #fff; color: var(--rdb-dark); }
.rdb-btn--outline { border-color: currentColor; color: inherit; background: transparent; }
.rdb-btn--outline:hover { background: currentColor; }
/* The button background fades over .18s but the label inside it used to snap,
   which read as a flash on every outline button on every page. This finishes
   the existing transition rather than adding a new one. */
.rdb-btn--outline span { transition: color .18s ease; }
.rdb-btn--outline:hover span { color: var(--rdb-dark); }

/* Visible focus for keyboard users */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
    outline: 2px solid var(--rdb-accent); outline-offset: 3px;
}
/* The accent ring only works on the light grounds. Measured against the colour
   actually painted behind each focusable element:
       #C0322B on the .rdb-cta band (#C0322B)  1.00:1  invisible
       #C0322B on --rdb-dark        (#14273D)  2.69:1  fails 1.4.11
       #C0322B on --rdb-ink         (#122236)  2.85:1  fails 1.4.11
       #C0322B on --rdb-warm        (#EAE2D2)  4.37:1  passes
       #C0322B on --rdb-light       (#F4EFE3)  4.90:1  passes
       #C0322B on white             (#ffffff)  5.63:1  passes
   On a money page that was 59 of 116 focusable elements, the whole footer link
   set included. White scores 5.63 / 15.14 / 16.06 on the three failing grounds,
   so one override covers all of them. */
.rdb-cta :focus-visible,
.rdb-footer :focus-visible,
.rdb-midcta :focus-visible,
.rdb-services :focus-visible,
.rdb-pillars :focus-visible,
.rdb-promises :focus-visible,
.rdb-fhero :focus-visible,
.rdb-drawer :focus-visible,
.rdb-mbar :focus-visible,
.site-header :focus-visible,
.site-nav__panel :focus-visible,
.rdb-skip:focus-visible {
    outline-color: #fff;
}

/* Skip link. Off-canvas via clip-path rather than left: -9999px, which gave
   11,279px of horizontal scroll under dir="rtl". */
.rdb-skip {
    position: absolute; left: 0; top: 0; z-index: 10000;
    clip-path: inset(50%);
    padding: 14px 22px; background: var(--rdb-accent); color: #fff;
    font: 700 13px/1 var(--rdb-body); letter-spacing: .16em; text-transform: uppercase;
}
.rdb-skip:focus { clip-path: none; }

/* ─── Interior page header (solid sticky nav; home uses the hero header) ───────── */
.site-header {
    position: sticky; top: 0; z-index: 900;
    background: var(--rdb-ink);
    border-bottom: 1px solid rgba(255,255,255,.08);
}
.site-header__inner {
    max-width: var(--rdb-col); margin: 0 auto;
    padding: 14px var(--rdb-pad-x);
    display: flex; align-items: center; justify-content: space-between; gap: 20px;
}
.site-header__brand {
    font-family: var(--rdb-display); font-weight: 800; text-transform: uppercase;
    letter-spacing: .04em; font-size: 22px; color: #fff;
}
.site-header__brand:hover { color: #fff; }
.site-nav { display: flex; align-items: center; gap: clamp(14px, 2.4vw, 30px); }
.site-nav a {
    font: 600 13px/1 var(--rdb-body); letter-spacing: .14em; text-transform: uppercase;
    color: rgba(255,255,255,.72);
}
/* 33px targets instead of 13px. Scoped to the top level only: `.site-nav a`
   would also catch `.site-nav__panel a` (sized to 44px rows) and
   `.site-nav__phone` (11px 16px), and at (0,1,1) it would outrank both.
   The links pass 2.5.8 either way through the spacing exception, adjacent
   centres sit ~96px apart, but 13px is a mean thing to ask a mouse to hit
   inside a 65px header with this much room to spare. No visual change: the
   header is a flex row and the links were already vertically centred. */
.site-nav > a:not(.site-nav__phone),
.site-nav__group > a { padding-block: 10px; }
.site-nav a:hover, .site-nav a[aria-current="page"] { color: var(--rdb-accent-on-dark); }
.site-nav__phone {
    background: var(--rdb-accent); color: #fff !important;
    padding: 11px 16px; letter-spacing: .1em !important;
}
.site-nav__phone:hover { background: var(--rdb-accent-hover); }

/* Dropdown groups (Services, Locations). Pure CSS: reveal on hover or
   focus-within. The top-level anchor still navigates to the hub, so mobile
   users tap through to the hub page directly, no JS required. */
.site-nav__group { position: relative; display: inline-flex; align-items: center; }
.site-nav__caret { font-size: 10px; opacity: .5; margin-left: 4px; transition: transform .15s ease; }
.site-nav__group:hover .site-nav__caret,
.site-nav__group:focus-within .site-nav__caret { opacity: 1; transform: rotate(-180deg); }
.site-nav__panel {
    position: absolute;
    top: 100%;
    left: -14px;
    min-width: 260px;
    background: var(--rdb-ink);
    border-top: 3px solid var(--rdb-accent);
    padding: 8px 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity .15s ease, transform .15s ease, visibility .15s;
    box-shadow: 0 12px 28px rgba(0,0,0,.28);
    z-index: 40;
}
.site-nav__group:hover .site-nav__panel,
.site-nav__group:focus-within .site-nav__panel {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.site-nav__panel a {
    display: flex; align-items: center; gap: 12px;
    padding: 11px 20px;
    color: rgba(255,255,255,.72) !important;
    font: 600 13px/1.3 var(--rdb-body) !important;
    letter-spacing: .04em !important;
    text-transform: none !important;
    white-space: nowrap;
}
.site-nav__panel a:hover { background: var(--rdb-dark); color: var(--rdb-accent-on-dark) !important; }
.site-nav__panel-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; flex: 0 0 22px;
    color: var(--rdb-accent);
    opacity: .78;
    transition: opacity .15s ease, transform .15s ease;
}
.site-nav__panel-ico svg { width: 22px; height: 22px; stroke-width: 1.7; }
.site-nav__panel a:hover .site-nav__panel-ico { opacity: 1; transform: translateX(1px); }
.site-nav__count { margin-left: auto; color: rgba(255,255,255,.62); font-size: 11px; }
/* The catch-all link closes the panel rather than opening it, so it reads as an
   escape hatch after the real choices instead of intercepting the first row. */
.site-nav__panel-all {
    border-top: 1px solid rgba(255,255,255,.12);
    margin-top: 6px;
    padding-top: 14px !important;
    color: var(--rdb-accent) !important;
    text-transform: uppercase !important;
    letter-spacing: .14em !important;
    font-size: 11px !important;
    font-weight: 700 !important;
}
.site-nav__panel-all:hover { color: #fff !important; background: transparent !important; }

/* Hamburger: three lines that morph into an X on aria-expanded. Includes a
   press animation so the tap has real tactile feedback. */
.site-header__menu {
    display: none;
    background: transparent; border: 0;
    color: #fff; padding: 8px; margin-left: 8px;
    cursor: pointer;
    align-items: center; justify-content: center;
    transition: transform .15s ease, color .18s ease;
    -webkit-tap-highlight-color: transparent;
}
.site-header__menu:hover { color: var(--rdb-accent); }
.site-header__menu:active { transform: scale(.9); }
/* Constantly spinning gear that transforms to an X on tap. Both the gear
   teeth and the X are separate <svg> elements stacked in the same square.
   The gear spins in idle; on aria-expanded=true it fades out while the X
   fades in. SVG-on-<svg> transforms are reliable across browsers (whereas
   CSS transforms on <g> children have inconsistent transform-box behavior). */
.rdb-hamb {
    position: relative;
    display: inline-flex;
    width: 26px; height: 26px;
    color: inherit;
}
.rdb-hamb__teeth,
.rdb-hamb__x {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    overflow: visible;
    transform-origin: 50% 50%;
    transition: opacity .3s ease, transform .35s cubic-bezier(.5, 0, .2, 1.3);
}
.rdb-hamb__teeth {
    opacity: 1;
    animation: rdbGearSpin 6s linear infinite;
}
@keyframes rdbGearSpin { to { transform: rotate(360deg); } }
.rdb-hamb__x {
    opacity: 0;
    transform: scale(.3) rotate(-90deg);
}
[aria-expanded="true"] .rdb-hamb__teeth {
    opacity: 0;
    animation: none;
    transform: scale(.4);
}
[aria-expanded="true"] .rdb-hamb__x {
    opacity: 1;
    transform: scale(1) rotate(0deg);
    transition-delay: .1s;
}
@media (prefers-reduced-motion: reduce) {
    .rdb-hamb__teeth { animation: none; }
}

/* .rdb-hero__menu, .rdb-hero__nav and .rdb-hero__phone were removed 2026-08-25.
   Zero matches in build.py and zero in any built page: the homepage uses
   .site-header like every other page. The block also carried a
   border-radius: 999px and a backdrop-filter, both against standing rules,
   in a render blocking file. */

@media (max-width: 720px) {
    .site-header__menu { display: inline-flex; }
    .site-nav__group { display: none; }
    /* Hide all nav items on mobile including the phone. The sticky bottom bar
       and the drawer both provide the call action, so this one is redundant. */
    .site-nav a { display: none; }
    .site-nav { display: none; }
    .site-header__inner { justify-content: space-between; }

}

/* ─── Mobile drawer menu (opened by the hamburger) ──────────────────────────
   The drawer stays in the DOM at all times on mobile but is hidden with
   visibility + pointer-events so both open AND close can transition. The
   nav items stagger in with per-index transition delays, then reverse
   quickly on close. */
.rdb-drawer { display: none; }
@media (max-width: 720px) {
    .rdb-drawer {
        display: block;
        position: fixed; inset: 0;
        /* 950, not 90. The sticky header is 900 and .rdb-mbar is 100, and
           all three are body children with no intervening stacking context,
           so at 90 the open drawer was painted under both: its own close
           button sat behind the header and its call CTA behind the bar. */
        z-index: 950;
        pointer-events: none;
    }
    .rdb-drawer[data-open], .rdb-drawer:target { pointer-events: auto; }
    /* HANDOFF.md already documents that the bar hides behind the drawer.
       It never did; this is the rule that makes the documentation true.
       Slides out on the same curve as the drawer sliding in, rather than
       vanishing mid-sequence, which is what display:none did here. */
    body[data-menu-open] .rdb-mbar {
        transform: translateY(100%);
        visibility: hidden;
        transition: transform .25s cubic-bezier(.4,0,.2,1), visibility 0s .25s;
    }
}
.rdb-drawer__scrim {
    position: absolute; inset: 0;
    background: rgba(0,0,0,.55);
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s ease;
}
.rdb-drawer[data-open] .rdb-drawer__scrim , .rdb-drawer:target .rdb-drawer__scrim { opacity: 1; pointer-events: auto; }
.rdb-drawer__panel {
    position: absolute; top: 0; bottom: 0; right: 0;
    width: min(320px, 88vw);
    background: var(--rdb-ink);
    color: rgba(255,255,255,.72);
    display: flex; flex-direction: column;
    box-shadow: -4px 0 32px rgba(0,0,0,.5);
    transform: translate3d(100%, 0, 0);
    /* visibility lives here, not on .rdb-drawer. The comment on the drawer used
       to claim it was hidden with visibility plus pointer-events, but only the
       pointer-events half was ever written, leaving 31 reachable focus stops
       inside a closed drawer on every phone width page.
       Putting it on the ancestor instead broke the slide: a non-rendered element
       does not run transitions, so the transform never left 100%. On the same
       element as the transform, the two coordinate and the delay holds the panel
       visible for exactly as long as the slide out takes. */
    visibility: hidden;
    transition: transform .32s cubic-bezier(.4,0,.2,1), visibility 0s .32s;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    will-change: transform;
}
.rdb-drawer[data-open] .rdb-drawer__panel, .rdb-drawer:target .rdb-drawer__panel {
    transform: translate3d(0, 0, 0);
    visibility: visible;
    transition: transform .32s cubic-bezier(.4,0,.2,1), visibility 0s 0s;
}
.rdb-drawer__head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 18px 22px;
    border-bottom: 1px solid rgba(255,255,255,.1);
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity .3s ease .1s, transform .3s ease .1s;
}
.rdb-drawer[data-open] .rdb-drawer__head , .rdb-drawer:target .rdb-drawer__head { opacity: 1; transform: translateY(0); }
.rdb-drawer__brand {
    font-family: var(--rdb-display); font-weight: 800;
    text-transform: uppercase; letter-spacing: .1em;
    font-size: 14px; color: #fff;
}
.rdb-drawer__close {
    background: transparent; border: 0; color: #fff;
    cursor: pointer; padding: 6px;
    display: inline-flex; align-items: center; justify-content: center;
    transition: transform .15s ease, color .18s ease;
    -webkit-tap-highlight-color: transparent;
}
.rdb-drawer__close:hover { color: var(--rdb-accent); }
.rdb-drawer__close:active { transform: scale(.9) rotate(90deg); }
.rdb-drawer__nav { flex: 1; padding: 8px 0; }
.rdb-drawer__link,
.rdb-drawer__group > summary {
    display: block;
    padding: 16px 22px;
    color: #fff;
    font-family: var(--rdb-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .1em;
    font-size: 14px;
    text-decoration: none;
    border-bottom: 1px solid rgba(255,255,255,.06);
    cursor: pointer;
    list-style: none;
    position: relative;
    /* padding-inline, not padding-left: the :hover below moves padding-RIGHT,
       so the old declaration transitioned a property nothing changed and the
       26px indent snapped while the colour faded. */
    transition: color .18s ease, background .18s ease, padding-inline .18s ease;
    -webkit-tap-highlight-color: transparent;
}
.rdb-drawer__link:hover,
.rdb-drawer__group > summary:hover { color: var(--rdb-accent); padding-right: 26px; }
.rdb-drawer__link:active,
.rdb-drawer__group > summary:active { background: rgba(192,50,43,.14); }
.rdb-drawer__group > summary::-webkit-details-marker { display: none; }
.rdb-drawer__group > summary::after {
    content: "+"; position: absolute; right: 22px; top: 50%;
    transform: translateY(-50%);
    color: var(--rdb-accent); font-weight: 400; font-size: 22px;
    transition: transform .25s ease;
    line-height: 1;
}
/* Same bug wearing a different hat. This rotated a minus sign 180 degrees, which
   is invisible by definition, so the .25s transition above ran and showed
   nothing. Keep the plus, quarter turn, and the declared transition earns itself. */
.rdb-drawer__group[open] > summary::after { transform: translateY(-50%) rotate(45deg); }
.rdb-drawer__sub { background: rgba(0,0,0,.24); padding: 4px 0 12px; }
.rdb-drawer__sub a {
    display: flex; justify-content: flex-start; align-items: center;
    gap: 14px;
    padding: 12px 22px;
    color: rgba(255,255,255,.72);
    font-family: var(--rdb-body);
    font-weight: 500;
    font-size: 14px;
    letter-spacing: .04em;
    text-transform: none;
    text-decoration: none;
    transition: color .18s ease, background .18s ease, padding-left .18s ease;
    -webkit-tap-highlight-color: transparent;
}
.rdb-drawer__sub a:hover { color: var(--rdb-accent); background: rgba(255,255,255,.04); padding-left: 30px; }
.rdb-drawer__sub a:active { background: rgba(192,50,43,.16); }
.rdb-drawer__sub a span { color: inherit; font-size: inherit; }
.rdb-drawer__sub a span.rdb-drawer__sub-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; flex: none;
    color: var(--rdb-accent);
}
.rdb-drawer__sub-ico svg {
    width: 22px !important; height: 22px !important;
    color: var(--rdb-accent);
}
/* Region rows (no icon) show a small count on the right */
.rdb-drawer__sub a:not(:has(.rdb-drawer__sub-ico)) { justify-content: space-between; }
.rdb-drawer__sub a:not(:has(.rdb-drawer__sub-ico)) span:not(.rdb-drawer__sub-lead) { color: rgba(255,255,255,.62); font-size: 11px; }
.rdb-drawer__sub-lead {
    color: var(--rdb-accent-on-dark) !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    font-size: 11px !important;
    letter-spacing: .16em !important;
    padding-top: 14px !important;
}
.rdb-drawer__call {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    padding: 20px;
    background: var(--rdb-accent);
    color: #fff !important;
    font-family: var(--rdb-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .1em;
    font-size: 14px;
    text-decoration: none;
    margin-top: auto;
    opacity: 0;
    transform: translateY(12px);
    transition: opacity .3s ease .35s, transform .3s ease .35s, background .18s ease;
    -webkit-tap-highlight-color: transparent;
}
.rdb-drawer[data-open] .rdb-drawer__call , .rdb-drawer:target .rdb-drawer__call { opacity: 1; transform: translateY(0); }
.rdb-drawer__call:hover { background: var(--rdb-accent-hover); }
.rdb-drawer__call:active { background: var(--rdb-accent-hover); transform: translateY(0) scale(.98); }

/* Staggered item entrance. Each nav row eases in with a per-index delay so
   the menu unfurls rather than snapping in as a block. */
.rdb-drawer__nav > * {
    opacity: 0;
    transform: translateX(14px);
    /* colour/background/padding-inline are carried here too, not just on
       .rdb-drawer__link. This rule is (0,2,0) and sits later in the file, so it
       replaced the link's own transition outright and the hover feedback never
       actually animated. Merging keeps the entrance choreography and restores
       the hover. */
    transition: opacity .28s ease, transform .32s cubic-bezier(.4,0,.2,1),
                color .18s ease, background .18s ease, padding-inline .18s ease;
}
.rdb-drawer[data-open] .rdb-drawer__nav > * , .rdb-drawer:target .rdb-drawer__nav > * { opacity: 1; transform: translateX(0); }
/* Was five hand written nth-child delays against seven children, so Careers
   and Contact inherited delay 0 and animated in BEFORE Home. Counting from
   nth-child works at any child count, including the next one added. */
.rdb-drawer[data-open] .rdb-drawer__nav > * , .rdb-drawer:target .rdb-drawer__nav > * { transition-delay: calc(60ms * var(--rdb-i, 0) + 120ms); }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(1) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(1) { --rdb-i: 0; }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(2) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(2) { --rdb-i: 1; }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(3) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(3) { --rdb-i: 2; }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(4) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(4) { --rdb-i: 3; }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(5) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(5) { --rdb-i: 4; }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(6) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(6) { --rdb-i: 5; }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(7) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(7) { --rdb-i: 6; }
.rdb-drawer[data-open] .rdb-drawer__nav > *:nth-child(n+8) , .rdb-drawer:target .rdb-drawer__nav > *:nth-child(n+8) { --rdb-i: 7; }

body[data-menu-open] { overflow: hidden; }
@media (min-width: 721px) { .rdb-drawer { display: none !important; } }

/* Respect the user's motion preferences */
@media (prefers-reduced-motion: reduce) {
    .rdb-drawer__panel,
    .rdb-drawer__scrim,
    .rdb-drawer__head,
    .rdb-drawer__nav > *,
    .rdb-drawer__call,
    .rdb-hamb__teeth,
    .rdb-hamb__x { transition-duration: .01ms !important; animation-duration: .01ms !important;
                   transition-delay: 0s !important; animation-delay: 0s !important; }
}

/* ─── Breadcrumb ───────────────────────────────────────────────────────────────── */
.rdb-crumbs {
    max-width: var(--rdb-col); margin: 0 auto;
    padding: 18px var(--rdb-pad-x) 0;
    font: 500 12px/1.4 var(--rdb-body); letter-spacing: .08em; text-transform: uppercase;
    color: var(--rdb-muted);
}
.rdb-crumbs a { color: var(--rdb-muted); }
.rdb-crumbs a:hover { color: var(--rdb-accent); }
.rdb-crumbs span { margin: 0 8px; opacity: .5; }

/* ─── Interior page hero ───────────────────────────────────────────────────────── */
.page-hero { background: var(--rdb-light); padding: clamp(32px,4.5vw,60px) var(--rdb-pad-x) clamp(28px,3.5vw,44px); }
.page-hero__inner { max-width: var(--rdb-col); margin: 0 auto; display: grid; grid-template-columns: 1fr; gap: 40px; align-items: end; }
.page-hero__l { min-width: 0; }
/* The eyebrow is the h1 on pages whose subject is a short label rather than a
   sentence: /about/, /careers/, /contact/. That is a deliberate choice, so the
   display treatment has to be attached to the ROLE, not to the tag. Without
   the :not() an eyebrow promoted to h1 would inherit this rule at (0,1,1)
   against .rdb-eyebrow at (0,1,0) and render as 74px serif. */
.page-hero h1:not(.rdb-eyebrow),
.page-hero .page-hero__headline {
    margin: 0 0 20px; font-family: var(--rdb-serif); font-weight: 800;
    font-size: clamp(34px, 5vw, 74px); line-height: .98; letter-spacing: -.01em;
    text-transform: uppercase; color: var(--rdb-dark); max-width: 22ch;
}
/* Top aligned, not bottom. The base rule sets align-items: end, so on a
   1466px /about/ the 354px text column was pushed down to meet the bottom
   of a 525px stat strip and the h1 started 187px below the strip's first
   row. The most important thing on the page began a third of the way down
   while a table of secondary facts held the top edge. Sharing a top edge
   also gives the two columns one alignment edge instead of two. */
@media (min-width: 900px) {
    .page-hero__inner { grid-template-columns: minmax(0,1.35fr) minmax(280px, 1fr); column-gap: 60px; align-items: start; }
    .page-hero__meta { align-self: start; }
}
/* Service hero photo. On wide screens the text sits left and the photo sits
   right; the meta strip drops full width below. */
.rdb-hphoto { margin: 0; min-width: 0; }
.rdb-hphoto img {
    display: block; width: 100%; height: 100%; max-height: 420px;
    object-fit: cover; aspect-ratio: 16 / 10;
    border: 1px solid var(--rdb-divider);
    box-shadow: 0 10px 30px rgba(18,34,54,.14);
}
/* Service hero: text left, photo right, then a tight horizontal stats strip
   full width below (not a tall floating box). */
.page-hero--photo .page-hero__meta {
    max-width: var(--rdb-col); margin: 34px auto 0;
    grid-template-columns: 1fr;
}
/* 2 up from 360px, not 720px. The strip was 1 up on every phone, which put
   342px of stats between the hero CTA and the first line of local copy at
   375px, 42% of the fold. Two columns plus the tighter cell below brings that
   to 162px. The gate is 360 and not 320 because "Permits" carries up to 46
   characters: at 320px a two column cell wraps it to four lines, at 375px to
   two. Below 360 the strip stays 1 up and only takes the tighter padding. */
@media (min-width: 360px) {
    .page-hero--photo .page-hero__meta { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 980px) {
    .page-hero--photo .page-hero__inner { grid-template-columns: minmax(0,1fr) minmax(340px,1.05fr); align-items: center; }
    .page-hero--photo .page-hero__meta { grid-template-columns: repeat(4, 1fr); }
}
.page-hero--photo .page-hero__stat { padding: 16px 20px; }

.page-hero__meta { display: grid; gap: 1px; background: var(--rdb-divider); border: 1px solid var(--rdb-divider); }
.page-hero__stat { background: var(--rdb-warm); padding: 20px 24px; }
.page-hero__stat dt { font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; letter-spacing: .14em; font-size: 12px; color: var(--rdb-muted); margin: 0 0 8px; }
.page-hero__stat dd { font-family: var(--rdb-body); font-weight: 500; font-size: 17px; line-height: 1.45; color: var(--rdb-dark); margin: 0; letter-spacing: normal; }
/* Phone sizing for the strip, placed AFTER the base declarations above rather
   than beside the 360px rule. A media query adds no specificity, so while this
   sat earlier in the file the plain .page-hero--photo .page-hero__stat padding
   and the 17px dd both won and the strip measured 221px instead of 162px.
   dd drops to 15px and the padding halves, which is what makes a 2 up cell
   readable rather than merely narrower. The 980px rule keeps desktop at 4 up
   and this block stops at 719px, so desktop never sees it. */
@media (max-width: 719px) {
    .page-hero--photo .page-hero__stat { padding: 12px 16px; }
    .page-hero__stat dt { font-size: 11px; margin-bottom: 5px; }
    .page-hero__stat dd { font-size: 15px; }
}
.page-hero__stat--big dd { font-family: var(--rdb-display); font-weight: 700; font-size: clamp(48px, 7vw, 82px); line-height: 1; color: var(--rdb-accent); letter-spacing: -.02em; }

/* ─── Homepage: Why ────────────────────────────────────────────────────────────── */
.rdb-why { background: var(--rdb-light); }
/* align-items:start left a large empty block under the short left column,
   because the three step cards on the right run much taller. Stretching the
   left cell and pushing the lead paragraph to its bottom edge lines the two
   columns up at both ends and the void closes. */
.rdb-why__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%),1fr)); gap: clamp(30px,5vw,64px); align-items: stretch; }
.rdb-why__grid > div:first-child { display: flex; flex-direction: column; }
.rdb-why__grid > div:first-child .rdb-lead { margin-top: auto; padding-top: 22px; }
.rdb-steps { display: grid; gap: 2px; background: var(--rdb-divider); }
.rdb-step { background: #fff; padding: 26px; display: flex; gap: 22px; align-items: flex-start; }
.rdb-step__n { font-family: var(--rdb-display); font-weight: 700; font-size: 44px; line-height: 1; color: var(--rdb-accent); }
.rdb-step__t { font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; font-size: 21px; color: var(--rdb-dark); margin: 0 0 6px; }
.rdb-step__d { margin: 0; font-size: 15px; }

/* Homepage: schedule diagram. Sits on the warm cream and drops the eyebrow, so
   it breaks the eyebrow / huge-H2 / grid rhythm that ran for seven sections
   straight and stopped registering by section four. */
.rdb-timeline { background: var(--rdb-warm); }
.rdb-timeline .rdb-lead { max-width: 62ch; }
.rdb-timeline .rdb-diagram { margin-top: 40px; }
.rdb-timeline .rdb-diagram svg { background: #fff; }

/* ─── Homepage: Services (dark) ────────────────────────────────────────────────── */
.rdb-services { background: var(--rdb-dark); color: #fff; }
.rdb-services .rdb-h2 { color: #fff; }
.rdb-services__head { display: flex; flex-wrap: wrap; gap: 20px 40px; justify-content: space-between; align-items: flex-end; margin-bottom: 46px; }
.rdb-services__link { color: var(--rdb-accent); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; font-size: 13px; }
.rdb-grid-tiles { display: grid; grid-template-columns: 1fr; gap: 1px; background: rgba(255,255,255,.13); }
@media (min-width: 560px) { .rdb-grid-tiles { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 720px) { .rdb-grid-tiles { grid-template-columns: repeat(3, 1fr); } }
.rdb-tile { background: var(--rdb-dark); padding: 34px 30px; transition: background .18s ease, transform .18s ease; display: block; color: inherit; }
a.rdb-tile:hover { background: var(--rdb-dark-hover); color: inherit; }
/* Service tiles were the only card on the site that changed colour on hover but
   did not lift. .rdb-card, .rdb-tml, .rdb-promise and .rdb-process__step all
   move 2 to 3px. Hover-gated so a touch device never sticks in the lifted
   state after a tap. */
@media (hover: hover) and (pointer: fine) {
    a.rdb-tile:hover { transform: translateY(-2px); }
}
.rdb-tile__i { font-family: var(--rdb-display); font-weight: 700; font-size: 15px; color: rgba(255,255,255,.4); letter-spacing: .1em; }
.rdb-tile__t { margin: 14px 0 10px; font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; font-size: clamp(19px, 1.9vw, 23px); color: #fff; }
.rdb-tile__d { margin: 0; font-size: 15px; color: rgba(255,255,255,.62); }
.rdb-tile__more { display: inline-block; margin-top: 16px; color: var(--rdb-accent-on-dark); font-weight: 700; font-size: 12px; letter-spacing: .12em; text-transform: uppercase; }

/* ─── Homepage: Recent work ────────────────────────────────────────────────────── */
.rdb-work { background: var(--rdb-light); }
.rdb-work__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%),1fr)); gap: 22px; margin-top: 40px; }
.rdb-work__img { aspect-ratio: 4/3; background: var(--rdb-divider); object-fit: cover; width: 100%; }
.rdb-work__ph {
    aspect-ratio: 4/3;
    background: repeating-linear-gradient(45deg, var(--rdb-divider), var(--rdb-divider) 12px, #e9e0d2 12px, #e9e0d2 24px);
    display: flex; align-items: flex-end; justify-content: flex-start;
    position: relative;
    overflow: hidden;
}
.rdb-work__ph span {
    display: inline-block;
    margin: 14px;
    padding: 6px 10px;
    background: var(--rdb-ink);
    color: rgba(255,255,255,.62);
    font: 700 10px/1 var(--rdb-body);
    letter-spacing: .16em;
    text-transform: uppercase;
}
.rdb-work__cap { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; margin-top: 14px; }
.rdb-work__t { font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; font-size: 21px; color: var(--rdb-dark); }
.rdb-work__city { color: var(--rdb-muted); font-size: 13px; letter-spacing: .06em; text-transform: uppercase; }

/* ─── Diagrams (inline SVG on service pages) ─────────────────────────────────
   On desktop the SVG scales to the container. On narrow screens the SVG
   would shrink to the point where its internal labels become unreadable, so
   the figure scrolls horizontally instead and a subtle "scroll to see more"
   hint appears on mobile. */
/* .rdb-diagram-band had no rule at all: the class was emitted by _wrap_diagram
   and styled nowhere, so it was the one section on a page computing transparent,
   and the drawing floated cream on cream because the SVG paints --rdb-light
   internally. White gives it a frame and doubles as a tonal break, matching
   .rdb-process which is the other white band on the site. */
.rdb-diagram-band { background: #fff; }
/* The figure carries tabindex="0" so keyboard users can scroll it, which
   means it needs a ring. The global :focus-visible rule lists a, button and
   input, so it does not reach a figure. */
.rdb-diagram:focus-visible { outline: 2px solid var(--rdb-accent); outline-offset: 3px; }
.rdb-diagram { margin: 48px 0 40px; padding: 0; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.rdb-diagram svg { display: block; width: 100%; height: auto; min-width: 720px; background: var(--rdb-light); border: 1px solid var(--rdb-divider); }
.rdb-diagram figcaption { margin: 14px 0 0; font-size: 14px; line-height: 1.6; color: var(--rdb-text-soft); }
.rdb-diagram figcaption strong { color: var(--rdb-dark); font-weight: 700; }
/* The hint is real markup now, emitted by _diagram_html(), not a ::before.
   CSS generated content is not reliably exposed to assistive technology, and
   this sentence is the only thing telling a reader that 61% of the drawing is
   off screen. It also said "Swipe", which is touch-only wording on a region
   that is equally scrollable by mouse and by arrow keys once focused, so the
   instruction failed for the two input methods that are not a finger.
   Hidden above 720px, where the drawing fits and there is nothing to scroll. */
.rdb-diagram__hint {
    display: inline-block;
    color: var(--rdb-accent);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .1em;
    font-size: 11px;
    margin-inline-end: 6px;
}
@media (min-width: 721px) {
    .rdb-diagram__hint { display: none; }
}
@media (max-width: 720px) {
}
/* Phones keep the same scrollable treatment as the 481 to 720px band. */
@media (max-width: 480px) {
    /* Was min-width: 0, which squeezed a 900px drawing into 335px and
       rendered its 11px labels at 4.1px. Illegible beats undiscoverable
       only in theory. Keep it scrollable at authored size; the hint below
       and the cut right edge are the affordance. */
    .rdb-diagram svg { min-width: 720px; }
    /* Hint restored. It was blanked here, so the 481 to 720px band was the
       only width that ever explained the scroll, and no phone sits in it. */
}

/* ─── Process bar: horizontal 4-step "how we work" pattern ─────────────────── */
.rdb-process { background: #fff; }
.rdb-process__grid { display: grid; grid-template-columns: 1fr; gap: 32px; padding: 0; margin: 40px 0 0; list-style: none; }
@media (min-width: 640px) { .rdb-process__grid { grid-template-columns: repeat(2, 1fr); gap: 36px 40px; } }
@media (min-width: 960px) { .rdb-process__grid { grid-template-columns: repeat(4, 1fr); gap: 40px; } }
.rdb-process__step {
    padding-top: 24px;
    border-top: 3px solid var(--rdb-divider);
    position: relative;
    transition: border-top-color .25s ease, transform .25s ease;
}

.rdb-process__step:hover .rdb-process__n { color: var(--rdb-accent-deep, var(--rdb-accent)); }
.rdb-process__n {
    font-family: var(--rdb-display); font-weight: 700;
    font-size: 42px; line-height: 1;
    color: var(--rdb-accent); letter-spacing: -.02em;
    display: block; margin-bottom: 14px;
    transition: color .25s ease;
}
.rdb-process__t {
    font-family: var(--rdb-display); font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
    font-size: 18px; line-height: 1.2;
    color: var(--rdb-dark); margin: 0 0 10px;
}
.rdb-process__d {
    margin: 0; font-size: 14px; line-height: 1.6;
    color: var(--rdb-text);
}

/* ─── Contact form ────────────────────────────────────────────────────────────
   Two-column on desktop (intro/CTA on left, form on right), stacked on
   mobile. Wired to Netlify Forms via data-netlify attribute + a hidden
   form-name field, plus a honeypot for spam. Redirects to /thank-you/ on
   success (native form action, no JS needed). */
.rdb-form { background: var(--rdb-warm); }
.rdb-form__wrap {
    max-width: var(--rdb-col);
    margin: 0 auto;
    display: grid;
    /* minmax(0,1fr), not 1fr. A bare 1fr is minmax(auto,1fr) and cannot shrink
       below its content's min-content, so at 320px the select's longest option
       string pushed this track to 326px and the conversion form was clipped by
       26px with no way to scroll to it. body has overflow-x: clip, so it was
       silent. The desktop rule below already got this right. */
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
    align-items: start;
}
/* Grid and flex items default to min-width: auto, so the floor has to be
   cleared on every link in the chain down to the control, not just the track. */
.rdb-form__wrap > *, .rdb-form__box, .rdb-form__field { min-width: 0; }
@media (min-width: 900px) {
    .rdb-form__wrap { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: 60px; }
}
.rdb-form__intro { position: sticky; top: calc(var(--rdb-nav-h) + 28px); }
@media (max-width: 899px) { .rdb-form__intro { position: static; } }
.rdb-form__lead {
    color: var(--rdb-text);
    font-size: 16px;
    line-height: 1.6;
    max-width: 44ch;
    margin: 0 0 32px;
}
.rdb-form__aside {
    padding: 20px 22px;
    background: #fff;
    border-left: 4px solid var(--rdb-accent);
    display: inline-flex;
    flex-direction: column;
    gap: 12px;
    align-items: flex-start;
}
.rdb-form__aside-lbl {
    margin: 0;
    font: 700 12px/1 var(--rdb-display);
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--rdb-muted);
}
.rdb-form__box {
    background: #fff;
    padding: 32px 30px;
    display: flex;
    flex-direction: column;
    gap: 18px;
    border-top: 4px solid var(--rdb-accent);
}
.rdb-form__row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 18px;
}
@media (min-width: 560px) {
    .rdb-form__row { grid-template-columns: 1fr 1fr; }
}
.rdb-form__field { display: flex; flex-direction: column; gap: 6px; }
.rdb-form__lbl {
    font: 700 12px/1 var(--rdb-body);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--rdb-dark);
}
.rdb-form__lbl em { font-style: normal; color: var(--rdb-accent); }
.rdb-form__field input,
.rdb-form__field select,
.rdb-form__field textarea {
    background: var(--rdb-light);
    border: 1px solid var(--rdb-divider);
    padding: 12px 14px;
    /* 16px, not 15. iOS Safari zooms the whole page when a focused input is
       under 16px, so the lead form jumped on every tap on an iPhone. */
    font: 400 16px/1.5 var(--rdb-body);
    color: var(--rdb-ink);
    border-radius: 2px;
    transition: border-color .18s ease, background .18s ease, box-shadow .18s ease;
    -webkit-appearance: none;
    appearance: none;
}
.rdb-form__field select {
    /* A grid track of 1fr is minmax(auto,1fr) and cannot shrink below the
       control's min-content, which here is the longest option string. Measured
       266px against a 280px container at 320px, clipping the form. */
    min-width: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1 L6 7 L11 1' stroke='%235F5449' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
    padding-right: 36px;
}
.rdb-form__field textarea { resize: vertical; min-height: 110px; font-family: var(--rdb-body); }
/* :focus-visible, not :focus. The bare version scored (0,2,1) against the
   global input:focus-visible ring at (0,1,1) and sat later in the file, so it
   won for keyboard users too, and its box-shadow measured roughly 1.05:1 on
   white. Mouse users keep the quiet border tint; keyboard users get a real ring. */
.rdb-form__field input:focus,
.rdb-form__field select:focus,
.rdb-form__field textarea:focus {
    border-color: var(--rdb-accent);
    background: #fff;
}
.rdb-form__field input:focus-visible,
.rdb-form__field select:focus-visible,
.rdb-form__field textarea:focus-visible {
    outline: 2px solid var(--rdb-accent);
    outline-offset: 2px;
    border-color: var(--rdb-accent);
    background: #fff;
}
.rdb-form__field input::placeholder,
.rdb-form__field textarea::placeholder { color: var(--rdb-muted); }
.rdb-form__hp { display: none; }
.rdb-form__submit {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 8px;
    padding-top: 22px;
    border-top: 1px solid var(--rdb-divider);
}
@media (min-width: 560px) {
    .rdb-form__submit { flex-direction: row; align-items: center; gap: 20px; }
}
.rdb-form__submit .rdb-btn { align-self: flex-start; }
.rdb-form__legal {
    margin: 0;
    font-size: 12px;
    line-height: 1.5;
    color: var(--rdb-text-soft);
}
.rdb-form__legal a { color: var(--rdb-accent); text-decoration: underline; text-underline-offset: 3px; }
/* Measured 70x15 and sitting directly under the submit button, so a mistap
   lands on the form's most sensitive control. Inline in a sentence, so it
   takes padding rather than a min-height box. */
@media (max-width: 767px) { .rdb-form__legal a { display: inline-block; padding: 12px 0; } }

/* ─── Nearby areas section (city page tail) ─────────────────────────────────── */
.rdb-nearby { background: #fff; }

/* ─── Pillars: dark editorial spread with oversized ambient numbers ─────────
   The two pillars sit on a dark ground rather than in white cards. Each
   pillar's number becomes a large ghosted design element behind the copy,
   which adds depth and typographic drama without noisy decoration. Used on
   the services hub "why hire us" block and on city page section 3. */
.rdb-pillars { background: var(--rdb-ink); color: rgba(255,255,255,.72); }
/* Accent TEXT on the navy surfaces. The barn red reaches only 2.69:1 to
   2.85:1 on #14273D and #122236. Deliberately not done by rebinding
   --rdb-accent on the dark containers: that would also lighten every
   accent BACKGROUND inside them, and white on the light tint is 3.36:1
   where white on the barn red is 5.63:1. Text only. */
.rdb-services .rdb-eyebrow,
.rdb-promises .rdb-eyebrow,
.rdb-pillars .rdb-eyebrow,
.rdb-midcta .rdb-eyebrow,
.rdb-fhero .rdb-eyebrow { color: var(--rdb-accent-on-dark); }
.rdb-services .rdb-eyebrow::before,
.rdb-promises .rdb-eyebrow::before,
.rdb-pillars .rdb-eyebrow::before,
.rdb-midcta .rdb-eyebrow::before,
.rdb-fhero .rdb-eyebrow::before { background: var(--rdb-accent-on-dark); }
.rdb-services .rdb-services__link { color: var(--rdb-accent-on-dark); }
.rdb-pillars .rdb-h2 { color: #fff; }
.rdb-pillars__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 48px;
    background: transparent;
    border: none;
    margin-top: 44px;
}
@media (min-width: 900px) {
    .rdb-pillars__grid { grid-template-columns: repeat(2, 1fr); column-gap: 72px; }
    /* _city_sections drops a section when it is too similar to another city's,
       which is the right content call with a layout consequence nobody caught:
       20 of the 48 city hubs survive with a single pillar and rendered it into
       the hard two column grid above, leaving half a full bleed navy band empty.
       One child, one column. */
    .rdb-pillars__grid:has(> .rdb-pillar:only-child) { grid-template-columns: minmax(0, 720px); }
}
.rdb-pillar {
    background: transparent;
    padding: 44px 0 0;
    display: block;
    position: relative;
    border-top: 2px solid rgba(255,255,255,.14);
    overflow: hidden;
    /* Matches the .3s the numeral behind it already uses. Without this the
       border snapped to accent while .rdb-pillar__n faded, so the two halves
       of one hover landed 300ms apart. */
    transition: border-top-color .3s ease;
}
.rdb-pillar__n {
    position: absolute;
    top: 26px;
    right: -8px;
    font-family: var(--rdb-display);
    font-weight: 700;
    font-size: 148px;
    line-height: .82;
    color: rgba(255,255,255,.06);
    letter-spacing: -.04em;
    pointer-events: none;
    z-index: 0;
    user-select: none;
}
.rdb-pillar__head {
    position: relative;
    z-index: 1;
    display: block;
    margin: 0 0 22px;
}
.rdb-pillar .rdb-pillar__label {
    font-family: var(--rdb-serif);
    font-weight: 700;
    font-size: clamp(24px, 2.4vw, 34px);
    line-height: 1.15;
    color: #fff;
    margin: 0;
    max-width: 22ch;
    text-wrap: balance;
    letter-spacing: normal;
}
.rdb-pillar p:not(.rdb-pillar__label) {
    position: relative;
    z-index: 1;
    margin: 0 0 16px;
    font-size: 16px;
    line-height: 1.7;
    color: rgba(255,255,255,.66);
    max-width: 46ch;
}
.rdb-pillar p:not(.rdb-pillar__label):last-child { margin-bottom: 0; }
.rdb-pillar:hover { border-top-color: var(--rdb-accent); }

.rdb-pillar__n { transition: color .3s ease; }
@media (max-width: 899px) {
    .rdb-pillar { padding-top: 32px; }
    .rdb-pillar__n { font-size: 82px; top: 20px; right: -4px; }
    .rdb-pillar .rdb-pillar__label { max-width: calc(100% - 70px); }
}
@media (max-width: 480px) {
    .rdb-pillar__n { font-size: 64px; top: 24px; }
    .rdb-pillar .rdb-pillar__label { max-width: calc(100% - 56px); font-size: 22px; }
}

/* ─── Sticky mobile action bar (Call, Free Quote) ────────────────────────────
   Fixed to the bottom of the viewport on mobile only. Two big tap targets,
   phone on the left, primary CTA on the right in terracotta. Bar has safe
   area inset padding so it sits above the iOS home indicator. */
.rdb-mbar { display: none; }
@media (max-width: 720px) {
    .rdb-mbar {
        display: flex;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 100;
        background: var(--rdb-ink);
        border-top: 1px solid rgba(255,255,255,.12);
        padding: 0 0 env(safe-area-inset-bottom, 0);
        box-shadow: 0 -6px 24px rgba(0,0,0,.24);
        /* Slides rather than pops. This used to be a display:none toggle, which
           cannot transition, so a 60px navy bar materialised out of nothing the
           instant scroll crossed 140px, on every mobile page view, and vanished
           again on the way back up. visibility still removes it from hit
           testing, which is the thing display:none was actually protecting:
           at scroll 0 the bar sat on top of the hero's own CTA.
           Curve and the `visibility 0s <dur>` idiom both borrowed from
           .rdb-drawer__panel so the two fixed surfaces agree. */
        transition: transform .25s cubic-bezier(.4,0,.2,1), visibility 0s 0s;
    }
    .rdb-mbar__btn {
        flex: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
        min-height: 60px;
        padding: 14px 12px;
        color: #fff !important;
        font-family: var(--rdb-body);
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: .1em;
        font-size: 13px;
        text-decoration: none;
    }
    .rdb-mbar__btn--call { background: transparent; }
    .rdb-mbar__btn--call:active { background: rgba(255,255,255,.06); }
    .rdb-mbar__btn--quote { background: var(--rdb-accent); }
    .rdb-mbar__btn--quote:active { background: var(--rdb-accent-hover); }
    .rdb-mbar__ico { width: 20px; height: 20px; flex: none; }
    /* Hold the bar back while the reader is still in the hero. It is fixed to the
       bottom 61px, so anything landing there at scroll 0 is untappable, and on a
       375x667 money page that was the hero's own "Get a free quote" button:
       button 617-666, bar 606-667, and elementFromPoint at the button's centre
       returned the bar. The page's primary CTA was dead at first paint.
       reveal.js sets data-attop only while scrollY <= 140, so with no JS the
       attribute never appears and the bar behaves exactly as it does today. */
    body[data-attop] .rdb-mbar {
        transform: translateY(100%);
        visibility: hidden;
        transition: transform .25s cubic-bezier(.4,0,.2,1), visibility 0s .25s;
    }

    /* Push page content up so the fixed bar does not cover the CTA or footer */
    body { padding-bottom: calc(60px + env(safe-area-inset-bottom, 0)); }
}


/* Both of these changed colour on hover without lifting, the same gap already
   ruled on for .rdb-tile. Hover gated so a tap never sticks in the lifted state. */
@media (hover: hover) and (pointer: fine) {
    a.rdb-loc:hover, a.rdb-notice__card:hover { transform: translateY(-2px); }
}

/* The one rare, high emotion frame on the site, and it rendered flat. Scale only,
   never opacity: the clamp(90px,18vw,220px) mark is the LCP candidate here and
   starting it transparent would push LCP out by the whole animation. */
@keyframes rdbMarkIn { from { transform: scale(.86); } to { transform: none; } }
.rdb-notice--ok .rdb-notice__code {
    animation: rdbMarkIn .42s cubic-bezier(.5,0,.2,1.3) both;
}

/* Hover transforms, gated. iOS keeps :hover after a tap, so an ungated
   lift or zoom sticks until the next tap elsewhere. .rdb-tile, .rdb-loc
   and .rdb-notice__card were already gated for this; these were not. */
@media (hover: hover) and (pointer: fine) {
    .rdb-process__step:hover { border-top-color: var(--rdb-accent); transform: translateY(-3px); }
    .rdb-promise:hover { transform: translateY(-3px); }
    .rdb-tml:hover { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(43,35,28,.1); }
    a.rdb-card:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(43,35,28,.1); color: inherit; }
    a.rdb-card:hover .rdb-card__media img { transform: scale(1.03); }
    .rdb-pillar:hover .rdb-pillar__n { color: rgba(192,50,43,.16); transition: color .3s ease; }
}

/* ─── Notice pages (404, thank-you) ───────────────────────────────────────────── */
.rdb-notice { background: var(--rdb-light); padding: clamp(72px, 12vw, 160px) var(--rdb-pad-x); }
.rdb-notice--ok { background: var(--rdb-warm); }
.rdb-notice__inner { max-width: var(--rdb-col); margin: 0 auto; text-align: center; }
.rdb-notice__code { font-family: var(--rdb-display); font-weight: 800; font-size: clamp(90px, 18vw, 220px); line-height: 1; letter-spacing: -.02em; color: var(--rdb-accent); margin: 0 0 12px; }
.rdb-notice--ok .rdb-notice__code { color: var(--rdb-success); }
.rdb-notice__t { font-family: var(--rdb-serif); font-weight: 700; font-size: clamp(32px, 4.6vw, 58px); line-height: 1.05; color: var(--rdb-dark); margin: 0 auto 20px; max-width: 20ch; }
.rdb-notice__d { max-width: 62ch; margin: 0 auto 40px; font-size: 18px; line-height: 1.6; color: var(--rdb-text); }
.rdb-notice__btns { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; margin: 0 auto 60px; }
.rdb-notice__sub { font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; letter-spacing: .14em; font-size: 14px; color: var(--rdb-accent-hover); margin: 0 0 22px; }
.rdb-notice__grid { display: grid; grid-template-columns: 1fr; gap: 1px; background: var(--rdb-divider); border: 1px solid var(--rdb-divider); text-align: left; max-width: 940px; margin: 0 auto; }
@media (min-width: 640px) { .rdb-notice__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .rdb-notice__grid { grid-template-columns: repeat(4, 1fr); } .rdb-notice--ok .rdb-notice__grid { grid-template-columns: repeat(2, 1fr); } }
.rdb-notice__card { background: #fff; padding: 26px 24px; color: inherit; transition: background .18s ease, transform .18s ease; display: block; }
.rdb-notice__card:hover { background: var(--rdb-warm); color: inherit; }
.rdb-notice--ok .rdb-notice__card:hover { background: var(--rdb-light); }
.rdb-notice__card h3 { font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; font-size: 17px; color: var(--rdb-dark); margin: 0 0 8px; }
.rdb-notice__card p { margin: 0; font-size: 14px; color: var(--rdb-text-soft); line-height: 1.5; }

/* ─── Locations hub: regions grouped ──────────────────────────────────────────── */
.rdb-regions .rdb-wrap { display: grid; gap: 56px; }
.rdb-region__head { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; padding-bottom: 14px; border-bottom: 2px solid var(--rdb-dark); margin-bottom: 22px; }
.rdb-region__t { margin: 0; font-family: var(--rdb-serif); font-weight: 700; font-size: clamp(24px, 3vw, 34px); color: var(--rdb-dark); line-height: 1.15; }
.rdb-region__c { margin: 0; font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; letter-spacing: .12em; font-size: 12px; color: var(--rdb-accent); }
/* Services hub groups. Borrows .rdb-region__head from the locations hub so the
   two stop being the same six section spine with different nouns. */
.rdb-svcgroup + .rdb-svcgroup { margin-top: 56px; }
.rdb-svcgroup__d { margin: -8px 0 22px; max-width: 60ch; font-size: 16px; line-height: 1.6; color: var(--rdb-text-soft); }

.rdb-region__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--rdb-divider); border: 1px solid var(--rdb-divider); }
@media (min-width: 640px) { .rdb-region__grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 960px) { .rdb-region__grid { grid-template-columns: repeat(4, 1fr); } }

/* ─── Homepage: Locations strip ────────────────────────────────────────────────── */
.rdb-locs { background: var(--rdb-light); }
.rdb-locs__head { display: flex; flex-wrap: wrap; gap: 20px 40px; justify-content: space-between; align-items: flex-end; }
.rdb-locs__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; margin-top: 40px; background: var(--rdb-divider); border: 1px solid var(--rdb-divider); }
@media (min-width: 640px)  { .rdb-locs__grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 960px)  { .rdb-locs__grid { grid-template-columns: repeat(4, 1fr); } }
.rdb-loc { background: #fff; padding: 22px 22px; display: flex; flex-direction: column; gap: 4px; color: inherit; transition: background .18s ease, transform .18s ease; }
.rdb-loc:hover { background: var(--rdb-warm); color: inherit; }
.rdb-loc__n { font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; font-size: 18px; letter-spacing: .04em; color: var(--rdb-dark); }
.rdb-loc__r { font-size: 13px; color: var(--rdb-muted); }

/* ─── Homepage: Promises / trust pillars ───────────────────────────────────────── */
.rdb-promises { background: var(--rdb-ink); color: #fff; }
.rdb-promises .rdb-h2 { color: #fff; }
.rdb-promises__grid { display: grid; grid-template-columns: 1fr; gap: 34px; margin-top: 40px; }
@media (min-width: 480px) { .rdb-promises__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .rdb-promises__grid { grid-template-columns: repeat(4, 1fr); } }
.rdb-promise { padding-top: 22px; border-top: 3px solid var(--rdb-accent); position: relative; transition: transform .25s ease, border-top-color .25s ease; }

.rdb-promise__n { display: block; font-family: var(--rdb-display); font-weight: 700; font-size: 24px; line-height: 1; color: var(--rdb-accent-on-dark); letter-spacing: -.01em; margin-bottom: 12px; }
.rdb-promise__t { margin: 0 0 10px; font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; font-size: clamp(18px, 1.7vw, 20px); letter-spacing: .04em; color: #fff; }
.rdb-promise__d { margin: 0; font-size: 15px; line-height: 1.65; color: rgba(255,255,255,.72); }

/* ─── Homepage: Testimonials ───────────────────────────────────────────────────── */
.rdb-tmls { background: var(--rdb-warm); }
.rdb-tmls__grid { display: grid; grid-template-columns: 1fr; gap: 20px; margin-top: 40px; }
@media (min-width: 640px) { .rdb-tmls__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .rdb-tmls__grid { grid-template-columns: repeat(3, 1fr); } }
.rdb-tml {
    background: #fff;
    padding: 30px 26px 26px;
    margin: 0;
    display: flex; flex-direction: column; gap: 16px;
    position: relative;
    border-top: 5px solid var(--rdb-accent);
    transition: transform .18s ease, box-shadow .18s ease;
}

.rdb-tml__stars { display: inline-flex; gap: 2px; color: var(--rdb-accent); margin: 2px 0 14px; }
.rdb-tml__stars svg { width: 18px; height: 18px; }
.rdb-tml__q { margin: 0; font-size: 16px; color: var(--rdb-quote); line-height: 1.65; flex: 1; }
.rdb-tml__by {
    display: flex; flex-direction: column; gap: 3px;
    font-weight: 700; letter-spacing: .12em; text-transform: uppercase; font-size: 12px;
    color: var(--rdb-dark); margin-top: 16px;
}
.rdb-tml__src { font-weight: 600; letter-spacing: .1em; font-size: 12px; color: var(--rdb-muted); }
.rdb-tmls__more { margin: 34px 0 0; }
.rdb-tmls__more a { font: 700 12px/1 var(--rdb-body); letter-spacing: .14em; text-transform: uppercase; color: var(--rdb-accent); }
.rdb-tmls__more a:hover { color: var(--rdb-accent-hover); }

/* ─── CTA band ─────────────────────────────────────────────────────────────────── */
.rdb-cta { background: var(--rdb-accent); color: #fff; padding: clamp(48px,7vw,84px) var(--rdb-pad-x); }
.rdb-cta__inner { max-width: var(--rdb-col); margin: 0 auto; display: flex; flex-wrap: wrap; gap: 28px 48px; justify-content: space-between; align-items: center; }
.rdb-cta__l { flex: 1 1 460px; max-width: 720px; }
.rdb-cta .rdb-h2 { color: #fff; font-size: clamp(30px,3.6vw,44px); line-height: 1.1; }
.rdb-cta__sub { margin: 14px 0 0; color: rgba(255,255,255,.9); font-size: 17px; max-width: 56ch; }
.rdb-cta__btns { display: flex; flex-direction: row; flex-wrap: wrap; gap: 12px; flex: 0 0 auto; }
@media (max-width: 640px) { .rdb-cta__btns { flex-direction: column; width: 100%; } }
.rdb-cta .rdb-btn--outline { color: #fff; }
.rdb-cta .rdb-btn--outline:hover { background: #fff; }
.rdb-cta .rdb-btn--outline:hover span { color: var(--rdb-accent); }

/* ─── Generic content blocks (service and city pages) ──────────────────────────── */
.rdb-prose { background: var(--rdb-light); }
/* Body text stays at a comfortable reading measure, but headings, diagrams,
   and the includes list break out wider so the page uses its horizontal
   space rather than stranding content in a narrow column. */
/* var(--rdb-col), not 1160px. Every sibling container on the page uses the
   token: .rdb-wrap, breadcrumbs, .page-hero__inner, .rdb-cta__inner, footer.
   At 1160 the prose heading started 10px inboard of the h1 above it, which is
   invisible on a paragraph and obvious on the left edge of 38px display type. */
.rdb-prose__inner { max-width: var(--rdb-col); margin: 0 auto; container-type: inline-size; }
/* 62ch, about 545px, not 820px. At 16px with line-height 1.65 the old value
   carried 115 to 125 characters a line against a comfortable 45 to 75, and it
   disagreed with .rdb-brief__body which already uses 62ch inside the SAME
   container on money pages. Two paragraph systems, one box, 1.5x apart. */
.rdb-prose__inner > p { max-width: 62ch; }

/* Money page topic block. Was 576 words of flat h2-then-paragraph in one column
   before the reader hit 24 collapsed accordions. Each heading is genuinely local
   ("Architectural review is part of the timeline"), so it earns a rail of its own
   rather than sitting inline like a chapter break nobody reads.

   Each block also carries its own reveal. The whole body used to sit inside one,
   which promoted a multi-thousand-pixel layer on mid scroll. */
/* 64px, not 52px. The heading is now :first-child of its brief, so
   .rdb-prose h2:first-child zeroes the 64px margin-top it used to carry. At 52px
   this "break the wall of text" change was measurably TIGHTER than the wall it
   replaced: 52px between topics against 64px before. */
.rdb-brief + .rdb-brief { margin-top: 72px; }

/* Money and commercial prose is a grid of equal-height field-note panels, not
   a rail and not CSS columns.

   Three earlier layouts all flowed blocks down independent tracks (a 340px
   rail, then 2 CSS columns), and every one of them could drop a heading at the
   foot of one track while its own body opened the next. On a money page that
   reads as a paragraph with no heading, which is a comprehension bug, not a
   cosmetic one. A grid ties each block to a row, rows equalise, and that class
   of failure cannot occur. The content is uniform enough to carry it: measured
   across all 22 money pages the blocks are a median 127 words and vary within a
   page by a median 1.25x, so a row's height difference lands as panel padding,
   not as a hole.

   The grid is opt-in via .rdb-prose__inner--grid, added at build time by
   prose_layout.gridify only to pages shaped "3 or 4 argument blocks plus a
   scope list". --rdb-prose-cols carries 3 (three prose blocks, 3-up) or 2 (four
   prose blocks, 2-up) so neither count strands a block. Service pillars keep
   the rail, /privacy/ keeps its stacked prose; neither gets the class. */
@media (min-width: 1080px) {
    .rdb-prose__inner--grid {
        display: grid;
        grid-template-columns: repeat(var(--rdb-prose-cols, 3), minmax(0, 1fr));
        /* 1px gap over the divider colour is the card-separator idiom the site
           already uses on the notice, locations and region grids. Hairlines on
           cream, no card fill, so the section reads as ruled plan notes and the
           cream run between the white bands above and below stays unbroken. */
        gap: 1px;
        background: var(--rdb-divider);
        border: 1px solid var(--rdb-divider);
        align-items: stretch;
    }
    .rdb-prose__inner--grid > .rdb-brief {
        display: block;
        background: var(--rdb-light);
        margin: 0;
        padding: 34px 32px 38px;
    }
    /* The scope list spans every column and sits on its own row. It is a
       reference list, not argument, and its own two columns of items would
       fight three columns of prose above it. :has(ul) not :last-child, so a
       page whose trailing block is prose never gets stretched. */
    .rdb-prose__inner--grid > .rdb-brief:has(.rdb-brief__body > ul) {
        grid-column: 1 / -1;
    }
    .rdb-prose__inner--grid > .rdb-brief > h2 {
        position: static;
        margin: 0 0 16px;
        font-size: 22px;
        line-height: 1.2;
        max-width: none;
    }
    .rdb-prose__inner--grid > .rdb-brief:has(.rdb-brief__body > ul) > h2 {
        font-size: clamp(24px, 2.1vw, 30px);
    }
    .rdb-prose__inner--grid .rdb-brief__body { margin-top: 0; }
    /* No ch cap. Each panel is its own column now, ~330px of text, so the
       measure is already in range and a cap would only leave dead space. */
    .rdb-prose__inner--grid .rdb-brief__body > p { max-width: none; }
    /* Lead-in: the first paragraph carries the darkest body ink so a skimmer
       reading only heading + first paragraph still gets each panel's point.
       Colour only, no size bump, so it stays a lead-in and not a second
       heading. */
    .rdb-prose__inner--grid .rdb-brief__body > p:first-child { color: var(--rdb-dark); }
    .rdb-prose__inner--grid .rdb-brief__body > ul { columns: 2; column-gap: 56px; }
}
.rdb-brief__body > p:first-child { margin-top: 0; }
.rdb-brief__body > p { max-width: 62ch; }
/* Below the breakpoint the heading and body stack instead of sitting side by
   side, and the heading carries margin: 0, so without this they collide. The
   grid gap handles the two track case, which is why this is reset there. */
.rdb-brief__body { margin-top: 20px; container-type: inline-size; }

/* The rule belongs to the block, not the heading. On the heading it painted the
   h2's own box, which the rail capped at 300px on desktop and an inherited
   max-width capped at ~274px in a 335px column on mobile. Either way it stopped
   short of its own column and read as a fragment rather than a divider. On the
   container it spans all 1160px, crosses both tracks, and gives the block a top
   edge, which is what makes the empty rail read as negative space instead of an
   unfinished column. */
/* No separator rule. Every block used to carry a 2px accent rule, which put
   five of them on a money page and 151 across the site, doing work the gap
   below already does. Space groups first, lines last. */
.rdb-brief { }
/* A figure in the heading rail. Only the about page uses one: its single
   photograph used to sit in its own section at the very bottom, the position
   where the only image on a page does the least work. As a third child of the
   block it lands under the h2 in the 378px rail, so it fills the column that
   was empty and sits beside the copy rather than after all of it. Below 1080px
   the block is not a grid, so it reads as a picture between the heading and
   the text, which is the right order anyway. */
.rdb-brief__fig { margin: 20px 0 0; }
.rdb-brief__fig img {
    display: block; width: 100%; height: auto;
    aspect-ratio: 4 / 3; object-fit: cover;
    border: 1px solid var(--rdb-divider);
    box-shadow: 0 8px 22px rgba(18,34,54,.12);
}
@media (min-width: 1080px) {
    .rdb-brief__fig { grid-column: 1; grid-row: 2; }
    /* Span both rows so the body still starts at the top of the block instead
       of dropping to row 2 beside the picture. */
    .rdb-brief:has(> .rdb-brief__fig) > .rdb-brief__body { grid-column: 2; grid-row: 1 / span 2; }
}

/* .rdb-prose is on the ancestor here for specificity, not decoration.
   `.rdb-brief > h2` and `.rdb-prose h2` are both (0,1,1) and .rdb-prose h2 sits
   later in this file, so it was winning every shared property: the heading
   rendered at 35.84px instead of 26, kept a 389px max-width instead of none, and
   carried a 22px bottom margin instead of 0. Three line wraps on the median
   heading followed from that, and that was the whole "looks unfinished" report.
   Measured on a money page before the fix. */
.rdb-prose .rdb-brief > h2 {
    margin: 0;
    font-size: clamp(24px, 2.1vw, 30px);
    line-height: 1.2;
    max-width: none;
    /* At two or three lines in a narrow rail, a one word orphan on the last line
       is a large part of what reads as unfinished. */
    text-wrap: balance;
}

/* 1080, not 900. At a 900px viewport the body track resolves to 462px against a
   300px rail, which is 1:1.5 and reads as two similar columns rather than a rail
   and a body. Two tracks are only worth having once the body still holds a full
   measure and the rail holds a heading in two lines or fewer. */
@media (min-width: 1080px) {
    .rdb-brief {
        display: grid;
        /* The body track was ~230px wider than the paragraphs ever used, so the
           rail takes that slack for free. The percentage holds a steady ~1:2
           ratio across container widths instead of degrading near the
           breakpoint the way a fixed 300px does. */
        /* 340 and 560, centred, not a full width 1180 grid. The block used to
           span the whole column while only ~958px carried anything, so the pair
           now reads as one 948px band. 560 still clears the 58ch cap below at
           524px, so no paragraph reflows.

           340 and not 260. The rail was tried at 260 first, which broke the
           longest heading on a money page (55 characters, "Where older Santa
           Clarita kitchens need electrical work") onto a third line and left a
           one word orphan. 340 is the width at which every heading on the
           thickest page holds two lines or fewer. It costs nothing: the section
           measured 1,926px tall at 260, 290, 320, 340 and 378, because the body
           column drives the height and the rail never does. Rail width is free,
           so it should be set by the headings rather than guessed at.

           minmax(0, 560px) and not a fixed 560, so at a 1080px viewport, where
           the column is only ~952px, the body track shrinks instead of pushing
           the band past its container. */
        grid-template-columns: 340px minmax(0, 560px);
        gap: 0 48px;
        justify-content: center;
        align-items: start;
    }
    .rdb-brief__body > p { max-width: 58ch; }
    .rdb-brief__body { margin-top: 0; }
    /* The heading travels with the body it belongs to.

       The rail is 340px wide and a heading is two lines, so under every heading
       sat 227 to 457px of empty column, measured per block on a money page. It
       read as dead space on every pass. Sticky spends it: the heading pins
       beside whatever paragraph you are actually reading and releases when its
       own block ends, so the rail is never empty next to live text.

       align-items: start above is what gives this travel room. A sticky grid
       item resolves its offsets against its grid area, and the area is as tall
       as the body track, so the heading has the whole block to move through.

       Blocks holding a rail figure are excluded. There is one, the first block
       on /about/, and pinning its heading while the photo underneath scrolled
       away would pull the pair apart. */
    .rdb-prose__inner:not(.rdb-prose__inner--grid) .rdb-brief:not(:has(> .rdb-brief__fig)) > h2 {
        position: sticky;
        top: calc(var(--rdb-nav-h) + 28px);
    }
    /* No .rdb-brief__body ul override here any more. It used to set
       grid-template-columns: 1fr, but it never applied: it and .rdb-prose ul are
       both (0,1,1) and .rdb-prose ul sits later in this file, so the two column
       rule won and the brief list rendered at 338px a column. Same collision
       class as the .rdb-brief > h2 bug above. The container query on
       .rdb-prose ul now measures .rdb-brief__body itself, which is ~717px, so
       the list stays single column because it is genuinely too narrow. */
}

/* Inline editorial links in body copy. The barn red reads 1.45:1 against the
   slate body text at the same weight, far under the 3:1 that WCAG 1.4.1 wants
   before colour alone can signal a link, so body links are underlined. Chrome
   links are excluded on purpose: a card, chip or button already reads as
   interactive from its shape. */
.rdb-prose__inner p a,
.rdb-pillar p a,
.rdb-faq details p:not(.rdb-faq__src) a {
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
}
/* No 24ch cap here. It was scoped to every .rdb-prose h2 and then cancelled a
   few rules down for the ones that are direct children of __inner, which is the
   overwhelming majority. That left the cap applying only to the h2s nobody
   meant it for, and the file's own comment on the override already called it a
   bug. The briefs set their own width at site.css:1257. */
.rdb-prose h2 { font-family: var(--rdb-serif); font-weight: 700; font-size: clamp(26px,2.8vw,38px); line-height: 1.15; letter-spacing: normal; color: var(--rdb-dark); margin: 64px 0 22px; text-wrap: balance; }
/* Includes list: 2-column grid once the BOX is wide enough, not the window.
   This was @media (min-width: 720px), which measured the viewport while the
   list lives in a container. At a 720px viewport each item got 274px of text
   against a median item of 59 characters, so every row wrapped. 900px of
   container leaves ~400px of measure per column after the 40px gap and the
   30px bullet indent, which holds the median item on one line.
   .rdb-prose__inner and .rdb-brief__body are both containers, so a list in a
   brief resolves against the brief's own ~717px track and stays single
   column on its own, with no specificity fight against this rule. */
.rdb-prose ul { display: grid; grid-template-columns: 1fr; gap: 0 40px; }
@container (min-width: 900px) { .rdb-prose ul { grid-template-columns: repeat(2, 1fr); } }
.rdb-prose h2:first-child { margin-top: 0; }
/* (0,2,1), so it beats `.rdb-prose h2` above. The child combinator matters:
   .rdb-brief > h2 is a grandchild of __inner and keeps its rail treatment.

   max-width: 24ch resolved to 413px while the paragraph beneath it ran 820px,
   so the heading rendered at half the width of its own body copy and over half
   of the site's 346 headings wrapped at every desktop size. Uncapping puts even
   the longest 64 character heading on one line.

   The rule underneath is the same device that fixed .rdb-brief: a full width
   edge declares the block's real width, which is what turns the space beside a
   narrower paragraph into margin instead of an unfinished column. */
.rdb-prose > .rdb-prose__inner > h2 {
    max-width: none;
    text-wrap: balance;
    padding-bottom: 14px;
    /* 2px dark, not 1px divider. At 1px divider this was pixel identical to the
       li dividers right beneath it, so on a page whose first prose block is
       h2 then a nine item list, ten matching hairlines rendered in a row and the
       heading rule read as the list's top border. .rdb-region__head already uses
       2px dark for a section head; reuse it rather than invent a fourth edge. */
    border-bottom: 2px solid var(--rdb-dark);
}
.rdb-prose p { margin: 0 0 1.1em; }
.rdb-prose ul { margin: 0 0 1.4em; padding: 0; list-style: none; }
.rdb-prose li { position: relative; padding: 10px 0 10px 30px; border-bottom: 1px solid var(--rdb-divider); }
.rdb-prose li::before { content: ""; position: absolute; left: 0; top: 18px; width: 14px; height: 2px; background: var(--rdb-accent); }

/* Card list (used for "services in city" and "related services") */
.rdb-cards { background: var(--rdb-light); }
/* Label for the nearby cities chip row, which used to be a whole section with
   its own eyebrow, h2 and lead sentence wrapped around a single chip. Sized and
   coloured like an eyebrow so the row still reads as a labelled group, without
   claiming section rank in the heading outline. */
.rdb-cards__alt {
    margin: 34px 0 12px;
    font-family: var(--rdb-display); font-weight: 700;
    text-transform: uppercase; letter-spacing: .14em;
    font-size: 12px; color: var(--rdb-muted);
}
.rdb-cards__grid { display: grid; grid-template-columns: 1fr; gap: 20px; margin-top: 40px; }
@media (min-width: 560px) { .rdb-cards__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 800px) { .rdb-cards__grid { grid-template-columns: repeat(3, 1fr); } }
.rdb-card {
    background: #fff;
    padding: 28px 26px 26px;
    border-top: 5px solid var(--rdb-accent);
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-height: 220px;
    position: relative;
    color: inherit;
    transition: transform .18s ease, box-shadow .18s ease;
}

.rdb-card__no {
    position: absolute; top: 22px; right: 24px;
    font-family: var(--rdb-display); font-weight: 700;
    font-size: 30px; line-height: 1;
    color: var(--rdb-divider);
    letter-spacing: -.02em;
}
.rdb-card__ico { width: 44px; height: 44px; color: var(--rdb-accent); flex: none; }
/* The 15ch cap keeps titles from running the full width of a wide card, but at
   320px it computed to 136px inside a 281px card and forced "Tile Work in
   Valencia" onto 3 lines using less than half the room available. Cap only
   where there is width to spare. */
.rdb-card__t { font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; font-size: 20px; line-height: 1.15; color: var(--rdb-dark); margin: 0; text-wrap: balance; }
@media (min-width: 560px) { .rdb-card__t { max-width: 15ch; } }
.rdb-card__d { margin: 0; font-size: 14px; line-height: 1.55; color: var(--rdb-text); flex: 1; }
.rdb-card__more { margin-top: 8px; color: var(--rdb-accent); font-weight: 700; font-size: 12px; letter-spacing: .12em; text-transform: uppercase; }

/* Chips (nearby cities / quick links) */
.rdb-chips { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
.rdb-chip {
    border: 1px solid var(--rdb-divider); background: #fff; color: var(--rdb-dark);
    padding: 12px 18px; font: 600 13px/1 var(--rdb-body); letter-spacing: .06em; text-transform: uppercase;

    /* The only hover state in this file that had no transition declared.
       transform is here for the :active press, which would otherwise snap:
       every other pressable card already carries transform in its list. */
    transition: border-color .18s ease, color .18s ease, transform .18s ease;
}
.rdb-chip:hover { border-color: var(--rdb-accent); color: var(--rdb-accent); }

/* FAQ */
.rdb-faq { background: var(--rdb-warm); }
.rdb-faq .rdb-eyebrow, .rdb-tmls .rdb-eyebrow, .rdb-form .rdb-eyebrow { color: var(--rdb-accent-hover); }
.rdb-faq .rdb-eyebrow::before, .rdb-tmls .rdb-eyebrow::before, .rdb-form .rdb-eyebrow::before { background: var(--rdb-accent-hover); }
.rdb-faq__inner { max-width: 860px; margin: 0 auto; }
.rdb-faq__lead { max-width: 640px; margin: 12px 0 40px; font-size: 17px; line-height: 1.6; color: var(--rdb-text); }
.rdb-faq details { background: #fff; border: 1px solid var(--rdb-divider); border-radius: 2px; margin-bottom: 10px; transition: box-shadow .18s ease, border-color .18s ease; }
.rdb-faq details:hover { border-color: var(--rdb-muted); }
.rdb-faq details[open] { border-color: var(--rdb-accent); box-shadow: 0 2px 12px rgba(43,35,28,.05); }
/* The colour and border-bottom used to snap while the panel below expanded
   over .22s, and that snap happened in every engine, including the ones where
   ::details-content does animate. :active because on a phone this is the most
   tapped control on the page, roughly 22 of them on a money page, and it had
   no press response at all. */
.rdb-faq summary { cursor: pointer; list-style: none; padding: 22px 24px; font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; font-size: 16px; letter-spacing: .04em; color: var(--rdb-dark); display: flex; justify-content: space-between; align-items: center; gap: 20px; line-height: 1.3; transition: color .18s ease, border-color .18s ease; -webkit-tap-highlight-color: transparent; }
.rdb-faq summary:active { background: rgba(192,50,43,.05); }
.rdb-faq summary::-webkit-details-marker { display: none; }
.rdb-faq summary::after { content: "+"; color: var(--rdb-accent); font-weight: 300; font-size: 26px; line-height: 1; flex: none; transition: transform .2s ease; }
/* The plus stays a plus and rotates into a cross. This used to swap content to
   a minus sign, which meant the `transition: transform` declared one line above
   could never fire: `content` is not an interpolable property. */
.rdb-faq details[open] summary::after { transform: rotate(45deg); }
.rdb-faq details[open] summary { color: var(--rdb-accent); border-bottom: 1px solid var(--rdb-divider); }
.rdb-faq details > p { margin: 0; padding: 20px 24px 26px; font-size: 16px; line-height: 1.7; color: var(--rdb-text); }
/* Animate the accordion open. The FAQ is roughly two thirds of a money page and
   a reader opens several in a row, so the snap was the most repeated jarring
   change on the site. interpolate-size lets height animate to a keyword, which
   is the only way to do this without JS measuring the panel. Engines without
   ::details-content simply keep today's snap, so this is purely additive. */
.rdb-faq { interpolate-size: allow-keywords; }
.rdb-faq details::details-content {
    height: 0; overflow: hidden;
    transition: height .22s cubic-bezier(.2,.7,.2,1),
                content-visibility .22s allow-discrete;
}
.rdb-faq details[open]::details-content { height: auto; }
/* One quiet CTA partway down the FAQ run. The FAQ is about two thirds of a
   money page, so without this the reader has nothing to act on between the mid
   page band and the closing band. Sized to read as a break in the list, not as
   a third banner. */
.rdb-faq__cta {
    background: var(--rdb-warm);
    border: 1px solid var(--rdb-divider);
    border-left: 3px solid var(--rdb-accent);
    padding: 24px;
    margin-bottom: 10px;
}
.rdb-faq__cta-t {
    margin: 0 0 16px;
    font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase;
    letter-spacing: .04em; font-size: 17px; line-height: 1.3; color: var(--rdb-dark);
}
.rdb-faq__cta-btns { display: flex; flex-wrap: wrap; gap: 12px; }
@media (max-width: 520px) { .rdb-faq__cta-btns .rdb-btn { width: 100%; } }

/* Citation line under an FAQ answer. Only rendered where the copy passes a
   (label, url) third element. See LOCAL-SEO-HANDBOOK.md section 17. */
.rdb-faq details > p:has(+ .rdb-faq__src) { padding-bottom: 10px; }
.rdb-faq details > p.rdb-faq__src { padding: 0 24px 24px; font-size: 14px; line-height: 1.6; color: var(--rdb-muted); }
.rdb-faq__src a { color: var(--rdb-accent); text-decoration: underline; text-underline-offset: 2px; }
.rdb-faq__src a:hover { text-decoration: none; }
@media (min-width: 720px) {
    .rdb-faq summary { padding: 24px 30px; font-size: 17px; }
    .rdb-faq details > p { padding: 20px 30px 28px; }
}

/* ─── Footer ───────────────────────────────────────────────────────────────────── */
.rdb-footer { background: var(--rdb-ink); color: rgba(255,255,255,.62); padding: clamp(56px,7vw,90px) var(--rdb-pad-x) 30px; }
.rdb-footer__grid { max-width: var(--rdb-col); margin: 0 auto; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px,1fr)); gap: 40px; }
.rdb-footer__brand { font-family: var(--rdb-display); font-weight: 800; text-transform: uppercase; font-size: 26px; color: #fff; margin: 0 0 16px; letter-spacing: .04em; }
.rdb-footer__blurb { margin: 0; font-size: 14px; max-width: 30ch; }
.rdb-footer h3 { font: 700 12px/1 var(--rdb-body); letter-spacing: .18em; text-transform: uppercase; color: rgba(255,255,255,.62); margin: 0 0 18px; }
.rdb-footer ul { list-style: none; margin: 0; padding: 0; }
.rdb-footer li { margin: 0 0 11px; }
.rdb-footer a { color: rgba(255,255,255,.62); font-size: 14px; }
.rdb-footer a:hover { color: var(--rdb-accent); }
.rdb-footer__c { color: rgba(255,255,255,.62); font-size: 12px; margin-left: 4px; }
.rdb-footer__all { color: var(--rdb-accent); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; font-size: 12px; }
.rdb-footer__all:hover { color: #fff; }
.rdb-footer__privacy { color: rgba(255,255,255,.62); font-size: inherit; text-decoration: underline; text-underline-offset: 3px; }
.rdb-footer__privacy:hover { color: var(--rdb-accent); }
.rdb-footer__bottom { max-width: var(--rdb-col); margin: 46px auto 0; padding-top: 24px; border-top: 1px solid rgba(255,255,255,.1); display: flex; flex-wrap: wrap; gap: 16px 30px; justify-content: space-between; align-items: center; font-size: 13px; }

/* Built by Kliq, short, and it stands out */
.rdb-kliq {
    display: inline-flex; align-items: center; gap: 9px;
    font: 700 12px/1 var(--rdb-body); letter-spacing: .14em; text-transform: uppercase;
    color: rgba(255,255,255,.55);
}
.rdb-kliq b {
    color: #fff; letter-spacing: .12em;
    background: var(--rdb-accent); padding: 6px 10px;
    transition: background .18s ease;
}
.rdb-kliq:hover { color: rgba(255,255,255,.75); }
.rdb-kliq:hover b { background: var(--rdb-accent-hover); }

/* ─── Reveal on scroll (reveal.js adds .js to <html>, then .is-in per element) ───
   A builder's sections should feel set in place, not faded in. Blocks rise and
   settle with a firm ease; grid children lay in one after another like courses,
   driven by --rdb-stagger set on each child by reveal.js. */
.js [data-rdb-reveal] { opacity: 0; transform: translateY(30px); }
.js [data-rdb-reveal].is-in {
    opacity: 1; transform: none;
    transition: opacity .6s cubic-bezier(.2,.7,.2,1), transform .7s cubic-bezier(.16,.84,.28,1);
}

/* Staggered children: cards, tiles, steps, promises build in sequence. */
.js [data-rdb-reveal].is-in > .rdb-cards__grid > *,
.js [data-rdb-reveal].is-in > .rdb-grid-tiles > *,
.js [data-rdb-reveal].is-in > .rdb-locs__grid > *,
.js [data-rdb-reveal].is-in > .rdb-promises__grid > *,
.js [data-rdb-reveal].is-in > .rdb-tmls__grid > *,
.js [data-rdb-reveal].is-in > .rdb-band__grid > *,
.js [data-rdb-reveal].is-in .rdb-process__grid > *,
.js [data-rdb-reveal].is-in .rdb-pillars__grid > * {
    animation: rdbLayIn .55s cubic-bezier(.16,.84,.28,1) both;
    animation-delay: calc(var(--rdb-stagger, 0) * 70ms);
}
@keyframes rdbLayIn {
    from { opacity: 0; transform: translateY(24px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    /* Delays need resetting too, not just durations. Clamping duration alone
       left calc(60ms * var(--rdb-i,0) + 120ms) on the drawer rows, so a
       reduced-motion reader still waited up to 540ms for the last one, and
       left calc(var(--rdb-stagger,0) * 70ms) on the reveal grids, so children
       still arrived in sequence, just instantly each. */
    *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important;
                             animation-delay: 0s !important; transition-delay: 0s !important; }
    /* * matches elements, not arbitrary pseudo-elements, so the line above
       never reached ::details-content and the FAQ accordion kept animating
       under reduced motion. ::before and ::after are named explicitly for
       the same reason; this is the third one that needs naming. */
    .rdb-faq details::details-content { transition: none !important; }
    html { scroll-behavior: auto !important; }
    [data-rdb-reveal] { opacity: 1; transform: none; }
}

/* Footer NAP block. Kept visually quiet but present in the markup, because
   name, address, and phone need to be crawlable text that matches the Google
   Business Profile exactly. */
.rdb-footer__nap {
    margin-top: 16px;
    font-style: normal;
    font: 400 13px/1.7 var(--rdb-body);
    color: rgba(255, 255, 255, .52);
}
.rdb-footer__nap a { color: rgba(255, 255, 255, .72); }
.rdb-footer__nap a:hover { color: var(--rdb-accent); }

/* ─── Fast homepage hero ──────────────────────────────────────────────────────
   Text-first banner that replaces the WebGL hero on the homepage. The point is
   Largest Contentful Paint: the H1 is real text, so it paints as soon as the
   CSS parses, with no JavaScript, no canvas, and no hero image in the critical
   path. The blueprint grid is a pure CSS gradient, so it costs no request.
   The WebGL hero engine still exists for pages that can afford it. */
.rdb-fhero {
    position: relative;
    background: var(--rdb-ink);
    padding: clamp(40px, 5.5vw, 72px) var(--rdb-pad-x) clamp(32px, 4vw, 56px);
    overflow: hidden;
    border-bottom: 3px solid var(--rdb-accent);
}
/* Blueprint grid, drawn with gradients so it adds zero bytes over the wire. */
.rdb-fhero::before {
    content: "";
    position: absolute; inset: 0;
    background-image:
        repeating-linear-gradient(to right, rgba(255,255,255,.045) 0 1px, transparent 1px 64px),
        repeating-linear-gradient(to bottom, rgba(255,255,255,.045) 0 1px, transparent 1px 64px);
    mask-image: radial-gradient(120% 90% at 78% 12%, #000 0%, transparent 72%);
    -webkit-mask-image: radial-gradient(120% 90% at 78% 12%, #000 0%, transparent 72%);
    pointer-events: none;
}
.rdb-fhero__inner { position: relative; max-width: var(--rdb-col); margin: 0 auto; }

.rdb-fhero__eyebrow {
    display: inline-flex; align-items: center; flex-wrap: wrap; gap: 10px;
    margin: 0 0 22px;
    font: 600 12px/1.5 var(--rdb-body);
    letter-spacing: .2em; text-transform: uppercase;
    color: rgba(255,255,255,.56);
}
.rdb-fhero__eyebrow b { color: var(--rdb-accent-on-dark); font-weight: 700; }
.rdb-fhero__eyebrow span[aria-hidden] { color: rgba(255,255,255,.28); }

.rdb-fhero__h1 {
    margin: 0;
    font-family: var(--rdb-serif);
    font-weight: 800;
    font-size: clamp(34px, 5vw, 72px);
    line-height: .96;
    letter-spacing: -.005em;
    text-transform: uppercase;
    color: #fff;
    text-wrap: balance;
    max-width: 24ch;
}
.rdb-fhero__sub {
    margin: 18px 0 0;
    font-size: clamp(16px, 1.7vw, 20px);
    line-height: 1.6;
    color: rgba(255,255,255,.74);
    max-width: 60ch;
}
/* Google rating line, above the CTA. */
/* Wraps. Four flex items needed about 375px in a 335px row at 375px wide and
   the five fixed 18px stars cannot shrink, so the text wrapped raggedly. */
.rdb-fhero__rating { flex-wrap: wrap; row-gap: 8px;
    display: flex; align-items: center; gap: 10px; margin: 18px 0 0;
    font-size: 15px; color: rgba(255,255,255,.72);
}
.rdb-fhero__rating b { color: #fff; font-weight: 700; }
.rdb-stars { display: inline-flex; gap: 2px; color: var(--rdb-accent); }
.rdb-stars svg { width: 18px; height: 18px; }

.rdb-fhero__cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 22px; }
.rdb-fhero__cta .rdb-btn--outline { color: #fff; }

/* Credential badges under the CTA. */
.rdb-fhero__rating-cta {
    font: 700 12px/1 var(--rdb-body); letter-spacing: .14em;
    text-transform: uppercase; color: var(--rdb-accent-on-dark);
}
@media (max-width: 520px) { .rdb-fhero__rating-cta { flex-basis: 100%; } }
.rdb-fhero__trust {
    display: flex; flex-wrap: wrap; gap: 10px;
    margin: 20px 0 0; padding: 0; list-style: none;
}
.rdb-fhero__trust li, .rdb-fhero__trust a {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 8px 13px;
    border: 1px solid rgba(255,255,255,.18);
    font: 700 12px/1 var(--rdb-body); letter-spacing: .1em; text-transform: uppercase;
    color: rgba(255,255,255,.82); white-space: nowrap;
}
/* The fourth credential is a link, and stripping its padding and border made
   it the only one in the row that did not read as a chip. It keeps the chip
   shape now; being tappable is a bonus, not a reason to look different. */
.rdb-fhero__trust li:has(a) { padding: 0; border: 0; }
.rdb-fhero__trust li:has(a) > a { padding: 8px 12px; border: 1px solid rgba(255,255,255,.22); }
.rdb-fhero__trust svg { width: 16px; height: 16px; color: var(--rdb-accent); }
.rdb-fhero__trust a:hover { color: #fff; }
.rdb-fhero__trust a:hover svg { color: #fff; }

.rdb-fhero__stats {
    display: grid; grid-template-columns: repeat(2, 1fr);
    gap: 20px 18px; margin: 30px 0 0;
    border-top: 1px solid rgba(255,255,255,.12); padding-top: 24px;
}
.rdb-fhero__stats > div > dt {
    margin: 0 0 6px;
    font: 600 12px/1 var(--rdb-body);
    letter-spacing: .18em; text-transform: uppercase;
    color: rgba(255,255,255,.72);
}
.rdb-fhero__stats > div > dd {
    margin: 0;
    font-family: var(--rdb-display); font-weight: 700;
    font-size: clamp(22px, 2.6vw, 32px); line-height: 1;
    text-transform: uppercase; color: #fff;
}
.rdb-fhero__stats > div:first-child > dd { color: var(--rdb-accent-on-dark); }

@media (min-width: 760px) {
    .rdb-fhero__stats { grid-template-columns: repeat(4, 1fr); gap: 24px; }
}
@media (max-width: 520px) {
    .rdb-fhero__cta .rdb-btn { width: 100%; }
}

/* Contractor license line. Required in advertising under California B&P 7030.5,
   and it doubles as a trust signal, so it sits in the footer of every page. */
.rdb-footer__lic {
    margin: 12px 0 0;
    font: 600 12px/1.6 var(--rdb-body);
    letter-spacing: .04em;
    color: rgba(255, 255, 255, .62);
}

/* "See all services" link at the foot of the shortened services dropdown. */
.site-nav__panel-more {
    display: block; margin-top: 6px;
    border-top: 1px solid rgba(255,255,255,.1);
    padding: 12px 20px 4px !important;
    color: var(--rdb-accent) !important;
    font: 700 11px/1 var(--rdb-body) !important;
    letter-spacing: .14em !important; text-transform: uppercase !important;
}
.site-nav__panel-more:hover { color: #fff !important; background: transparent !important; }

/* Recent-work photo band on service pages. Two photos side by side. */
.rdb-band { background: var(--rdb-light); }
/* White, the same as .rdb-diagram-band, because both are figure bands and the
   site should not have two conventions for the same thing. Scoped to the money
   page band rather than applied to .rdb-band globally: /about/ and /commercial/
   measure 2.37 and 3.11 viewports on their worst cream run, so neither needs
   the break and neither was measured for it. On a money page it splits what was
   a 6.39 viewport hero-prose-band run on a 375px phone. */
.rdb-band--figure { background: #fff; }
.rdb-band__grid { display: grid; grid-template-columns: 1fr; gap: 16px; }
@media (min-width: 700px){ .rdb-band__grid{ grid-template-columns: 1fr 1fr; } }
.rdb-band__fig { margin: 0; }
/* Caption under each band figure. Sized like .rdb-diagram figcaption, which is
   the other captioned figure on the site, so the two read as one convention.
   The figures are 16/10 and sit in a 2 up grid from 700px, and captions run to
   different lengths, so the grid aligns items at the start and lets the caption
   box below each image be its own height rather than stretching the images. */
.rdb-band__grid { align-items: start; }
.rdb-band__cap {
    margin: 12px 0 0;
    font-size: 14px; line-height: 1.6;
    color: var(--rdb-text-soft);
    max-width: 52ch;
}
.rdb-band__fig img {
    display: block; width: 100%; height: 100%;
    aspect-ratio: 16 / 10; object-fit: cover;
    border: 1px solid var(--rdb-divider);
    box-shadow: 0 8px 22px rgba(18,34,54,.12);
}

/* Branded photo placeholder (used until real job photos land). */
.rdb-hphoto--phold, .rdb-phold {
    aspect-ratio: 16 / 10; width: 100%;
}
.rdb-phold {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 12px; text-align: center; padding: 24px;
    background:
        repeating-linear-gradient(45deg, rgba(255,255,255,.03) 0 2px, transparent 2px 12px),
        var(--rdb-ink);
    border: 1px solid var(--rdb-dark);
}
.rdb-phold__ico { color: var(--rdb-accent); }
.rdb-phold__ico svg { width: 54px; height: 54px; stroke-width: 1.4; }
.rdb-phold__name {
    font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase;
    letter-spacing: .06em; font-size: clamp(18px,2.4vw,26px); color: #fff;
}
.rdb-phold__note {
    font: 600 11px/1 var(--rdb-body); letter-spacing: .2em; text-transform: uppercase;
    color: rgba(255,255,255,.5);
}

/* Services are a catalog, not a sequence: drop the numbers on service cards and
   tiles (numbering stays on the real process steps). */
.rdb-card__no, .rdb-tile__i { display: none; }

/* Contact page map. Free keyless Google embed, lazy-loaded, so it costs nothing
   and does not slow the rest of the site. */
.rdb-map__frame {
    border: 1px solid var(--rdb-divider);
    box-shadow: 0 10px 30px rgba(18,34,54,.12);
    line-height: 0;
}
.rdb-map__addr { margin: 18px 0 0; }
.rdb-map__addr a { font: 700 12px/1 var(--rdb-body); letter-spacing: .14em; text-transform: uppercase; color: var(--rdb-accent); }
.rdb-map__addr a:hover { color: var(--rdb-accent-hover); }

/* Gallery variant of the recent-work band (3+ photos). */
.rdb-band__grid--gallery { grid-template-columns: 1fr; }
@media (min-width: 640px){ .rdb-band__grid--gallery{ grid-template-columns: 1fr 1fr; } }
@media (min-width: 960px){ .rdb-band__grid--gallery{ grid-template-columns: repeat(3, 1fr); } }

/* Single featured photo in the recent-work band (one extra photo). */
.rdb-band__grid--single { grid-template-columns: 1fr; max-width: 900px; }

/* ─── Mobile touch targets ─────────────────────────────────────────────────────
   Every tappable thing needs a comfortable finger-sized hit area on a phone.
   Inline text links in lists and section headers were rendering 14 to 21px tall,
   which is well under the 44px guideline, so they get real padding on small
   screens without changing how they look on desktop. */
@media (max-width: 720px) {
    /* Footer nav lists: one comfortable row per link. */
    .rdb-footer li { margin: 0; }
    .rdb-footer li a {
        display: block;
        padding: 12px 0;
        min-height: 44px;
        box-sizing: border-box;
    }
    .rdb-footer__nap a { display: inline-block; padding: 6px 0; }

    /* Section "see all" links and other inline actions. */
    .rdb-services__link,
    .rdb-tmls__more a,
    .rdb-map__addr a,
    .rdb-fhero__trust a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    /* Hero credential chips: give them room to be tapped and read. */
    .rdb-fhero__trust li { min-height: 44px; }

    /* Nudge the smallest labels up so they stay legible on a phone. */
    .rdb-tml__src { font-size: 12px; }
    .rdb-fhero__trust li,
    .rdb-fhero__trust a { font-size: 12px; }
}

/* Remaining interactive controls brought up to a full finger target. */
@media (max-width: 720px) {
    .site-header__menu { min-width: 44px; min-height: 44px; }
    .rdb-drawer__close { min-width: 44px; min-height: 44px; }
    .rdb-drawer__call { min-height: 48px; }
    .rdb-footer__nap a[href^="tel:"] { display: inline-flex; align-items: center; min-height: 44px; }
}

/* Responsive images are wrapped in <picture>, which is inline by default and
   would otherwise leave a baseline gap under every photo. */
picture { display: block; }
.rdb-hphoto picture, .rdb-band__fig picture, .rdb-work__grid picture { height: 100%; }

/* ─── Service cards lead with the work ────────────────────────────────────────
   A remodeler is judged on photos, so each card opens with a real project image
   and the text sits beneath it. Cards without a photo yet fall back to the line
   icon in the same slot, so a mixed grid still lines up. */
.rdb-card { padding: 0 0 26px; overflow: hidden; }
.rdb-card__media {
    display: block; margin: 0 0 4px;
    background: var(--rdb-divider);
}
.rdb-card__media img {
    display: block; width: 100%; height: auto;
    aspect-ratio: 16 / 10; object-fit: cover;
    transition: transform .35s ease;
}

.rdb-card__iconwrap {
    display: flex; align-items: center; justify-content: center;
    aspect-ratio: 16 / 10; background: var(--rdb-ink);
    margin: 0 0 4px;
}
.rdb-card__iconwrap svg { width: 54px; height: 54px; color: var(--rdb-accent); }
.rdb-card__t, .rdb-card__d, .rdb-card__more { padding-left: 26px; padding-right: 26px; }
.rdb-card__t { margin-top: 20px; }

/* ─── Homepage hero background ────────────────────────────────────────────────
   One kitchen photograph, full bleed, painted from a CSS background and
   preloaded in head() so it is the Largest Contentful Paint element. The scrim
   below carries the contrast, heaviest under the headline and opening up on the
   right so the photo still reads as a photograph.

   This used to be two photos split by a diagonal clip-path with a barn red seam.
   Removed 2026-08-25: the left photo sat under a scrim heavy enough to make it
   near black, so the composition read as dark mush, a red gash, then a bright
   kitchen. hero-addition* remain on disk but are referenced by nothing. */
.rdb-fhero__bg {
    position: absolute; inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-attachment: scroll;   /* fixed jitters badly on iOS Safari */
}

/* Kitchen. The only photo on small screens. */
.rdb-fhero__bg--b {
    background-image: image-set(url("/assets/img/hero-800.webp") type("image/webp"),
                                url("/assets/img/hero-800.jpg") type("image/jpeg"));
    background-image: -webkit-image-set(url("/assets/img/hero-800.webp") type("image/webp"),
                                        url("/assets/img/hero-800.jpg") type("image/jpeg"));
}
/* Second hero photo removed 2026-08-25. The diagonal clip-path split read as a
   rendering artifact rather than a device: the left photo sat under a scrim
   heavy enough to make it near black, so the page showed dark mush, a red seam,
   then a bright kitchen. One photo, full bleed, is stronger and drops an image
   from the critical path. hero-addition* files remain on disk. */
.rdb-fhero__bg--a { display: none !important; }

/* Readability scrim. Deepest under the text, easing right so the kitchen shows.
   This is what keeps white type legible over a photograph. */
.rdb-fhero__scrim {
    position: absolute; inset: 0;
    /* Mobile: the headline runs the full width, so the scrim stays heavy all the
       way across. Measured contrast against the kitchen photo is 12.6:1 at its
       weakest, comfortably past the 4.5:1 requirement. */
    background: linear-gradient(100deg,
        rgba(18,34,54,.90) 0%,
        rgba(18,34,54,.80) 100%);
}
@media (min-width: 900px) {
    /* Desktop: the text only occupies the left half, so the scrim can open up
       sharply on the right and let the kitchen actually read as a photograph.
       Measured: 10:1 or better everywhere text sits, dropping to a visible
       mid-tone on the open right side. */
    .rdb-fhero__scrim {
        background: linear-gradient(100deg,
            rgba(18,34,54,.92) 0%,
            rgba(18,34,54,.88) 35%,
            rgba(18,34,54,.60) 60%,
            rgba(18,34,54,.32) 100%);
    }
}

@media (min-width: 700px) {
    .rdb-fhero__bg--b {
        background-image: image-set(url("/assets/img/hero-1200.webp") type("image/webp"),
                                    url("/assets/img/hero-1200.jpg") type("image/jpeg"));
        background-image: -webkit-image-set(url("/assets/img/hero-1200.webp") type("image/webp"),
                                            url("/assets/img/hero-1200.jpg") type("image/jpeg"));
    }
}

@media (min-width: 1400px) {
    .rdb-fhero__bg--b {
        background-image: image-set(url("/assets/img/hero.webp") type("image/webp"),
                                    url("/assets/img/hero.jpg") type("image/jpeg"));
        background-image: -webkit-image-set(url("/assets/img/hero.webp") type("image/webp"),
                                            url("/assets/img/hero.jpg") type("image/jpeg"));
    }
}

/* Blueprint grid sits above the photo and below the text. It carried an
   opacity: .5 multiplier while it had to compete with a second photo and the
   red diagonal seam. Both were removed 2026-08-25, so the multiplier was just
   dimming texture against nothing: 0.045 times 0.5 is a 2.25% line, invisible
   on the navy. Back to the strength it was authored at. */
.rdb-fhero::before { z-index: 1; }
.rdb-fhero__inner { z-index: 2; }

/* ==========================================================================
   Money page conversion surfaces
   Hero CTA, one mid page CTA before the FAQ, and a mobile action bar. Money
   pages only. Info pages keep a single outline CTA, which is the tier rule in
   HANDOFF.md. Touch targets are 48px against a 44px minimum, labels clear
   4.5:1 on their fill, and the bar respects the iOS home indicator.
   ========================================================================== */

/* Hero. The grid gap is 40px sitewide, which is too airy once two more rows
   land under the lead, so it tightens only where the CTA is present. */
.page-hero__inner:has(.rdb-hero-cta) { gap: 16px; }
.rdb-hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; }
.rdb-hero-cta__alt { margin: 0; font-size: 15px; color: var(--rdb-text-soft); }
.rdb-hero-cta__alt a {
    color: inherit;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}
.rdb-hero-cta__alt a:hover { color: var(--rdb-accent); }

/* Mid page CTA. Sits after the prose and before the FAQ, which is the point
   the reader has the local proof and has not yet entered twenty questions. */
.rdb-midcta { background: var(--rdb-dark); color: #fff; padding: clamp(32px,4vw,52px) var(--rdb-pad-x); }
.rdb-midcta__inner { max-width: 860px; margin: 0 auto; text-align: center; }
.rdb-midcta__t { margin: 0; font-family: var(--rdb-display); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; font-size: clamp(22px,3vw,30px); line-height: 1.2; }
.rdb-midcta__d { margin: 12px auto 24px; max-width: 560px; font-size: 16px; line-height: 1.65; color: #D6DCE4; }
.rdb-midcta__btns { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }

/* One button rule for phones.
   At 375px this page used to show five button widths (198, 169, 214, 268, 320)
   across four different left edges (20, 47, 73, 81): the hero sized to its
   label, the mid CTA sized to its label AND centred, the FAQ callout filled its
   own inset card, and the closing band filled the page gutter. Four ways of
   answering the same question on one scroll.
   Below 480px every CTA pair now stacks full width on the container it sits in,
   so they all share one left edge. The FAQ callout keeps its 27px inset because
   that inset is the card, not a stray margin, and .rdb-cta__btns already did
   this at 640px. */
@media (max-width: 479px) {
    .rdb-hero-cta,
    .rdb-midcta__btns { flex-direction: column; align-items: stretch; }
    .rdb-midcta__btns { justify-content: flex-start; }
    .rdb-hero-cta .rdb-btn,
    .rdb-midcta__btns .rdb-btn { width: 100%; }
}
.rdb-midcta .rdb-btn--outline { color: #fff; }
.rdb-midcta .rdb-btn--outline:hover span { color: var(--rdb-dark); }

/* Mobile action bar removed 2026-08-25.
   Money pages and commercial city pages shipped .rdb-actionbar on top of the
   sitewide .rdb-mbar from footer(). The mbar is z-index 100 and the actionbar
   was z-index 50, so on every phone the money page's own conversion bar sat
   underneath the generic one and was never seen. Only the 721 to 767px sliver
   ever showed it, because the mbar stops at 720px.

   The two also fought over body padding: 60px from the mbar rule, 78px from
   body:has(.rdb-actionbar). The mbar won that too, so the actionbar's extra
   18px never applied either.

   Kept the mbar: it is sitewide, navy rather than white translucent, has 60px
   touch targets against the actionbar's 48px, and costs no backdrop-filter. */

/* Mobile touch targets. Audited at 375px: the brand link, the footer credit and
   the skip link all sat under the 44px minimum. They are text links rather than
   buttons, so the fix is vertical padding that does not disturb the layout. */
@media (max-width: 767px) {
  .site-header__brand { display: inline-flex; align-items: center; min-height: 44px; }
  .rdb-skip { min-height: 44px; display: inline-flex; align-items: center; }
  .rdb-footer__credit a, .rdb-footer a[href*="kliq"] { display: inline-flex; align-items: center; min-height: 44px; }
}

/* Route commercial visitors out of the residential grid without putting a
   commercial link on any residential money page or pillar. */
.rdb-services__aside { margin: 28px 0 0; font-size: 16px; line-height: 1.6; color: #D8D2C6; }
.rdb-services__aside a { color: var(--rdb-accent-on-dark); text-underline-offset: 3px; }

/* Mobile touch targets, interior pages. Audited at 375px on a money page:
   breadcrumb links measured 14px tall, chips 39px, the hero secondary link 18px
   and citation links 39px. Chips and standalone links are button-like and go to
   the full 44px. Breadcrumb links are inline navigation, so they get generous
   padding without turning the trail into a stack. */
@media (max-width: 767px) {
  .rdb-crumbs { line-height: 1.9; }
  .rdb-crumbs a,
  .rdb-crumbs [aria-current] { display: inline-flex; align-items: center; min-height: 36px; }
  .rdb-chip { min-height: 44px; display: inline-flex; align-items: center; }
  .rdb-hero-cta__alt a { display: inline-flex; align-items: center; min-height: 44px; }
  .rdb-faq__src a { display: inline-flex; align-items: center; min-height: 44px; }
}
