﻿/* ==========================================================================
   Design tokens — "Advisory Warm" (navy + gold).
   Ported from the Claude Design handoff at
   ClaudeDesign/Redesign Mota Systems/design_handoff_mota_home/, merging
   _ds/.../tokens/{colors,typography,spacing,shape,motion}.css with the four
   band overrides the home design layers on top of them.
   Prefer these over hardcoded hex values in any new rule.
   ========================================================================== */
:root {
    /* --- base palette --- */
    --navy: #131C2E;
    --gold: #C08A2E;
    --gold-deep: #A2731F;
    --gold-shadow: #2A1E06;
    --sand-strong: #D3C9B4;
    --cream: #F7F4EE;
    --white: #ffffff;
    --hairline: #E1DACC;

    /* Darkened bands. The base system grounds everything on --navy; this
       direction darkens header/testimonial/footer to --ink and the hero to
       --petrol. A deliberate deviation, recorded in the handoff README. */
    --ink: #080C15;
    --petrol: #0D1424;

    /* --- semantic surfaces --- */
    --surface-page: var(--cream);
    --surface-card: var(--white);
    --surface-subtle: #F1ECE2;
    --surface-inverse: var(--ink);

    /* --- text --- */
    --text-strong: var(--navy);
    --text-body: #4E5871;
    --text-muted: #6E6A62;
    --text-on-inverse: var(--cream);
    /* Cool grey, deliberately not the system's warm #9C8F76, so secondary text
       on the dark bands does not read as washed-out gold. 7.96:1 on --petrol,
       8.47:1 on --ink, 7.37:1 on --navy. */
    --text-on-inverse-muted: #A3ABBA;
    /* The label on a gold fill. 5.37:1 on --accent. Never white — white on the
       same fill measures 3.04:1. Note this is the ceiling for a dark label:
       pure black on --gold-deep only reaches 5.01:1, which is why the button
       hover brightens the fill rather than darkening it (see --accent-hover). */
    --text-on-accent: var(--gold-shadow);

    /* --- accent. Gold appears once per view, as the thing to click. --- */
    --accent: var(--gold);
    /* The hover FILL, and it brightens rather than darkens. --gold-deep was the
       obvious choice and was wrong: --text-on-accent on it measures 3.89:1, and
       no darker label rescues that (see the ceiling noted above). Brightening
       takes the label to 6.50:1 instead. The cost is that the fill separates
       less from the cream page (2.29:1 against 2.55:1 at rest), which is why
       the two buttons using it also take a --gold-deep rim on hover: that edge
       measures 3.82:1 on cream, so the button's boundary gets stronger, not
       weaker. Use --accent-hover for fills, --gold-deep for the rim that goes
       with it, and --accent-text for type. */
    --accent-hover: #D09A3C;
    --accent-on-inverse: var(--gold);
    /* Gold for TEXT on light grounds. The design system points these at
       --gold-deep (#A2731F), which measures only 3.82:1 on cream and 3.56:1 on
       the subtle card — below AA for the 11-12px labels and kickers that use
       it. This is the same problem the pre-redesign palette solved with its own
       darker gold, so the deviation is kept: 4.69:1 on --surface-subtle,
       5.03:1 on cream, 5.52:1 on white. Every gold LABEL on a light ground
       takes this; every gold fill, border or edge takes --accent. */
    --accent-text: #8A6119;

    /* --- semantic. The only hue outside navy/gold, and it earns its place: a
       form error must not be carried by the accent, because gold means "the
       thing to click". Warm brick so it sits with the gold rather than fighting
       it. Scoped to form feedback — do not reach for it as a general colour.
       6.69:1 on --surface-card, 5.98:1 on --danger-surface. Replaces Bootstrap's
       #dc3545/#28a745, which were never part of this palette (and the green
       measured 3.13:1 on white). Success is carried by the accent and a
       heading, not by a second semantic hue. --- */
    --danger: #A8321F;
    --danger-surface: #FBF0ED;
    --danger-border: #E3B8AE;

    /* --- borders --- */
    --border-subtle: var(--hairline);
    --border-strong: var(--sand-strong);
    /* Darkened to sit with --ink/--petrol; the base system ships #2A3549. */
    --border-inverse: #1E2739;
    --focus-ring: var(--gold);

    /* --- type --- */
    --font-display: "Sora", "Libre Franklin", system-ui, sans-serif;
    --font-body: "Libre Franklin", system-ui, sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, monospace;

    --text-display: 56px;
    --text-h2: 32px;
    --text-h3: 26px;
    --text-h4: 20px;
    --text-lg: 18px;
    --text-md: 16px;
    --text-sm: 14px;
    --text-2xs: 12px;
    --text-micro: 11px;
    --text-eyebrow: 10px;

    --tracking-display: -0.03em;
    --tracking-mono: 0.08em;
    --tracking-label: 0.12em;
    --tracking-eyebrow: 0.2em;

    --type-display: 600 var(--text-display)/1.1 var(--font-display);
    --type-h2: 600 var(--text-h2)/1.2 var(--font-display);
    --type-h3: 600 var(--text-h3)/1.2 var(--font-display);
    --type-h4: 600 var(--text-h4)/1.2 var(--font-display);
    --type-body: 400 var(--text-md)/1.6 var(--font-body);
    --type-body-sm: 400 var(--text-sm)/1.6 var(--font-body);
    --type-label: 600 var(--text-2xs)/1.3 var(--font-body);

    /* --- layout --- */
    --container-max: 1180px;
    --container-text: 720px;
    --gutter-lg: 56px;
    /* Narrower gutters below the design's desktop-only brief. Mobile was not
       designed; these are our steps down from 56px. */
    --gutter-md: 32px;
    --gutter-mobile: 20px;
    --section-y: 96px;

    /* --- shape. Cards and fields 2px; buttons 4px, softened per the client. --- */
    --radius-md: 2px;
    --radius-btn: 4px;
    --shadow-raise: 0 1px 2px rgba(19, 28, 46, 0.07);

    /* --- motion. No bounce, no scale, no spring. --- */
    --ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
    --duration-fast: 140ms;
    --duration-base: 220ms;
    --duration-slow: 420ms;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
}

/* Contains .place-band's bleed.

   The band spans the viewport with width: 100vw, and 100vw counts the
   scrollbar while the content area does not. It is centred on the content but
   sized to the wider box, so on desktop it hangs about half a scrollbar past
   each side: measured at 1280px, the band runs -7..1273 inside a 1265px
   content area, and that 8px raised a horizontal scrollbar on the four pages
   that use it (about, fractional-technology-leadership, software-development,
   staff-augmentation). Mobile never showed it -- overlay scrollbars make 100vw
   equal to the content width.

   On <main> rather than the root: overflow on the root element propagates to
   the viewport and the root's own value becomes visible, and Chrome does not
   propagate `clip` that way, so setting it there measured as having no effect
   at all. <main> spans the same content width, which is exactly where the
   bleed should stop.

   clip, not hidden, so this never becomes a scroll container. The sticky
   navbar lives in <header>, outside <main>, so it is untouched either way --
   but a stray scroll container here would also strand the page's anchor
   scrolling. There is no CSS unit for "viewport minus scrollbar", so
   containing the bleed is the fix, not a workaround: the band is meant to end
   at the edge of the visible page. */
main {
    overflow-x: clip;
}

body {
    font-family: var(--font-body);
    color: var(--text-body);
    background: var(--surface-page);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    text-wrap: pretty;
}

h1, h2, h3, h4 {
    font-family: var(--font-display);
    font-weight: 600;
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
}

:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

::selection {
    background: var(--accent);
    color: var(--text-on-accent);
}

/* Bypass block (WCAG 2.4.1). The header is ten-plus tab stops including the
   services submenu, and it repeats on every page, so a keyboard visitor
   otherwise tabs the whole bar again on every navigation. Off-canvas rather
   than display:none, because a hidden link is not focusable and so is never
   reachable to be revealed. */
