/* ============================================================
   BEINC — Navigation Bar (Section CSS)

   Architecture:
   - Single pill (.nav-bg) animates from full-width (contracted)
     to right-of-cube (expanded), exposing the 3D cube as a
     floating element on desktop/tablet, or the flat-B square
     on mobile (which has its own dark bg).
   - Menu trigger is absolutely centered at 50% of nav-container,
     which (being margin: 0 auto) resolves to 50vw horizontally
     in both contracted and expanded states.
   - Mega menu sits at z:900 (below nav z:1000) so the trigger
     stays on top and acts as both opener and closer.

   Depends on: global.css
   ============================================================ */

/* ── Nav-specific tokens ── */
:root {
    --nav-section-height: 8vh;
    --nav-pad-top: 2vh;
    --nav-height: calc(var(--nav-section-height) - var(--nav-pad-top));
    --nav-pad-x: 2%;
    --nav-font: var(--text-xs);
    --nav-grid-size: 20px;

    /* Inner horizontal padding (pill edge → cube/wordmark, pill edge → cta).
       Smaller value = cube/wordmark/cta sit closer to their pill edges,
       and the cube appears more "left aligned" in the expanded viewport. */
    --nav-inner-pad: var(--space-3);
    /* Gap between the cube and the pill when the nav is expanded */
    --nav-split-gap: var(--space-4);
}


/* ── NAVBAR ── */
.navbar {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: var(--z-nav);
    height: var(--nav-section-height);
    padding: var(--nav-pad-top) var(--nav-pad-x) 0 var(--nav-pad-x);
    pointer-events: none;
}

.nav-container {
    position: relative;
    max-width: 60vw;
    height: 100%;
    margin: 0 auto;
    pointer-events: auto;
    /* Grow/shrink symmetrically in both directions between contracted and expanded.
       Matches the other nav transitions (clip-path, logo transforms) so the whole
       nav animates as one coordinated motion. */
    transition: max-width 0.5s cubic-bezier(0.65, 0, 0.35, 1);
}


/* ── PILL BACKGROUND ──
   The dark surface. In expanded state, clip-path trims the left portion so
   the cube is revealed. Using clip-path (not `left`) keeps the animation
   GPU-composited — no layout/paint thrashing — and border-radius is preserved
   via the `round` keyword on the clip. */
.nav-bg {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--color-surface);
    border-radius: var(--radius);
    overflow: hidden;
    z-index: 0;
    clip-path: inset(0 0 0 0 round var(--radius));
    transition: clip-path var(--duration-normal) cubic-bezier(0.65, 0, 0.35, 1);
    will-change: clip-path;
}

.grid-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    pointer-events: none;
    z-index: 0;
}


/* ── LOGO ZONE (left) ── */
.nav-logo {
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--nav-inner-pad);
    display: flex;
    align-items: center;
    z-index: 2;
}

.logo-wordmark {
    display: flex;
    align-items: center;
    height: 100%;
    opacity: 1;
    transform: translateX(0) scale(1);
    transform-origin: left center;
    /* Wordmark is a link to home. Clickable in the contracted state only;
       pointer-events are flipped off when the navbar expands so the (now
       hidden) wordmark can never catch a stray click. The visible cube
       (or mobile-B) becomes the active home link instead. */
    pointer-events: auto;
    text-decoration: none;
    color: inherit;
    /* Opacity fades fast (350ms) so we get a quick crossfade.
       Transform rides the full 500ms so the physical slide-and-shrink
       dominates the perception (the "morph" feel the user wants).
       Opacity has a 120ms delay on the RETURN (expanded → contracted)
       so the wordmark waits for the pill's clip-path to retract enough
       to cover its space before fading in — prevents the wordmark from
       briefly appearing outside the dark pill on the left. The
       `.navbar.expanded .logo-wordmark` rule below resets this delay
       to 0s so the OPEN direction (which feels perfect) is unchanged. */
    transition: opacity 0.35s ease 0.2s,
                transform 0.5s cubic-bezier(0.65, 0, 0.35, 1);
    will-change: transform, opacity;
}

.wordmark-svg {
    height: 35%;
    width: auto;
    display: block;
}

.logo-cube-wrapper {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    position: absolute;
    top: 0;
    left: 0;
    width: var(--nav-height);
    height: 100%;
    opacity: 0;
    /* Starts offset to the right + scaled down so the expand reads as
       slide-in-from-wordmark-position. Generous values so the morph is
       clearly visible, not subtle. */
    transform: translateX(24px) scale(0.55);
    transform-origin: left center;
    pointer-events: none;
    transition: opacity 0.35s ease,
                transform 0.5s cubic-bezier(0.65, 0, 0.35, 1);
    will-change: transform, opacity;
    z-index: 2;
}

#cubeCanvas {
    display: block;
    position: absolute;
    top: 50%;
    left: 0;
}

/* Mobile flat-B square: has its own dark bg so it appears isolated
   once the main pill shrinks (the pill retreats; the square stays). */
.logo-b-mobile {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    width: var(--nav-height);
    height: 100%;
    aspect-ratio: 1;
    background: var(--color-surface);
    color: var(--color-white);
    border-radius: var(--radius);
    opacity: 0;
    transform: translateX(24px) scale(0.55);
    transform-origin: left center;
    pointer-events: none;
    align-items: center;
    justify-content: center;
    z-index: 2;
    transition: opacity 0.35s ease,
                transform 0.5s cubic-bezier(0.65, 0, 0.35, 1),
                background-color 0.3s ease,
                color 0.3s ease;
    will-change: transform, opacity;
}
.logo-b-mobile.-theme-light {
    background: var(--color-white);
    color: var(--color-black2);
}

.b-mobile-svg {
    height: 45%;
    width: auto;
    display: block;
}


/* ── MENU TRIGGER ──
   Absolutely centered at 50% of nav-container. Because nav-container
   is margin: 0 auto, its 50% resolves to 50vw horizontally in every state. */
.menu-trigger {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    background: none;
    border: none;
    color: var(--color-white);
    font-family: var(--font-body);
    font-size: var(--nav-font);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    cursor: pointer;
    padding: var(--space-2) var(--space-3);
    white-space: nowrap;
    z-index: 3;
    transition: color var(--duration-normal) ease;
}

.menu-trigger:focus-visible {
    outline: 1px dashed var(--color-greyish);
    outline-offset: 2px;
}

/* ── Menu label — directional drop dissolve ──
   Both "Menu" and "Close" live in the DOM, stacked via grid so the wrapper
   sizes to the wider word (no icon jump on swap). The actual entry/exit
   animations are driven by GSAP in nav.js because the motion is direction-
   aware: the OUTGOING text always falls DOWN + fades, the INCOMING text
   always drops in from ABOVE. A pure CSS transition between two rest
   states can't do this (it would just reverse on the way back). CSS here
   only sets the initial paint state so the first render before JS hooks
   in shows "Menu" centered and "Close" hidden. */
.menu-label {
    display: inline-grid;
    grid-template-areas: "stack";
    line-height: 1;
    /* Clip the texts as they enter/exit so the motion reads as a
       "scrolling window" (iOS picker style) — text appears to slide
       INTO the visible area rather than fading in over the top. The
       wrapper height equals line-height; texts moving outside it are
       cleanly hidden. Both labels have no descenders ("Menu", "Close")
       so this won't clip glyph tails. */
    overflow: hidden;
}
.menu-label__text {
    grid-area: stack;
    will-change: opacity, filter, transform;
}
/* Initial paint — Menu visible at rest, Close fully transparent. nav.js
   immediately overrides these via GSAP on every click, so no transition
   here. Pointer-events off on Close so the (currently invisible) text
   can't catch a stray click on top of the visible one. */
.menu-label__text--close {
    opacity: 0;
    pointer-events: none;
}

/* ── Menu icon — single SVG, two paths that morph ──
   We keep Jose's original hamburger SVG paths (with their custom rounded ends
   and slight tilt) and animate THEM into an X by translating each path to the
   icon's vertical centre + rotating ±45°. The exact same bars stay on screen
   the whole time — no cross-fade between separate icons, no swapped SVGs.

   transform-box: fill-box + transform-origin: center → each path rotates
   around its OWN visual centre (not the SVG origin), which is what makes
   the rotation read as the bar pivoting in place rather than swinging in
   an arc around (0,0).

   overflow: visible on the SVG → the rotated bars extend slightly past the
   14×11 viewBox without being clipped (a 14-wide bar rotated 45° stretches
   ~10px outside the box). The .nav-bg pill behind it has plenty of room. */
.menu-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    position: relative;
    flex-shrink: 0;
}
.menu-icon-svg {
    overflow: visible;
    display: block;
}
.menu-icon__bar {
    transform-box: fill-box;
    transform-origin: center;
    /* Default morph tween — uses --ease-out-expo (a smooth deceleration
       curve) instead of the previous ease-in-out which had a snappier
       start AND end. 450ms gives the rotation room to breathe — short
       enough to still feel responsive, long enough that the eye reads
       the path of motion rather than just the endpoints. */
    transition: transform 0.45s var(--ease-out-expo);
    will-change: transform;
}

/* Hover state — bars rotate to the horizontally-mirrored version of the
   hamburger. Each path's intrinsic tilt is ~3.35° off horizontal, so a
   ±7° rotation flips it to the mirror angle. Slightly longer 0.28s and
   the gentle curve so the rotation eases in/out instead of snapping. */
.menu-trigger:hover .menu-icon__bar--top {
    transform: rotate(7deg);
    transition: transform 0.28s var(--ease-gentle);
}
.menu-trigger:hover .menu-icon__bar--bottom {
    transform: rotate(-7deg);
    transition: transform 0.28s var(--ease-gentle);
}

/* Open state — both bars slide to the icon's vertical centre and rotate
   ±45° to form an X. translateY ±3.43px puts them at y≈5.35 (icon centre).
   Declared AFTER the :hover rules so it wins source-order at equal
   specificity — once .is-open is on, hovering does nothing extra.
   Restores the longer ease-out-expo tween for the morph itself. */
.menu-trigger.is-open .menu-icon__bar--top {
    transform: translateY(3.43px) rotate(45deg);
    transition: transform 0.45s var(--ease-out-expo);
}
.menu-trigger.is-open .menu-icon__bar--bottom {
    transform: translateY(-3.43px) rotate(-45deg);
    transition: transform 0.45s var(--ease-out-expo);
}


/* ── CTA ── */
.nav-cta {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    display: flex;
    align-items: center;
    padding: 0 var(--space-1);
    z-index: 2;
}

.cta-button-wrap {
    position: relative;
    height: calc(var(--nav-height) - 8px);
    z-index: 2;
}

