/* =========================================================
   WINDSOR CLEARWATER — "THE REGISTER"
   A visual system drawn from maritime registries, ship's
   charts and classification-society reports rather than
   yacht-brochure luxury: hairline rules instead of shadows,
   folio/coordinate labels instead of badges, ledger lists
   instead of cards.
   ========================================================= */

:root {
    /* Palette */
    --ink: #101e2b;
    --ink-soft: #17293c;
    --paper: #f4f0e6;
    --brass: #52606b;
    --brass-light: #c2cad0;
    --anthracite: #303436;
    --signal: #a63b2e;

    --text: #1c2a33;
    --text-muted: #5c6670;
    --text-on-ink: #f4f0e6;
    --text-on-ink-muted: #aab3ba;

    --hairline: rgba(16, 30, 43, 0.15);
    --hairline-on-ink: rgba(244, 240, 230, 0.2);

    /* Type */
    --font-display: 'Playfair Display', serif;
    /* Kept the --font-mono name (it drives every eyebrow label, the
       nav, buttons, footer and form labels below) even though it's no
       longer a monospace face — renaming it would mean touching every
       rule that references it for no functional gain. Helvetica Neue
       is macOS/iOS-only; Windows/Android fall through to Arial. */
    --font-mono: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --font-body: 'Lato', sans-serif;

    /* Rhythm */
    --gutter: clamp(20px, 5vw, 64px);
    --max: 1400px;
    --header-h: 84px;

    /* Motion system — a small, consistent set of durations/easing so
       every animation on the site (reveals, hovers, ambient loops)
       reads as one deliberate language rather than assorted effects. */
    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --dur-xs: 0.25s;
    --dur-sm: 0.4s;
    --dur-md: 0.9s;
    --dur-lg: 1.3s;
    --reveal-y: 20px;
}

/* ===== RESET & BASE ===== */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* scroll-behavior: smooth is deliberately NOT set globally here — it would
   fight the browser's own native wheel/trackpad momentum during ambient
   scrolling, which is exactly what made section transitions feel
   inconsistent across devices (more noticeable on high refresh-rate
   trackpads). The one place a smooth *animated* jump is still wanted —
   in-page anchor links — gets it explicitly via
   scrollIntoView({behavior:'smooth'}) in script.js instead, which doesn't
   fight live scroll input since it's a one-shot navigation. */
/* scrollbar-gutter: stable reserves the vertical scrollbar's track from
   the very first paint, on browsers that render a classic (non-overlay)
   scrollbar. Without it, that space isn't reserved until the browser
   actually commits to showing the scrollbar — on a page this tall that
   happens almost immediately, but not quite instantly, so width-based
   layout (grid columns, %-based blocks) briefly renders ~16px wider,
   then snaps narrower the moment the scrollbar appears. Mobile/overlay
   scrollbars don't reserve layout space either way, so this is a no-op
   there — it only steadies desktop-class rendering. */
html {
    /* This site has exactly one, hand-designed color scheme — never a
       system/browser-driven dark variant. Without this, browsers that
       support prefers-color-scheme still assume the page might support
       dark mode and auto-theme native UI (form controls, scrollbars) to
       the OS setting even though no dark colors are ever declared here;
       "light only" (not just "light") also stops user-forced dark modes
       (e.g. some browsers'/OS's page-level dark override) from
       overriding it. */
    color-scheme: light only;
    scroll-padding-top: var(--header-h);
    scrollbar-gutter: stable;
    /* Every page now ends on the dark footer. Without this, the body's
       own --paper background is what paints the canvas beyond the
       document's edge — visible as a beige strip in the elastic
       overscroll/rubber-band area past the bottom (touch, and trackpad
       on desktop). Matching it to the footer's ink here means
       overscrolling past the end just extends the footer's color
       instead of exposing the page's base tone. */
    background-color: var(--ink);
}

body {
    font-family: var(--font-body);
    line-height: 1.7;
    color: var(--text);
    background-color: var(--paper);
    padding-top: var(--header-h);
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    font-weight: 500;
    color: var(--ink);
}

a {
    color: inherit;
}

img {
    max-width: 100%;
    display: block;
}

::selection {
    background: var(--brass);
    color: var(--paper);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
    outline: 2px solid var(--brass);
    outline-offset: 3px;
}

/* ===== REDUCED MOTION =====
   Every scroll-reveal, ambient loop and load-in stagger added below is
   disabled here in one place: content simply appears in its resting
   state instead of animating into it. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    .reveal,
    .reveal-stagger > *,
    .quote-reveal .quote-mark,
    .quote-reveal .quote-text,
    .hero-content > * {
        opacity: 1 !important;
        transform: none !important;
    }
}

/* ===== SHARED SYSTEM COMPONENTS ===== */

/* Folio / reference label — the recurring registry motif */
.folio-label {
    display: inline-flex;
    align-items: center;
    gap: 14px;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--brass);
    margin-bottom: clamp(14px, 2.5vh, 28px);
}

.folio-label::before {
    content: '';
    width: 28px;
    height: 1px;
    background: currentColor;
    opacity: 0.7;
}

.folio-label--on-ink {
    color: var(--brass-light);
}

.folio-label::after {
    content: '';
    width: 28px;
    height: 1px;
    background: currentColor;
    opacity: 0.7;
}

/* A very small, slow drift on the folio-label's bracketing dashes —
   the one continuously "alive" detail on the page, deliberately kept
   to this single recurring motif rather than scattered everywhere. */
@media (min-width: 640px) {
    .folio-label::before,
    .folio-label::after {
        animation: folio-dash-drift 6s ease-in-out infinite;
    }

    .folio-label::after {
        animation-delay: -3s;
    }
}

@keyframes folio-dash-drift {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-2px); }
}

.coordinates {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.1em;
    color: var(--text-muted);
    text-transform: uppercase;
}

/* Generic scroll reveal for labels / text blocks */
.reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.reveal.is-visible {
    opacity: 1;
    transform: translateY(0);
}

/* Grouped content reveal — direct children cascade in with a short
   stagger once their shared parent enters view, instead of every list
   item animating on its own trigger. */
.reveal-stagger > * {
    opacity: 0;
    transform: translateY(var(--reveal-y));
    transition: opacity var(--dur-md) var(--ease), transform var(--dur-md) var(--ease);
}

.reveal-stagger.is-visible > * {
    opacity: 1;
    transform: translateY(0);
}

.reveal-stagger > *:nth-child(1) { transition-delay: 0s; }
.reveal-stagger > *:nth-child(2) { transition-delay: 0.08s; }
.reveal-stagger > *:nth-child(3) { transition-delay: 0.16s; }
.reveal-stagger > *:nth-child(4) { transition-delay: 0.24s; }
.reveal-stagger > *:nth-child(5) { transition-delay: 0.32s; }
.reveal-stagger > *:nth-child(6) { transition-delay: 0.4s; }
.reveal-stagger > *:nth-child(7) { transition-delay: 0.48s; }
.reveal-stagger > *:nth-child(8) { transition-delay: 0.56s; }

/* Tracked text link — replaces boxed buttons */
.text-cta {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--ink);
    background: none;
    border: none;
    padding: 0 0 4px;
    border-bottom: 1px solid var(--hairline);
    cursor: pointer;
    transition: color var(--dur-sm) ease, border-color var(--dur-sm) ease, transform var(--dur-sm) var(--ease);
}

.text-cta::after {
    content: '\2192';
    transition: transform var(--dur-sm) var(--ease);
}

.text-cta:hover {
    color: var(--brass);
    border-color: var(--brass);
    transform: translateX(2px);
}

.text-cta:hover::after {
    transform: translateX(5px);
}

.text-cta--on-ink {
    color: var(--text-on-ink);
    border-color: var(--hairline-on-ink);
}

.text-cta--on-ink:hover {
    color: var(--brass-light);
    border-color: var(--brass-light);
}

.max-width {
    max-width: var(--max);
    margin: 0 auto;
    padding-left: var(--gutter);
    padding-right: var(--gutter);
}

/* ===== HEADER & NAVIGATION ===== */
.header {
    background-color: var(--ink);
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    z-index: 1000;
    border-bottom: 1px solid var(--hairline-on-ink);
    transition: box-shadow var(--dur-sm) var(--ease);
}

/* Very subtle lift once the page has scrolled past the hero — the
   header otherwise never changes, keeping the effect understated. */
.header--scrolled {
    box-shadow: 0 12px 28px rgba(6, 12, 18, 0.28);
}

.logo-container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 32px;
    padding: 0 var(--gutter);
    height: var(--header-h);
}

.logo-link {
    text-decoration: none;
    display: flex;
    align-items: center;
    flex-shrink: 0;
}

.logo-image {
    max-height: 34px;
    width: auto;
    display: block;
    object-fit: contain;
    transition: opacity var(--dur-sm) var(--ease);
}

.logo-link:hover .logo-image {
    opacity: 0.8;
}

.logo-image.logo-large {
    max-height: 34px;
}

.main-nav {
    flex: 0 0 auto;
}

.main-nav-links {
    list-style: none;
    display: flex;
    align-items: center;
    gap: 40px;
    margin: 0;
    padding: 0;
}

.nav-link {
    position: relative;
    color: var(--text-on-ink-muted);
    text-decoration: none;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    padding-bottom: 8px;
    transition: color 0.3s ease;
}

.nav-link::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    background: var(--brass-light);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

.nav-link:hover,
.nav-link.active {
    color: var(--text-on-ink);
}

.nav-link:hover::after,
.nav-link.active::after {
    transform: scaleX(1);
    transform-origin: left;
}

/* ===== MOBILE NAV (hamburger + dropdown) =====
   Hidden entirely above the 1024px breakpoint (see the media query
   below), where the inline .main-nav-links above stays exactly as it
   was. Both live in the header markup on every page; only one is ever
   visible at a given width. */
.nav-toggle {
    display: none;
    position: relative;
    width: 34px;
    height: 34px;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
}

/* Each bar is positioned by `top` (a layout property) rather than
   centered via flex/transform — the open state below then animates
   `top` and `transform: rotate()` as two separate, simple properties
   instead of a combined translate+rotate on one transform. Combining
   both in one transform interpolates as a single matrix, which swings
   the bar through an arc (briefly reads as a star, then two dots) —
   animating them apart keeps it a plain "slide to center, then/while
   rotate" so it reads as a clean X, same as the very first version. */
.nav-toggle-bar {
    display: block;
    position: absolute;
    left: 6px;
    right: 6px;
    height: 1.5px;
    background-color: var(--text-on-ink);
    transition: top var(--dur-sm) var(--ease), transform var(--dur-sm) var(--ease), opacity var(--dur-sm) var(--ease);
}

.nav-toggle-bar:nth-child(1) { top: 8.75px; }
.nav-toggle-bar:nth-child(2) { top: 16.25px; }
.nav-toggle-bar:nth-child(3) { top: 23.75px; }