.skip-link {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1100;
    transform: translateY(-120%);
    padding: 12px 20px;
    background: var(--accent);
    color: var(--text-on-accent);
    font-family: var(--font-body);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
}

.skip-link:focus {
    transform: translateY(0);
}

/* Sections reached by the header's in-page anchors have to clear the sticky
   bar, or their heading lands underneath it. */
section[id] {
    scroll-margin-top: 88px;
}

/* Same reason, for the skip link's landing point. tabindex="-1" in the markup
   is what lets focus actually move here; the ring stays hidden because the
   outline above is :focus-visible, which a programmatic focus does not match. */
#main {
    scroll-margin-top: 88px;
}

.container {
    max-width: var(--container-max);
    margin: 0 auto;
    padding: 0 var(--gutter-lg);
}

/* ==========================================================================
   Site chrome — header, nav, footer, buttons, CTA band.
   Shared by every page, so every rule in this block is site-wide.
   ========================================================================== */

/* Navigation */
.navbar {
    background: var(--ink);
    /* The gold rule is the deliberate divider between the bar and the hero. */
    border-bottom: 2px solid var(--accent);
    position: sticky;
    top: 0;
    z-index: 1000;
}

.navbar .container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 32px;
    padding: 18px var(--gutter-lg);
    transition: padding var(--duration-fast) var(--ease-standard);
}

/* Condensed past ~40px of scroll (script.js toggles .is-scrolled). Low
   priority -- the 2px gold rule already separates the bar from the cream
   ground -- but it keeps the bar from permanently spending the same 18px
   the hero itself only pays once. */
.navbar.is-scrolled .container {
    padding-top: 12px;
    padding-bottom: 12px;
}

.logo {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: none;
    text-decoration: none;
}

.logo svg {
    display: block;
    flex: none;
}

.logo-word {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 16px;
    letter-spacing: var(--tracking-display);
    line-height: 1;
    color: var(--text-on-inverse);
}

.nav-menu {
    display: flex;
    list-style: none;
    gap: 28px;
    align-items: center;
}

.nav-menu a,
.dropdown-toggle {
    font: var(--type-body-sm);
    font-weight: 500;
    color: var(--text-on-inverse);
    text-decoration: none;
    padding: 4px 0;
    border-bottom: 2px solid transparent;
    transition: border-color var(--duration-fast) var(--ease-standard),
                color var(--duration-fast) var(--ease-standard);
}

/* The top-level items must share a box model or they will not sit on the same
   line. A <button> is inline-block, so its padding and 2px underline grow its
   <li> to 32px; the links are inline, where the same padding overflows the
   line box and leaves the <li> at 26px. Under `align-items: center` that 6px
   difference rode the Services label 3px above its neighbours. Scoped to
   direct children so the submenu links keep their own `display: block` and
   the header CTA keeps the `inline-flex` it needs for its arrow. */
.nav-menu > li > a:not(.btn),
.dropdown-toggle {
    display: inline-block;
    /* Transparent counterpart to the 2px underline. Without it the box is
       asymmetric by 2px, and `align-items: center` on the row then lifts every
       label 1.4px above where it sat when these were inline -- measured
       against production, nav labels moved 26 -> 24.6 while the header CTA
       stayed at 27. Balancing the box puts them back. */
    border-top: 2px solid transparent;
}

/* The submenu trigger is a <button>, not a link: its job is to open the
   submenu, never to go anywhere. It used to be <a href="#services">, which
   was a dead link on every page but one -- only index.html has a
   #services section -- and the one live case cost two bugs, because the same
   element had to be a link on desktop and a disclosure on mobile. A button
   also keeps it focusable, which a bare href-less <a> would not: on desktop
   the submenu opens from :focus-within, so an unfocusable trigger would put
   the submenu out of reach of the keyboard entirely. */
.dropdown-toggle {
    background: none;
    border-left: 0;
    border-right: 0;
    font-family: var(--font-body);
    cursor: pointer;
}

.nav-menu a:hover,
.nav-menu a.active,
.dropdown-toggle:hover,
.dropdown-toggle.active {
    border-bottom-color: var(--accent);
}

.dropdown {
    position: relative;
}

.dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    background: var(--ink);
    border: 1px solid var(--border-inverse);
    border-radius: var(--radius-md);
    min-width: 240px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity var(--duration-base) var(--ease-standard),
                transform var(--duration-base) var(--ease-standard),
                visibility var(--duration-base);
    list-style: none;
    padding: 6px 0;
}

.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.dropdown-menu li a {
    display: block;
    padding: 10px 20px;
    white-space: nowrap;
    color: var(--text-on-inverse-muted);
    border-bottom-color: transparent;
}

.dropdown-menu li a:hover,
.dropdown-menu li a.active {
    color: var(--accent);
    border-bottom-color: transparent;
}

.mobile-toggle {
    display: none;
    flex-direction: column;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.5rem;
}

.mobile-toggle span {
    width: 25px;
    height: 2px;
    background: var(--text-on-inverse);
    margin: 3px 0;
    transition: background var(--duration-fast) var(--ease-standard),
                transform var(--duration-fast) var(--ease-standard),
                opacity var(--duration-fast) var(--ease-standard);
}

/* The open state. The script has always toggled .active here, but nothing
   styled it, so the control looked identical open and closed -- the only
   affordance was the menu itself sliding down behind it. Bars one and three
   cross; the middle one goes. 7px is the bar's own height plus its margins,
   which is the distance to the centre. */
.mobile-toggle.active span:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
}

.mobile-toggle.active span:nth-child(2) {
    opacity: 0;
}

.mobile-toggle.active span:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
}

/* The bars are the only content, so the cross must not be animated away from
   someone who has asked for stillness -- it just appears. */
@media (prefers-reduced-motion: reduce) {
    .mobile-toggle span {
        transition: none;
    }
}

/* Inner-page hero. Same treatment as the home hero — petrol ground, display
   type, left aligned. The atom field is opt-in here via .hero-atoms, and static
   when it is used. Banner photography was removed: the design system uses no
   imagery, so the scrim it needed went with it. */
.hero {
    background: var(--petrol);
    padding: var(--section-y) 0;
}

/* Opt-in atom field for an inner-page hero. Static — the nodes drift and link
   but don't answer the cursor, so the header stays a title block. */
.hero-atoms {
    position: relative;
    overflow: hidden;
    background: radial-gradient(circle at 78% 18%, rgba(200, 155, 60, 0.06), transparent 55%), var(--petrol);
}

.hero-atoms .container {
    position: relative;
}

.hero-content h1 {
    font: var(--type-display);
    font-size: clamp(32px, 6.4vw, var(--text-display));
    letter-spacing: var(--tracking-display);
    color: var(--text-on-inverse);
    margin: 0;
    max-width: 940px;
}

.hero-content p {
    font: var(--type-body);
    font-size: var(--text-lg);
    color: var(--text-on-inverse-muted);
    margin: 24px 0 0;
    max-width: var(--container-text);
}

/* The header CTA is a .btn inside .nav-menu, so it has to out-specify that
   rule's 2px underline border. Small size, per the design. */
.nav-menu .btn {
    padding: 7px 14px;
    font-size: var(--text-micro);
    border: 1px solid var(--accent);
    /* `.nav-menu a` out-specifies .btn-primary, so the colour has to be
       restated here — otherwise the CTA renders cream on gold at 2.77:1. */
    color: var(--text-on-accent);
}

/* The rim that goes with the brightened hover fill — see --accent-hover. */
.nav-menu .btn:hover {
    border-color: var(--gold-deep);
}