.cta-shape {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    color: var(--color-white);
    transition: color var(--duration-normal) ease;
}

.cta-button {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    padding: 0 var(--space-5);
    background: none;
    color: var(--color-surface);
    font-size: var(--nav-font);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    border: none;
    white-space: nowrap;
    z-index: 2;
    transition: color var(--duration-normal) ease;
}

.cta-text { display: inline; }

.cta-arrow {
    display: inline-flex;
    align-items: center;
    width: 0;
    padding-left: 0;
    opacity: 0;
    overflow: hidden;
}

.cta-arrow svg {
    width: 10px;
    height: 14px;
    flex-shrink: 0;
}

/* CTA contrast (matches current behavior) */
.cta-button.cta-dark {
    color: var(--color-white);
}
.cta-button.cta-dark ~ .cta-shape,
.cta-shape.cta-shape--dark {
    color: var(--color-black);
}


/* ── EXPANDED STATE ──
   Pill clips its left portion (clip-path) so the cube appears isolated.
   Wordmark slides-and-shrinks leftward while the cube slides-in from its
   right resting offset. All three transitions share the same duration +
   easing so the eye reads them as one coordinated motion. */
.navbar.expanded .nav-container {
    max-width: 100%;
}

.navbar.expanded .nav-bg {
    /* Pill's visible left edge = cube width + gap from the container's left.
       Cube/flat-B now sit at container.left (no nav-inner-pad offset), so the
       calc drops that term. */
    clip-path: inset(0 0 0 calc(var(--nav-height) + var(--nav-split-gap)) round var(--radius));
}

.navbar.expanded .logo-wordmark {
    opacity: 0;
    /* Exits stage-left while shrinking — reads as "retreating into the corner" */
    transform: translateX(-28px) scale(0.5);
    /* Reset the opacity delay defined on the resting `.logo-wordmark` rule.
       That delay is intentional for the RETURN trip (expanded → contracted),
       but the OPEN trip needs the wordmark to start fading the moment the
       pill's clip-path begins exposing the cube — no delay. */
    transition: opacity 0.35s ease 0s,
                transform 0.5s cubic-bezier(0.65, 0, 0.35, 1);
}

.navbar.expanded .logo-cube-wrapper {
    opacity: 1;
    transform: translateX(0) scale(1);
    pointer-events: auto;
}

/* When the navbar expands, the wordmark fades out — disable its clicks so
   the now-invisible wordmark can never intercept a click meant for empty
   space. The cube takes over as the active home link. */
.navbar.expanded .logo-wordmark { pointer-events: none; }


/* ── GUIDE LINES ── */
.nav-guide-lines {
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 100%;
    pointer-events: none;
    z-index: var(--z-indicator);
    overflow: hidden;
}

.nav-guide {
    position: absolute;
    opacity: 0;
    background: none;
}

.nav-guide--left-top,
.nav-guide--left-bottom,
.nav-guide--right-top,
.nav-guide--right-bottom {
    height: 0;
    border-top: var(--guide-border);
}

.nav-guide--left-top,
.nav-guide--left-bottom {
    left: 0;
    transform-origin: left center;
}

.nav-guide--right-top,
.nav-guide--right-bottom {
    transform-origin: right center;
}

.nav-guide--top-left,
.nav-guide--top-right {
    width: 0;
    top: 0;
    border-left: var(--guide-border);
    transform-origin: center top;
}


/* ============================================================
   MEGA MENU
   ------------------------------------------------------------
   Architecture:
   .mega-menu         fixed full-viewport wrapper (z:900, below nav z:1000)
     .mm-backdrop     blurred + dark-tinted layer over the page behind
     .mm-panel        rounded content panel, inset from viewport edges
       .mm-main       LEFT column (priority content)
         .mm-main__top    (row: .mm-nav + .mm-preview)
         .mm-main__bottom (row: .mm-contact + .mm-others + .mm-social)
       .mm-promo      RIGHT column (promo cards; hides at tablet)

   Responsive priority-hiding (in order of what hides first when
   viewport shrinks): .mm-promo → .mm-main__bottom → .mm-preview.
   .mm-nav always visible.
   ============================================================ */

.mega-menu {
    position: fixed;
    inset: 0;
    z-index: 900;
    /* Visibility-gate ONLY — no CSS opacity transition. The .is-open class
       flips visibility instantly; GSAP owns the smooth fades on the backdrop
       and panels inside. We use `visibility` instead of `display` so the
       child elements keep their layout (GSAP needs measurable boxes), and
       opacity:0 ensures nothing paints if styles load before JS runs (FOUC
       guard — without this, the panels flash visible on page reload until
       the menu trigger is clicked for the first time). */
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
}
.mega-menu.is-open {
    visibility: visible;
    opacity: 1;
    pointer-events: auto;
}

/* ── BACKDROP ──
   Initial opacity:0 with no CSS transition — nav.js GSAP-animates it on
   open/close so the fade starts the INSTANT the user clicks (no waiting
   for the parent class transition). Smooth power2 easing in both directions.
   Explicit `pointer-events: auto` so the backdrop reliably blocks hover
   and clicks from reaching anything painted underneath when the menu is
   open. (Without this we were inheriting from .mega-menu.is-open which
   is correct in theory, but some browsers / overlay-effect combinations
   leaked events through to the page below.) */
.mm-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    cursor: pointer;   /* clicking closes the mega menu */
    pointer-events: auto;
    z-index: 1;
    opacity: 0;
    will-change: opacity;
}

/* ── PANEL ── */
.mm-panel {
    position: absolute;
    /* Top offset: full nav height (section + pad-top) + same pad again as a gap */
    top: calc(var(--nav-section-height) + var(--nav-pad-top));
    left: var(--pad-x);
    right: var(--pad-x);
    bottom: var(--nav-pad-top);
    display: flex;
    gap: var(--space-4);
    z-index: 2;
    min-height: 0;
}

/* ── LEFT: MAIN CONTENT ── */
.mm-main {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    background: var(--color-surface);
    border-radius: var(--radius);
    overflow: hidden;
    /* No outer dashed border — dashed lines only live INSIDE (between mm-nav
       and mm-preview, and between mm-main__top and mm-main__bottom). */
}

.mm-main__top {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    border-bottom: var(--guide-border);
}

/* Primary page nav */
.mm-nav {
    flex: 0 0 clamp(160px, 18%, 260px);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-5);
    border-right: var(--guide-border);
    min-width: 0;
}

.mm-nav__list {
    display: flex;
    flex-direction: column;
    /* More breathing room between page links — matches the reference mockup */
    gap: var(--space-6);
}

.mm-nav__link {
    position: relative;
    display: inline-flex;
    align-items: center;
    /* Default (inactive): no left padding — text sits flush with the list's left edge,
       exactly where the indicator would be when active. When .is-active is applied,
       padding-left reserves room for the indicator, pushing the text right. */
    padding-left: 0;
    color: var(--color-white);
    font-family: var(--font-heading);
    font-size: clamp(20px, 2.2vw, 32px);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    line-height: 1;
    text-decoration: none;
    width: fit-content;
    /* Hover-swap transition is set in the override block AFTER the
       fade-in stagger (further down) so it wins source-order. */
}
/* (Sticky-hover dim rule moved BELOW .is-active so it wins source-order at
   equal specificity. Without this, hovering the active page does nothing —
   the active rule's white color stays. See `.mm-nav__link.is-hover-target`
   further down.) */

.mm-nav__link.is-active {
    /* Dot (10px) + gap between dot and text */
    padding-left: calc(10px + var(--space-3));
    color: var(--color-white);
}
/* Sticky-hover dim — declared AFTER .is-active so it wins source-order at
   equal specificity. Mouse-leave does NOT reset; another link's hover does.
   We deliberately don't use :hover so the dim state survives when the cursor
   moves to the preview area or off the panel. Applies even to the active
   page — its label dims to #7b7b7b. The indicator square is intentionally
   excluded so it stays white as a visual anchor. */