/* The three bars resolve into an X while the panel is open — the same
   affordance language as everywhere else on the site, just built from
   the hairline weight already used for rules and dividers. */
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(1) {
    top: 16.25px;
    transform: rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(2) {
    opacity: 0;
}

.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(3) {
    top: 16.25px;
    transform: rotate(-45deg);
}

/* A full-screen takeover rather than a small anchored dropdown — the
   whole page below the header goes dark-ink, with the four pages
   centered in the middle of that space and the language picker
   pinned to the bottom-left. display:none by default (desktop); the
   media query below switches it to a real, animatable box on mobile,
   where opacity/transform/visibility do the actual open/close work so
   it can fade and settle into place instead of snapping. */
.mobile-nav {
    display: none;
    position: fixed;
    top: var(--header-h);
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 999;
    flex-direction: column;
    background-color: var(--ink);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-10px);
    transition: opacity var(--dur-sm) var(--ease), transform var(--dur-sm) var(--ease), visibility var(--dur-sm);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.mobile-nav.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

/* Centered in whatever vertical space is left once the bottom
   language block (flex-shrink:0, see .mobile-nav-bottom below) has
   taken its own height — grows/shrinks with the viewport rather than
   a fixed offset, so it stays centered regardless of device height. */
.mobile-nav-links {
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* A short, centered rule instead of a full-width border — ::before
   rather than border-top so its width can stay independent of the
   li's own (full-width) box. */
.mobile-nav-links li + li {
    position: relative;
}

.mobile-nav-links li + li::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 88px;
    height: 1px;
    background: var(--hairline-on-ink);
}

/* Reuses .nav-link's typeface, tracking and color logic — just centered
   and without the left-rule indicator that made sense for a
   left-aligned list but not a centered one. Sized clearly below the
   old inline-nav scale but still a step above .mobile-lang-option
   below, so the page links read as the primary tier and the
   language picker as the clearly secondary one. */
.mobile-nav-link {
    display: block;
    text-align: center;
    font-size: clamp(14px, 3.6vw, 16px);
    letter-spacing: 0.12em;
    padding: clamp(16px, 3vh, 26px) 24px;
    border-left: none;
    transition: background-color var(--dur-sm) ease, color 0.3s ease;
}

.mobile-nav-link::after {
    content: none;
}

.mobile-nav-link:hover,
.mobile-nav-link.active {
    background-color: rgba(244, 240, 230, 0.05);
}

/* Bottom language block: the globe sits above the three options, both
   centered under the page links above them — a deliberate,
   smaller-scale secondary element rather than a second row of primary
   nav. flex-shrink:0 keeps it pinned to its own content height so
   .mobile-nav-links above can freely claim the rest of the space. */
.mobile-nav-bottom {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding: 12px clamp(24px, 8vw, 48px) clamp(28px, 6vh, 48px);
}

.mobile-lang-globe {
    display: inline-flex;
    color: var(--text-on-ink-muted);
}

.mobile-lang-globe .lang-switch-icon {
    width: 20px;
    height: 20px;
}

.mobile-lang-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
}

.mobile-lang-option {
    display: block;
    background: none;
    border: none;
    padding: 0;
    color: var(--text-on-ink-muted);
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 300;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-align: center;
    cursor: pointer;
    transition: color 0.3s ease;
}

.mobile-lang-option:hover,
.mobile-lang-option[aria-checked="true"] {
    color: var(--text-on-ink);
}

/* ===== LANGUAGE SWITCHER (desktop) =====
   An extra <li> at the end of .main-nav-links (same flex row, same
   40px gap as Home/Services/Partnership — see .main-nav-links above),
   so it sits "immediately to the right of Partnership" for free rather
   than needing its own positioning. Icon-only on desktop by design —
   the dropdown itself is where the current language is confirmed, so
   the trigger stays a single quiet glyph rather than a second visible
   text label competing with the three nav words next to it. */
.lang-switch {
    position: relative;
    display: flex;
    align-items: center;
}

.lang-switch-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: 4px;
    margin: 0;
    cursor: pointer;
    color: var(--text-on-ink-muted);
    line-height: 1;
    transition: color 0.3s ease;
    -webkit-tap-highlight-color: transparent;
}

.lang-switch-toggle:hover,
.lang-switch-toggle[aria-expanded="true"] {
    color: var(--text-on-ink);
}

.lang-switch-icon {
    width: 17px;
    height: 17px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.3;
}

/* A small dropdown (ink panel, hairline border, soft shadow, fade +
   settle transition) — desktop-only, so unlike the full-screen mobile
   nav below this stays a compact popover anchored under the toggle,
   right-aligned so it never overhangs the viewport edge from the
   rightmost nav item. */
.lang-menu {
    position: absolute;
    top: calc(100% + 18px);
    right: -10px;
    z-index: 999;
    min-width: 150px;
    list-style: none;
    margin: 0;
    padding: 8px 0;
    background-color: var(--ink);
    border: 1px solid var(--hairline-on-ink);
    box-shadow: 0 20px 40px rgba(6, 12, 18, 0.35);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity var(--dur-sm) var(--ease), transform var(--dur-sm) var(--ease), visibility var(--dur-sm);
}

.lang-menu.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

/* Same "row in a small dark panel" language as the rest of the header
   dropdowns — a left-rule indicator plus hover fill, and its own
   checkmark to mark the active language. */
.lang-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    width: 100%;
    background: none;
    border: none;
    border-left: 2px solid transparent;
    padding: 10px 18px;
    color: var(--text-on-ink-muted);
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.02em;
    text-align: left;
    cursor: pointer;
    transition: background-color var(--dur-sm) ease, color 0.3s ease, border-color var(--dur-sm) ease;
}

.lang-option:hover,
.lang-option:focus-visible {
    background-color: rgba(244, 240, 230, 0.05);
    border-left-color: var(--brass-light);
    color: var(--text-on-ink);
}

.lang-option[aria-checked="true"] {
    color: var(--text-on-ink);
}

.lang-option-check {
    color: var(--brass-light);
    opacity: 0;
    font-size: 11px;
}

.lang-option[aria-checked="true"] .lang-option-check {
    opacity: 1;
}

/* ===== ONE VIEWPORT TALL, MINIMUM =====
   Every main section is at least one viewport (minus the fixed header)
   tall on desktop, so short sections still read as a full "screen" —
   but never taller than that only, and never scrolls internally.
   Content that exceeds one viewport simply pushes the section taller
   and the page keeps scrolling through it in one continuous motion,
   same as it always has on mobile (see the max-width: 1024px override
   further down, which this now matches). The hero is intentionally
   excluded here — it gets its own sizing below to support the
   sticky-background reveal effect. */
.section-content,
.team-section,
.quote-section,
.contact-section {
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h));
}

/* ===== HERO (sticky-background reveal) =====
   The hero occupies two viewport "slots" worth of height. Its background
   image is position: sticky, so once the user starts scrolling it
   holds still at the top of the viewport. The section immediately
   after the hero is pulled up (see ".hero + .section-content" below)
   so it slides up from below and covers the still-pinned image —
   after which normal scrolling continues completely unchanged for
   every section from there on.

   The height below is 2 * (one viewport minus the header) rather than
   a flat 200vh: every section's own height already has the header
   subtracted once (see .section-content etc.), so the hero and the
   section-content right after it both need that same subtraction —
   otherwise the two don't line up and the sticky image is left
   showing through a header-sized gap once it's past its stuck phase.
   Desktop-only: the split-column layout this depends on doesn't exist
   below 1024px, see the media query. */
.hero {
    display: grid;
    grid-template-columns: minmax(300px, 5fr) minmax(0, 7fr);
    align-content: start;
    background: var(--ink);
    height: calc(200vh - 2 * var(--header-h));
    height: calc(200svh - 2 * var(--header-h));
}

/* Hero is always the dark opening section on both pages, so its own
   text/label/link colors are set directly here rather than via the
   reusable --on-ink modifier classes (those still exist for shared
   components used in both light and dark contexts elsewhere). */
.hero .folio-label {
    color: var(--brass-light);
}

.hero .coordinates {
    color: var(--text-on-ink-muted);
}

.hero .text-cta {
    color: var(--text-on-ink);
    border-color: var(--hairline-on-ink);
}

.hero .text-cta:hover {
    color: var(--brass-light);
    border-color: var(--brass-light);
}

/* Now spans the same full width as .hero-video-frame below (instead of
   a left-hand column) — the video is a shared full-bleed background
   rather than something sitting beside the text, so there's no longer
   a separate column for this to be confined to. .hero-content's own
   max-width/margin (see below) is what actually keeps the copy at a
   readable, left-aligned measure, centered on the page — the same
   "centered block, left-aligned text" treatment Services' intro slide
   already uses. */
.hero-text-col {
    position: relative;
    z-index: 2;
    grid-column: 1 / -1;
    grid-row: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    justify-content: safe center;
    padding: clamp(24px, 6vh, 64px) var(--gutter);
    overflow-y: auto;
    height: calc(100vh - var(--header-h));
    height: calc(100svh - var(--header-h));
    align-self: start;
}

/* Centers the hero's copy block on the page — left-aligned text inside
   a centered column, same measure and mechanism as .services-intro
   (Services' intro slide reuses .hero-content too, see services.html)
   — rather than the text sitting pinned to the left edge of a wide,
   full-bleed hero. */
.hero-content {
    max-width: 640px;
    margin: 0 auto;
}

/* Home and Partnership's full-bleed video background — sticky-pinned
   at the header, spanning the whole grid (grid-column: 1 / -1) rather
   than a single column, since the video is shared behind both the text
   and where a separate image column used to be, rather than confined
   next to the text. .hero-text-col (z-index 2 above) sits on top of it
   instead of beside it. section-photo-scrim (shared with the site's
   other full-bleed photo/video sections, nested inside this frame in
   the markup) darkens it enough to keep the text reliably readable
   over the moving footage. */
.hero-video-frame {
    grid-column: 1 / -1;
    grid-row: 1;
    position: sticky;
    top: var(--header-h);
    align-self: start;
    height: calc(100vh - var(--header-h));
    height: calc(100svh - var(--header-h));
    overflow: hidden;
    background: var(--ink);
    z-index: 1;
}

/* The section right after the hero — pulled up to overlap the hero's
   second "hold" viewport so it visibly slides over the sticky image. */
.hero + .section-content {
    position: relative;
    z-index: 2;
    margin-top: calc(-100vh + var(--header-h));
    margin-top: calc(-100svh + var(--header-h));
}

/* Home and Partnership's hero video (see .hero-video-frame above,
   initHeroVideo() in script.js). */
.hero-video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
    transform: scale(1.06);
    animation: hero-settle 1.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes hero-settle {
    to {
        transform: scale(1);
    }
}

/* Same problem, one level up: in photo-backed sections
   (.section-content--photo, display:flex) the <picture> wrapping the
   full-bleed background sits directly in the flex container and would
   become an unwanted flex item. A plain div wrapper — same pattern as
   .hero-video-frame above — takes it out of flow instead; .section-photo-bg
   below (on the img itself) is unchanged and still does the actual
   full-bleed positioning. (Also needs excluding from the
   .section-content--photo > :not(...) content-layering rule further
   down, or that would force it back to position: relative.) */
.section-photo-bg-frame {
    position: absolute;
    inset: 0;
    z-index: 0;
}

/* After the initial settle, the hero image breathes very slowly — a
   Ken-Burns-like idle loop. Desktop only: skipped on smaller screens
   where the image is short and static, saving battery/perf there. */