/* Buttons.
   Primary is sentence-case 14px solid gold; the outlined secondary is uppercase
   12px with tracking. They are deliberately different shapes, per the design. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 15px 26px;
    /* 4px rather than the system's 2px — softened for buttons only, at the
       client's request. */
    border-radius: var(--radius-btn);
    font-family: var(--font-body);
    font-weight: 600;
    font-size: var(--text-sm);
    line-height: 1.3;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    border: 1px solid transparent;
    transition: background-color var(--duration-fast) var(--ease-standard),
                border-color var(--duration-fast) var(--ease-standard),
                color var(--duration-fast) var(--ease-standard),
                transform 80ms var(--ease-standard);
}

/* Press nudges 1px. No scale, no bounce, no spring. */
.btn:active {
    transform: translateY(1px);
}

.btn-primary {
    background: var(--accent);
    color: var(--text-on-accent);
    border-color: var(--accent);
}

/* Brightened fill, --gold-deep rim. Label holds 6.50:1. */
.btn-primary:hover {
    background: var(--accent-hover);
    border-color: var(--gold-deep);
    color: var(--text-on-accent);
}

/* Outlined CTA. Defaults to the dark-ground variant, since the heroes on the
   not-yet-redesigned pages are all dark; .cta flips it for the cream band. */
.btn-secondary {
    background: transparent;
    color: var(--text-on-inverse);
    border-color: rgba(247, 244, 238, 0.34);
    padding: 15px 24px;
    font-size: var(--text-2xs);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
}

.btn-secondary:hover {
    background: transparent;
    border-color: var(--accent);
    color: var(--accent);
}

/* Gold arrow glyph shared by outlined CTAs and card links. */
.arrow {
    display: inline-block;
    color: var(--accent);
    transition: transform var(--duration-fast) var(--ease-standard);
}

.btn:hover .arrow {
    transform: translateX(4px);
}

/* Standalone call-to-action link — the home page's .card-link treatment. */
.btn-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font: var(--type-label);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--accent-text);
    text-decoration: none;
}

.btn-link:hover .arrow {
    transform: translateX(4px);
}

/* Links inside a sentence stay sentence-shaped; the tracked uppercase treatment
   above is only for standalone CTAs. */
.text-link {
    color: var(--accent-text);
    font-weight: 600;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color var(--duration-fast) var(--ease-standard);
}

.text-link:hover {
    border-bottom-color: currentColor;
}

/* ==========================================================================
   Inner page content, held to the home page's standard.
   Same section rhythm, heading scale, 720px measure, hairline cards, mono
   kickers and gold arrow links. Nothing is centred.
   ========================================================================== */

.service-detail,
.about-section,
.contact-section {
    padding: var(--section-y) 0;
}

.service-detail .container,
.about-section .container {
    position: relative;
}

.service-detail .content,
.about-section .content {
    position: relative;
    max-width: var(--container-max);
}

/* Fills the empty gutter to the right of the 720px reading column on
   text-heavy inner pages with a few drifting dots (see initAtomField in
   script.js) instead of leaving it bare. The small cream dots are the
   same colour as the page ground and stay invisible; --atom-link tints
   the connecting lines gold instead of the field's default cream so a
   few of them read too, at the same low opacity as the sparse "big"
   nodes -- a quiet trail rather than the busy hero field. .content is
   `position: relative` above so it paints over this, and any full-width
   card or grid inside .content simply covers the field wherever the two
   overlap. Hidden below 1080px, where the gutter narrows enough to
   crowd the text column. */
.content-decor-field {
    position: absolute;
    top: 0;
    left: var(--container-text);
    right: 0;
    bottom: 0;
    pointer-events: none;
    --atom-link: 192, 138, 46;
}

/* Body paragraphs only — the label roles below are <p> too, and a bare
   `... p` would out-specify their single class. The selector-list :not() keeps
   this at (0,2,1) so component rules can still override it. */

/* Section headings use the home page's H2 role on the 720px measure. The wide
   top margin is what separates one block of content from the next. */
.service-detail h2,
.about-section h2,
.contact-section h2,
.objections h2 {
    font: var(--type-h2);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 64px 0 20px;
    max-width: var(--container-text);
    text-align: left;
}

/* A section's inset is --section-y, and nothing else should be adding to it.
   The blocks that land at a section's edges carry their own margins -- an h2
   opens with 64px (above), a .cta-card closes with 40px, a paragraph with
   16px -- and those stack on top of the section padding. That is why one
   "section of space" measured 96px above the footer on three pages, 112px on
   the two legal pages and 136px on the five service pages, and why the
   fractional hero handed off to its first heading across 257px where every
   other hero uses 192px.

   The two-selector version of this rule already existed for the leading edge
   of .service-detail and .about-section. It missed .objections, whose h2 is a
   child of .container rather than .content, and there was no trailing-edge
   rule at all. Generalised to both edges and both wrapper depths, so
   --section-y is the only thing that sets a boundary.

   Scoped to the section's own first/last block: margins between blocks inside
   a section are untouched, so the intra-section rhythm is unchanged. */
main > section > .container > :first-child,
main > section > .container > .content > :first-child {
    margin-top: 0;
}

main > section > .container > :last-child,
main > section > .container > .content > :last-child {
    margin-bottom: 0;
}

.service-detail h3,
.about-section h3 {
    font: var(--type-h4);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 32px 0 12px;
}

.service-detail p:not(.eyebrow, .kicker, .bio-meta, .associate-role),
.about-section p:not(.eyebrow, .kicker, .bio-meta, .associate-role) {
    font: var(--type-body);
    color: var(--text-body);
    margin: 0 0 16px;
    max-width: var(--container-text);
}

.service-detail ul,
.about-section ul {
    margin: 0 0 24px 20px;
    max-width: var(--container-text);
}

.service-detail li,
.about-section li {
    font: var(--type-body);
    color: var(--text-body);
    margin-bottom: 8px;
}

.service-detail strong,
.about-section strong {
    color: var(--text-strong);
    font-weight: 600;
}

/* About page opener: intro/mission text beside a portrait image, same split
   as .contact-layout. The only place on the page with an image up top rather
   than mid-scroll. */
.about-intro {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 48px;
    align-items: start;
}

.about-intro-image {
    aspect-ratio: 3 / 4;
    overflow: hidden;
    border-radius: var(--radius-md);
}

.about-intro-image img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* .about-intro stacks at 960px rather than joining the shared 768px step
   below. Between those two widths the image column is narrow while the text
   column is tall, and stretching one to the other rendered the portrait as a
   273x798 sliver. */
@media (max-width: 960px) {
    .about-intro {
        grid-template-columns: 1fr;
    }
}

/* Neither column may leave a hole under it. The text column sets the row
   height and the image takes it, instead of holding its own 3/4 and letting
   whichever side is shorter trail off into empty cream -- which it did at
   every width, on one side or the other, depending on how the copy wrapped.
   Scoped to the two-column layout: below 961px the grid is a single column,
   there is no row to match, and the ratio governs again. */
@media (min-width: 961px) {
    .about-intro-image {
        align-self: stretch;
        aspect-ratio: auto;
    }
}

/* Place band. A bounded strip of duotoned photography dropped mid-content on
   the long service/about pages, at the point where the longest unbroken run
   of cards would otherwise carry straight on into more cream. Breaks out of
   the 720px/1180px content column to full viewport width -- the standard
   negative-margin technique -- while staying an ordinary flow element, so it
   can sit directly inside .content without any layout changes around it.
   Photography as place, not as hero: see docs/VISUAL-IMPROVEMENT-PLAN.md #2. */
.place-band {
    position: relative;
    left: 50%;
    right: 50%;
    margin: 56px -50vw;
    width: 100vw;
    height: 180px;
    overflow: hidden;
}

/* Oversized by the parallax's full travel range (12px each direction, see
   initPlaceBandParallax in script.js) and re-centred with margin-top, so
   translating within that range never exposes the band's edge. */