.mm-nav__link.is-hover-target { color: #7b7b7b; }

/* Active indicator — ~10px filled square anchored at the link's left edge (x:0).
   Inactive text sits at x:0 too (no left padding), so the indicator position
   aligns with the natural text-start column across ALL links. */
.mm-nav__indicator {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 10px;
    height: 10px;
    background: var(--color-white);
    opacity: 0;
    pointer-events: none;
}
.mm-nav__link.is-active .mm-nav__indicator { opacity: 1; }
/* The indicator square stays WHITE even when its link is the hover target —
   it's the visual anchor for "this is your current page", so it shouldn't
   blend in with the dimmed label. Only the link text changes colour. */

/* Dynamic preview — all slides stacked, opacity-swap on hover / active. */
.mm-preview {
    flex: 1 1 auto;
    position: relative;
    padding: var(--space-5);
    min-width: 0;
    min-height: 0;
}
.mm-preview__slide {
    position: absolute;
    inset: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
}
.mm-preview__slide.is-visible {
    opacity: 1;
    pointer-events: auto;
}
.mm-preview__eyebrow {
    font-family: var(--font-body);
    font-weight: var(--font-weight-regular);
    font-size: 12px;
    letter-spacing: var(--tracking-body);
    color: var(--color-greyish);
}
.mm-preview__text {
    font-family: var(--font-heading);
    font-size: clamp(24px, 2.5vw, 40px);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    line-height: 1.1;
    color: var(--color-white);
    margin: 0;
    max-width: 40ch;
}

/* ---------- Home slide (modifier: --home) ----------
   Three-up row inside the slide: a small landscape (16:9) video centered
   between two asterisk icons. Each side is a flex:1 column so the icons are
   auto-centered between the video edge and the panel edge. The asterisks
   have two layers of motion at once:
     • Per-bar "breathe" — each of the three bars pulses on its own axis
       (transform-origin via fill-box on the bar's bounding box). This is
       the same animation the standalone Animated Asterisk SVG defines
       internally — re-implemented here with scoped class names so the
       inline SVG doesn't need its own <style> block and we keep one
       source of truth for the animation curves.
     • Whole-icon spin — the outer SVG element rotates at 12s/turn. The
       LEFT icon spins counter-clockwise, the right clockwise (via
       animation-direction: reverse on --left). The two transform layers
       compose cleanly because the bars use fill-box (their own boxes)
       while the spin acts on the outer <svg> element's box.
   The slide inherits its 24px outer inset from the base .mm-preview__slide
   (position: absolute; inset: var(--space-5);) and the 12px gap between
   the eyebrow and the row from the base flex column layout. */
.mm-preview__home-label {
    text-align: center;
    flex: 0 0 auto;
}
.mm-preview__home-row {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.mm-preview__home-asterisk {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-white);
}
.mm-preview__home-asterisk .mm-asterisk-svg {
    width: 30px;
    height: auto;
    display: block;
    transform-origin: center;
    animation: mmAsteriskSpin 12s linear infinite;
}
.mm-preview__home-asterisk--left .mm-asterisk-svg {
    animation-direction: reverse;
}
.mm-asterisk__bar {
    transform-box: fill-box;
    transform-origin: center;
}
.mm-asterisk__bar--1 {
    animation: mmAsteriskBreathe1 2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
.mm-asterisk__bar--2 {
    animation: mmAsteriskBreathe2 2.2s cubic-bezier(0.45, 0, 0.55, 1) 0.3s infinite backwards;
}
.mm-asterisk__bar--3 {
    animation: mmAsteriskBreathe3 2.4s cubic-bezier(0.45, 0, 0.55, 1) 0.7s infinite backwards;
}
@keyframes mmAsteriskBreathe1 {
    0%, 100% { transform: rotate(35.6deg) scaleX(0.7) rotate(-35.6deg); }
    50%      { transform: rotate(35.6deg) scaleX(1)   rotate(-35.6deg); }
}
@keyframes mmAsteriskBreathe2 {
    0%, 100% { transform: rotate(88deg) scaleX(0.72) rotate(-88deg); }
    50%      { transform: rotate(88deg) scaleX(1)    rotate(-88deg); }
}
@keyframes mmAsteriskBreathe3 {
    0%, 100% { transform: rotate(131.7deg) scaleX(0.68) rotate(-131.7deg); }
    50%      { transform: rotate(131.7deg) scaleX(1)    rotate(-131.7deg); }
}
@keyframes mmAsteriskSpin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}
.mm-preview__video-wrap {
    flex: 0 0 auto;
    width: 65%;
    aspect-ratio: 16 / 9;
    max-height: 100%;
    border-radius: var(--radius);
    background: #000;
    overflow: hidden;
}
.mm-preview__video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ---------- About slide (modifier: --about) ----------
   Infinite marquee of polaroid cards arranged around the rim of a giant
   circle that rotates CCW on its own axis. Only the top arc is visible —
   the rest of the wheel sits well below the panel and gets clipped by the
   stage's overflow:hidden. Truly seamless: a 360° animation cycle returns
   the wheel to its identical starting state, so there's no JS reset, no
   flicker, no jump. Pause/resume is free via animation-play-state.
   Polaroids drift right→left along the arc and tilt with the wheel as they
   travel, matching the SVG reference (rim-tangent orientation + small
   handmade tilt per card).
   • Side fades = a CSS mask gradient on .about-stage (hard fade to nothing).
   • Bottom vignette = a darkening ::after overlay (soft, doesn't fully hide).
   • Polaroid count, radius, spin time, card size, top-offset = CSS vars on
     .about-stage__wheel — tune in one place. */
.mm-preview__slide--about .mm-preview__about-caption {
    /* Caption is upgraded from the default eyebrow look (Sora 12px) to a
       larger heading-style line — Indivisible Medium 25px — but keeps the
       same greyish colour inherited from .mm-section__label.
       margin-bottom pushes it UP away from the slide's bottom edge so it
       sits closer to the polaroid arc above (in flex column, that margin
       eats into the stage's flex-grow allowance, shrinking the stage and
       therefore raising the caption inside the slide). */
    text-align: center;
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    font-size: 25px;
    letter-spacing: var(--tracking-tight);
    line-height: 1.1;
    margin-bottom: 80px;
}

.about-stage {
    flex: 1 1 auto;
    position: relative;
    overflow: hidden;
    min-height: 0;
    /* Hard L/R fades — polaroids fully fade to transparent at the edges. */
    -webkit-mask-image: linear-gradient(
        to right,
        transparent 0%,
        #000 18%,
        #000 82%,
        transparent 100%
    );
    mask-image: linear-gradient(
        to right,
        transparent 0%,
        #000 18%,
        #000 82%,
        transparent 100%
    );
}

.about-stage::after {
    /* Soft bottom-to-top vignette. Same colour as panel surface so polaroids
       look like they're emerging from the dark background, not floating
       on top of an overlay. Reaches transparent ~halfway up. */
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to top,
        var(--color-surface) 0%,
        rgba(38, 38, 38, 0) 55%
    );
    pointer-events: none;
    z-index: 2;
}

.about-stage__wheel {
    --pol-w: 180px;
    --pol-h: 232px;
    --wheel-radius: 1140px;
    --wheel-spin-duration: 120s;
    /* Top edge of the topmost polaroid measured from the top of .about-stage.
       Tuned so the arc sits a touch below vertical centre of the stage —
       leaves a comfortable gap above for the eyebrow + breathing room, and
       only a small dark strip below before the caption. */
    --polaroid-top-offset: 110px;

    position: absolute;
    left: 50%;
    /* Wheel div top = polaroid-top-offset + half a polaroid (because the top
       polaroid's CENTRE sits at the very top of the wheel div). */
    top: calc(var(--polaroid-top-offset) + var(--pol-h) / 2);

    width: calc(var(--wheel-radius) * 2);
    height: calc(var(--wheel-radius) * 2);
    transform-origin: 50% 50%;
    /* The animation drives the full transform — translateX(-50%) for centring
       PLUS the rotation. Don't set transform here, the keyframes own it. */
    animation: about-wheel-spin var(--wheel-spin-duration) linear infinite;
    will-change: transform;
}

@keyframes about-wheel-spin {
    from { transform: translateX(-50%) rotate(0deg); }
    to   { transform: translateX(-50%) rotate(-360deg); }
}

.about-pol {
    /* Per-card vars set inline by JS: --angle (slot on rim), --tilt (handmade) */
    --angle: 0deg;
    --tilt: 0deg;

    position: absolute;
    left: 50%;
    top: 50%;
    width: var(--pol-w);
    height: var(--pol-h);
    margin-left: calc(var(--pol-w) / -2);
    margin-top: calc(var(--pol-h) / -2);

    /* Order matters: rotate(tilt) → translate to rim → rotate to slot.
       The final rotate(angle) carries the polaroid AROUND the wheel centre
       (so its position changes); the inner rotate(tilt) only spins it in
       place. Net visible rotation = angle + tilt. */
    transform: rotate(var(--angle))
               translate(0, calc(-1 * var(--wheel-radius)))
               rotate(var(--tilt));
    transform-origin: 50% 50%;

    /* Classic instant-photo frame: thin cream margin on top/L/R, fatter
       caption strip at the bottom. Cream colour matches --color-white so it
       reads as "real" polaroid stock against the dark panel. */
    background: var(--color-white);
    padding: 9px 10px 40px 10px; /* top right bottom left */
    box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.1);
    box-sizing: border-box;
    overflow: hidden;
}

/* Photo lives on a DIV with background-image — NOT an <img> element.
   Chromium has a quirk where <img> children inside multi-level rotated
   transforms (polaroid rotated → wheel rotated → wheel translated) sometimes
   skip painting and the photo stays blank. background-image bypasses this
   entirely because it's part of the parent's paint, not a separate replaced
   element. We trade off a bit of accessibility (no semantic <img>) but the
   photos are decorative anyway (alt=""), so no real loss. */
.about-pol__photo {
    width: 100%;
    height: 100%;
    background-color: var(--color-white);
    background-position: center center;
    background-size: cover;
    background-repeat: no-repeat;
    pointer-events: none;
    user-select: none;
}

/* ---------- Services slide (modifier: --services) ----------
   Tabbed services preview. Top half is a 4+3 cell grid of service tabs
   (rounded outer dashed frame + dashed inner dividers); bottom half is
   the dynamic content panel (left = body text + Learn More CTA, right =
   landscape image vertically centred).

   Tab states (4):
     1. default        — bg #3C3B3A, white-cream text
     2. hover          — text gets a 1px center-out underline (currentColor)
     3. is-clicked     — bg --color-grid, white-cream text (preview cursor)
     4. is-active-page — bg cream, dark text (you're on this service's page)

   Per-spec (Option A): page-active wins. The cream cue persists even when
   the user clicks another tab to peek; the click-selected (grid) state
   layers on top of OTHER tabs. The active-page tab itself never receives
   is-clicked — clicking it just swaps the preview content + flips the CTA
   from "Learn More →" to "You're on this page" (no arrow, no link).
   Hovering the cream tab shows the underline in dark via currentColor. */

/* Slide-level: bump the inter-element gap so eyebrow / tabs grid / content
   row breathe more than the default .mm-preview__slide gap of 12px. */
.mm-preview__slide--services {
    gap: var(--space-5); /* 24px between eyebrow → tabs → content */
}

.mm-preview__services-eyebrow {
    flex: 0 0 auto;
}

/* TABS GRID
   - Square corners, full outer dashed border on all 4 sides.
   - Inner dashed dividers between cells (vertical) and between the two
     rows (horizontal — drawn by .services-tabs__divider's ::before).
   - We do NOT use border-top on row 2 directly because that would take
     1px of layout and push row 2's content asymmetrically down. The
     divider element is 0-height, the line sits exactly on the boundary. */
.services-tabs {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    border: var(--guide-border);
}
.services-tabs__row {
    display: grid;
}
.services-tabs__row--top { grid-template-columns: repeat(4, 1fr); }
.services-tabs__row--bot { grid-template-columns: repeat(3, 1fr); }

/* Horizontal dashed divider between the two rows. Zero-height structural
   element so the rows touch each other in flex flow — no visible gap. The
   1px dashed line is drawn by the absolutely-positioned ::before sitting
   on the boundary, so it doesn't add layout space (the line itself is
   "free" — overlays row 2's top edge by 1px without pushing anything). */
.services-tabs__divider {
    position: relative;
    height: 0;
}
.services-tabs__divider::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    border-top: var(--guide-border);
    pointer-events: none;
}

/* TAB — base
   Default: transparent bg, cream text. Bg flips to grid on :hover OR when
   the tab is the currently-shown one (.is-clicked). The page-active tab
   (.is-active-page) overrides both with cream-bg + dark-text via source-
   order specificity.
   background-clip: padding-box keeps the dashed cell-dividers (border-right
   on each non-last tab) drawn against the panel surface — without it, the
   bg would fill into the border area and dashes (#444) would disappear
   into the grid bg (#444) when a cell is hovered.
   Easing: cubic-bezier(0.4, 0, 0.2, 1) is the Material "standard" curve —
   accelerates briefly then decelerates, more natural-feeling than plain ease
   for state transitions. */