@media (min-width: 900px) {
    .hero-video {
        animation: hero-settle 1.6s cubic-bezier(0.16, 1, 0.3, 1) forwards,
                   hero-breathe 22s ease-in-out 1.6s infinite alternate;
        will-change: transform;
    }
}

@keyframes hero-breathe {
    from { transform: scale(1); }
    to { transform: scale(1.035); }
}

.hero-title {
    font-family: var(--font-display);
    font-size: clamp(2.1rem, 4.2vw, 3.4rem);
    font-weight: 500;
    font-style: normal;
    line-height: 1.18;
    color: var(--text-on-ink);
    letter-spacing: 0.2px;
    max-width: 15ch;
    /* Renders an explicit "\n" in a translation (currently only the
       German home.hero.title, to force its break right after the
       comma) as a real line break, while still wrapping normally at
       spaces for languages with no "\n" in their string — inert for
       those, not just for German. */
    white-space: pre-line;
}

.hero-subtitle {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-size: 16px;
    font-weight: 400;
    color: var(--text-on-ink);
    margin-top: 18px;
    max-width: 42ch;
    line-height: 1.8;
}

/* Above the two-column hero breakpoint there's room to keep this on
   one line, which reads cleaner than the mid-phrase wrap. */
@media (min-width: 1025px) {
    .hero-subtitle {
        max-width: none;
        white-space: nowrap;
    }
}

.hero-content > .text-cta {
    margin-top: clamp(16px, 4vh, 40px);
}

.hero-content .coordinates {
    display: block;
    margin-top: clamp(20px, 5vh, 56px);
}

/* Hero content settles in on load, one line after another, instead of
   appearing all at once — the site's single strongest "first moment". */
.hero-content > * {
    opacity: 0;
    animation: hero-content-in var(--dur-md) var(--ease) forwards;
}

.hero-content > *:nth-child(1) { animation-delay: 0.15s; }
.hero-content > *:nth-child(2) { animation-delay: 0.3s; }
.hero-content > *:nth-child(3) { animation-delay: 0.45s; }
.hero-content > *:nth-child(4) { animation-delay: 0.6s; }
.hero-content > *:nth-child(5) { animation-delay: 0.75s; }

@keyframes hero-content-in {
    from { opacity: 0; transform: translateY(16px); }
    to { opacity: 1; transform: translateY(0); }
}

/* ===== SECTION CONTENT (asymmetric split) ===== */
.section-content {
    background-color: var(--paper);
    padding: clamp(20px, 4vh, 48px) var(--gutter);
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    justify-content: safe center;
}

/* The dark variant in the alternating dark/light rhythm (see the
   pattern comment above .hero). Only the color-relevant descendants
   are overridden here — layout, spacing and typography are untouched. */
.section-content--alt {
    background-color: var(--ink);
    color: var(--text-on-ink);
}

.section-content--alt h2,
.section-content--alt h3 {
    color: var(--text-on-ink);
}

.section-content--alt .folio-label {
    color: var(--brass-light);
}

.section-content--alt .ledger-number,
.section-content--alt .triptych-number {
    color: var(--brass-light);
}

.section-content--alt .ledger-body p,
.section-content--alt .triptych-item p,
.section-content--alt .simple-list-item p {
    color: var(--text-on-ink-muted);
}

.section-content--alt .ledger-item,
.section-content--alt .ledger-item:nth-child(2n),
.section-content--alt .ledger-item:last-child,
.section-content--alt .simple-list-item {
    border-color: var(--hairline-on-ink);
}

/* border-color (not border-left-color) so this also covers the top
   border .triptych-item switches to on mobile — otherwise that border
   stays the dark, light-background hairline colour, which is nearly
   invisible on this section's dark/photo background. */
.section-content--alt .triptych-item {
    border-color: var(--hairline-on-ink);
}

.section-wrapper {
    max-width: var(--max);
    min-width: 0;
    margin: 0 auto;
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: clamp(32px, 6vw, 80px);
    align-items: center;
}

/* Grid items default to min-width: auto, which lets a wide nested grid
   (e.g. the Services tag-grid) force this — and everything above it —
   wider than the viewport instead of shrinking. min-width: 0 restores
   normal shrinking behaviour. */
.section-text {
    min-width: 0;
}

.section-wrapper.reverse {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
}

.section-wrapper.reverse .section-image {
    order: 2;
}

.section-image {
    position: relative;
    width: 100%;
    min-width: 0;
    height: min(78vh, 820px);
    overflow: hidden;
}

.section-image img,
.section-image video {
    width: 100%;
    height: 100%;
    min-height: 220px;
    object-fit: cover;
    display: block;
    transition: transform var(--dur-lg) var(--ease);
}

#who-we-are .section-image img {
    object-position: center 48%;
}

/* Split-layout & team photos: a very slight zoom on hover — signals
   the image is a deliberate, considered element, not a static block.
   Gated to real pointer devices so touch taps never get stuck "hovered". */
@media (hover: hover) and (pointer: fine) {
    .section-image:hover img,
    .section-image:hover video,
    .team-member-image:hover .team-photo {
        transform: scale(1.045);
    }
}

/* Selective grid-break: image lifts and overlaps the section boundary above it.
   transform is used (not margin) so it shifts the rendered image only and
   never affects the grid row's own height calculation. */
.section-image--overlap {
    transform: translateY(-64px);
}

/* Services section pairs a much taller text column (9-item grid) with the
   fixed-height image, so the shared overlap lift reads as heavily
   top-aligned here — keep the image centered with equal top/bottom space. */
#services .section-image--overlap {
    transform: none;
}

/* Who We Are pairs two paragraphs with a fixed-height image; same
   top-heavy issue as Services, same fix — keep the image centered. */
#who-we-are .section-image--overlap {
    transform: none;
}

/* Our Approach (Partnership) pairs three paragraphs with a fixed-height
   image; same top-heavy issue, same fix — keep the image centered. */
#our-approach .section-image--overlap {
    transform: none;
}

.section-text h2 {
    font-size: clamp(1.9rem, 3.4vw, 2.6rem);
    font-weight: 500;
    margin-bottom: clamp(14px, 2.5vh, 26px);
    line-height: 1.2;
}


.section-body p {
    font-size: 16px;
    line-height: 1.85;
    margin-bottom: clamp(10px, 1.8vh, 18px);
    color: var(--text);
}

.text-intro {
    color: var(--text);
}

.section-body strong {
    font-weight: 600;
    color: var(--ink);
}

.section-text .text-cta {
    margin-top: 12px;
}

/* ===== TEAM SECTION — editorial bio spread ===== */
.team-section {
    background: var(--paper);
    padding: 0;
}

.team-container {
    max-width: var(--max);
    margin: 0 auto;
    height: 100%;
    display: grid;
    /* Text first/left, portrait second/right (see index.html — the
       photo used to lead in the markup); same 5fr/7fr split as before,
       just mirrored so the photo keeps the narrower column. */
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    align-items: stretch;
}

.team-member-image {
    position: relative;
    overflow: hidden;
}

.team-photo {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform var(--dur-lg) var(--ease);
}

.team-member-info {
    display: flex;
    flex-direction: column;
    justify-content: center;
    justify-content: safe center;
    padding: clamp(24px, 6vh, 72px) var(--gutter);
    /* Extra breathing room on the side facing the portrait, now the
       right (see .team-container above — text leads, photo trails). */
    padding-right: calc(var(--gutter) + clamp(16px, 4vw, 56px));
    overflow-y: auto;
}

.team-member-info h2 {
    font-size: clamp(2.4rem, 5vw, 4rem);
    font-weight: 600;
    color: var(--ink);
    line-height: 1.05;
    white-space: nowrap;
}

.team-title {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--brass);
    margin-top: clamp(12px, 2.5vh, 22px);
    padding-top: 18px;
    border-top: 1px solid var(--hairline);
}

/* Name + circular portrait row — on desktop this wrapper is
   transparent to layout (display: contents) so the heading behaves
   exactly as before; the small circular photo only appears on mobile,
   see the media query below. */
.team-name-row {
    display: contents;
}

.team-photo-circle,
.team-photo-circle-img {
    display: none;
}

.team-description {
    font-size: 16px;
    line-height: 1.9;
    color: var(--text-muted);
    margin-top: clamp(14px, 3vh, 30px);
    max-width: 48ch;
}

.team-contact-links {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: clamp(14px, 3vh, 26px);
}

/* Dark variant — used where the alternating dark/light rhythm needs
   the team section to land on a dark step (currently: Home, now that
   it sits right after Benefits instead of after Services). Same
   scoped-override pattern as .section-content--alt/.contact-section--dark:
   only the color-relevant descendants change. */
.team-section--dark {
    background: var(--ink);
}

.team-section--dark .folio-label {
    color: var(--brass-light);
}

.team-section--dark .team-member-info h2 {
    color: var(--text-on-ink);
}

.team-section--dark .team-title {
    color: var(--brass-light);
    border-top-color: var(--hairline-on-ink);
}

.team-section--dark .team-description {
    color: var(--text-on-ink-muted);
}

.team-section--dark .text-cta {
    color: var(--text-on-ink);
    border-color: var(--hairline-on-ink);
}

.team-section--dark .text-cta:hover {
    color: var(--brass-light);
    border-color: var(--brass-light);
}

/* ===== QUOTE SECTION ===== */
.quote-section {
    background: var(--paper);
    padding: clamp(20px, 4vh, 48px) var(--gutter);
    overflow-x: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}

.quote-inner {
    display: inline-flex;
    align-items: center;
    gap: clamp(6px, 1.2vw, 18px);
    max-width: 100%;
}

.quote-mark {
    font-family: var(--font-display);
    font-size: clamp(2.6rem, 5vw, 4.5rem);
    color: var(--brass);
    opacity: 0.45;
    line-height: 1;
    flex-shrink: 0;
    user-select: none;
}

.quote-text {
    font-size: clamp(1.3rem, 2.4vw, 2.2rem);
    font-weight: 400;
    font-style: italic;
    line-height: 1.5;
    color: var(--text);
    white-space: nowrap;
    text-align: center;
}

/* The single most important line on the site gets the strongest
   reveal treatment: the marks settle in from a slight scale, the
   text follows a beat behind with a longer rise. */
.quote-reveal .quote-mark {
    opacity: 0;
    transform: scale(0.7);
    transition: opacity var(--dur-md) var(--ease), transform var(--dur-md) var(--ease);
}

.quote-reveal.is-visible .quote-mark {
    opacity: 0.45;
    transform: scale(1);
}

.quote-reveal .quote-text {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity var(--dur-lg) var(--ease) 0.15s, transform var(--dur-lg) var(--ease) 0.15s;
}

.quote-reveal.is-visible .quote-text {
    opacity: 1;
    transform: translateY(0);
}

@media (max-width: 720px) {
    /* Marks stay flanking the text on the left/right (never their own
       stacked row above/below) — the quote is forced onto exactly two
       lines (see .quote-line below) instead of wrapping wherever it
       happens to run out of width. */
    .quote-inner {
        flex-wrap: nowrap;
        align-items: center;
        gap: clamp(6px, 3vw, 14px);
    }

    .quote-mark {
        flex-shrink: 0;
        font-size: clamp(1.6rem, 8vw, 2.4rem);
    }

    .quote-text {
        white-space: normal;
        flex: 1;
        min-width: 0;
    }

    .quote-line {
        display: block;
    }
}