.place-band img {
    display: block;
    width: 100%;
    height: calc(100% + 24px);
    margin-top: -12px;
    object-fit: cover;
    transform: translateY(var(--parallax, 0px));
    transition: transform 0.2s linear;
}

/* The top edge is where the band meets the paragraph above it; fading to ink
   there seats the photo against the cream ground instead of cutting it with
   a hard edge. */
.place-band::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(8, 12, 21, 0.55), transparent 45%);
    pointer-events: none;
}

@media (min-width: 769px) {
    .place-band {
        height: 280px;
        margin: 72px -50vw;
    }
}

/* Contact page. The form is a card in the same idiom as the rest: hairline
   border, 2px radius, no shadow, left aligned rather than centred. */
.contact-section p:not(.eyebrow, .kicker, .bio-meta, .associate-role) {
    font: var(--type-body);
    color: var(--text-body);
    margin: 0 0 12px;
    max-width: var(--container-text);
}

.contact-section .eyebrow {
    margin-bottom: 16px;
}

/* Form on the left, booking aside on the right. */
.contact-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 48px;
    align-items: start;
    margin-top: 40px;
}

/* The booking alternative, as a quiet card beside the form. */
.contact-aside {
    background: var(--surface-subtle);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 32px;
}

/* P2 (see docs/VISUAL-IMPROVEMENT-PLAN.md #2), bled to the card's own edges.
   This is where place matters most on the site -- next to the form asking
   someone to commit -- and the source is already portrait. */
.contact-aside-image {
    margin: -32px -32px 24px;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.contact-aside-image img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.contact-section .contact-aside h2 {
    font: var(--type-h4);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 14px 0 0;
    max-width: none;
}

.contact-aside p:not(.kicker) {
    font: var(--type-body-sm);
    color: var(--text-body);
    /* 16px, matching the heading-to-body gap in the closing CTA band. */
    margin: 16px 0 0;
}

.contact-aside .btn {
    width: 100%;
    margin-top: 24px;
}

/* Light ground, so the outlined CTA takes the sand border and dark label. */
.contact-aside .btn-secondary {
    color: var(--text-strong);
    border-color: var(--border-strong);
}

/* Same light-ground correction as section.cta / .cta-card below. */
.contact-aside .btn-secondary:hover {
    color: var(--accent-text);
    border-color: var(--accent);
}

/* The form and the panel that replaces it on success are the same white box —
   same card treatment, so the swap reads as the box changing its contents
   rather than the page replacing one component with another. */
.contact-form,
.form-success {
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    /* 2px accent top edge, the same emphasis rule the flagship card uses. */
    border-top: 2px solid var(--accent);
    border-radius: var(--radius-md);
    padding: 40px;
}

/* ---- Form feedback ----------------------------------------------------
   Both states used to be a bare line of coloured text under the button, easy
   to miss and set from inline styles in hex values outside the palette. The
   error is now a bordered alert sitting directly above the button, where the
   eye already is after a click; the success case replaces the fields entirely
   (see .form-success), since a reset form plus a small green line gave no
   clear signal that anything had happened.
   ---------------------------------------------------------------------- */
.form-alert {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    margin-bottom: 24px;
    padding: 16px 18px;
    background: var(--danger-surface);
    border: 1px solid var(--danger-border);
    /* 2px left edge — the same emphasis device as .bio-card, in the semantic
       colour rather than the accent. */
    border-left: 2px solid var(--danger);
    border-radius: var(--radius-md);
}

.form-alert[hidden] {
    display: none;
}

.form-alert-icon {
    flex: none;
    color: var(--danger);
    margin-top: 1px;
}

.form-alert-title {
    font: var(--type-label);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--danger);
    margin: 0 0 6px;
}

/* Out-specifies `.contact-section p:not(...)`, which would otherwise impose the
   16px body role and a bottom margin on both lines. */
.contact-section .form-alert p:not(.form-alert-title) {
    font: var(--type-body-sm);
    color: var(--text-strong);
    margin: 0;
    max-width: none;
}

.contact-section .form-alert p.form-alert-hint {
    color: var(--text-body);
    margin-top: 8px;
}

.form-alert a {
    color: var(--danger);
    font-weight: 600;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color var(--duration-fast) var(--ease-standard);
}

.form-alert a:hover {
    border-bottom-color: currentColor;
}

/* Focus lands here for server and network failures, so it needs a visible ring
   without the dotted default. */
.form-alert:focus-visible,
.form-success:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

/* The field a validation message points at, so the error is not only described
   but located. */
.form-group input.is-invalid,
.form-group textarea.is-invalid {
    border-color: var(--danger);
}

/* ---- Success panel ---- */
.form-success[hidden] {
    display: none;
}

.form-success-icon {
    color: var(--accent-text);
}

/* Same override pattern as .contact-aside h2 — the section's own h2 role is
   32px with a 64px top margin, which is wrong for a heading inside a card. */
.contact-section .form-success h2 {
    font: var(--type-h3);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 16px 0 0;
    max-width: none;
}

.contact-section .form-success p {
    font: var(--type-body);
    color: var(--text-body);
    margin: 16px 0 0;
    max-width: none;
}

.form-success-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 24px;
    margin-top: 28px;
    padding-top: 24px;
    border-top: 1px solid var(--border-subtle);
}

/* A .card-link that is a <button>, since it restores state rather than
   navigating. Strips the button chrome so it matches the anchor beside it. */
.link-button {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

.form-group {
    margin-bottom: 24px;
}

.form-group label {
    display: block;
    margin-bottom: 8px;
    font: var(--type-body-sm);
    font-weight: 600;
    color: var(--text-strong);
}

/* Required marker, carried in the accent rather than as plain punctuation. */
.req {
    color: var(--accent-text);
    font-weight: 600;
}

.form-group input,
.form-group textarea {
    width: 100%;
    padding: 12px 14px;
    background: var(--surface-card);
    color: var(--text-strong);
    /* Fields take the card radius, not the button radius. */
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    font: var(--type-body);
    transition: border-color var(--duration-fast) var(--ease-standard);
}

.form-group input:hover,
.form-group textarea:hover {
    border-color: var(--border-strong);
}

.form-group input:focus,
.form-group textarea:focus {
    border-color: var(--accent);
}

.form-group textarea {
    resize: vertical;
    min-height: 150px;
}

.optional-hint {
    font-weight: 400;
    color: var(--text-muted);
}

/* Honeypot. Kept in the layout but off-screen, so bots that read the DOM
   still fill it in while real users never see it. */
.hp-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.form-consent {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
}

.form-consent input[type="checkbox"] {
    width: auto;
    margin-top: 0.25rem;
    flex-shrink: 0;
}

.form-consent label {
    margin-bottom: 0;
    font: var(--type-body-sm);
    text-transform: none;
    letter-spacing: normal;
    color: var(--text-body);
}

.form-consent a {
    color: var(--accent-text);
    font-weight: 600;
}

/* Same gold button as everywhere else, just full width. */
.submit-btn {
    width: 100%;
    padding: 15px 26px;
    background: var(--accent);
    color: var(--text-on-accent);
    border: 1px solid var(--accent);
    border-radius: var(--radius-btn);
    font-family: var(--font-body);
    font-weight: 600;
    font-size: var(--text-sm);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard),
                border-color var(--duration-fast) var(--ease-standard),
                transform 80ms var(--ease-standard);
}

.submit-btn:hover {
    background: var(--accent-hover);
    border-color: var(--gold-deep);
}

.submit-btn:active {
    transform: translateY(1px);
}

/* Cards follow the home page: white ground, hairline border, 2px radius, and
   no shadow at rest — separation is carried by the border, not by elevation.
   These cards are not links, so they get no hover lift; on the home page that
   raise signals "this navigates somewhere". The links inside them do react. */