.services-tab {
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    margin: 0;
    background: transparent;
    background-clip: padding-box;
    color: var(--color-white);
    padding: 20px;
    cursor: pointer;
    text-align: center;
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    font-size: 14px;
    letter-spacing: var(--tracking-tight);
    line-height: 1.1;
    transition:
        background-color 0.32s cubic-bezier(0.4, 0, 0.2, 1),
        color 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}
.services-tab:hover {
    background-color: var(--color-grid); /* #444 — same as currently-shown */
}
.services-tabs__row > .services-tab:not(:last-child) {
    border-right: var(--guide-border);
}

/* Text span — kept as positioned inline-block so the page-indicator
   (.services-tab__indicator) inside it can pin itself relative to the
   word, not to the cell. */
.services-tab__text {
    position: relative;
    display: inline-block;
}

/* TAB — click-active (= "this tab's content is currently in the preview").
   Cream bg + dark text — the visual the cream-bg state used to have for
   page-active. Click-active is now the only thing that flips the cell to
   cream. cursor:default signals "no further action here" (preview already
   shows this tab). */
.services-tab.is-clicked {
    background: var(--color-white);
    color: var(--color-black2);
    cursor: default;
}
/* Hover on a click-active tab keeps the cream bg (.services-tab:hover above
   would flip it to grid otherwise — we override here with higher specificity). */
.services-tab.is-clicked:hover {
    background-color: var(--color-white);
}

/* PAGE INDICATOR — small 8×8 square pinned to the LEFT of the tab's text.
   Shown only when the tab matches the current URL (.is-active-page). The
   square is absolutely positioned via right:100% on the text span so it
   doesn't push the centred text — it floats outside the text's left edge.
   - White-cream by default (visible against transparent / grid bgs)
   - Dark #262626 when the tab is ALSO click-active (cream bg) — the
     combined-class selector .is-active-page.is-clicked wins via higher
     specificity (3 classes vs 2). */
.services-tab__indicator {
    position: absolute;
    right: 100%;
    margin-right: 12px; /* small extra gap between square and text */
    top: 50%;
    width: 6px;
    height: 6px;
    background: var(--color-white);
    opacity: 0;
    pointer-events: none;
    /* Hidden state pops in via opacity + a subtle scale-up. Two transforms
       compose: the always-present translateY(-50%) for vertical centering,
       and a scale that goes 0.4 → 1 when the page-active class is added.
       cubic-bezier(0.16, 1, 0.3, 1) is "ease-out-expo" — a soft, slightly
       overshoot-feeling decelerator that gives the square a "land into
       place" feel rather than a flat fade. */
    transform: translateY(-50%) scale(0.4);
    transition:
        opacity 0.36s cubic-bezier(0.16, 1, 0.3, 1),
        transform 0.36s cubic-bezier(0.16, 1, 0.3, 1),
        background-color 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}
.services-tab.is-active-page .services-tab__indicator {
    opacity: 1;
    transform: translateY(-50%) scale(1);
}
.services-tab.is-active-page.is-clicked .services-tab__indicator {
    background: var(--color-black2);
}

/* CONTENT ROW */
.services-content {
    flex: 1 1 auto;
    display: flex;
    gap: var(--space-5);
    min-height: 0;
    /* Extra breathing room above the content row, on top of the slide's
       24px flex gap. Total visual gap between tabs grid and content row
       = 24px + 24px = 48px. Tweak this value alone to change tabs→content
       spacing without affecting eyebrow→tabs spacing. */
    margin-top: var(--space-5);
}

/* Smooth swap on tab change, using CSS TRANSITIONS (not keyframes).
   Single class .is-fading-out toggled by JS:
     • Class ADDED → opacity 0 + transform offset, transitioned over the
       short OUT duration with an ease-in curve (accelerating departure).
     • Class REMOVED → opacity / transform return to defaults, transitioned
       over the longer IN duration with an ease-out-expo curve (decelerating
       arrival).
   Same property is animated both ways, so there's NO positional jump at
   the swap point (which is what produced the "bounce" with the previous
   keyframe-pair approach where fade-out ended at one position and fade-in
   started at a different one). */

.services-content__body,
.services-content__media-img,
.services-content__cta {
    /* DEFAULT (in / settled) transition — used when .is-fading-out is removed
       and the element returns to its resting state. */
    transition:
        opacity 0.34s cubic-bezier(0.16, 1, 0.3, 1),
        transform 0.34s cubic-bezier(0.16, 1, 0.3, 1);
}
/* OUT — properties dip + offset, with an ease-IN curve so the leaving feels
   like it's gathering speed. Override transition timing/curve here so the
   out half feels distinct from the in half. */
.services-content.is-fading-out .services-content__body,
.services-content.is-fading-out .services-content__media-img,
.services-content.is-fading-out .services-content__cta {
    transition:
        opacity 0.18s cubic-bezier(0.4, 0, 1, 1),
        transform 0.18s cubic-bezier(0.4, 0, 1, 1);
}
.services-content.is-fading-out .services-content__body,
.services-content.is-fading-out .services-content__cta {
    opacity: 0;
    transform: translateY(-6px);
}
.services-content.is-fading-out .services-content__media-img {
    opacity: 0;
    transform: scale(0.98);
}
.services-content__text {
    /* Hugs ~1/3 of the row, headline body sits at top, CTA pinned bottom */
    flex: 0 0 36%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    min-width: 0;
}
.services-content__body {
    margin: 0;
    color: var(--color-white);
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.4;
    letter-spacing: var(--tracking-body);
}
.services-content__cta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    align-self: flex-start;
    color: var(--color-white);
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    font-size: var(--text-sm);
    letter-spacing: var(--tracking-tight);
    text-decoration: none;
    line-height: 1;
}
.services-content__cta-icon {
    width: 11px;
    height: auto;
    flex: 0 0 auto;
}
/* On the page-active tab: arrow disappears, link is dead, copy swaps. */
.services-content__cta.is-current {
    cursor: default;
}
.services-content__cta.is-current .services-content__cta-icon {
    display: none;
}

.services-content__media {
    flex: 1 1 auto;
    overflow: hidden;
    min-width: 0;
    /* Radius lives on the CONTAINER so the rounded shape is always present
       regardless of the image's natural aspect — overflow:hidden +
       border-radius clips the image to the rounded box. */
    border-radius: var(--radius);
}
.services-content__media-img {
    width: 100%;
    height: 100%;
    object-fit: cover; /* Fill BOTH width and height; crop centre instead of distorting */
    display: block;
}


/* ---------- Projects slide (modifier: --projects) ----------
   Horizontal card carousel with infinite right→left auto-loop and
   discrete card-by-card snaps via the arrow buttons below.

   Frosted-glass mark technique:
   We deliberately do NOT use backdrop-filter on the cards. Chromium's
   implementation breaks down inside the mega-menu's nesting (deep flex
   chain, opacity transitions, will-change ancestors all conspire to
   no-op the filter). Instead the mark is painted as TWO copies:
     • .projects-mark--blurred — a CSS-blurred SVG (filter: blur 30px)
       living inside an overflow:hidden wrapper whose top edge sits
       exactly at the cards' top edge. The wrapper clips the blur halo
       extending UP (above the cards), but lets the halo extending DOWN
       into the cards' row stay visible. The cards' translucent surface
       (rgba(white-cream, 0.10)) lets that blurred glow shine through.
     • .projects-mark--sharp — a normal SVG copy clipped via clip-path
       to its TOP HALF only. Sticks up sharp above the cards; never
       bleeds through them.

   Infinite loop strategy (see initProjectsCarousel in nav.js):
   4 unique projects rendered as 3 consecutive passes (12 cards in DOM).
   Active idx range is the MIDDLE pass (4..7). When idx steps out the
   JS lets the slide transition complete then snaps idx back to the
   equivalent card in the middle pass — invisible because every pass
   is identical. */

/* Slide-level: wider inter-element gap than the default 12px so
   eyebrow / stage / arrows breathe. */
.mm-preview__slide--projects {
    gap: var(--space-5);
}
.mm-preview__projects-eyebrow {
    flex: 0 0 auto;
}

/* STAGE — clipped row that hosts the mark + the carousel. Side-fade
   gradients (::before / ::after) soften card edges visually. */
.projects-stage {
    /* Per-stage knobs — change card size / gap / mark size here.
       Tuned a touch smaller than the mm-promo card so 1 full + ~0.5
       either side fits inside the panel comfortably. */
    --proj-card-w: 280px;
    --proj-card-h: var(--proj-card-w);   /* full square */
    --proj-gap:    var(--space-5);       /* 24px */
    --proj-mark-h: 100px;

    flex: 1 1 auto;
    position: relative;
    overflow: hidden;     /* clip cards extending past the stage edges */
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Left + right fade overlays (panel-surface → transparent). Sits ON
   TOP of the cards so their edges fade into the slide background. */
.projects-stage::before,
.projects-stage::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 12%;
    pointer-events: none;
    z-index: 3;
}
.projects-stage::before {
    left: 0;
    background: linear-gradient(to right,
        var(--color-surface) 0%,
        rgba(38, 38, 38, 0) 100%);
}
.projects-stage::after {
    right: 0;
    background: linear-gradient(to left,
        var(--color-surface) 0%,
        rgba(38, 38, 38, 0) 100%);
}

/* Wrapper for the BLURRED mark. overflow:hidden + a top edge that
   sits exactly at the cards' top edge clips the upward blur halo
   while keeping the downward halo visible. Height extends down to
   enclose the full halo spread. */
.projects-mark-blur-mask {
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    margin-top: calc(var(--proj-card-h) / -2);
    height: var(--proj-card-h);
    overflow: hidden;
    pointer-events: none;
}
.projects-mark--blurred {
    position: absolute;
    left: 50%;
    top: 0;            /* sits at the wrapper's top edge */
    height: var(--proj-mark-h);
    width: auto;
    transform: translate(-50%, -50%);   /* centred on (50%, 0) */
    color: var(--color-white);
    filter: blur(30px);
    pointer-events: none;
}
.projects-mark--sharp {
    position: absolute;
    left: 50%;
    top: 50%;
    height: var(--proj-mark-h);
    width: auto;
    color: var(--color-white);
    /* Centre on (50%, 50%) of stage, then shift up by half a card so
       the mark's centre sits exactly on the cards' top edge. */
    transform: translate(-50%, -50%) translateY(calc(var(--proj-card-h) / -2));
    pointer-events: none;
    /* Clip away the BOTTOM half so this copy only paints above the
       cards' top edge — keeps the top half crisp without any of it
       bleeding through the translucent cards. */
    clip-path: inset(0 0 50% 0);
    -webkit-clip-path: inset(0 0 50% 0);
}