/* ===== LEDGER LIST (Benefits) =====
   Two columns of two, filled column-first (01/02 left, 03/04 right)
   so the whole list fits one viewport instead of needing an inner
   scroll on top of the page's own section-snap scrolling. */
.ledger {
    max-width: var(--max);
    margin: 0 auto;
    display: grid;
    grid-template-rows: repeat(2, auto);
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    column-gap: clamp(32px, 5vw, 64px);
}

.ledger-item {
    display: grid;
    grid-template-columns: 96px 1fr;
    gap: clamp(24px, 4vw, 48px);
    padding: clamp(8px, 1.8vh, 22px) 0;
    border-top: 1px solid var(--hairline);
    transition: background-color var(--dur-sm) ease, transform var(--dur-sm) var(--ease);
}

.ledger-item:nth-child(2n) {
    border-bottom: 1px solid var(--hairline);
}

.ledger-item:hover {
    background-color: rgba(82, 96, 107, 0.06);
    transform: translateX(4px);
}

.ledger-number {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 2.6rem;
    color: var(--brass);
    line-height: 1;
}

.ledger-body h3 {
    font-size: 1.3rem;
    font-weight: 600;
    margin-bottom: 12px;
    max-width: 40ch;
}

.ledger-body p {
    font-size: 15px;
    line-height: 1.8;
    color: var(--text-muted);
    max-width: 62ch;
}

/* ===== TWO-COLUMN LIST (Clients) ===== */
.simple-list {
    max-width: var(--max);
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    column-gap: clamp(40px, 6vw, 96px);
}

.simple-list-item {
    padding: clamp(14px, 2.6vh, 28px) 0;
    border-top: 1px solid var(--hairline);
    transition: background-color var(--dur-sm) ease, transform var(--dur-sm) var(--ease);
}

.simple-list-item:hover {
    background-color: rgba(82, 96, 107, 0.06);
    transform: translateX(4px);
}

.simple-list-item h3 {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 10px;
}

.simple-list-item p {
    font-size: 14px;
    line-height: 1.8;
    color: var(--text-muted);
}

/* Checkmark variant — used for the "Your Benefits" list only */
.simple-list--check .simple-list-item h3 {
    padding-left: 26px;
    position: relative;
}

.simple-list--check .simple-list-item h3::before {
    content: '\2713';
    position: absolute;
    left: 0;
    color: var(--brass);
}

/* ===== TRIPTYCH (Why Choose) ===== */
.triptych {
    max-width: var(--max);
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
}

.triptych-item {
    padding: 0 clamp(20px, 3vw, 44px);
    border-left: 1px solid var(--hairline);
    transition: transform var(--dur-sm) var(--ease);
}

.triptych-item:first-child {
    padding-left: 0;
    border-left: none;
}

.triptych-item:hover {
    transform: translateX(4px);
}

.triptych-number {
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--brass);
}

.triptych-item h3 {
    font-size: 1.2rem;
    font-weight: 600;
    margin: 18px 0 14px;
}

.triptych-item p {
    font-size: 14px;
    line-height: 1.85;
    color: var(--text-muted);
}

/* ===== TAG GRID (Services / Our Focus) — short labels, no elaboration =====
   Each item is its own fully-bordered, fixed-height box, separated by
   a real gap rather than a shared border/background — every box reads
   as a distinct, equal-sized unit instead of a continuous ledger grid. */
.tag-grid {
    width: 100%;
    max-width: var(--max);
    min-width: 0;
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: clamp(10px, 1.6vw, 18px);
}

.tag-grid-item {
    position: relative;
    background: var(--paper);
    padding: 26px clamp(20px, 3vw, 32px);
    border: 1px solid var(--hairline);
    min-height: 112px;
    font-family: var(--font-display);
    font-size: 1.05rem;
    font-weight: 500;
    color: var(--ink);
    text-decoration: none;
    display: flex;
    align-items: center;
    transition: background-color var(--dur-sm) ease, color var(--dur-sm) ease, border-color var(--dur-sm) ease, transform var(--dur-sm) var(--ease);
}

/* At 220px-per-column, auto-fit silently drops from 3 columns to 2 the
   moment a laptop is a bit narrower than ~1400px (very common at 1366
   and 1280px widths) — the grid still "fits" by CSS Grid's own rules,
   it just no longer matches the intended 3-column desktop layout, and
   the extra row can push the Services section taller than one
   viewport. Above the mobile/tablet breakpoint the column count is
   pinned to 3 instead, with the items shrinking fluidly (padding and
   font both ease down toward the narrow end of the desktop range)
   rather than the grid silently re-wrapping to fewer, wider columns. */
@media (min-width: 1025px) {
    .tag-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .tag-grid-item {
        padding: 26px clamp(14px, 2.4vw, 32px);
        font-size: clamp(0.85rem, 0.36rem + 0.77vw, 1.05rem);
    }
}

/* Gated to real pointer devices (matches the same pattern used for
   .section-image/.team-member-image above) so a tap on touch devices
   never leaves this stuck in its "hovered" state — on mobile, Services
   gets its own 3D card-stack treatment instead (see initServicesWheel
   in script.js). */
@media (hover: hover) and (pointer: fine) {
    .tag-grid-item:hover {
        background-color: rgba(82, 96, 107, 0.1);
        color: var(--brass);
        border-color: var(--brass);
    }
}

/* Services (Home) tiles: a stronger emphasis than the generic
   brass-tint hover above — solid ink (navy) fill plus a small lift —
   since these are links through to the dedicated Services page and
   need to read clearly as clickable, not just highlighted, the way
   a plain (non-link) tag-grid does. any-hover/any-pointer
   (checking whether ANY attached input supports hover/fine pointing,
   rather than just the device's single "primary" one, as the plain
   hover/pointer features used elsewhere on the site do) so a laptop
   that also has a touchscreen — where the primary-pointer heuristic
   can report touch instead of the trackpad or mouse actually in use —
   still gets this feedback. */
@media (any-hover: hover) and (any-pointer: fine) {
    #services .tag-grid-item:hover {
        background-color: var(--ink);
        color: var(--text-on-ink);
        border-color: var(--ink);
        transform: translateY(-4px);
    }
}

/* ===== SPEC GRID (Our Focus) =====
   A 2x2 "vessel particulars" panel rather than a grid of filled boxes:
   one shared hairline frame divided into quarters, brass small-caps
   label over a serif value, sitting directly on the photo/scrim so it
   reads as an inset spec sheet rather than a stack of buttons. */
/* A single hairline cross through the middle (via ::before/::after,
   placed as grid items in the two 1px divider tracks) instead of a
   border around each item or around the group — separates all four
   without boxing any of them in. */
.spec-grid {
    width: 100%;
    max-width: 640px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1fr 1px 1fr;
    grid-template-rows: auto 1px auto;
    column-gap: clamp(32px, 6vw, 56px);
    row-gap: clamp(28px, 5vw, 44px);
}

.spec-grid::before {
    content: '';
    grid-column: 2;
    grid-row: 1 / -1;
    background: var(--hairline-on-ink);
}

.spec-grid::after {
    content: '';
    grid-row: 2;
    grid-column: 1 / -1;
    background: var(--hairline-on-ink);
}

.spec-grid-item {
    padding: clamp(8px, 1.5vw, 16px) 0;
    text-align: center;
    transition: opacity var(--dur-sm) ease;
}

.spec-grid-item:nth-child(1) { grid-column: 1; grid-row: 1; }
.spec-grid-item:nth-child(2) { grid-column: 3; grid-row: 1; }
.spec-grid-item:nth-child(3) { grid-column: 1; grid-row: 3; }
.spec-grid-item:nth-child(4) { grid-column: 3; grid-row: 3; }

.spec-label {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--brass-light);
    margin-bottom: 10px;
}

.spec-value {
    font-family: var(--font-display);
    font-size: clamp(1.05rem, 0.85rem + 0.5vw, 1.3rem);
    font-weight: 500;
    color: var(--text-on-ink);
}

@media (hover: hover) and (pointer: fine) {
    .spec-grid-item:hover {
        opacity: 0.72;
    }
}

/* ===== PHOTO-BACKED SECTION =====
   A dark section with a full-bleed photograph instead of a flat ink
   fill. A single semi-opaque scrim (matching --ink) sits between the
   photo and the content so every existing --alt text/border color
   rule still reads correctly — no new text colors needed. */
.section-content--photo {
    overflow: hidden;
}

.section-photo-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 0;
}

/* Same slow idle loop as the hero image, on the other large full-bleed
   photographs. Desktop only, same reasoning as the hero breathe. */
@media (min-width: 900px) {
    .section-photo-bg {
        animation: hero-breathe 26s ease-in-out infinite alternate;
        will-change: transform;
    }
}

.section-photo-scrim {
    position: absolute;
    inset: 0;
    background: rgba(16, 30, 43, 0.74);
    z-index: 1;
}

/* Why Windsor Clearwater (Partnership) and Our Focus (Home): each gets
   a separate mobile-only background photo, same full-bleed frame
   mechanics as the desktop one — hidden here, swapped in for it only
   inside the mobile media query below. Desktop is untouched. */
#why-us .section-photo-bg-frame--mobile,
#focus .section-photo-bg-frame--mobile {
    display: none;
}

.section-content--photo > :not(.section-photo-bg):not(.section-photo-scrim):not(.section-photo-bg-frame) {
    position: relative;
    z-index: 2;
}

/* Shared section heading for list sections */
.list-section-head {
    max-width: var(--max);
    margin: 0 auto clamp(12px, 2.4vh, 28px);
    text-align: center;
}

.list-section-head h2 {
    font-size: clamp(1.9rem, 3.4vw, 2.6rem);
    font-weight: 500;
    max-width: 20ch;
}

#owner-reality h2 {
    max-width: 34ch;
}

#benefits h2 {
    max-width: 40ch;
}

#why-us h2 {
    max-width: 40ch;
}

/* ===== SINGLE-COLUMN INTRO ===== */
.section-wrapper-single {
    max-width: 860px;
    margin: 0 auto;
}

.section-wrapper-single h1,
.section-wrapper-single h2 {
    font-size: clamp(1.9rem, 3.4vw, 2.6rem);
    font-weight: 500;
    margin-bottom: 28px;
}

.section-wrapper-single p:not(.folio-label) {
    font-size: 16px;
    line-height: 1.9;
    margin-bottom: 20px;
    color: var(--text-muted);
}

/* ===== CONTACT SECTION ===== */
.contact-section {
    background-color: var(--paper);
    padding: clamp(20px, 4vh, 40px) var(--gutter);
    display: flex;
    flex-direction: column;
    justify-content: center;
    justify-content: safe center;
}

/* Dark variant — used where the alternating dark/light rhythm needs
   the contact section to land on a dark step (currently: Home). The
   Partnership contact section keeps the default light styling above. */
.contact-section--dark {
    background-color: var(--ink);
}

.contact-section--dark h2 {
    color: var(--text-on-ink);
}

.contact-section--dark .folio-label {
    color: var(--brass-light);
}

.contact-section--dark .coordinates {
    color: var(--text-on-ink-muted);
}