.case-study-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
    margin-top: 24px;
}

/* Same card idiom as .case-study-grid, but for the one page (fractional
   leadership) with four cards instead of three — 3 columns would orphan the
   4th card on its own row. */
.roles-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
    margin-top: 24px;
}

.case-study-card,
.associate-card {
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 28px;
}

.case-study-tag {
    display: block;
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-micro);
    letter-spacing: var(--tracking-mono);
    text-transform: uppercase;
    color: var(--accent-text);
    margin-bottom: 16px;
}

.case-study-card p:not(.kicker) {
    font: var(--type-body-sm);
    color: var(--text-body);
    margin: 0;
}

/* Bio / leader card. The 2px accent edge is the design's emphasis rule, the
   same device as the flagship card's top edge and the testimonial tick. */
.bio-card {
    background: var(--surface-subtle);
    border: 1px solid var(--border-subtle);
    border-left: 2px solid var(--accent);
    border-radius: var(--radius-md);
    padding: 32px;
    margin: 24px 0 32px;
    max-width: var(--container-max);
}

/* Extra breathing room for a .bio-card sandwiched between two other cards
   (e.g. ai-consulting.html's "Who This Is For", boxed between two
   .cta-cards) so the run of boxes doesn't read as one crowded stack. */
.bio-card-breathe {
    margin: 56px 0 64px;
}

.bio-card h3 {
    font: var(--type-h4);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 0 0 6px;
}

/* The aside's own heading, at whatever level the surrounding document calls
   for: h4 on the leadership page, where "Credentials" sits under the h3 of the
   person's name, and h3 on the service pages, where "Related Services" sits
   beside an h2 and had been skipping a level. Keyed to the container rather
   than the tag so the level stays free to follow the hierarchy instead of the
   design. Must stay after `.bio-card h3` above -- equal specificity, and that
   rule would otherwise give this the person's-name treatment. */
.bio-credentials h3,
.bio-credentials h4 {
    font: var(--type-label);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-strong);
    margin: 0 0 12px;
}

/* Narrative on the left, credentials pulled into their own scannable card on
   the right — same proportion as .contact-layout's form/aside split. Without
   this the bio read as one long, undifferentiated block of prose. */
.bio-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 40px;
    align-items: start;
    margin-top: 20px;
}

.bio-body p {
    max-width: none;
}

/* When .bio-body opens with a heading (e.g. a "Who This Is For" section
   paired with a .bio-credentials aside) it needs to start flush with the
   grid row, same as the aside's own heading — otherwise the h2's normal
   64px top margin drops the narrative column well below the aside card,
   throwing the two columns out of alignment. */
.bio-body > h2:first-child {
    margin-top: 0;
}

/* Same fix for a .bio-card used standalone (no .bio-layout split) with a
   heading as its first child -- e.g. software-development.html's boxed
   "Who This Is For". Without it the h2's 64px top margin reads as dead
   space at the top of the card, since the card's own padding blocks the
   margin from collapsing away. */
.bio-card > h2:first-child {
    margin-top: 0;
}

.bio-credentials {
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 24px;
}

/* auto-fit so a single associate's card takes the full row instead of being
   squeezed to a bare third of it; wraps into columns cleanly once there is
   more than one. Left out of the breakpoint overrides below on purpose. */
.associate-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
    margin-top: 24px;
}

.associate-card h3 {
    font: var(--type-h4);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 0 0 6px;
}

.associate-card p:not(.associate-role) {
    font: var(--type-body-sm);
    color: var(--text-body);
    margin: 0;
}

/* Closing CTA band. Cream ground, left-aligned, copy and buttons on one row.
   Scoped to section.cta because .cta is also used as an inline card inside
   service-page prose — see .cta-card below. */
section.cta {
    padding: var(--section-y) 0;
    background: var(--surface-page);
}

section.cta .container {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 40px;
    flex-wrap: wrap;
}

.cta-copy {
    max-width: 620px;
}

section.cta h2 {
    font: var(--type-h2);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 0;
}

section.cta p {
    font: var(--type-body);
    color: var(--text-body);
    margin: 16px 0 0;
}

.cta-buttons {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}

/* .cta-boxed is gone. It was a closing CTA in its own <section>, used only by
   about.html, and being a section is what made it wrong: .about-section closed
   with a 96px inset and the CTA section opened with another, stacking 192px of
   empty cream under "Let's Work Together" with no colour change or rule to
   justify it. Every other inner page closes with a .cta-card sitting in the
   content flow 40px below the last paragraph, so about.html now does too and
   the variant has no users left.

   The rule that survives from working this out: two sections stack two insets,
   which reads correctly across a colour change (index's .cta-inverse) or a
   hairline (.objections, .experience-inner) and reads as a void without one.
   A closing CTA is not a new section -- it belongs in the flow. */

/* Inline CTA card, used mid-page on the service pages. Was a gold band with a
   12px radius set inline; now a quiet card in the new system. */
.cta-card {
    background: var(--surface-subtle);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 32px;
    margin: 40px 0;
}

.cta-card h2 {
    font: var(--type-h3);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 0;
}

.cta-card p {
    font: var(--type-body);
    color: var(--text-body);
    margin: 12px 0 24px;
}

/* Light-ground variant of the outlined CTA, for both cream contexts. */
section.cta .btn-secondary,
.cta-card .btn-secondary {
    color: var(--text-strong);
    border-color: var(--border-strong);
}

/* The label takes the text-safe gold, not --accent: raw gold on these light
   grounds measures 2.77:1 on cream and 2.58:1 on the subtle card. The border
   keeps --accent, where 2.55:1 is fine for a 1px edge and still an improvement
   on the --border-strong it replaces. */
section.cta .btn-secondary:hover,
.cta-card .btn-secondary:hover {
    color: var(--accent-text);
    border-color: var(--accent);
}

/* Inverse variant of the closing CTA band — index.html only. The page opens
   and closes on the same petrol ground so the bottom third reads as one dark
   movement instead of a 272px cream sliver between two dark bands; the
   footer's --ink still separates by value. Re-points .btn-secondary back to
   its dark-ground default since the light-ground override above wins by
   specificity otherwise. */
section.cta.cta-inverse {
    background: var(--petrol);
}

section.cta.cta-inverse h2 {
    color: var(--text-on-inverse);
}

section.cta.cta-inverse p {
    color: var(--text-on-inverse-muted);
}

section.cta.cta-inverse .btn-secondary {
    color: var(--text-on-inverse);
    border-color: rgba(247, 244, 238, 0.34);
}

section.cta.cta-inverse .btn-secondary:hover {
    color: var(--accent);
    border-color: var(--accent);
}

/* Footer */
footer {
    background: var(--surface-inverse);
    color: var(--text-on-inverse);
    padding: 72px 0 32px;
}

.footer-content {
    display: grid;
    grid-template-columns: 1.4fr 1fr 1fr 1fr;
    gap: 40px;
    margin-bottom: 56px;
}

.footer-brand p {
    font: var(--type-body-sm);
    color: var(--text-on-inverse-muted);
    margin: 20px 0 0;
    max-width: 280px;
}

/* h2, not h4. These are the top-level groupings inside the <footer> landmark
   with nothing above them to nest under, and the page's last heading before
   them is an h2 -- so h4 skipped two levels for no structural reason. The look
   is unaffected: it comes from --type-label, not from the heading level. */
.footer-section h2 {
    font: var(--type-label);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-on-inverse);
    /* Wider than the 12px gap between the links themselves, so the heading reads
       as sitting above the group rather than as the first item in it. Matches
       the 20px the brand column puts under the logo. */
    margin-bottom: 20px;
}