/* CAROUSEL viewport (full stage width) and TRACK (the moving bar).
   Track holds all 12 cards in a flex row; JS animates the track's
   `left` per click / auto-tick to centre the active card. We use
   `left:` instead of `transform: translateX(...)` because transform
   on this element promotes it to its own GPU compositing layer in
   Chromium, which would block any backdrop-filter sampling on the
   cards inside (we don't currently use backdrop-filter, but keeping
   the layout this way leaves that option open). `left` only animates
   on click / auto-tick (≤ once per 5s), so layout cost is negligible. */
.projects-carousel {
    position: relative;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: flex-start;
}
.projects-track {
    position: relative;
    left: 0;
    display: flex;
    align-items: stretch;
    gap: var(--proj-gap);
    transition: left 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.projects-track.no-transition {
    transition: none;
}

/* CARD — square, translucent surface with a 1px dashed greyish
   stroke. Hover flips to solid cream + dark text (matches mm-promo). */
.projects-card {
    position: relative;
    flex: 0 0 var(--proj-card-w);
    /* Hard-cap width — prevents a long single-line title (white-space:
       nowrap) from forcing the card wider than its declared flex-basis,
       which would then propagate height through aspect-ratio + the
       track's align-items: stretch. */
    width: var(--proj-card-w);
    max-width: var(--proj-card-w);
    aspect-ratio: 1 / 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background: rgba(255, 251, 240, 0.10);  /* white-cream 10% */
    border: var(--guide-width) var(--guide-style) var(--color-greyish);
    border-radius: var(--radius);
    color: var(--color-white);
    text-decoration: none;
    cursor: pointer;
    transition:
        background-color 0.32s var(--ease-gentle),
        color            0.32s var(--ease-gentle);
}
.projects-card:hover {
    background-color: var(--color-white);
    color: var(--color-black2);
}

.projects-card__label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
    font-family: var(--font-body);
    font-size: 12px;
    letter-spacing: var(--tracking-body);
    color: var(--color-greyish);
    transition: color 0.32s var(--ease-gentle);
}
.projects-card:hover .projects-card__label {
    color: var(--color-black2);
}

.projects-card__mark {
    width: 10px;
    height: 11px;
    color: currentColor;
    flex-shrink: 0;
}

.projects-card__title {
    margin: 0;
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    font-size: 18px;
    letter-spacing: var(--tracking-tight);
    line-height: 1.2;
    color: inherit;
    flex: 0 0 auto;
    /* Single-line title with ellipsis. JS in nav.js refines this to a
       word-boundary truncation after cards are built; the CSS rule below
       is the immediate fallback before JS runs. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    min-width: 0;
}

/* Image area fills the remaining space between title and body. */
.projects-card__media {
    flex: 1 1 0;
    min-height: 0;
    border-radius: var(--radius);
    overflow: hidden;
}
.projects-card__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Body — clamped to 2 lines so cards stay uniform even with varying
   copy. */
.projects-card__body {
    margin: 0;
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.4;
    letter-spacing: var(--tracking-body);
    color: inherit;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    flex: 0 0 auto;
}

/* ARROWS pill — two cream buttons side-by-side under the carousel.
   Only OUTER corners rounded so the pair reads as one split control;
   a 1px dashed greyish line marks the inner seam. Pulled UP via
   transform (purely visual offset — doesn't change flex layout, so
   the carousel above stays exactly where it sits). */
.projects-arrows {
    flex: 0 0 auto;
    align-self: center;
    display: inline-flex;
    align-items: stretch;
    transform: translateY(-56px);
}
.projects-arrow {
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    margin: 0;
    width: 56px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-white);    /* solid cream-white default */
    color: var(--color-black);         /* arrow icon at rest */
    cursor: pointer;
    /* Snappy 0.12s easing — feels responsive to clicks vs. card's
       slower 0.32s hover crossfade. */
    transition:
        background-color 0.12s var(--ease-gentle),
        color            0.12s var(--ease-gentle);
}
.projects-arrow:first-child {
    border-radius: var(--radius) 0 0 var(--radius);
}
.projects-arrow:last-child {
    border-radius: 0 var(--radius) var(--radius) 0;
}
.projects-arrow + .projects-arrow {
    /* Dashed greyish split — same line style as the cards' stroke so
       the pill reads as part of the same family. Visible against
       both default cream and the hover grid surface. */
    border-left: var(--guide-width) var(--guide-style) var(--color-greyish);
}
.projects-arrow:hover,
.projects-arrow:focus-visible,
.projects-arrow:active {
    background-color: var(--color-grid);
    color: var(--color-white);
}
.projects-arrow svg {
    width: 9px;
    height: auto;
    display: block;
    fill: currentColor;
}
/* Same arrow asset for both buttons; the prev one is mirrored. */
.projects-arrow--prev svg {
    transform: scaleX(-1);
}


/* ---------- Insights slide (modifier: --insights) ----------
   Identical structure / behaviour to the Projects slide above. Same
   carousel mechanics, same frosted-glass mark technique, same arrow
   pill styling — the only differences are the SVG paths (Insight Mark
   vs Project Mark, swapped at the markup level) and the class prefix
   (insights- instead of projects-). Kept as a parallel block rather
   than a shared selector list so the two slides can evolve
   independently if either ever needs slide-specific tweaks. */

.mm-preview__slide--insights {
    gap: var(--space-5);
}
.mm-preview__insights-eyebrow {
    flex: 0 0 auto;
}

.insights-stage {
    --ins-card-w: 280px;
    --ins-card-h: var(--ins-card-w);
    --ins-gap:    var(--space-5);
    --ins-mark-h: 100px;

    flex: 1 1 auto;
    position: relative;
    overflow: hidden;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.insights-stage::before,
.insights-stage::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 12%;
    pointer-events: none;
    z-index: 3;
}
.insights-stage::before {
    left: 0;
    background: linear-gradient(to right,
        var(--color-surface) 0%,
        rgba(38, 38, 38, 0) 100%);
}
.insights-stage::after {
    right: 0;
    background: linear-gradient(to left,
        var(--color-surface) 0%,
        rgba(38, 38, 38, 0) 100%);
}

.insights-mark-blur-mask {
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    margin-top: calc(var(--ins-card-h) / -2);
    height: var(--ins-card-h);
    overflow: hidden;
    pointer-events: none;
}
.insights-mark--blurred {
    position: absolute;
    left: 50%;
    top: 0;
    height: var(--ins-mark-h);
    width: auto;
    transform: translate(-50%, -50%);
    color: var(--color-white);
    filter: blur(30px);
    pointer-events: none;
}
.insights-mark--sharp {
    position: absolute;
    left: 50%;
    top: 50%;
    height: var(--ins-mark-h);
    width: auto;
    color: var(--color-white);
    transform: translate(-50%, -50%) translateY(calc(var(--ins-card-h) / -2));
    pointer-events: none;
    clip-path: inset(0 0 50% 0);
    -webkit-clip-path: inset(0 0 50% 0);
}

.insights-carousel {
    position: relative;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: flex-start;
}
.insights-track {
    position: relative;
    left: 0;
    display: flex;
    align-items: stretch;
    gap: var(--ins-gap);
    transition: left 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.insights-track.no-transition {
    transition: none;
}

.insights-card {
    position: relative;
    flex: 0 0 var(--ins-card-w);
    /* Hard-cap width — prevents a long single-line title (white-space:
       nowrap) from forcing the card wider than its declared flex-basis,
       which would then propagate height through aspect-ratio + the
       track's align-items: stretch. */
    width: var(--ins-card-w);
    max-width: var(--ins-card-w);
    aspect-ratio: 1 / 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background: rgba(255, 251, 240, 0.10);
    border: var(--guide-width) var(--guide-style) var(--color-greyish);
    border-radius: var(--radius);
    color: var(--color-white);
    text-decoration: none;
    cursor: pointer;
    transition:
        background-color 0.32s var(--ease-gentle),
        color            0.32s var(--ease-gentle);
}
.insights-card:hover {
    background-color: var(--color-white);
    color: var(--color-black2);
}

.insights-card__label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
    font-family: var(--font-body);
    font-size: 12px;
    letter-spacing: var(--tracking-body);
    color: var(--color-greyish);
    transition: color 0.32s var(--ease-gentle);
}
.insights-card:hover .insights-card__label {
    color: var(--color-black2);
}

.insights-card__mark {
    width: 10px;
    height: 11px;
    color: currentColor;
    flex-shrink: 0;
}

.insights-card__title {
    margin: 0;
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    font-size: 18px;
    letter-spacing: var(--tracking-tight);
    line-height: 1.2;
    color: inherit;
    flex: 0 0 auto;
    /* Single-line title with ellipsis. JS in nav.js refines this to a
       word-boundary truncation after cards are built; the CSS rule below
       is the immediate fallback before JS runs. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    min-width: 0;
}

.insights-card__media {
    flex: 1 1 0;
    min-height: 0;
    border-radius: var(--radius);
    overflow: hidden;
}
.insights-card__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.insights-card__body {
    margin: 0;
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.4;
    letter-spacing: var(--tracking-body);
    color: inherit;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    flex: 0 0 auto;
}

.insights-arrows {
    flex: 0 0 auto;
    align-self: center;
    display: inline-flex;
    align-items: stretch;
    transform: translateY(-56px);
}
.insights-arrow {
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    margin: 0;
    width: 56px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-white);
    color: var(--color-black);
    cursor: pointer;
    transition:
        background-color 0.12s var(--ease-gentle),
        color            0.12s var(--ease-gentle);
}
.insights-arrow:first-child {
    border-radius: var(--radius) 0 0 var(--radius);
}
.insights-arrow:last-child {
    border-radius: 0 var(--radius) var(--radius) 0;
}
.insights-arrow + .insights-arrow {
    border-left: var(--guide-width) var(--guide-style) var(--color-greyish);
}
.insights-arrow:hover,
.insights-arrow:focus-visible,
.insights-arrow:active {
    background-color: var(--color-grid);
    color: var(--color-white);
}
.insights-arrow svg {
    width: 9px;
    height: auto;
    display: block;
    fill: currentColor;
}
.insights-arrow--prev svg {
    transform: scaleX(-1);
}


/* ---------- Contact slide (modifier: --contact) ----------
   Two-column layout that diverges from the base flex-column .mm-preview__slide:
     LEFT  — hugs content. Holds a re-used copy of contact.html's .cl-block
             (socials column + diagonal-staggered info labels) plus a "Let's
             talk" heading planted in the top-right negative space the
             staircase creates. The .cl-* visuals are RE-DECLARED below
             scoped to .mm-preview__slide--contact so we don't have to load
             css/components/contact-labels.css on every page that uses the
             mega menu — contact.html still loads that file for its hero,
             unaffected.
     RIGHT — fills remaining horizontal space, right-aligned content.
             Rating pill sits at the top; "Click Me" button floats vertically
             through the column's available height (linear ease, GSAP-driven)
             until the cursor hovers it. initContactPreview() in nav.js sets
             the exact per-label positions (mirroring contact-labels.js's
             layoutHero math) and starts the float tween.
   ============================================================ */
/* Slide itself stays in the base flex-column flow so the eyebrow at
   the top behaves identically to every other slide. The two-column
   layout (left labels / right buttons) happens INSIDE .mm-preview__contact-row,
   which takes the slide's remaining vertical space below the eyebrow.
   align-items: center vertically centres both columns inside the row;
   their heights are matched in JS (syncColumnHeights) so they don't
   stretch to the row's full height — the row keeps empty breathing
   space above + below them. */
.mm-preview__contact-row {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-5);
}