.contact-section--dark .contact-form-wrapper {
    border-right-color: var(--hairline-on-ink);
}

.contact-section--dark .form-group label {
    color: var(--text-on-ink-muted);
}

.contact-section--dark .form-group input[type="text"],
.contact-section--dark .form-group input[type="email"],
.contact-section--dark .form-group input[type="tel"],
.contact-section--dark .form-group select,
.contact-section--dark .form-group textarea {
    color: var(--text-on-ink);
    border-bottom-color: var(--hairline-on-ink);
}

.contact-section--dark .form-group select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23aab3ba' stroke-width='1.4' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
}

.contact-section--dark .form-group input[type="text"]:focus,
.contact-section--dark .form-group input[type="email"]:focus,
.contact-section--dark .form-group input[type="tel"]:focus,
.contact-section--dark .form-group select:focus,
.contact-section--dark .form-group textarea:focus {
    border-color: var(--brass-light);
}

.contact-section--dark .form-group.checkbox input[type="checkbox"] {
    accent-color: var(--brass-light);
}

.contact-section--dark .form-group.checkbox label {
    color: var(--text-on-ink-muted);
}

.contact-section--dark .form-note {
    color: var(--text-on-ink-muted);
}

.contact-section--dark .submit-btn {
    color: var(--text-on-ink);
    border-bottom-color: var(--text-on-ink);
}

.contact-section--dark .submit-btn:hover {
    color: var(--brass-light);
    border-bottom-color: var(--brass-light);
}

.contact-section--dark .contact-info p,
.contact-section--dark .contact-info .label {
    color: var(--text-on-ink-muted);
}

.contact-section--dark .contact-info a {
    color: var(--text-on-ink);
    border-bottom-color: var(--hairline-on-ink);
}

.contact-section--dark .contact-info a:hover {
    color: var(--brass-light);
    border-bottom-color: var(--brass-light);
}

.contact-section--dark .section-body-intro {
    color: var(--text-on-ink-muted);
}

.contact-container {
    max-width: var(--max);
    margin: 0 auto;
    display: grid;
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    gap: clamp(40px, 6vw, 100px);
}

.contact-form-wrapper {
    padding-right: clamp(0px, 4vw, 40px);
    border-right: 1px solid var(--hairline);
}

.contact-info-wrapper {
    padding-left: clamp(0px, 4vw, 8px);
}

.contact-form-wrapper h2,
.contact-info-wrapper h2 {
    font-size: 1.6rem;
    font-weight: 500;
    margin-bottom: clamp(16px, 3vh, 34px);
}

.section-body-intro {
    font-size: 15px;
    line-height: 1.8;
    color: var(--text-muted);
    margin-top: -12px;
    margin-bottom: clamp(16px, 3vh, 28px);
    max-width: 46ch;
}

.contact-form {
    display: flex;
    flex-direction: column;
}

/* display:flex above otherwise wins the cascade over the UA [hidden]
   rule (same specificity, author beats user-agent), so setting the
   hidden attribute from script.js — done once the Contact page's form
   submits successfully — would silently do nothing without this. */
.contact-form[hidden] {
    display: none;
}

.form-group {
    margin-bottom: clamp(12px, 2.2vh, 30px);
    display: flex;
    flex-direction: column;
}

.form-group label {
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    font-weight: 300;
    margin-bottom: 8px;
    color: var(--text-muted);
}

.form-group input[type="text"],
.form-group input[type="email"],
.form-group input[type="tel"],
.form-group textarea {
    padding: 8px 2px 12px;
    border: none;
    border-bottom: 1px solid var(--hairline);
    border-radius: 0;
    background: transparent;
    font-family: var(--font-body);
    font-size: 15px;
    color: var(--text);
    transition: border-color 0.3s ease;
}

.form-group input[type="text"]:focus,
.form-group input[type="email"]:focus,
.form-group input[type="tel"]:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--brass);
}

.form-group.checkbox {
    flex-direction: row;
    align-items: flex-start;
    margin-bottom: 14px;
}

.form-group.checkbox input[type="checkbox"] {
    margin-right: 14px;
    margin-top: 4px;
    cursor: pointer;
    width: 16px;
    height: 16px;
    accent-color: var(--brass);
}

.form-group.checkbox label {
    margin-bottom: 0;
    font-family: var(--font-body);
    text-transform: none;
    letter-spacing: normal;
    font-size: 13px;
    color: var(--text-muted);
    cursor: pointer;
}

.form-note {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: clamp(12px, 2vh, 26px);
}

.submit-btn {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 0 0 4px;
    background: none;
    border: none;
    border-bottom: 1px solid var(--ink);
    border-radius: 0;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink);
    cursor: pointer;
    align-self: flex-start;
    transition: color var(--dur-sm) ease, border-color var(--dur-sm) ease, transform var(--dur-sm) var(--ease);
}

.submit-btn::after {
    content: '\2192';
    transition: transform var(--dur-sm) var(--ease);
}

.submit-btn:hover {
    color: var(--brass);
    border-color: var(--brass);
    transform: translateX(2px);
}

.submit-btn:hover::after {
    transform: translateX(5px);
}

.contact-info {
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.contact-info p {
    font-size: 15px;
    line-height: 1.7;
    color: var(--text-muted);
}

.contact-info .label {
    display: block;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 8px;
}

.contact-info a {
    color: var(--ink);
    text-decoration: none;
    border-bottom: 1px solid var(--hairline);
    display: inline-block;
    transition: color var(--dur-sm) ease, border-color var(--dur-sm) ease, transform var(--dur-sm) var(--ease);
}

.contact-info a:hover {
    color: var(--brass);
    border-color: var(--brass);
    transform: translateY(-1px);
}

/* ===== CONTACT PAGE — EXTENDED FORM =====
   Additional field types (select, checkbox groups, radio groups) and
   inline validation states used only by the Contact page's dedicated
   enquiry form (#contactPageForm in script.js) — styled to match the
   underline-style .form-group inputs above rather than introducing a
   second visual language. */
.form-group select {
    appearance: none;
    -webkit-appearance: none;
    padding: 8px 22px 12px 2px;
    border: none;
    border-bottom: 1px solid var(--hairline);
    border-radius: 0;
    background: transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%235c6670' stroke-width='1.4' d='M1 1l5 5 5-5'/%3E%3C/svg%3E") no-repeat right 2px center / 11px 7px;
    font-family: var(--font-body);
    font-size: 15px;
    color: var(--text);
    cursor: pointer;
    transition: border-color 0.3s ease;
}

.form-group select:focus {
    outline: none;
    border-color: var(--brass);
}

.form-fieldset {
    border: none;
    padding: 0;
    margin: 0 0 clamp(12px, 2.2vh, 30px);
}

.form-fieldset legend {
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    font-weight: 300;
    padding: 0;
    margin-bottom: 10px;
    color: var(--text-muted);
}

.form-checkbox-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 20px;
}

.form-radio-group {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 28px;
}

.form-checkbox-item,
.form-radio-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

.form-checkbox-item input[type="checkbox"],
.form-radio-item input[type="radio"] {
    margin: 3px 0 0;
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    cursor: pointer;
    accent-color: var(--brass);
}

.form-checkbox-item label,
.form-radio-item label {
    font-family: var(--font-body);
    text-transform: none;
    letter-spacing: normal;
    font-size: 14px;
    color: var(--text-muted);
    cursor: pointer;
}

.field-error {
    display: block;
    font-family: var(--font-body);
    font-size: 12.5px;
    color: var(--signal);
    margin-top: 7px;
}

.form-group.has-error input[type="text"],
.form-group.has-error input[type="email"],
.form-group.has-error input[type="tel"],
.form-group.has-error textarea {
    border-bottom-color: var(--signal);
}

.form-fieldset.has-error legend {
    color: var(--signal);
}

.form-status {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--signal);
    margin: -8px 0 clamp(12px, 2vh, 22px);
}

.submit-btn[disabled] {
    opacity: 0.6;
    cursor: default;
}

.form-success h2 {
    font-size: 1.6rem;
    font-weight: 500;
    margin-bottom: 16px;
}

/* ===== CONTACT PAGE — COMPACT, FULL-WIDTH LAYOUT =====
   Contact intentionally breaks from the site's one-section-per-
   viewport rhythm (see "ONE-SECTION-PER-SCROLL" above): its intro is a
   few lines rather than a full-screen chapter, flowing straight into a
   full-width form instead of a hero followed by a squeezed sidebar
   column. Scoped to this page's own classes so Home/Services/
   Partnership — which still rely on the shared full-height section
   rules — are untouched. */
.contact-page-flow {
    height: auto;
    min-height: 0;
    overflow-y: visible;
}

/* width:100% for the same reason .contact-form-full below needs it:
   .contact-section (the flex parent both sit in) disables flexbox's
   stretch-to-fill once .section-wrapper-single's own auto margins are
   present, so without this a short paragraph (this intro is only two
   sentences) shrink-wraps to its own text width instead of reaching
   the 860px cap — on a wide screen that left the intro measurably
   narrower than the form below it, throwing their left edges out of
   alignment. */
.contact-intro {
    width: 100%;
    margin-bottom: clamp(32px, 6vh, 64px);
}

/* A single elegant column, the same measure as the intro right above
   it (.section-wrapper-single's own 860px) so the two share one
   consistent left edge rather than the form stretching wider than the
   copy that introduces it. .contact-section (this element's parent) is
   display:flex, so — unlike the plain block parents .max-width/
   .section-wrapper-single normally sit in elsewhere on the site — this
   needs an explicit width:100%: without it, the auto left/right
   margins below disable flexbox's default stretch-to-fill behavior and
   the column ends up narrower than intended. */
.contact-form-full {
    width: 100%;
    max-width: 860px;
    margin: 0 auto;
}

.contact-form-full .form-success {
    max-width: 100%;
}

/* Contact details, repositioned to a quiet closing signature at the
   foot of the page instead of a sidebar or an inline byline under the
   intro — left-aligned to the same 860px measure/left edge as the form
   and intro above it (not the wider section), and set off from the
   form above by the site's usual 1px hairline so it reads as the
   page's last word rather than another form field. Reuses
   .contact-info's existing label/link typography wholesale; only the
   direction and alignment change. width:100% for the same reason
   .contact-form-full/.contact-intro need it above — without it, this
   flex item (a child of .contact-section, display:flex) shrink-wraps
   to its own short content width instead of reaching the 860px cap,
   throwing its left edge out of alignment with the form/intro column
   above it. */
.contact-footer-info.contact-info {
    width: 100%;
    max-width: 860px;
    margin: clamp(48px, 8vh, 96px) auto 0;
    padding-top: clamp(28px, 5vh, 48px);
    border-top: 1px solid var(--hairline);
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 10px 48px;
}

/* ===== SERVICE FEATURE (Services page) =====
   Each of the 9 services is composed the same way as .ledger-item's
   number + body pairing (large Playfair Display numeral in brass,
   text alongside) except scaled up to editorial size and given its
   own full section rather than sharing a shared list — the number
   becomes the section's visual anchor. Built as a 3-child grid so it
   can double as a .reveal-stagger container: number, then heading,
   then paragraph settle in in that order using the same stagger
   mechanism already driving every other grouped reveal on the site. */