.footer-section ul {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.footer-section ul li {
    margin: 0;
}

.footer-section a {
    font: var(--type-body-sm);
    color: var(--text-on-inverse-muted);
    text-decoration: none;
    transition: color var(--duration-fast) var(--ease-standard);
}

.footer-section a:hover {
    color: var(--accent-on-inverse);
}

/* The booking link is the single gold item in the footer — the thing to click. */
.footer-section a.footer-book-link {
    color: var(--accent-on-inverse);
}

.footer-section a.footer-book-link:hover {
    color: var(--text-on-inverse);
}

.footer-bottom {
    display: flex;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
    padding-top: 24px;
    border-top: 1px solid var(--border-inverse);
}

.footer-bottom p,
.footer-bottom a {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-micro);
    letter-spacing: var(--tracking-mono);
    color: var(--text-on-inverse-muted);
    text-decoration: none;
    transition: color var(--duration-fast) var(--ease-standard);
}

.footer-bottom a:hover {
    color: var(--accent-on-inverse);
}

.footer-bottom .legal-links {
    display: flex;
    gap: 20px;
    margin: 0;
}

    /* Responsive Design */
    @media (max-width: 768px) {
        .mobile-toggle {
            display: flex;
        }

        .nav-menu {
            position: absolute;
            top: 100%;
            left: 0;
            width: 100%;
            background: var(--ink);
            border-bottom: 1px solid var(--border-inverse);
            flex-direction: column;
            align-items: flex-start;
            gap: 0;
            padding: 12px var(--gutter-mobile);
            transform: translateY(-100%);
            opacity: 0;
            visibility: hidden;
            transition: opacity var(--duration-base) var(--ease-standard),
                        transform var(--duration-base) var(--ease-standard),
                        visibility var(--duration-base);
        }

            .nav-menu > li {
                width: 100%;
                padding: 10px 0;
            }

            /* Make the control the whole row, not just the text.
               These are inline by default, so on a 350px row the "Services"
               trigger was a 57px target with 293px beside it that looked
               tappable and did nothing -- the row reads as the button, so it
               should behave as one. The padding moves off the <li> and onto
               the control so the target covers the row's full height too,
               rather than leaving a dead 10px strip above and below.
               .btn is excluded: the header CTA is a button shape already and
               should keep its own size. */
            .nav-menu > li:not(:has(> .btn)) {
                padding: 0;
            }

            .nav-menu > li > a:not(.btn),
            .dropdown-toggle {
                display: block;
                width: 100%;
                padding: 10px 0;
                text-align: left;
            }

            .nav-menu.active {
                transform: translateY(0);
                opacity: 1;
                visibility: visible;
            }

        .dropdown-menu {
            position: static;
            opacity: 1;
            visibility: visible;
            transform: none;
            border: 0;
            min-width: 0;
            display: none;
            padding: 4px 0 0 16px;
        }

        .dropdown-menu li a {
            padding: 8px 0;
        }

        .dropdown.active .dropdown-menu {
            display: block;
        }
    }
/* Cookie consent banner (injected by script.js only when analytics is configured).
   Navy ground so the bar reads as an overlay rather than part of the page it
   covers, closed by the same gold rule the header uses. --navy is the bluest of
   the three dark grounds, which also keeps it distinct from the --ink footer
   sitting behind it. */
.consent-banner {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.5rem;
    background: var(--navy);
    border-top: 3px solid var(--accent);
    box-shadow: 0 -4px 16px rgba(8, 12, 21, 0.4);
}

.consent-banner p {
    margin: 0;
    font: var(--type-body-sm);
    /* 7.37:1 on --navy. */
    color: var(--text-on-inverse-muted);
    max-width: 60ch;
}

.consent-banner a {
    /* Full gold, not the text-safe gold: that one is tuned for light grounds and
       goes muddy here. 5.61:1 on --navy. */
    color: var(--accent-on-inverse);
    font-weight: 600;
}

.consent-banner a:hover {
    color: var(--text-on-inverse);
}

.consent-actions {
    display: flex;
    gap: 0.75rem;
    flex-shrink: 0;
}

/* On a dark ground the site's own .btn-primary and .btn-secondary already read
   correctly, so the banner only adjusts their size. */
.consent-actions .btn {
    padding: 10px 20px;
    cursor: pointer;
}

@media (max-width: 600px) {
    .consent-banner {
        flex-direction: column;
        align-items: stretch;
        text-align: center;
    }

    .consent-actions {
        justify-content: center;
    }
}

/* Objection-handling block, directly under the hero on
   fractional-technology-leadership.html (the only page that uses it).

   The bottom padding used to be 48px rather than --section-y, to cut what was
   measured as "~230px of dead cream space" running into the .service-detail
   below. Two 96px insets only come to 192px, though: the extra ~40px was the
   trailing margin of this section's last block leaking past the padding, the
   same leak that made the gap above the footer 136px on the service pages.
   That is fixed at the source now (see the edge-margin rule near the section
   headings), so the trim was correcting a symptom and has been removed --
   this boundary is a standard 192px like every other one on the site.

   The borders stay. They were added alongside the trim so the handoff reads
   as one deliberate break rather than two padding values in a row, and that
   is still true and still worth having on a same-colour boundary. */
.objections {
    padding: var(--section-y) 0;
    border-top: 1px solid var(--border-subtle);
    border-bottom: 1px solid var(--border-subtle);
}

.objection-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
}

/* 2px accent top edge, matching the flagship card on the home page. */
.objection-card {
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-top: 2px solid var(--accent);
    border-radius: var(--radius-md);
    padding: 28px;
}

.objection-card h3 {
    font: var(--type-h4);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 0 0 12px;
}

.objection-card p:not(.kicker) {
    font: var(--type-body-sm);
    color: var(--text-body);
    margin: 0;
}


/* Credentials list inside the bio card. Grouped and scannable — the previous
   prose paragraph buried the qualifications a buyer is actually scanning for. */
.credential-list {
    margin: 0 0 0 20px;
}

.credential-list li {
    font: var(--type-body-sm);
    color: var(--text-body);
    margin-bottom: 8px;
}

.credential-list strong {
    color: var(--text-strong);
    font-weight: 600;
}

/* Related-services list inside a .bio-credentials aside — stacked .card-link
   entries, so no bullet marker (the gold arrow already carries that role).
   Scoped to .bio-credentials to out-specify the generic `.service-detail ul`
   indent/margin rule above. */
.bio-credentials .related-list {
    list-style: none;
    margin: 0;
    max-width: none;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.bio-link {
    margin: 20px 0 0;
}

/* Bullet markers take the accent, matching the buttons. Lists that opt out of
   markers (nav, footer, card lists) are unaffected. */
ul li::marker {
    color: var(--accent);
}


/* ==========================================================================
   Home page — "Advisory Warm" redesign.
   Only index.html uses these rules. Section padding is vertical only; the
   shared .container supplies the horizontal gutter.
   ========================================================================== */

/* Shared type roles used across the home sections */
.eyebrow {
    font-family: var(--font-body);
    font-weight: 600;
    font-size: var(--text-eyebrow);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--text-muted);
}

.eyebrow-on-inverse {
    color: var(--text-on-inverse);
}

/* Meta and role lines share the kicker's mono treatment, muted rather than gold
   — the same distinction the home page draws for the testimonial attribution. */
.bio-meta,
.associate-role {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-micro);
    letter-spacing: var(--tracking-mono);
    color: var(--text-muted);
    font-style: normal;
    margin: 0 0 16px;
}

.kicker {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-micro);
    letter-spacing: var(--tracking-mono);
    text-transform: uppercase;
    color: var(--accent-text);
}

/* Card and CTA links: uppercase label plus the gold arrow. */
.card-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font: var(--type-label);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    /* Text-safe gold: these labels are 12px, so they need the full 4.5:1. */
    color: var(--accent-text);
    text-decoration: none;
}

/* "Gold is the thing to click": today only the arrow moves on hover. This
   grows a 1px rule under the whole label to match. */