/* Eyebrow — same .mm-section__label typography as the other slides'
   eyebrows. No special positioning needed; flex-column gap on the
   slide pushes the row below it. */
.mm-preview__contact-eyebrow {
    flex: 0 0 auto;
}

/* LEFT column — fit-content. The cl-block + heading both live here.
   Heading is absolutely positioned so it doesn't grow this column. */
.mm-preview__contact-left {
    position: relative;
    flex: 0 0 auto;
}

/* "Let's talk" — sits in the negative space (right of socials, above email).
   Pixel position set inline by initContactPreview() so it scales with the
   measured icon-wrap widths. */
.mm-preview__contact-heading {
    position: absolute;
    margin: 0;
    color: var(--color-white);
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    font-size: 36px;
    line-height: var(--leading-none);
    white-space: nowrap;
}

/* RIGHT column — fills remaining horizontal space. align-items: flex-end
   right-aligns each child along the cross axis. Height is set in JS to
   match the left column's natural content height (cl-block cascade) so
   both columns share a height and get centered together by the row's
   align-items: center — no align-self: stretch (which would let it
   blow out to the row's full height and break the centering). */
.mm-preview__contact-right {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-3);
}

/* ============================================================
   Diagonal labels — visual styling re-declared from contact-labels.css
   but scoped to .mm-preview__slide--contact. The contact page itself
   still loads contact-labels.css; this scoped copy only fires inside
   the mega menu, so the two never fight.
   ============================================================ */

/* Override .cl-block's hero sticky behaviour. We want a STATIC block
   here — its socials + infos are absolutely positioned within it by
   JS, and the block itself is the size of the diagonal cascade.

   IMPORTANT: we deliberately do NOT redeclare `pointer-events: auto`
   on .cl-block or its descendants the way contact-labels.css does.
   On the contact page that rule exists because the .cl-block is a
   zero-size sticky parent with pointer-events: none, and the children
   need to escape that to be clickable. In the mega menu, the slide
   itself drives the hit-test state via .mm-preview__slide's
   pointer-events (none at idle → auto when .is-visible). If we
   re-asserted pointer-events: auto on the descendants here, they'd
   override the slide's "none" state and keep capturing hover/click
   events while the CONTACT slide is hidden — overlaying every other
   slide (services tabs, projects carousel, etc.) and blocking their
   interactions. Letting hit-testing flow naturally from the slide
   fixes that. */
.mm-preview__slide--contact .cl-block {
    position: relative;
    top: auto;
    width: auto;
    height: auto;
    overflow: visible;
    z-index: auto;
    isolation: auto;
}

/* Socials column — 3 stacked 58×48 squares with dashed frame */
.mm-preview__slide--contact .cl-socials {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 58px;
}
.mm-preview__slide--contact .cl-social {
    position: relative;
    width: 58px;
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(38, 38, 38, 0.20);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: var(--guide-border);
    transition: background 0.3s var(--ease-primary), backdrop-filter 0s linear 0.3s;
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}
.mm-preview__slide--contact .cl-social + .cl-social { margin-top: -1px; }
.mm-preview__slide--contact .cl-social__icon {
    width: 20px;
    height: 20px;
    display: block;
    color: var(--color-white);
    transition: color 0.3s var(--ease-primary), transform 0.45s var(--ease-out-expo);
}
.mm-preview__slide--contact .cl-social__icon svg {
    width: 100%;
    height: 100%;
    display: block;
    fill: currentColor;
}
.mm-preview__slide--contact .cl-social:hover {
    background: var(--color-white);
    backdrop-filter: blur(0);
    -webkit-backdrop-filter: blur(0);
    transition: background 0.3s var(--ease-primary), backdrop-filter 0s;
}
.mm-preview__slide--contact .cl-social:hover .cl-social__icon {
    color: var(--color-black2);
    transform: rotate(-45deg);
}

/* Info labels — each is a flex of [icon-wrap][text-wrap]. JS sets the
   per-label width + absolute top/left for the staircase. */
.mm-preview__slide--contact .cl-infos {
    position: absolute;
    top: 0;
    left: 0;
}
.mm-preview__slide--contact .cl-info {
    position: absolute;
    height: 71px;
    display: inline-flex;
    align-items: stretch;
    border: var(--guide-border);
    box-sizing: border-box;
    cursor: pointer;
    text-decoration: none;
    color: var(--color-white);
}
.mm-preview__slide--contact .cl-info__icon-wrap {
    position: relative;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    padding: 14px 14px;
    box-sizing: border-box;
    border-right: var(--guide-border);
    background: transparent;
    transition: background 0.3s var(--ease-primary);
    flex-shrink: 0;
}
.mm-preview__slide--contact .cl-info__icon-label {
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    font-size: 12px;
    line-height: 1;
    color: var(--color-greyish);
    transition: color 0.3s var(--ease-primary);
    white-space: nowrap;
}
.mm-preview__slide--contact .cl-info__icon {
    width: 20px;
    height: 20px;
    display: block;
    color: var(--color-white);
    transition: color 0.3s var(--ease-primary), transform 0.45s var(--ease-out-expo);
}
.mm-preview__slide--contact .cl-info__icon svg {
    width: 100%;
    height: 100%;
    display: block;
    fill: currentColor;
    overflow: visible;
}
.mm-preview__slide--contact .cl-info__text-wrap {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px 14px;
    box-sizing: border-box;
    overflow: hidden;
    backdrop-filter: blur(0);
    -webkit-backdrop-filter: blur(0);
    transition: background 0.3s var(--ease-primary),
                backdrop-filter 0.3s var(--ease-primary),
                -webkit-backdrop-filter 0.3s var(--ease-primary);
}
.mm-preview__slide--contact .cl-info__text {
    font-family: var(--font-body);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-body);
    color: var(--color-white);
    line-height: var(--leading-snug);
    text-align: center;
}
.mm-preview__slide--contact .cl-info--location .cl-info__text { text-align: left; }

/* Hover — invert each label half */
.mm-preview__slide--contact .cl-info:hover .cl-info__icon-wrap { background: var(--color-white); }
.mm-preview__slide--contact .cl-info:hover .cl-info__icon-label { color: var(--color-black2); }
.mm-preview__slide--contact .cl-info:hover .cl-info__icon { color: var(--color-black2); transform: scale(1.08); }
.mm-preview__slide--contact .cl-info:hover .cl-info__text-wrap { background: var(--color-black); }

/* Email flap rotation on hover — opens like an envelope */
.mm-preview__slide--contact .cl-icon-email-flap {
    transform-origin: 0 0;
    transform-box: fill-box;
    transition: transform 0.45s var(--ease-out-expo);
}
.mm-preview__slide--contact .cl-info:hover .cl-icon-email-flap {
    transform: rotate(-12deg);
}

/* Phone-specific icon tweaks (matches contact-labels.css behaviour) */
.mm-preview__slide--contact .cl-info--phone .cl-info__icon svg { transition: transform 0.45s var(--ease-out-expo); }
.mm-preview__slide--contact .cl-info.cl-info--phone:hover .cl-info__icon { transform: scale(1.08) rotate(-12deg); }

/* Location pin gap — the inner diamond. Idle fill matches the panel's
   dark surface so it reads as a "cut-out" of the pin; on hover it
   flips to cream. Rotation + scale on hover are driven by GSAP in
   initContactPreview() (mirror of contact-icon-hover.js) so we get
   the same clean transform-origin behaviour (svgOrigin) that the
   contact page uses. CSS only handles the fill transition. */
.mm-preview__slide--contact .cl-icon-location-gap {
    fill: var(--color-black2);
    transition: fill 0.3s var(--ease-primary);
}
.mm-preview__slide--contact .cl-info:hover .cl-icon-location-gap {
    fill: var(--color-white);
}

/* ============================================================
   R-RATED RATING PILL — new component. Design DNA from .rr-btn but
   inverted (cream-on-dashed left + cream-solid arrow right at idle,
   swap on hover). Both panels always carry a 1px dashed border on
   all sides; visibility flips via border-color (transparent vs
   --color-grid) so the layout never jumps on hover.
   ============================================================ */
.mm-rating-pill {
    display: inline-flex;
    align-items: stretch;
    text-decoration: none;
    cursor: pointer;
    color: var(--color-white);
    flex: 0 0 auto;
}
.mm-rating-pill__content,
.mm-rating-pill__arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    border: var(--guide-width) var(--guide-style) transparent;
    transition: background 0.3s var(--ease-primary),
                color 0.3s var(--ease-primary),
                border-color 0.3s var(--ease-primary);
    box-sizing: border-box;
}
/* Left panel — idle: dashed grey, transparent fill, cream content.
   Only the LEFT corners round so it butts flush against the arrow. */