/* ===== SERVICES PAGE: INTRO SECTION =====
   A single naturally-tall section (min-height, not a hard height, so a
   visitor's dynamic browser toolbar never clips the text) with the
   video as a plain full-bleed background — used to be a double-height
   sticky-reveal section (like .hero) that the old swipe stage slid up
   over; now that the stage is gone (replaced by .services-accordion,
   ordinary block flow right below), there's nothing left to reveal, so
   this is just one screen-filling moment, same treatment on every
   breakpoint. Video-frame and text wrapper share the same grid-row/
   column so they overlap instead of sitting side by side. */
.services-intro-section {
    position: relative;
    display: grid;
    grid-template-columns: 1fr;
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h));
    min-height: calc(100dvh - var(--header-h));
    min-height: calc(var(--vh100, 100dvh) - var(--header-h));
    background-color: var(--paper);
    color: var(--text);
}

.services-intro-section--dark {
    background-color: var(--ink);
    color: var(--text-on-ink);
}

/* Plain full-bleed background — same treatment as .hero-video-frame's
   own mobile fallback. section-photo-scrim lives nested inside this
   frame (see services.html) so it stays pinned together with the
   video instead of scrolling away on its own partway through the
   section. */
.services-intro-video-frame {
    grid-column: 1;
    grid-row: 1;
    position: absolute;
    inset: 0;
    background: var(--ink);
    z-index: 0;
}

.services-intro-video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
}

/* The text "column" — same grid cell as the video-frame above (so it
   visually overlaps it) — its own flex/align-items centers
   .services-intro the way the section's own flex used to before this
   became a grid. Padding lives here rather than on
   .services-intro-section itself: that's a grid container, and a grid
   container's own padding would shift the containing block of the
   absolutely-positioned video-frame (inset: 0 above) inward with it. */
.services-intro-section > .services-slide-inner {
    grid-column: 1;
    grid-row: 1;
    align-self: start;
    position: relative;
    z-index: 2;
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h));
    min-height: calc(100dvh - var(--header-h));
    min-height: calc(var(--vh100, 100dvh) - var(--header-h));
    padding-top: clamp(20px, 4vh, 40px);
    padding-bottom: clamp(20px, 4vh, 40px);
    display: flex;
    align-items: center;
}

/* Every line of copy in this section reads pure white against the
   video, rather than the slightly muted/tinted tones (--brass-light,
   --text-on-ink-muted) those same elements use elsewhere against a
   flat ink background — deliberately higher-contrast here since it's
   sitting over moving footage rather than a flat color. */
.services-intro-section .folio-label--on-ink,
.services-intro-section .services-intro-text {
    color: var(--text-on-ink);
}

/* .services-slide-inner is shared with the intro section's own text
   wrapper above (the horizontal editorial stage that used to reuse it
   for its 9 slides is gone — see .services-accordion further down). */
.services-slide-inner {
    max-width: var(--max);
    width: 100%;
    margin: 0 auto;
    padding: 0 var(--gutter);
}

/* Start slide: reuses .hero-content/.hero-title as-is (see the "HERO"
   rules above) so the opening statement carries the exact same weight
   as Home's "The confidence of never being alone." and Partnership's
   "Windsor Clearwater Partnership" — just centered as its own block
   here rather than sitting in a two-column split. .hero-subtitle isn't
   reused for the lede below it: that class is tuned for a short
   one-line tagline (including a desktop nowrap rule), which would
   badly overflow this slide's full explanatory paragraph. */
.services-intro {
    max-width: 640px;
    margin: 0 auto;
}

.services-intro-text {
    margin-top: 18px;
    max-width: 52ch;
    font-size: 16px;
    line-height: 1.9;
    color: var(--text-on-ink-muted);
}

/* ===== FOOTER ===== */
.footer {
    background-color: var(--ink);
    color: var(--text-on-ink-muted);
    padding: 30px var(--gutter);
    border-top: 1px solid var(--hairline-on-ink);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
}

.footer p {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* ===== COOKIE BANNER ===== */
.cookie-banner {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: var(--paper);
    border-top: 1px solid var(--ink);
    padding: 22px var(--gutter);
    z-index: 999;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 24px;
}

.cookie-content {
    flex: 1;
    font-size: 12.5px;
    color: var(--text-muted);
    line-height: 1.6;
}

.cookie-icon {
    width: 18px;
    height: 18px;
    margin-right: 10px;
    display: inline-block;
    vertical-align: middle;
}

.cookie-link {
    color: var(--ink);
    text-decoration: underline;
    cursor: pointer;
}

.cookie-link:hover {
    color: var(--brass);
}

.cookie-buttons {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
    flex-shrink: 0;
}

.cookie-btn {
    padding: 10px 22px;
    background: none;
    border: 1px solid var(--ink);
    border-radius: 0;
    cursor: pointer;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink);
    transition: all 0.3s ease;
}

.cookie-btn.accept {
    background-color: var(--ink);
    color: var(--paper);
}

/* ===== WHATSAPP FLOAT ===== */
/* A permanently fixed, static contact shortcut — present on every
   page, same corner, same size, no entrance animation, no hover
   movement and no scroll-linked behaviour of any kind (deliberate:
   this is a utility fixture, not a promotional widget). Styled in
   the site's own ink/paper palette with a hairline border rather
   than stock WhatsApp green, so it reads as part of "The Register"
   system instead of a bolted-on chat plugin. z-index sits below the
   header/cookie-banner/mobile-nav (all 999–1000) so the cookie
   banner and the full-screen mobile menu naturally cover it while
   they're open — no extra show/hide logic needed for that. */
.whatsapp-float {
    position: fixed;
    right: clamp(16px, 4vw, 28px);
    bottom: clamp(16px, 4vw, 28px);
    z-index: 500;
    width: 52px;
    height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--ink);
    border: 1px solid var(--hairline-on-ink);
    border-radius: 50%;
    box-shadow: 0 6px 18px rgba(6, 12, 18, 0.28);
}

.whatsapp-float svg {
    width: 24px;
    height: 24px;
    fill: var(--paper);
}

@media (hover: hover) and (pointer: fine) {
    .whatsapp-float:hover {
        background-color: var(--ink-soft);
        border-color: var(--brass-light);
    }
}

.cookie-btn.accept:hover {
    background-color: var(--brass);
    border-color: var(--brass);
}

.cookie-btn.reject {
    background: none;
    border-color: var(--hairline);
    color: var(--text-muted);
}

.cookie-btn.reject:hover {
    border-color: var(--ink);
    color: var(--ink);
}

/* ===== RESPONSIVE DESIGN =====
   Below 1024px the site deliberately switches from the desktop's
   one-viewport-per-section layout to a plain, natural-scrolling
   mobile/tablet reading flow: every section that was pinned to
   exactly one viewport tall switches from a hard `height` to
   `min-height` (or `auto`), so long content simply pushes the page
   taller instead of being clipped or trapped in its own inner scroll
   container. Nothing above this breakpoint changes. */