.card-link::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -3px;
    width: 0;
    height: 1px;
    background: var(--accent);
    transition: width var(--duration-base) var(--ease-standard);
}

.card-link:hover::after,
.card-link:focus-visible::after {
    width: 100%;
}

/* Scroll reveal. JS adds .reveal-pending only to below-the-fold elements and
   removes it as they enter the viewport. Under reduced motion the class is
   never added, which makes all of this inert. */
[data-reveal] {
    transition: opacity var(--duration-slow) var(--ease-standard),
                transform var(--duration-slow) var(--ease-standard);
}

[data-reveal].reveal-pending {
    opacity: 0;
    transform: translateY(14px);
}

/* Staggers the reveal across a grid's children instead of every tile fading
   in as one block. --i is assigned per child below; capped with min() so a
   grid gaining more children later still lands within 200ms. */
.service-grid > [data-reveal],
.hero-stats > [data-reveal] {
    transition-delay: min(calc(var(--i, 0) * 40ms), 200ms);
}

.service-grid > [data-reveal]:nth-child(1) { --i: 0; }
.service-grid > [data-reveal]:nth-child(2) { --i: 1; }
.service-grid > [data-reveal]:nth-child(3) { --i: 2; }
.service-grid > [data-reveal]:nth-child(4) { --i: 3; }
.service-grid > [data-reveal]:nth-child(5) { --i: 4; }
.service-grid > [data-reveal]:nth-child(6) { --i: 5; }

.hero-stats > [data-reveal]:nth-child(1) { --i: 0; }
.hero-stats > [data-reveal]:nth-child(2) { --i: 1; }
.hero-stats > [data-reveal]:nth-child(3) { --i: 2; }

/* --- Hero --- */
.home-hero {
    position: relative;
    overflow: hidden;
    background: radial-gradient(circle at 78% 18%, rgba(200, 155, 60, 0.06), transparent 55%), var(--petrol);
    padding: var(--section-y) 0 0;
}

/* The atom field sits behind the content and must never intercept clicks. */
.atom-field {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
}

.home-hero .container {
    position: relative;
}

.hero-title {
    font: var(--type-display);
    /* Degrades continuously rather than stepping at a breakpoint. */
    font-size: clamp(32px, 6.4vw, var(--text-display));
    letter-spacing: var(--tracking-display);
    color: var(--text-on-inverse);
    margin: 24px 0 0;
    max-width: 940px;
}

/* The hyphenated phrase must never break across lines. */
.hero-title .nowrap {
    white-space: nowrap;
}

.hero-sub {
    font: var(--type-body);
    font-size: var(--text-lg);
    color: var(--text-on-inverse-muted);
    margin: 24px 0 0;
    max-width: var(--container-text);
}

.hero-actions {
    display: flex;
    gap: 12px;
    margin-top: 36px;
    flex-wrap: wrap;
}

.hero-trust {
    font: var(--type-body-sm);
    color: var(--text-on-inverse-muted);
    margin: 40px 0 0;
}

/* --- Hero stats. Three columns split by hairlines, closing the dark band. --- */
.hero-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1px;
    margin-top: 40px;
    border-top: 1px solid var(--border-inverse);
}

.stat {
    padding: 32px 0 40px;
}

.stat + .stat {
    padding-left: 32px;
}

.stat:not(:last-child) {
    border-right: 1px solid var(--border-inverse);
}

.stat-number {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 44px;
    line-height: 1.1;
    letter-spacing: var(--tracking-display);
    color: var(--accent);
}

.stat-label {
    font: var(--type-label);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-on-inverse-muted);
    margin-top: 8px;
}

/* --- Services --- */
.services-section {
    padding: var(--section-y) 0;
}

.section-heading {
    font: var(--type-h2);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 16px 0 0;
    max-width: var(--container-text);
}

/* Cards carry no shadow at rest; hairlines do the separating. */
.flagship-card {
    margin-top: 40px;
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-top: 2px solid var(--accent);
    border-radius: var(--radius-md);
    padding: 40px;
    display: grid;
    grid-template-columns: 1.1fr 1fr;
    gap: 48px;
    transition: box-shadow var(--duration-base) var(--ease-standard),
                border-color var(--duration-base) var(--ease-standard);
}

.flagship-card h3 {
    font: var(--type-h3);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 14px 0 0;
}

.flagship-card .card-link {
    margin-top: 28px;
}

/* :not(.kicker) so the mono kicker keeps its own type — a bare `.flagship-card p`
   out-specifies `.kicker` and was rendering it as 16px body text. */
.flagship-card p:not(.kicker) {
    font: var(--type-body);
    color: var(--text-body);
    margin: 0;
}

.services-lead-in {
    font: var(--type-body-sm);
    color: var(--text-muted);
    margin: 48px 0 20px;
}

.service-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
}

.service-tile {
    display: flex;
    flex-direction: column;
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 28px;
    text-decoration: none;
    transition: box-shadow var(--duration-base) var(--ease-standard),
                border-color var(--duration-base) var(--ease-standard);
}

/* The "not sure which one you need" card is the quiet one in the set. */
.service-tile-subtle {
    background: var(--surface-subtle);
    justify-content: space-between;
}

/* The tile's header row: mono numeral on the left, brand glyph on the right.
   The glyph set is generated by tools/build-glyphs.py — edit the geometry
   there and re-run rather than hand-editing the SVG in the markup. */
.service-tile-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.service-numeral {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-micro);
    letter-spacing: var(--tracking-mono);
    color: var(--text-muted);
}

/* --accent-text rather than --accent: the glyph sits on white and on the
   subtle card, where the raw gold measures under 4:1. */
.service-glyph {
    flex: none;
    color: var(--accent-text);
    transition: transform var(--duration-base) var(--ease-standard);
}

.service-tile:hover .service-glyph,
.service-tile:focus-visible .service-glyph {
    transform: translateX(2px);
}

@media (prefers-reduced-motion: reduce) {
    .service-glyph {
        transition: none;
    }
}

.service-tile h3 {
    font: var(--type-h4);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin-top: 20px;
}

/* The subtle tile used to zero this because it had no numeral row to clear.
   It now carries a glyph row like the other five, so it takes the same gap. */

.service-tile p {
    font: var(--type-body-sm);
    color: var(--text-body);
    margin: 12px 0 24px;
}

.service-tile .card-link {
    margin-top: auto;
}

/* Card hover: raise, strengthen the border, nudge the arrow. Done in CSS rather
   than JS so keyboard focus gets the same treatment for free. */
.flagship-card:hover,
.service-tile:hover,
.service-tile:focus-visible {
    box-shadow: var(--shadow-raise);
    border-color: var(--border-strong);
}

.flagship-card:hover .arrow,
.service-tile:hover .arrow,
.service-tile:focus-visible .arrow,
.card-link:hover .arrow {
    transform: translateX(4px);
}

/* --- Experience tabs --- */
.experience-section {
    padding: 0 0 var(--section-y);
}

/* Same job as the border on .objections: a hairline break between two sections
   that share the cream ground, where no colour change does the separating.
   The inset was a magic 80px; it is --section-y now, so the two divided
   boundaries on the site open at the same distance from their rule. The
   section itself carries no top padding because this inner block provides it,
   which is what puts the rule flush against the services grid above. */
.experience-inner {
    border-top: 1px solid var(--border-subtle);
    padding-top: var(--section-y);
}

.experience-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 28px;
    margin-top: 40px;
    border-bottom: 1px solid var(--border-subtle);
}

.experience-tabs button {
    background: none;
    border: 0;
    border-bottom: 2px solid transparent;
    padding: 0 0 14px;
    margin: 0;
    cursor: pointer;
    font: var(--type-label);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-muted);
    transition: color var(--duration-fast) var(--ease-standard),
                border-color var(--duration-fast) var(--ease-standard);
}