.mm-rating-pill__content {
    gap: 8px;
    padding: 14px 20px;
    border-radius: var(--radius) 0 0 var(--radius);
    border-color: var(--color-grid);
    border-right-color: transparent;
    background: transparent;
    color: var(--color-white);
}
.mm-rating-pill__star {
    width: 18px;
    height: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.mm-rating-pill__star svg {
    width: 100%;
    height: 100%;
    display: block;
}
.mm-rating-pill__num {
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    font-size: 24px;
    line-height: 1;
    color: inherit;
}
/* Right panel — idle: cream solid, dark chevron, no visible border.
   Only RIGHT corners round so it butts flush against the content. */
.mm-rating-pill__arrow {
    padding: 0 10px;
    border-radius: 0 var(--radius) var(--radius) 0;
    border-color: transparent;
    background: var(--color-white);
    color: var(--color-black2);
}
.mm-rating-pill__arrow svg {
    width: auto;
    height: 15px;
    display: block;
}

/* HOVER — panels swap. Left becomes solid cream with dark content;
   right goes transparent with dashed border + cream chevron. */
.mm-rating-pill:hover .mm-rating-pill__content {
    background: var(--color-white);
    color: var(--color-black2);
    border-color: transparent;
}
.mm-rating-pill:hover .mm-rating-pill__arrow {
    background: transparent;
    color: var(--color-white);
    border-color: var(--color-grid);
    border-left-color: transparent;
}

/* ============================================================
   CLICK ME — fresh cream pill that floats vertically. The TRACK
   absorbs the right column's remaining vertical space below the
   rating pill; the BUTTON sits absolutely inside the track at
   right:0 so it stays right-aligned through every translateY tick
   of the GSAP tween. Pause/resume hooks live in nav.js.
   ============================================================ */
.mm-cta-track {
    position: relative;
    flex: 1 1 auto;
    width: 100%;
    min-height: 80px;
}
.mm-cta-button {
    position: absolute;
    right: 0;
    top: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-btn);
    font-weight: var(--font-weight-btn);
    letter-spacing: var(--tracking-btn);
    font-size: var(--text-buttons, 14px);
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    padding: 14px 32px;       /* vertical padding bumped to read closer to the rating pill's chunk */
    border-radius: var(--radius);
    background: var(--color-white);
    color: var(--color-black2);
    border: var(--guide-width) var(--guide-style) transparent;
    box-sizing: border-box;
    transition: background 0.3s var(--ease-primary),
                color 0.3s var(--ease-primary),
                border-color 0.3s var(--ease-primary);
    will-change: transform;
}
.mm-cta-button:hover {
    background: transparent;
    color: var(--color-white);
    border-color: var(--color-grid);
}


/* ---------- Fallback slide (modifier: --fallback) ----------
   Shown when the URL doesn't match any primary nav route — secondary
   pages like /careers, /internships, /privacy-policy, etc. Two parts:
     • Eyebrow at top, horizontally centered across the panel.
     • A large heading filling the rest of the slide's vertical space,
       both H + V centered. Uses its own flex layout inside the slot
       so the centering doesn't depend on the parent .mm-preview__slide's
       (base column flow + gap is enough to put the eyebrow above it).
   The forced line break ("Objective" / "by design.") is in the markup
   so the wrap doesn't depend on viewport width.
   ============================================================ */
.mm-preview__slide--fallback {
    /* Base .mm-preview__slide already does flex column + gap. We only
       override the children's individual alignment + sizing below. */
}
.mm-preview__fallback-eyebrow {
    flex: 0 0 auto;
    align-self: center;       /* horizontal centre on the cross axis */
    text-align: center;
}
.mm-preview__fallback-text {
    flex: 1 1 auto;
    min-height: 0;
    margin: 0;
    /* Inner flex centers the text both horizontally + vertically
       within the remaining slot height (the slot is everything below
       the eyebrow + the base slide's gap). */
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    font-size: clamp(40px, 4vw, 64px);
    line-height: var(--leading-none);
    color: var(--color-white);
}


/* Footer row: contact / subscribe / others / social.
   Each column hugs its content (flex: 0 0 auto, no equal-flex), and
   space-between distributes any leftover horizontal space evenly between
   them. The gap is a minimum spacer so columns never collide when content
   is wide enough to fill the row. */
.mm-main__bottom {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-5);
    padding: var(--space-5);
}
.mm-contact,
.mm-others,
.mm-social {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* Shared small-caps section label — caption size (smaller than body text) */
.mm-section__label {
    font-family: var(--font-body);
    font-weight: var(--font-weight-regular);
    letter-spacing: var(--tracking-body);
    font-size: 12px;
    color: var(--color-greyish);
}

.mm-contact__link,
.mm-contact__address,
.mm-others__link {
    font-family: var(--font-body);
    font-size: var(--text-sm);
    color: var(--color-white);
    text-decoration: none;
    line-height: 1.4;
    transition: color 0.2s ease;
}
.mm-contact__link:hover,
.mm-others__link:hover { color: var(--color-greyish); }

.mm-social__row {
    /* Always stack vertically — icons row on top, tag below. Never side-by-side,
       never wraps to a second line (tag was wrapping when space tightened). */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    margin-top: var(--space-1);
}
.mm-social__icons {
    display: flex;
    gap: var(--space-3);
}
.mm-social__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-white);
    transition: opacity 0.2s ease;
}
.mm-social__link:hover { opacity: 0.7; }
.mm-social__link img {
    display: block;
    width: 20px;
    height: 20px;
}

/* Em-dash tag — 8px L/R, 4px T/B, dashed border, no rounded corners */
.mm-tag {
    display: inline-block;
    padding: 4px 8px;
    border: var(--guide-border);
    border-radius: 0;
    font-family: var(--font-body);
    font-size: 12px;
    letter-spacing: var(--tracking-body);
    color: var(--color-white);
    white-space: nowrap;
}


/* ── SUBSCRIBE column ──
   Sits between .mm-contact and .mm-others in .mm-main__bottom. Hugs its
   own content (the form has an explicit width; everything else flows from
   that). Hidden at ≤1199px to match .mm-promo's priority-hide breakpoint. */
.mm-subscribe-col {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.mm-subscribe {
    display: flex;
    align-items: center;
    background: var(--color-white);
    border-radius: var(--radius);
    padding: 4px;
    /* Fixed width — gives input + button a consistent footprint regardless of
       parent width, so the column hugs to a known size. */
    width: 280px;
    gap: 0;
    margin-top: var(--space-1);
    /* Anchor for the absolutely-positioned animation overlay (.mm-subscribe-expand).
       Box-shadow transition powers the .is-error red flash on invalid submits. */
    position: relative;
    transition: box-shadow 0.4s ease-out;
}

.mm-subscribe__input {
    flex: 1;
    min-width: 0; /* allow shrinking inside flex */
    border: none;
    outline: none;
    background: transparent;
    color: var(--color-black2);
    font-family: var(--font-body);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-body);
    padding: 8px 12px;
}
.mm-subscribe__input::placeholder {
    color: var(--color-greyish);
}

.mm-subscribe__btn {
    flex: 0 0 auto;
    background: var(--color-black2);
    color: var(--color-white);
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-tight);
    /* 1px dashed transparent reserves the border space so hover doesn't shift layout. */
    border: 1px dashed transparent;
    border-radius: calc(var(--radius) - 2px);
    padding: 10px 18px;
    cursor: pointer;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}
.mm-subscribe__btn:hover {
    background: var(--color-white);
    color: var(--color-black2);
    border-color: var(--color-greyish);
}

/* ── Invalid-email feedback — red flash + GSAP shake (JS-driven). ── */
.mm-subscribe.is-error {
    box-shadow: 0 0 0 2px var(--color-red-brand);
    transition: box-shadow 0.1s ease-out;
}

/* ── Submit animation overlay.
   Grows leftward from the button's position on submit, holds the
   dynamic status text + reset icon. Initial width is set by JS. ── */
.mm-subscribe-expand {
    position: absolute;
    top: 4px;
    right: 4px;
    bottom: 4px;
    width: 0;
    background: var(--color-black2);
    border-radius: calc(var(--radius) - 2px);
    overflow: hidden;
    visibility: hidden;
    will-change: width;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.mm-subscribe-expand.is-active {
    visibility: visible;
}
.mm-subscribe-expand-text {
    flex: 0 0 auto;
    padding-left: 18px;
    color: var(--color-white);
    font-family: var(--font-heading);
    font-weight: var(--font-weight-medium);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-tight);
    white-space: nowrap;
}
.mm-subscribe-reset {
    flex: 0 0 auto;
    margin-right: 12px;
    width: 20px;
    height: 20px;
    background: transparent;
    border: none;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-white);
    transform: scale(0) rotate(-180deg);
    will-change: transform;
}
.mm-subscribe-reset svg {
    width: 100%;
    height: 100%;
    display: block;
}
.mm-subscribe-reset svg path {
    fill: currentColor;
}


/* ── RIGHT: PROMO ──
   Outer container (#262626 — matches mm-main) wraps both cards. Cards themselves
   are transparent by default so the container color shows through; on hover each
   card fills with cream and its text flips to black. */
.mm-promo {
    flex: 0 0 clamp(260px, 25%, 340px);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-4);
    background: var(--color-surface);
    border-radius: var(--radius);
    min-width: 0;
    min-height: 0;
}
.mm-promo__card {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-darkgreyish);
    border: var(--guide-width) var(--guide-style) var(--color-greyish);
    border-radius: var(--radius);
    min-height: 0;
    overflow: hidden;
    color: var(--color-white);
    text-decoration: none;
    cursor: pointer;
    /* Snappy hover swap target — the actual transition value here is
       overridden by the entrance-stagger shorthand below (same
       specificity, later in source). The override block AFTER the
       stagger re-applies the snappy 0.07s timings for the hover
       properties while keeping the stagger's opacity/transform timing
       intact. Mirrors the .mm-nav__link instant-swap pattern. */
    transition: background-color 0.07s ease-out, color 0.07s ease-out;
}
.mm-promo__card:hover {
    background: var(--color-white);
    color: var(--color-black2);
}
.mm-promo__label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-body);
    font-size: 12px;
    letter-spacing: var(--tracking-body);
    color: var(--color-greyish);
    transition: color 0.07s ease-out;
}
.mm-promo__card:hover .mm-promo__label { color: var(--color-black2); }

.mm-promo__mark {
    width: 10px;
    height: 11px;
    color: currentColor;
    flex-shrink: 0;
}
.mm-promo__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: clamp(18px, 1.4vw, 22px);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-tight);
    line-height: 1.2;
    color: inherit;  /* picks up the card's color, flips on hover automatically */
}
.mm-promo__image {
    flex: 1 1 0;
    width: 100%;
    min-height: 0;
    object-fit: cover;
    border-radius: var(--radius);
    display: block;
}


/* ── FADE-IN STAGGER FOR NAV LINKS ── */
.mm-nav__link,
.mm-promo__card,
.mm-preview,
.mm-main__bottom > * {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.4s ease,
                transform 0.5s var(--ease-out-expo),
                background-color 0.18s var(--ease-gentle),
                color 0.18s var(--ease-gentle);
    will-change: opacity, transform;
}
/* Override the stagger block's slow color transition for nav links specifically.
   Hover dim must feel TRULY instant — no perceptible fade between white and
   #7b7b7b. This rule sits AFTER the stagger block so source-order resolution
   lets it win at equal specificity. We use per-property `transition-delay`
   so the open-stagger's delay (set below as e.g. 0.28s for nth-child(6))
   only applies to opacity/transform — color/background-color always have
   0s delay AND 0s duration, so a class-driven swap snaps in the same frame.
   Promo card hover (0.18s) is unaffected — different selector. */