@media (max-width: 1024px) {
    /* Below 1024px the three inline links no longer fit comfortably
       next to the logo, so they move behind the hamburger/dropdown
       pair defined above instead. */
    .main-nav {
        display: none;
    }

    .nav-toggle {
        display: inline-flex;
    }

    .mobile-nav {
        display: flex;
    }

    /* The sticky-background reveal is a desktop-only enhancement (it
       depends on the two-column split layout). Below this breakpoint
       the hero reverts to a single, naturally-tall section stacked
       image-under-text, exactly like every other section here. */
    .hero {
        grid-template-columns: 1fr;
        align-content: normal;
        position: relative;
        height: auto;
        min-height: calc(100vh - var(--header-h));
        min-height: calc(100svh - var(--header-h));
        min-height: calc(100dvh - var(--header-h));
        min-height: calc(var(--vh100, 100dvh) - var(--header-h));
        overflow-y: visible;
    }

    .hero-text-col {
        padding: clamp(20px, 4vh, 40px) var(--gutter);
        position: relative;
        z-index: 2;
        min-height: calc(100vh - var(--header-h));
        min-height: calc(100svh - var(--header-h));
        min-height: calc(100dvh - var(--header-h));
        min-height: calc(var(--vh100, 100dvh) - var(--header-h));
        height: auto;
        overflow-y: visible;
    }

    /* Desktop's sticky, viewport-pinned video (see .hero-video-frame
       above) becomes a plain full-bleed background here instead — the
       double-height/sticky-pin mechanic is a desktop-only enhancement
       (it depends on the two-column grid), so below this breakpoint the
       video just fills the section like any other full-bleed photo
       background on the site (see .section-photo-bg-frame). */
    .hero-video-frame {
        grid-column: 1;
        grid-row: 1;
        position: absolute;
        inset: 0;
        height: 100%;
    }

    /* object-fit: cover on a narrow portrait screen keeps only roughly
       the middle third of this 16:9 clip's width — plain center crops
       the meeting's pointing hand/document out of frame for much of the
       loop (checked against actual extracted frames). Shifted right
       just enough to keep that in view without losing the handshake
       moment, which sits closer to center later in the same clip. */
    .hero-video--partnership {
        object-position: 62% center;
    }

    .hero + .section-content {
        margin-top: 0;
        z-index: auto;
    }

    .section-content,
    .team-section,
    .quote-section,
    .contact-section {
        height: auto;
        min-height: 0;
        overflow-y: visible;
    }

    /* A bit more room at the foot of every section (the hero is
       excluded — it fills the screen exactly, see above) — smaller
       than an earlier, too-generous version of this same idea, just
       enough that consecutive sections don't feel like they're
       touching. */
    .section-content,
    .quote-section,
    .contact-section,
    .team-section {
        padding-bottom: clamp(32px, 6vh, 72px);
    }

    /* Quote section: bigger than the rest of the page's default
       padding, its short single statement earns the extra room —
       still centered both ways via the shared .quote-section flex
       rule (align-items/justify-content: center), just with a taller
       box around it. */
    .quote-section {
        padding-top: clamp(32px, 7vh, 76px);
        padding-bottom: clamp(32px, 7vh, 76px);
    }

    /* width:100% makes the explicit fallback: as a flex child that's
       itself a grid, .section-wrapper mostly stretches to the parent's
       full available width on its own here, but its own intrinsic/
       fit-content sizing can still win in specific cases — Services
       being the clearest one, where removing/adding the section-image
       row changes the grid's own content-driven width and the whole
       carousel narrows along with it. Pinning width:100% removes that
       dependency for every section, not just Services. */
    .section-wrapper,
    .section-wrapper.reverse {
        grid-template-columns: 1fr;
        width: 100%;
    }

    .section-wrapper.reverse .section-image {
        order: 0;
    }

    .section-image--overlap {
        transform: none;
    }

    .section-image {
        height: 52vh;
    }

    /* Selected section headings, mobile only: centered within their
       column — equal left/right space either side. Desktop keeps each
       section's default left alignment.
       - Partnership: The Program (01), Our Approach (03)
       - Home: Who We Are (01), Services (05) — #services only exists
         on Home now, but the selector is left unprefixed since nothing
         else on either page needs to be excluded from it. */
    #our-approach .section-text .folio-label,
    .section-wrapper-single .folio-label,
    #who-we-are .section-text .folio-label,
    #services .section-text .folio-label {
        display: flex;
        width: fit-content;
        margin-inline: auto;
    }

    #our-approach .section-text h2,
    .section-wrapper-single h1,
    .section-wrapper-single h2,
    #who-we-are .section-text h2,
    #services .section-text h2 {
        width: fit-content;
        margin-inline: auto;
        text-align: center;
    }

    /* Home's Who We Are and Services ("What we offer") sections: image
       removed entirely on mobile, not just resized — the text carries
       each section alone here. Same treatment on every language, since
       this is a layout change rather than translated content. */
    #who-we-are .section-image,
    #services .section-image {
        display: none;
    }

    /* Why Windsor Clearwater (Partnership), mobile only: swap in its
       dedicated mobile background photo and hide the desktop one.
       Desktop (above this breakpoint) keeps the original untouched.
       Our Focus (Home) has a single background image shared by both
       breakpoints, so it isn't part of this swap. */
    #why-us .section-photo-bg-frame:not(.section-photo-bg-frame--mobile) {
        display: none;
    }

    #why-us .section-photo-bg-frame--mobile {
        display: block;
    }

    /* Our Focus, mobile only: the section itself gets more headroom
       (the general height:auto/min-height:0 reset above otherwise lets
       it shrink-wrap tightly to its content), and the spec-grid is
       pushed further down from the heading — same grid, same internal
       spacing, just shifted down within the taller section. Desktop is
       untouched — this whole block only exists below 1024px. */
    #focus {
        min-height: 88vh;
        min-height: 88svh;
    }

    #focus .spec-grid {
        margin-top: clamp(48px, 12vh, 96px);
    }

    /* A bit more breathing room around every folio-label eyebrow (e.g.
       "01 — Introduction") so mobile sections feel less cramped. */
    .folio-label {
        margin-top: clamp(16px, 4vh, 32px);
        margin-bottom: clamp(20px, 4vh, 36px);
    }

    .team-container {
        grid-template-columns: 1fr;
        min-height: 0;
    }

    /* Replaced by the small circular portrait next to the name — see
       .team-name-row / .team-photo-circle below. */
    .team-member-image {
        display: none;
    }

    .team-member-info {
        padding: clamp(20px, 4vh, 40px) var(--gutter);
    }

    .team-name-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 16px;
    }

    .team-member-info h2 {
        max-width: 9ch;
        line-height: 1.3;
        white-space: normal;
    }

    .team-photo-circle {
        display: block;
        position: relative;
        width: 160px;
        height: 160px;
        min-width: 160px;
        border-radius: 50%;
        overflow: hidden;
        flex-shrink: 0;
    }

    /* Scaled so the face fills the circle rather than a plain
       cover-fit crop of the whole wider source photo — zoomed and
       anchored from the top edge (object-position/transform-origin
       both "center top") so the extra zoom crops into the shoulders
       below, never into the head above. */
    .team-photo-circle-img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
        object-position: center top;
        transform: scale(1.2) translateY(-6px);
        transform-origin: center top;
    }

    .contact-form-wrapper {
        border-right: none;
        padding-right: 0;
        padding-bottom: clamp(16px, 3vh, 32px);
        border-bottom: 1px solid var(--hairline);
        margin-bottom: clamp(16px, 3vh, 32px);
    }

    .contact-container {
        grid-template-columns: 1fr;
    }

    .form-checkbox-grid {
        grid-template-columns: 1fr;
    }

    .contact-footer-info.contact-info {
        flex-direction: column;
        gap: 14px;
        text-align: left;
    }

    .simple-list {
        grid-template-columns: 1fr;
    }

    /* Typical Issues, mobile only: more headroom (the general
       height:auto/min-height:0 reset above otherwise lets the section
       shrink-wrap tightly to its content) and the heading forced onto
       two balanced lines rather than left to wrap wherever the
       viewport happens to cut it. Desktop is untouched. */
    #owner-reality {
        min-height: 78vh;
        min-height: 78svh;
    }

    #owner-reality h2 {
        max-width: 15ch;
    }

    /* Typical Issues for a Yacht Owner: a continuous, slowly
       auto-scrolling carousel (see initAutoCarousel in script.js —
       it duplicates these cards to loop seamlessly and drives
       scrollLeft directly). scroll-snap is deliberately left off: a
       snap point fights any non-gesture scrollLeft change, which is
       exactly what the continuous auto-scroll is, so free-scrolling
       is used for both the automatic motion and the visitor's own
       swipe. Pushed further down from the heading than the default
       list-section-head spacing (margin-top here, on top of that),
       so the carousel doesn't feel cramped right under it. */
    #owner-reality .simple-list {
        width: 100%;
        grid-template-columns: none;
        grid-auto-flow: column;
        grid-auto-columns: calc(50% - 10px);
        column-gap: 22px;
        overflow-x: auto;
        overflow-y: visible;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 4px;
        margin-top: clamp(20px, 4.7vh, 42px);
    }

    /* A soft lift instead of a flat outline: no border at all — a faint
       lightening of the card's own fill (barely more than the ink
       background it sits on) plus a shadow biased toward the bottom
       right, blurred enough to fade out gradually rather than reading
       as a hard-edged offset, is what now defines the card's shape.
       Kept short (small offset + blur, well inside the gap between
       cards above) so neighbouring cards read as separate rather than
       bleeding into one shared shadow. transform:none above still
       cancels the desktop hover-lift; the :active press below is this
       list's own touch-specific feedback instead. */
    #owner-reality .simple-list-item {
        border: none;
        padding: 16px 14px;
        background: rgba(244, 240, 230, 0.075);
        box-shadow: 3px 4px 9px rgba(0, 0, 0, 0.4);
        transition: transform var(--dur-sm) var(--ease), box-shadow var(--dur-sm) var(--ease);
    }

    #owner-reality .simple-list-item:hover {
        transform: none;
    }

    #owner-reality .simple-list-item:active {
        transform: scale(0.98);
        box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.32);
    }

    /* Your Benefits, mobile only: same extra headroom as Typical
       Issues above. Desktop is untouched. */
    #benefits {
        min-height: 78vh;
        min-height: 78svh;
    }

    /* Your Benefits: the same continuously auto-scrolling carousel as
       Typical Issues above, just on a light background, pushed down
       from the heading the same way. */
    #benefits .simple-list {
        width: 100%;
        grid-template-columns: none;
        grid-auto-flow: column;
        grid-auto-columns: calc(50% - 10px);
        column-gap: 22px;
        overflow-x: auto;
        overflow-y: visible;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 4px;
        margin-top: clamp(14px, 3.7vh, 34px);
    }

    /* Same borderless, bottom/right-biased soft shadow as Typical
       Issues above, just ink-tinted rather than black to stay warm
       against the paper background instead of muddy. */
    #benefits .simple-list-item {
        border: none;
        padding: 16px 14px;
        background: rgba(255, 255, 255, 0.55);
        box-shadow: 3px 4px 9px rgba(16, 30, 43, 0.18);
        transition: transform var(--dur-sm) var(--ease), box-shadow var(--dur-sm) var(--ease);
    }

    #benefits .simple-list-item:hover {
        transform: none;
    }

    #benefits .simple-list-item:active {
        transform: scale(0.98);
        box-shadow: 2px 2px 5px rgba(16, 30, 43, 0.15);
    }

    /* Checkmarks read fine as a plain list marker on desktop's
       hairline-divided rows, but inside these mobile boxes they read
       like a completed-task tick — dropped here, along with the h3's
       compensating left padding, so the title just sits flush in the
       box. Desktop (above this breakpoint) keeps the checkmark. */
    #benefits .simple-list-item h3 {
        padding-left: 0;
    }

    #benefits .simple-list-item h3::before {
        content: none;
    }

    /* Base mobile sizing for tag-grid-item, shorter and horizontally
       centered than the taller, left-aligned desktop boxes. Services is
       the only remaining consumer on mobile, and only as a base: the 3D
       wheel block below overrides position/height with its own
       rotateX/translateZ placement. */
    .tag-grid-item {
        min-height: 64px;
        padding: 14px 18px;
        justify-content: center;
        text-align: center;
    }

    /* ===== SERVICES: mobile-only 3D card stack =====
       The rotation model underneath is unchanged from the original
       wheel this evolved from: 9 fixed --item-angle slots (one per
       nth-child below, 40deg apart), a single --wheel-rotation script.js
       advances every frame, same direction, same speed, same touch-drag
       handling. What's different is how an item's angle turns into its
       on-screen position. It used to be rotateX(angle) — which both
       places a card and *tilts* it around that same axis, and at wide
       angles that tilt foreshortens a card down to a barely-visible
       sliver (a card 80deg out was rendering under 12px tall). Cards now
       stay flat and face the viewer: --item-y (a sine curve over the
       same angle, computed in script.js) moves each one vertically the
       way a point on a rim would move, --item-depth pushes it back along
       Z, and --item-scale shrinks it — gentle enough that five cards
       (front plus two either side) stay clearly present at once, fading
       gradually beyond that, rather than the original wheel's
       whole-height spread or an earlier, steeper version of this stack
       that only kept about three legible. The fade itself is handled by
       --item-fade on a same-color ::after scrim rather than the item's
       own opacity — see the ::after rule below for why. transform is
       deliberately left out of this rule's transition (unlike the
       background/color/border-color one just below, which IS
       transitioned) — it already changes smoothly frame-to-frame from
       the rAF loop's own interpolation, and a CSS transition layered on
       top of already-smooth values only adds lag.
       Untouched above 1024px: .tag-grid stays display:grid, and this
       whole block simply doesn't apply. */
    /* Services only: the heading sits noticeably closer to the carousel
       than the rest of the site's default section-text rhythm — a
       deliberate, requested exception, not a general heading-spacing
       change. */
    #services .section-text h2 {
        margin-bottom: clamp(0px, 0.5vh, 4px);
    }

    #services .tag-grid {
        display: block;
        position: relative;
        height: 400px;
        margin-top: -6px;
        perspective: 900px;
        perspective-origin: 50% 50%;
        overflow: hidden;
        touch-action: none;
        -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);
        mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);
    }

    /* Anchored at 50% of the (now shorter) box — 50% of 400px lands the
       front card at the same 200px offset from the top that 40% of the
       previous, taller 500px box did, so the gap up to the heading is
       untouched. What's gone is the box's own leftover height below the
       last card that 500px left unused (~140px of dead space nothing
       ever rendered into) — trimming the box removes exactly that,
       tightening the gap to the section below without moving a single
       card. */
    /* Borderless with the same soft, bottom/right-biased shadow used
       for the Typical Issues / Your Benefits carousels' cards, instead
       of the desktop hairline outline — keeps the same "raised card"
       language consistent across all three mobile carousels. */
    #services .tag-grid-item {
        position: absolute;
        left: 0;
        right: 0;
        top: 50%;
        height: 72px;
        margin-top: -36px;
        min-height: 0;
        border: none;
        box-shadow: 3px 4px 9px rgba(16, 30, 43, 0.18);
        transform: translateY(var(--item-y, 0px)) translateZ(calc(-1 * var(--item-depth, 0px))) scale(var(--item-scale, 1));
        backface-visibility: hidden;
        transition: background-color var(--dur-sm) ease, color var(--dur-sm) ease, box-shadow var(--dur-sm) ease;
    }

    /* Cards fade toward the background WITHOUT ever going translucent
       themselves — .tag-grid-item's own background stays fully opaque,
       so when a receding card's box overlaps the one in front of it,
       the front card still completely blocks it, border to border, no
       matter how far back the covered card sits. The "fade" is instead
       a same-color scrim painted on top of *this* card's own content
       only (never a neighbor's) — --item-fade is 0 at the front (scrim
       invisible) and grows toward the back, so a receding card
       visually dissolves into the page's own background color rather
       than becoming see-through. --paper here matches #services' own
       plain, light background — a card fading toward ink on a paper
       page would dissolve into a visible dark haze instead of the
       background. inset:0 lands on the padding edge (not the border
       edge), so each card's outline stays crisp even as its content
       fades — the stack still reads as a set of distinct, solid
       cards. */
    #services .tag-grid-item::after {
        content: '';
        position: absolute;
        inset: 0;
        background-color: var(--paper);
        opacity: var(--item-fade, 0);
        pointer-events: none;
    }

    #services .tag-grid-item:nth-child(1) { --item-angle: 0deg; }
    #services .tag-grid-item:nth-child(2) { --item-angle: 40deg; }
    #services .tag-grid-item:nth-child(3) { --item-angle: 80deg; }
    #services .tag-grid-item:nth-child(4) { --item-angle: 120deg; }
    #services .tag-grid-item:nth-child(5) { --item-angle: 160deg; }
    #services .tag-grid-item:nth-child(6) { --item-angle: 200deg; }
    #services .tag-grid-item:nth-child(7) { --item-angle: 240deg; }
    #services .tag-grid-item:nth-child(8) { --item-angle: 280deg; }
    #services .tag-grid-item:nth-child(9) { --item-angle: 320deg; }

    /* The item currently closest to "facing forward" (tracked in
       script.js, toggled only when it changes — not every frame): the
       same solid-ink emphasis the desktop hover above uses, so the
       wheel reads as the same design system on both breakpoints. */
    #services .tag-grid-item.is-focused {
        background-color: var(--ink);
        color: var(--text-on-ink);
    }

    /* prefers-reduced-motion: script.js never initializes the wheel (see
       the early return at the top of initServicesWheel) — --wheel-rotation
       and --item-fade/--item-scale are then permanently unset, which
       on its own would still leave every item individually splayed out
       around the cylinder at its fixed --item-angle (just frozen, not
       spinning) rather than a readable list. This restores the plain
       stacked-list rules the rest of the mobile breakpoint already uses
       for every other tag-grid, so reduced-motion visitors get the same
       simple, fully-readable Services list as before this feature. */
    @media (prefers-reduced-motion: reduce) {
        #services .tag-grid {
            display: grid;
            grid-template-columns: 1fr;
            gap: clamp(10px, 1.6vw, 18px);
            height: auto;
            margin-top: 0;
            perspective: none;
            overflow: visible;
            touch-action: auto;
            -webkit-mask-image: none;
            mask-image: none;
        }

        #services .tag-grid-item {
            position: static;
            height: auto;
            margin-top: 0;
            min-height: 64px;
            padding: 14px 18px;
            justify-content: center;
            text-align: center;
            transform: none;
            opacity: 1;
        }

        #services .tag-grid-item::after {
            display: none;
        }
    }

    .ledger {
        grid-template-rows: none;
        grid-auto-flow: row;
    }

    .ledger-item:nth-child(2n) {
        border-bottom: none;
    }

    .ledger-item:last-child {
        border-bottom: 1px solid var(--hairline);
    }

    .triptych {
        grid-template-columns: 1fr;
        row-gap: 40px;
    }

    .triptych-item {
        border-left: none;
        padding: 32px 0 0;
        border-top: 1px solid var(--hairline);
    }

    .triptych-item:first-child {
        border-top: none;
        padding-top: 0;
    }

}