.experience-tabs button:hover {
    color: var(--text-strong);
}

.experience-tabs button[aria-selected="true"] {
    color: var(--text-strong);
    border-bottom-color: var(--accent);
}

.experience-panels {
    padding-top: 40px;
    min-height: 140px;
    align-content: start;
}

.experience-panel {
    display: grid;
    grid-template-columns: 300px 1fr;
    gap: 48px;
    transition: opacity var(--duration-base) var(--ease-standard);
}

.experience-panel[hidden] {
    display: none;
}

.experience-panel.is-entering {
    opacity: 0;
}

.experience-panel h3 {
    font: var(--type-h4);
    letter-spacing: var(--tracking-display);
    color: var(--text-strong);
    margin: 0;
}

.experience-panel .kicker {
    margin-top: 10px;
}

.experience-panel p:not(.kicker) {
    font: var(--type-body);
    color: var(--text-body);
    margin: 0;
    max-width: var(--container-text);
}

.experience-panel strong {
    color: var(--text-strong);
    font-weight: 600;
}

/* --- Testimonial band --- */
.testimonial-band {
    position: relative;
    background: var(--surface-inverse);
    padding: var(--section-y) 0;
    overflow: hidden;
}

/* P1 (see docs/VISUAL-IMPROVEMENT-PLAN.md #2) at low opacity so the ink still
   wins -- texture on the largest dark block on the page, not a photo behind
   the quote. Verified >=4.5:1 for --text-on-inverse-muted over the brightest
   composited pixel; comes out around 6.5:1. */
.testimonial-band::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: url("images/place/home-testimonial-mobile.webp?v=20260825");
    background-size: cover;
    background-position: center;
    opacity: 0.12;
    pointer-events: none;
}

@media (min-width: 769px) {
    .testimonial-band::before {
        background-image: url("images/place/home-testimonial-desktop.webp?v=20260825");
    }
}

.testimonial-band .container {
    position: relative;
}

.testimonial-quote {
    position: relative;
    z-index: 0;
    font: var(--type-h2);
    letter-spacing: var(--tracking-display);
    color: var(--text-on-inverse);
    margin: 28px 0 0;
    max-width: 900px;
}

/* The open-quote glyph, low-opacity, behind the actual quote text -- an
   anchor for the block the way the gold hairline anchors .testimonial-
   attribution below it. z-index: -1 needs .testimonial-quote's own stacking
   context (the z-index: 0 above) to land behind the text rather than behind
   the whole band. */
.testimonial-quote::before {
    content: "\201C";
    position: absolute;
    z-index: -1;
    top: -0.5em;
    left: -0.05em;
    font-family: var(--font-display);
    font-size: 200px;
    line-height: 1;
    color: var(--accent);
    opacity: 0.18;
    pointer-events: none;
}

.testimonial-body {
    font: var(--type-body);
    color: var(--text-on-inverse-muted);
    margin: 32px 0 0;
    max-width: var(--container-text);
}

.testimonial-attribution {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-top: 36px;
    padding-top: 24px;
    border-top: 1px solid var(--border-inverse);
    max-width: var(--container-text);
}

/* The 2px gold tick. */
.testimonial-attribution::before {
    content: "";
    flex: none;
    width: 2px;
    height: 34px;
    background: var(--accent);
}

.testimonial-name {
    font: var(--type-body-sm);
    font-weight: 600;
    color: var(--text-on-inverse);
}

.testimonial-role {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-micro);
    letter-spacing: var(--tracking-mono);
    color: var(--text-on-inverse-muted);
    margin-top: 4px;
}

/* ==========================================================================
   Home page responsive.
   The handoff is desktop-only and states mobile was not designed; the steps
   below are ours, following its suggested minimums.
   ========================================================================== */

@media (max-width: 1080px) {
    .container {
        padding: 0 var(--gutter-md);
    }

    /* .navbar .container sets its own padding, so it needs the narrower gutter
       spelled out too — otherwise the logo stops aligning with the headline. */
    .navbar .container {
        padding: 18px var(--gutter-md);
    }

    .service-grid,
    .case-study-grid,
    .objection-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .bio-layout {
        grid-template-columns: 1fr;
        gap: 24px;
    }

    .flagship-card {
        grid-template-columns: 1fr;
        gap: 24px;
        padding: 32px;
    }

    .experience-panel {
        grid-template-columns: 1fr;
        gap: 20px;
    }

    .content-decor-field {
        display: none;
    }
}

@media (max-width: 768px) {
    .container {
        padding: 0 var(--gutter-mobile);
    }

    /* Must sit after the 1080px rule above, which also targets this selector. */
    .navbar .container {
        padding: 14px var(--gutter-mobile);
    }

    .home-hero {
        padding-top: 56px;
    }

    .service-grid,
    .case-study-grid,
    .roles-grid,
    .objection-grid {
        grid-template-columns: 1fr;
    }

    /* .hero belongs in this group and had been left out of it, so every other
       section dropped to 56px on a phone while the inner-page hero kept the
       full 96px. That put the first heading 96px under the header on ten
       pages against 56px on the home page, and made every hero->content
       handoff 152px where two mobile sections meet at 112px. */
    .hero,
    .service-detail,
    .about-section,
    .contact-section,
    .objections {
        padding: 56px 0;
    }

    .service-detail h2,
    .about-section h2,
    .contact-section h2,
    .objections h2 {
        margin-top: 48px;
    }

    .contact-layout {
        grid-template-columns: 1fr;
        gap: 24px;
    }

    .contact-form,
    .form-success {
        padding: 24px;
    }

    .contact-aside {
        padding: 24px;
    }

    .contact-aside-image {
        margin: -24px -24px 24px;
    }

    /* Stats keep their 3-column row and vertical hairlines on mobile — three
       short values fit fine; only the type shrinks. Stacking cost 230px of
       above-the-fold height for no readability gain. */
    .stat {
        padding: 20px 0 24px;
    }

    .stat + .stat {
        padding-left: 16px;
    }

    .stat-number {
        font-size: 28px;
    }

    .stat-label {
        font-size: var(--text-eyebrow);
    }

    /* Tabs become a horizontally scrollable row rather than wrapping into a
       block that pushes the panel off screen. The right-edge fade is the only
       hint that there's more to scroll to, so it isn't masked as clipping. */
    .experience-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        scroll-snap-type: x proximity;
        gap: 20px;
        -webkit-overflow-scrolling: touch;
        mask-image: linear-gradient(to right, black calc(100% - 32px), transparent 100%);
        -webkit-mask-image: linear-gradient(to right, black calc(100% - 32px), transparent 100%);
    }

    .experience-tabs button {
        flex: none;
        scroll-snap-align: start;
    }

    /* 56px, matching the mobile section inset above, so the divided boundary
       opens at the same distance from its rule as an undivided one does. */
    .experience-inner {
        padding-top: 56px;
    }

    .services-section,
    .testimonial-band,
    section.cta {
        padding: 56px 0;
    }

    .experience-section {
        padding-bottom: 56px;
    }

    .footer-content {
        grid-template-columns: 1fr 1fr;
        gap: 32px;
    }

    section.cta .container {
        align-items: flex-start;
    }
}

@media (max-width: 560px) {
    .footer-content {
        grid-template-columns: 1fr;
    }

    .hero-actions .btn,
    .cta-buttons .btn {
        width: 100%;
    }
}

/* Reduced motion: the reveal, the counters and the atom field are all skipped
   in script.js. This covers the CSS-driven transitions the design adds. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    [data-reveal],
    .experience-panel,
    .arrow,
    .navbar .container,
    .card-link::after,
    .place-band img {
        transition: none;
    }

    [data-reveal].reveal-pending {
        opacity: 1;
        transform: none;
    }
}