.mm-nav__link {
    transition-property:        opacity, transform,                    color,  background-color;
    transition-duration:        0.4s,    0.5s,                         0s,     0s;
    transition-timing-function: ease,    var(--ease-out-expo),         linear, linear;
    transition-delay:           0s,      0s,                           0s,     0s;
}
/* Re-declare the stagger delays as 4-value lists matching the property list
   above, so color + background-color always get 0s delay even mid-stagger. */
.mega-menu.is-open .mm-nav__link:nth-child(1) { transition-delay: 0.08s, 0.08s, 0s, 0s; }
.mega-menu.is-open .mm-nav__link:nth-child(2) { transition-delay: 0.12s, 0.12s, 0s, 0s; }
.mega-menu.is-open .mm-nav__link:nth-child(3) { transition-delay: 0.16s, 0.16s, 0s, 0s; }
.mega-menu.is-open .mm-nav__link:nth-child(4) { transition-delay: 0.20s, 0.20s, 0s, 0s; }
.mega-menu.is-open .mm-nav__link:nth-child(5) { transition-delay: 0.24s, 0.24s, 0s, 0s; }
.mega-menu.is-open .mm-nav__link:nth-child(6) { transition-delay: 0.28s, 0.28s, 0s, 0s; }
.mega-menu.is-open .mm-nav__link,
.mega-menu.is-open .mm-promo__card,
.mega-menu.is-open .mm-preview,
.mega-menu.is-open .mm-main__bottom > * {
    opacity: 1;
    transform: translateY(0);
}
/* Staggered entry — nav-link delays handled in the per-property block above
   (so color/background-color always retain 0s delay for instant hover swap). */
.mega-menu.is-open .mm-preview               { transition-delay: 0.18s; }
.mega-menu.is-open .mm-main__bottom > *:nth-child(1) { transition-delay: 0.28s; }
.mega-menu.is-open .mm-main__bottom > *:nth-child(2) { transition-delay: 0.32s; }
.mega-menu.is-open .mm-main__bottom > *:nth-child(3) { transition-delay: 0.36s; }
.mega-menu.is-open .mm-promo__card:nth-child(1) { transition-delay: 0.20s; }
.mega-menu.is-open .mm-promo__card:nth-child(2) { transition-delay: 0.28s; }

/* Promo cards — snappy hover swap.
   Same trick as the .mm-nav__link block above: write a per-property
   transition AFTER the stagger so source-order resolution lets us
   override only the color/background-color durations (0.07s) while
   the stagger keeps owning opacity/transform timing for the entrance.
   transition-delay is split per-property so the stagger's per-child
   delay only applies to opacity + transform — hover colour swap stays
   instant regardless of where the card sits in the stagger. */
.mm-promo__card {
    transition-property:        opacity, transform,           background-color, color;
    transition-duration:        0.4s,    0.5s,                0.07s,            0.07s;
    transition-timing-function: ease,    var(--ease-out-expo),ease-out,         ease-out;
    transition-delay:           0s,      0s,                  0s,               0s;
}
.mega-menu.is-open .mm-promo__card:nth-child(1) { transition-delay: 0.20s, 0.20s, 0s, 0s; }
.mega-menu.is-open .mm-promo__card:nth-child(2) { transition-delay: 0.28s, 0.28s, 0s, 0s; }


/* ── TABLET (<1200px): hide .mm-promo + .mm-subscribe-col (priority-based hiding, step 1).
   Both are "extras" — when overall content tightens, drop them so the core 3-col
   bottom row (contact / others / social) keeps breathing room. ── */
@media (max-width: 1199px) {
    .mm-promo,
    .mm-subscribe-col { display: none; }
}


/* ── MOBILE (<768px): full-screen under nav; only .mm-nav + .mm-social visible ── */
@media (max-width: 767px) {
    /* Skip heavy blur on mobile — just a solid dark tint layer */
    .mm-backdrop {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background: rgba(0, 0, 0, 0.6);
    }

    /* Panel covers the viewport below the nav, no side padding */
    .mm-panel {
        left: 0;
        right: 0;
        bottom: 0;
    }
    .mm-main {
        border: none;
        border-radius: 0;
        padding: var(--space-5) var(--pad-x);
        gap: var(--space-5);
    }

    /* mm-main__top collapses to just .mm-nav (preview hidden) */
    .mm-main__top {
        border-bottom: none;
        flex: 0 0 auto;
    }
    .mm-nav {
        flex: 1 1 auto;
        border-right: none;
        padding: 0;
        align-items: center;
        text-align: center;
    }
    .mm-nav__list { align-items: center; }
    /* Mobile: zero left-padding on the link itself (no reserved indicator gap),
       so the visible text is what gets centered by the parent's align-items.
       The indicator floats OUTSIDE the link to the left (negative offset) so
       it can still mark the active page without nudging the text off-center. */
    .mm-nav__link {
        font-size: clamp(28px, 7vw, 44px);
        padding-left: 0;
    }
    .mm-nav__link.is-active { padding-left: 0; }
    .mm-nav__indicator { left: calc(-10px - var(--space-2)); }
    .mm-preview { display: none; }

    /* Footer row on mobile: stacks vertically as Subscribe → divider → Socials.
       Contact + Others hide; Subscribe re-appears (was hidden ≤1199px); Socials
       stays. The dashed divider lives on .mm-social so it sits BETWEEN the two
       visible blocks (subscribe above, socials below). */
    .mm-main__bottom {
        padding: 0;
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-5);
        margin-top: auto; /* pushes the bottom block to bottom of panel */
    }
    .mm-contact,
    .mm-others { display: none; }

    /* Subscribe: re-show on mobile, full-width form, centered label. */
    .mm-subscribe-col {
        display: flex;
        align-items: center;
        flex: 0 0 auto;
        width: 100%;
    }
    .mm-subscribe {
        width: 100%;
        max-width: 480px; /* keeps it from looking too sprawling on tablet-sized phones */
    }
    .mm-subscribe__input { font-size: var(--text-sm); }

    /* Socials: centered horizontally, full-width dashed divider above. The
       negative side margins escape .mm-main's --pad-x so the divider line
       reaches viewport edge to viewport edge; padding then puts the socials
       content back inside the safe area. */
    .mm-social {
        flex: 0 0 auto;
        align-items: center;
        border-top: var(--guide-border);
        padding-top: var(--space-5);
        margin-left: calc(-1 * var(--pad-x));
        margin-right: calc(-1 * var(--pad-x));
        padding-left: var(--pad-x);
        padding-right: var(--pad-x);
    }
    .mm-social__row {
        flex-direction: column;
        align-items: center;
        gap: var(--space-3);
    }
    .mm-social__icons { justify-content: center; }
}


/* ── WHEN MEGA MENU IS OPEN ──
   The wordmark, CTA, and menu trigger STAY clickable while the mega
   menu is open — those are primary navigation actions and the user
   should always be able to reach Home or Let's Build. Clicking
   either takes you off this page (which closes the menu naturally
   via navigation), and the menu trigger itself is the explicit close.
   We only disable elements that are visually hidden in the contracted
   nav state (cube + flat-B) and the dark pill background (which has
   no purpose as an event target). Body scroll lock handled in JS. */
.navbar.mega-open .nav-bg,
.navbar.mega-open .logo-cube-wrapper,
.navbar.mega-open .logo-b-mobile { pointer-events: none; }

/* Global scroll-lock helper (applied to <html>/<body> from JS) */
.mega-scroll-locked {
    overflow: hidden !important;
    /* iOS-safe: JS also sets position:fixed + top:-scrollY on body */
}


/* ── RESPONSIVE ── */

/* Small desktop: 1024-1199 */
@media (max-width: 1199px) and (min-width: 1024px) {
    .nav-container { max-width: 60vw; }
}

/* Tablet: 768-1023 */
@media (max-width: 1023px) {
    :root { --nav-pad-x: 4%; }
    .nav-container { max-width: 50vw; }
}

/* Phone: ≤767 */
@media (max-width: 767px) {
    :root { --nav-font: var(--text-xs); }
    /* iOS Safari deprioritises (effectively skips) CSS transitions on
       layout-affecting properties — including max-width — when they fire
       during active scroll. Diagnostic confirmed: B was landing at the
       wrong horizontal position because the 80vw → 100% transition was
       being dropped while the user's finger was still moving the page.
       Fix: disable the CSS max-width transition on mobile, and let GSAP
       drive the width change from nav.js (`animateNavMaxWidth()`). GSAP
       sets inline style per frame, which iOS doesn't drop the same way.
       Override the .expanded rule too so a bare class toggle never snap-
       jumps to 100% — JS is the single source of truth here. */
    .nav-container {
        max-width: 80vw;
        transition: none;
    }
    .navbar.expanded .nav-container { max-width: 80vw; }

    /* Same iOS-Safari-during-scroll problem as max-width: the CSS transition
       on .nav-bg's clip-path (which uses calc() + var()) is deprioritised
       during active scroll, so the pill takes visibly longer to finish its
       morph than the wordmark/cube fade. Fix: read the inset value from a
       CSS custom property; disable the CSS transition; let GSAP drive
       --pill-inset-left from nav.js (animatePillClipPath). The .navbar.expanded
       override below uses the same shape so the class toggle never snap-jumps —
       JS owns the value in both states. */
    .nav-bg {
        --pill-inset-left: 0px;
        clip-path: inset(0 0 0 var(--pill-inset-left) round 4px);
        transition: none;
    }
    .navbar.expanded .nav-bg {
        clip-path: inset(0 0 0 var(--pill-inset-left) round 4px);
    }

    /* Only the icon, no "Menu"/"Close" label */
    .menu-label { display: none; }
    .menu-trigger { gap: 0; padding: var(--space-3) var(--space-3); }

    /* Mobile CTA: no morph shape, solid pill button */
    .cta-shape { display: none; }
    .cta-button {
        padding: 0 var(--space-4);
        background: var(--color-white);
        color: var(--color-black2);
        border-radius: var(--radius);
    }
    .cta-button.cta-dark {
        background: var(--color-black);
        color: var(--color-white);
    }

    /* Replace cube with flat B on phones */
    .logo-cube-wrapper { display: none !important; }
    .logo-b-mobile { display: flex; }
    .navbar.expanded .logo-b-mobile {
        opacity: 1;
        transform: translateX(0) scale(1);
        pointer-events: auto;
    }

    /* Mega menu link sizing */
    .mega-menu-link { font-size: clamp(28px, 8vw, 48px); }
}