/* Justified body copy (desktop and mobile): Services page paragraphs,
   Home's "Who We Are" and Joaquín Pereira bio, and Partnership's "What Is
   Windsor Clearwater?" and "Building Trust & Long Term Value" paragraphs. */
.services-intro-text,
.services-accordion-panel p,
.section-body p,
.team-description,
[data-i18n="partnership.program.text"],
[data-i18n^="partnership.approach.paragraphs"] {
    text-align: justify;
}

/* ===== SERVICES PAGE — ACCORDION =====
   The 9 services, listed vertically at every breakpoint — desktop
   included — with each description collapsed behind a tap instead of
   the old horizontal swipe-paged stage. Each row toggles independently
   — more than one can be open at once — via initServicesAccordion() in
   script.js. */
.services-accordion {
    max-width: 900px;
    margin: 0 auto;
    padding: clamp(28px, 6vh, 56px) var(--gutter) clamp(56px, 10vh, 96px);
}

.services-accordion-item {
    border-bottom: 1px solid var(--hairline);
}

.services-accordion-item:first-child {
    border-top: 1px solid var(--hairline);
}

/* Category groupings (MAINTENANCE / CHARTER / ADVISORY & MANAGEMENT /
   OPERATIONAL & CONCIERGE): the 9 services are unchanged —
   same rows, same plus/minus toggle, same panels — only sorted under
   these four headings. The heading is deliberately set in Helvetica bold
   caps so it reads as a section label, clearly apart from the service
   titles below it (which are Playfair). Each group's first row takes the
   same top hairline the very first item already had, so every group
   reads as its own bounded list. */
.services-category + .services-category {
    margin-top: clamp(40px, 7vh, 72px);
}

.services-category-title {
    margin: 0 0 14px;
    padding: 0 2px;
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: clamp(1.15rem, 0.95rem + 1vw, 1.6rem);
    line-height: 1.2;
    color: var(--ink);
}

.services-category-title + .services-accordion-item {
    border-top: 1px solid var(--hairline);
}

.services-accordion-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    width: 100%;
    padding: 18px 2px;
    background: none;
    border: none;
    text-align: left;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.services-accordion-title {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 1.05rem;
    line-height: 1.4;
    color: var(--ink);
}

/* Plus/minus toggle, drawn from two bars rather than a glyph so it can
   be animated precisely: the vertical bar collapses to zero height on
   open, leaving the horizontal one behind as a minus. */
.services-accordion-icon {
    position: relative;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
}

.services-accordion-icon::before,
.services-accordion-icon::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    background: var(--brass);
    transform: translate(-50%, -50%);
    transition: transform var(--dur-sm) var(--ease);
}

.services-accordion-icon::before {
    width: 100%;
    height: 1.5px;
}

.services-accordion-icon::after {
    width: 1.5px;
    height: 100%;
}

.services-accordion-item.is-open .services-accordion-icon::after {
    transform: translate(-50%, -50%) scaleY(0);
}

/* grid-template-rows 0fr → 1fr animates to the panel's natural height
   without knowing it in advance (services run from one to three
   paragraphs) — no fixed max-height guess needed. */
.services-accordion-panel-wrap {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows var(--dur-sm) var(--ease);
}

.services-accordion-item.is-open .services-accordion-panel-wrap {
    grid-template-rows: 1fr;
}

.services-accordion-panel {
    overflow: hidden;
    min-height: 0;
}

.services-accordion-panel p {
    padding: 0 2px 20px;
    font-size: 14px;
    line-height: 1.8;
    color: var(--text-muted);
}

/* ===== SHORT DESKTOP VIEWPORTS =====
   The one-section-per-viewport rule depends on section content fitting
   the available height, not just the available width. A laptop that's
   plenty wide but has a shorter screen (720/700px-tall windows, common
   on budget or older 1366×768 / 1280×720 displays) can still overflow
   content-heavy sections even though the equivalent 900px-tall viewport
   is fine. The vh-based clamp()s elsewhere already shrink with height,
   but their floors aren't low enough for these shorter windows — this
   tightens just those floors, purely for short desktop viewports. */
@media (min-width: 1025px) and (max-height: 760px) {
    .contact-section {
        padding-top: 16px;
        padding-bottom: 16px;
    }

    .contact-form-wrapper h2,
    .contact-info-wrapper h2 {
        margin-bottom: 14px;
    }

    .section-body-intro {
        margin-bottom: 12px;
    }

    .form-group {
        margin-bottom: 10px;
    }

    .form-group textarea {
        max-height: 60px;
    }

    .list-section-head {
        margin-bottom: 8px;
    }

    .simple-list-item,
    .ledger-item {
        padding-top: 6px;
        padding-bottom: 6px;
    }
}

@media (max-width: 640px) {
    :root {
        --header-h: 68px;
    }

    /* Content-heavy sections can't shrink to fit a single small-phone
       screen without hurting readability. They keep their own snap
       stop but scroll internally (overflow-y: auto, set globally) —
       these overrides just claw back as much room as possible first. */
    .team-member-image {
        height: 24vh;
        min-height: 160px;
    }

    .hero-text-col,
    .team-member-info {
        padding-top: 16px;
        padding-bottom: 16px;
    }

    .section-image {
        height: 40vh;
    }

    .form-group textarea {
        max-height: 70px;
    }

    .logo-container {
        gap: 16px;
        padding: 0 20px;
    }

    .logo-image.logo-large {
        max-height: 26px;
    }

    .main-nav-links {
        gap: 20px;
    }

    .hero-title {
        max-width: none;
    }

    /* Services intro heading, phone widths: a long translated word on
       its own ("Verantwortung", "Responsabilidad"…) can still be wider
       than the screen, with nowhere to break. overflow-wrap: anywhere
       (not the weaker break-word, which changes how a box wraps but is
       defined to NOT shrink that box's own min-content contribution —
       so it still fails to stop the overflow from propagating up
       through the flex chain) lets a too-long word factor into sizing
       as breakable, so .services-slide-inner (and the flex chain
       inside it) can actually shrink to the screen instead of being
       forced wider by one unbreakable word. min-width: 0 on that same
       flex chain is the belt-and-braces backstop: flex/grid items
       refuse to shrink below their content's min-content by default
       (min-width: auto), which is the actual mechanism that turns "one
       long word" into "the whole page scrolls sideways" — overriding
       it here is what keeps that failure mode from reopening for some
       future word or language this exact reasoning didn't anticipate. */
    .services-intro-text {
        overflow-wrap: anywhere;
    }

    .services-intro-section .services-slide-inner,
    .services-intro-section .hero-content {
        min-width: 0;
    }

    /* Spanish services intro heading, phone widths only: the owner wants
       "“Su Yate. Nuestra" on the first line and "Responsabilidad.”" on
       the second. white-space: normal drops the hard break that the
       source newline between the two title spans otherwise forces (via
       the inherited pre-line), letting the phrase reflow as one run;
       text-wrap: balance then splits it into two lines of near-equal
       length — which for this phrase is exactly that break. Desktop and
       the other languages are untouched. */
    html[lang="es"] .services-intro-section .hero-title {
        white-space: normal;
        text-wrap: balance;
    }

    .ledger-item {
        grid-template-columns: 1fr;
        gap: 14px;
    }

    .cookie-banner {
        flex-direction: column;
        align-items: flex-start;
    }

    .cookie-buttons {
        width: 100%;
    }

    .cookie-buttons .cookie-btn {
        flex: 1;
        text-align: center;
    }
}
