/* ==========================================================================
   NX — Dezignite / brutalist editorial layer
   Black-on-white, oversized grotesque type, mono micro-labels, scroll-driven
   motion. Loaded last on index.html so it wins over the legacy sheets.
   ========================================================================== */

/* ── Tokens ─────────────────────────────────────────────────────────────── */
:root {
    --nx-white:  #ffffff;
    --nx-black:  #000000;
    --nx-ink:    #0a0a0a;   /* section black, a hair off pure */
    --nx-paper:  #f4f4f2;   /* warm off-white for alternating bands */

    --nx-mute:      #7d7d7d;
    --nx-mute-dark: #9a9a9a;
    --nx-line:      rgba(0, 0, 0, .13);
    --nx-line-dark: rgba(255, 255, 255, .16);

    --nx-sans: 'Switzer', 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
    --nx-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

    --nx-gutter: 20px;
    --nx-ease:   cubic-bezier(.22, 1, .36, 1);      /* out-expo-ish */
    --nx-ease-io: cubic-bezier(.76, 0, .24, 1);

    --nx-bar: 78px;  /* fixed header height */
}

@media (min-width: 900px) {
    :root { --nx-gutter: 26px; }
}

/* ── Base ───────────────────────────────────────────────────────────────── */
.nx * { box-sizing: border-box; }

.nx {
    margin: 0;
    background: var(--nx-white);
    color: var(--nx-black);
    font-family: var(--nx-sans);
    font-weight: 400;
    font-size: 16px;
    line-height: 1.32;
    letter-spacing: -.011em;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: hidden;
}

.nx img,
.nx video { display: block; max-width: 100%; }

.nx a { color: inherit; text-decoration: none; }

.nx h1, .nx h2, .nx h3, .nx h4, .nx p, .nx figure { margin: 0; }

.nx ::selection { background: var(--nx-black); color: var(--nx-white); }
.nx-dark ::selection { background: var(--nx-white); color: var(--nx-black); }

/* Lock scroll while the loader runs / the menu is open */
.nx-locked { overflow: hidden; height: 100%; }
/* Menu lock: no height change, so scrollY survives the takeover. */
.nx-nav-open { overflow: hidden; }

/* ── Shared primitives ──────────────────────────────────────────────────── */

/* Mono micro-label, e.g. ( The approach ) */
.nx-label {
    font-family: var(--nx-mono);
    font-size: 11px;
    font-weight: 400;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--nx-mute);
}
.nx-dark .nx-label { color: var(--nx-mute-dark); }

.nx-label--paren::before { content: '( '; }
.nx-label--paren::after  { content: ' )'; }

/* Big editorial statement */
.nx-statement {
    font-size: clamp(1.9rem, 4.6vw, 4.3rem);
    font-weight: 500;
    line-height: 1.02;
    letter-spacing: -.035em;
    text-wrap: balance;
}

.nx-statement .dim { color: var(--nx-mute); }
.nx-dark .nx-statement .dim { color: var(--nx-mute-dark); }

.nx-shell {
    padding-inline: var(--nx-gutter);
    width: 100%;
}

.nx-section { position: relative; padding-block: clamp(90px, 13vw, 190px); }
.nx-section--flush { padding-block: 0; }

.nx-dark { background: var(--nx-ink); color: var(--nx-white); }

/* Pill button.
   Scoped under .nx so it out-specifies the `.nx a { color: inherit }` reset —
   the pill is usually an <a> and would otherwise inherit the page colour. */
.nx .nx-pill {
    display: inline-flex;
    align-items: center;
    gap: 14px;
    padding: 15px 26px;
    border-radius: 999px;
    background: var(--nx-black);
    color: var(--nx-white);
    font-family: var(--nx-mono);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    border: 1px solid var(--nx-black);
    cursor: pointer;
    overflow: hidden;
    position: relative;
    transition: color .5s var(--nx-ease), border-color .5s var(--nx-ease);
    will-change: transform;
}
.nx-pill > * { position: relative; z-index: 1; }
.nx-pill::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--nx-white);
    transform: translateY(101%);
    transition: transform .55s var(--nx-ease);
    z-index: 0;
}
.nx .nx-pill:hover { color: var(--nx-black); }
.nx-pill:hover::before { transform: translateY(0); }
.nx-pill svg { transition: transform .5s var(--nx-ease); }
.nx-pill:hover svg { transform: translateX(5px); }

.nx .nx-dark .nx-pill,
.nx .nx-foot .nx-pill {
    background: var(--nx-white);
    color: var(--nx-black);
    border-color: var(--nx-white);
}
.nx-dark .nx-pill::before { background: var(--nx-ink); }
.nx .nx-dark .nx-pill:hover { color: var(--nx-white); }

/* Parallax media frame.
   The image is taller than the frame that clips it and drifts through that
   surplus as the frame crosses the viewport, so a picture reads as moving
   without the page needing to hand it a full screen. js sets --par 0 → 1
   over the crossing; the image travels its 32% of extra height across that. */
.nx-par {
    position: relative;
    overflow: hidden;
    --par-d: clamp(22px, 3vw, 46px);
}

/* Scoped under .nx so it out-specifies the per-component `img { height:100% }`
   rules (.nx-case__media img and friends) — .nx-par is an opt-in modifier and
   has to win wherever it is applied, whatever the source order. */
.nx .nx-par > img,
.nx .nx-par > video {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    /* --par-d is the surplus height AND the exact distance the image travels,
       so at --par 1 its bottom edge lands flush with the frame's.
       It has to be a LENGTH, not a percentage: `height`'s percentages resolve
       against the frame, but `translateY`'s resolve against the image's own
       (taller) box, so a shared percentage over-travels and tears a gap open
       at the bottom of the frame. */
    height: calc(100% + var(--par-d));
    object-fit: cover;
    transform: translate3d(0, calc(var(--par, .5) * -1 * var(--par-d)), 0);
    will-change: transform;
}

/* Reveal-on-scroll: masked line lift */
.nx-rise {
    opacity: 0;
    transform: translateY(26px);
    transition: opacity .9s var(--nx-ease), transform .9s var(--nx-ease);
}
.nx-rise.is-in { opacity: 1; transform: none; }

/* Per-line masked reveal, lines are wrapped by js.
   The mask is padded top and bottom and pulled back with a matching negative
   margin: tight line-heights otherwise clip Czech diacritics (Ď, Ř, Ž) above
   the cap line and descenders (j, p, y) below it, without moving the layout. */
.nx-lines .nx-line {
    display: block;
    overflow: hidden;
    margin-block: -.18em -.16em;
}
.nx-lines .nx-line > span {
    display: block;
    padding-block: .18em .16em;
    transform: translateY(105%);
    transition: transform 1.05s var(--nx-ease);
}
.nx-lines.is-in .nx-line > span { transform: none; }
.nx-lines.is-in .nx-line:nth-child(2) > span { transition-delay: .08s; }
.nx-lines.is-in .nx-line:nth-child(3) > span { transition-delay: .16s; }
.nx-lines.is-in .nx-line:nth-child(4) > span { transition-delay: .24s; }

/* ── Preloader ──────────────────────────────────────────────────────────────
   Opener timeline, driven by boot() in js/nx.js:
     1. black ground — DEZIGNITE spins through a slot window, forward then
        back, while the loading line fills along the bottom edge
     2. the three parts of the Dezignite mark wipe on in turn, in white
     3. the mark holds while liquid splats burst out of it and flood the
        screen white — the ground the hero already stands on — and the hero
        rides in under it
   Every step runs on --nx-ease-io, so each one eases in and out. */
.nx-loader {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: var(--nx-black);
    overflow: hidden;
    display: grid;
    place-items: center;
    transition: transform 1.15s var(--nx-ease-io);
}
/* Sub-pages carry no mark — they keep the plain curtain lift */
.nx-loader.is-done { transform: translateY(-100%); }

/* 1 — slot reels. One column per letter of DEZIGNITE, each spinning a drum of
   nothing but its own letter. Every column starts together but runs a slightly
   longer spin than the one before it, so they lock in left to right and the
   wordmark assembles a letter at a time. The window is taller than a line, so
   the copies above and below are cut mid-glyph the way a drum reads.
   Because a column only ever shows one glyph, it can take that glyph's own
   width — the word then fits like ordinary type instead of leaving a hole
   around the narrow I. */
.nx-loader__roll {
    position: relative;
    z-index: 1;
    display: flex;
    justify-content: center;
    font-size: clamp(3rem, 19vw, 24rem);
    transition: opacity .4s var(--nx-ease-io) .25s;
}
.nx-loader__slot {
    height: 1.3em;
    overflow: hidden;
}
.nx-loader__reel {
    will-change: transform;
    animation: nx-roll calc(.8s + var(--nx-i) * .075s) var(--nx-ease-io) forwards;
}
.nx-loader__reel span {
    display: block;
    height: 1.06em;
    font-family: var(--nx-sans);
    font-weight: 700;
    font-size: 1em;
    line-height: 1.06;
    /* Same tracking as the hero wordmark this hands over to */
    letter-spacing: -.045em;
    color: var(--nx-white);
}
.nx-loader__slot:nth-child(1) { --nx-i: 0; }
.nx-loader__slot:nth-child(2) { --nx-i: 1; }
.nx-loader__slot:nth-child(3) { --nx-i: 2; }
.nx-loader__slot:nth-child(4) { --nx-i: 3; }
.nx-loader__slot:nth-child(5) { --nx-i: 4; }
.nx-loader__slot:nth-child(6) { --nx-i: 5; }
.nx-loader__slot:nth-child(7) { --nx-i: 6; }
.nx-loader__slot:nth-child(8) { --nx-i: 7; }
.nx-loader__slot:nth-child(9) { --nx-i: 8; }

/* Twelve 1.06em letters per drum; -90.72% parks the twelfth — the column's own
   letter — centred in the 1.3em window. Re-do the sum if either size moves. */
@keyframes nx-roll {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(0, -90.72%, 0); }
}
/* Backward: the drums wind the same way back, again left to right, as the
   mark takes over. */
@keyframes nx-unroll {
    from { transform: translate3d(0, -90.72%, 0); }
    to   { transform: translate3d(0, 0, 0); }
}
.nx-loader.is-mark .nx-loader__roll { opacity: 0; }
/* `both`, not `forwards`: a column still sitting out its delay has to hold the
   landed letter, otherwise it snaps to the top of its drum and winds twice. */
.nx-loader.is-mark .nx-loader__reel {
    animation: nx-unroll .42s var(--nx-ease-io) both;
    animation-delay: calc(var(--nx-i) * .035s);
}

/* 2 + 3 — the mark. Three white shapes — the top bar, the D curve and the
   diagonal blade — wipe on one after another, each along the direction it
   would be drawn in: the bar left to right, the curve and the blade downward
   from their own top edge. The clip rects carry a transform="scale(0)"
   attribute too, so the parts stay hidden on the frame before this sheet
   lands. */
.nx-loader__mark {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    place-items: center;
    pointer-events: none;
}
.nx-loader__logo {
    width: clamp(168px, 34vmin, 380px);
    height: auto;
    /* Load-bearing: the splash throws its splats well past the viewBox, and
       the outermost svg clips to it by default. */
    overflow: visible;
}
.nx-loader__wipe {
    transform: scale(0);
    transition: transform .52s var(--nx-ease-io);
}
/* Origins sit on the leading edge of each clip rect, in viewBox units */
.nx-loader__wipe--bar   { transform-origin: 16px 69px;  }
.nx-loader__wipe--curve { transform-origin: 165px 38px; }
.nx-loader__wipe--blade { transform-origin: 80px 112px; }

.nx-loader.is-mark .nx-loader__wipe--bar   { transform: scaleX(1); }
.nx-loader.is-mark .nx-loader__wipe--curve {
    transform: scaleY(1);
    transition-duration: .6s;
    transition-delay: .16s;
}
.nx-loader.is-mark .nx-loader__wipe--blade {
    transform: scaleY(1);
    transition-duration: .44s;
    transition-delay: .34s;
}

/* 3 — the splash. Liquid splats in the same white, thrown out of the mark by
   splash() in js/nx.js until they have flooded the screen. Each carries a
   transform="scale(0)" attribute, so it has no size until js gives it one;
   hiding the group as well is what covers the case where js never runs and
   the attribute is the only thing standing between a clean black ground and
   fourteen splats sitting on top of the logo. */
.nx-loader__splash { visibility: hidden; }
.nx-loader.is-open .nx-loader__splash { visibility: visible; }

/* Full-screen white by now, and the hero behind it is white too — so the
   handover is a straight fade instead of the sub-page curtain lift. */
.nx-loader.is-open.is-done {
    transform: none;
    opacity: 0;
    transition: opacity .55s var(--nx-ease-io);
}

.nx-loader__bar {
    position: absolute;
    left: 0;
    bottom: 0;
    z-index: 3;
    height: 2px;
    width: 100%;
    background: var(--nx-white);
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: opacity .4s linear;
}
/* White line on the white ground would only be a hairline seam */
.nx-loader.is-open .nx-loader__bar { opacity: 0; }

/* Everything below the loader starts hidden, js releases it */
.nx-boot .nx-reveal-on-load { opacity: 0; }

/* ── Custom cursor ──────────────────────────────────────────────────────── */
.nx-cursor {
    position: fixed;
    top: 0; left: 0;
    width: 9px; height: 9px;
    border-radius: 50%;
    background: var(--nx-black);
    pointer-events: none;
    z-index: 9998;
    mix-blend-mode: difference;
    opacity: 0;
    transform: translate3d(-50px, -50px, 0);
    transition: opacity .3s ease, width .35s var(--nx-ease), height .35s var(--nx-ease);
}
.nx-cursor.is-on { opacity: 1; background: var(--nx-white); }
.nx-cursor.is-big { width: 32px; height: 32px; }

@media (hover: none), (pointer: coarse) { .nx-cursor { display: none; } }

/* ── Header ─────────────────────────────────────────────────────────────── */

/* Scrim.
   The header is fixed and transparent, so every heading in the page scrolls
   straight under the mark and the Menu button. Blending alone does not save it:
   mix-blend-mode: difference keeps the chrome legible against the page, but the
   *copy* underneath still collides with it. This lays a soft, downward-fading
   veil between the two — blurred and tinted just enough to separate the layers
   while the section behind stays readable.

   It only shows once the hero is behind you (.is-on), because over the hero
   there is nothing to separate. .is-light flips the tint for the white
   sections; both variants are driven from chrome() in nx.js. */
.nx-head__scrim {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 890;
    height: calc(var(--nx-bar) + 30px);
    pointer-events: none;
    opacity: 0;
    transition: opacity .45s var(--nx-ease);
    background: linear-gradient(to bottom,
        rgba(8, 8, 8, .82) 0%,
        rgba(8, 8, 8, .58) 52%,
        rgba(8, 8, 8, 0) 100%);
    /* The blur is masked to the same ramp as the tint, so the bottom edge
       dissolves instead of ending on a visible seam. */
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 48%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 0%, #000 48%, transparent 100%);
}
.nx-head__scrim.is-on { opacity: 1; }
.nx-head__scrim.is-light {
    background: linear-gradient(to bottom,
        rgba(255, 255, 255, .86) 0%,
        rgba(255, 255, 255, .62) 52%,
        rgba(255, 255, 255, 0) 100%);
}

.nx-head {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 900;
    height: var(--nx-bar);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-inline: var(--nx-gutter);
    pointer-events: none;
    mix-blend-mode: difference;
}
.nx-head > * { pointer-events: auto; }

.nx-head__mark {
    display: block;
    width: 34px;
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity .5s var(--nx-ease), transform .5s var(--nx-ease);
    /* mix-blend-mode difference on the parent makes a white mark read on both */
    filter: brightness(0) invert(1);
}
.nx-head__mark.is-on { opacity: 1; transform: none; }

.nx-head__right {
    display: flex;
    align-items: center;
    gap: 22px;
}

.nx-menu-btn {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    font-family: var(--nx-mono);
    font-size: 13px;
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--nx-white);
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.nx-menu-btn__dots {
    display: grid;
    grid-template-columns: 3px 3px;
    gap: 2px;
    transition: transform .5s var(--nx-ease);
}
.nx-menu-btn__dots i {
    width: 3px; height: 3px;
    background: currentColor;
    border-radius: 50%;
    display: block;
}
.nx-menu-btn[aria-expanded="true"] .nx-menu-btn__dots { transform: rotate(45deg); }
/* Both labels ship in the markup so I18n.js translates them like anything
   else; the expanded state picks which one shows. */
.nx-menu-btn__label {
    display: inline-block;
    min-width: 4.6em;
    text-align: right;
}
.nx-menu-btn__label i { font-style: normal; }
.nx-menu-btn__label i + i { display: none; }
.nx-menu-btn[aria-expanded="true"] .nx-menu-btn__label i:first-child { display: none; }
.nx-menu-btn[aria-expanded="true"] .nx-menu-btn__label i + i { display: inline; }

/* ── Nav takeover ───────────────────────────────────────────────────────
   Not a dropdown: the whole viewport goes black and the four destinations
   come in at wordmark scale. The type is the hero's — 700 weight, -.045em,
   masked lines rising on a stagger — so opening the menu reads as the same
   gesture as landing on the site. JS sizes every row off the longest word
   (nav() in nx.js), which is why no font-size is set here. */
.nx-nav {
    position: fixed;
    inset: 0;
    z-index: 895;
    background: var(--nx-ink);
    color: var(--nx-white);
    display: flex;
    flex-direction: column;
    gap: clamp(24px, 5vh, 60px);
    padding: calc(var(--nx-bar) + 3vh) var(--nx-gutter) clamp(18px, 3vh, 34px);
    /* Curtain wipe from the top. `visibility` is what pulls the links out of
       the tab order while it is shut — opacity alone would leave them
       focusable behind the page. */
    clip-path: inset(0 0 100% 0);
    visibility: hidden;
    transition: clip-path .72s var(--nx-ease-io), visibility 0s linear .72s;
}
.nx-nav.is-open {
    clip-path: inset(0 0 0 0);
    visibility: visible;
    transition: clip-path .72s var(--nx-ease-io), visibility 0s;
}

/* Auto block margins centre the type in whatever is left above the foot.
   On a phone the words are width-bound and cannot fill the height, and
   bottom-aligning them stranded the block under a screen of empty black. */
.nx-nav__list {
    display: flex;
    flex-direction: column;
    margin-block: auto;
}
/* Each row is the mask; the span inside it is what rises. */
.nx-nav__row {
    display: block;
    overflow: hidden;
    line-height: .82;
    /* Leading this tight cuts the line box below the accents — Ž and Á lose
       their marks in Czech. Pad the mask open at the top and pull the same
       amount back off the flow so the rows keep their spacing. */
    padding-top: .18em;
    margin-top: -.18em;
}
.nx-nav__row > span {
    display: inline-block;
    white-space: nowrap;
    font-family: var(--nx-sans);
    font-weight: 700;
    line-height: .82;
    letter-spacing: -.045em;
    text-transform: uppercase;
    transform: translateY(105%);
    transition: transform 1.1s var(--nx-ease), color .35s var(--nx-ease),
                -webkit-text-stroke-color .35s var(--nx-ease);
}
.nx-nav.is-open .nx-nav__row > span { transform: none; }
.nx-nav.is-open .nx-nav__row:nth-child(1) > span { transition-delay: .06s; }
.nx-nav.is-open .nx-nav__row:nth-child(2) > span { transition-delay: .12s; }
.nx-nav.is-open .nx-nav__row:nth-child(3) > span { transition-delay: .18s; }
.nx-nav.is-open .nx-nav__row:nth-child(4) > span { transition-delay: .24s; }

/* Pointing at one row hollows out the other three, so the choice is the only
   solid thing on the screen. */
.nx-nav__list:hover .nx-nav__row > span,
.nx-nav__list:focus-within .nx-nav__row > span {
    color: transparent;
    -webkit-text-stroke: 1px var(--nx-white);
}
.nx-nav__list .nx-nav__row:hover > span,
.nx-nav__list .nx-nav__row:focus-visible > span {
    color: var(--nx-white);
    -webkit-text-stroke-color: transparent;
}

.nx-nav__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding-top: clamp(14px, 2vh, 22px);
    border-top: 1px solid var(--nx-line-dark);
    font-family: var(--nx-mono);
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
    opacity: 0;
    transform: translateY(12px);
    transition: opacity .5s var(--nx-ease) .3s, transform .5s var(--nx-ease) .3s;
}
.nx-nav.is-open .nx-nav__foot { opacity: 1; transform: none; }
.nx-nav__mail { text-transform: none; letter-spacing: .06em; }
.nx-nav__mail:hover { opacity: .55; }

/* I18n.js injects .lang-toggle here; these rules out-specify the styles it
   writes into <head>. */
.nx-nav__lang .lang-toggle { gap: 6px; }
.nx-nav__lang .lang-opt {
    font-family: var(--nx-mono);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--nx-white);
    opacity: .5;
}
.nx-nav__lang .lang-opt:hover,
.nx-nav__lang .lang-opt.active { color: var(--nx-white); opacity: 1; }
.nx-nav__lang .lang-opt::after {
    height: 1px;
    bottom: -4px;
    background: var(--nx-white);
}
.nx-nav__lang .lang-sep { color: var(--nx-white); opacity: .35; }

@media (max-width: 560px) {
    .nx-nav__foot {
        flex-direction: column;
        align-items: flex-start;
        gap: 10px;
    }
}

/* Hover has no touch equivalent and the hollow state would strand a tapped
   row mid-transition, so leave the type solid there. */
@media (hover: none) {
    .nx-nav__list:hover .nx-nav__row > span,
    .nx-nav__list:focus-within .nx-nav__row > span {
        color: var(--nx-white);
        -webkit-text-stroke-color: transparent;
    }
}

/* ── Hero ───────────────────────────────────────────────────────────────── */
.nx-hero {
    position: relative;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: calc(var(--nx-bar) * .38) 0 22px;
    background: var(--nx-white);
    overflow: hidden;
}

/* The fluid-mask canvas paints the hero's white ground plus the wordmark, so
   every other hero element has to sit above it. */
.nx-fluid { z-index: 0; }

.nx-hero__top,
.nx-hero__foot {
    position: relative;
    z-index: 2;
}

.nx-hero__top {
    padding-inline: var(--nx-gutter);
    max-width: 900px;
}

.nx-hero__intro {
    font-size: clamp(1rem, 1.55vw, 1.42rem);
    font-weight: 400;
    line-height: 1.22;
    letter-spacing: -.02em;
}

.nx-hero__cta { margin-top: 26px; }

/* Oversized wordmark, flexed edge-to-edge so it always bleeds full width */
.nx-word {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    padding-inline: var(--nx-gutter);
    /* Sized so the nine glyphs of DEZIGNITE very nearly fill the line on
       their own — space-between then only has a hair to distribute, which is
       what makes the wordmark read as one tight block rather than tracked-out
       letters. Re-tune this if the wordmark text ever changes length. */
    font-size: 20.3vw;
    font-weight: 700;
    line-height: .78;
    letter-spacing: -.045em;
    white-space: nowrap;
    user-select: none;
    margin-block: auto clamp(24px, 5vw, 70px);
}
.nx-word span {
    display: block;
    transform: translateY(105%);
    transition: transform 1.2s var(--nx-ease);
}
.nx-word.is-in span { transform: none; }

/* Once the fluid canvas is painting the wordmark, fade the live glyphs out but
   keep them laid out — nx-fluid re-measures these boxes on every resize, and
   the <h1> stays in the accessibility tree via its aria-label. Nothing else
   ever sets this: if the solver declines, the glyphs stay exactly as they are
   and that is the hero. */
.nx-word { position: relative; z-index: 1; }
.nx-word.is-baked span { opacity: 0; }
.nx-word span:nth-child(1) { transition-delay: .00s; }
.nx-word span:nth-child(2) { transition-delay: .04s; }
.nx-word span:nth-child(3) { transition-delay: .08s; }
.nx-word span:nth-child(4) { transition-delay: .12s; }
.nx-word span:nth-child(5) { transition-delay: .16s; }
.nx-word span:nth-child(6) { transition-delay: .20s; }
.nx-word span:nth-child(7) { transition-delay: .24s; }
.nx-word span:nth-child(8) { transition-delay: .28s; }
.nx-word span:nth-child(9) { transition-delay: .32s; }

.nx-hero__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    padding-inline: var(--nx-gutter);
    font-size: 13px;
    letter-spacing: -.01em;
}

.nx-hero__social {
    display: flex;
    align-items: center;
    gap: 14px;
    font-family: var(--nx-mono);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
}
.nx-hero__social .sep { color: var(--nx-mute); }
.nx-hero__social a { transition: opacity .3s ease; }
.nx-hero__social a:hover { opacity: .5; }

/* Language toggle sits in the hero footer — override I18n.js's injected
   dark-theme styles (its <style> lands after this sheet, so we out-specify). */
.nx-langwrap { display: inline-flex; }
.nx-langwrap .lang-toggle { gap: 5px; }
.nx-langwrap .lang-opt {
    font-family: var(--nx-mono);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .12em;
    color: var(--nx-mute);
}
.nx-langwrap .lang-opt::after { display: none; }
.nx-langwrap .lang-opt:hover,
.nx-langwrap .lang-opt.active { color: var(--nx-black); }
.nx-langwrap .lang-sep { color: var(--nx-line); }

/* ── Hero chrome: blend, don't sit on top ───────────────────────────────────
   These sit above the fluid canvas, so the moment the stroke passes beneath
   them black type on a black stroke disappears. They difference-blend against
   whatever the canvas is painting, exactly like .nx-head does: every source
   colour here is INVERTED, which renders identically over the white hero and
   flips to light the instant the stroke arrives underneath.
   The wordmark is deliberately not in this list — showing the material is the
   whole point of it, and inverting would fight that. */
.nx-hero__top,
.nx-hero__foot {
    mix-blend-mode: difference;
    color: var(--nx-white);
}

/* Inverted pill: white ground, dark label — reads as the usual black pill
   over the white hero, and swaps cleanly once the stroke is under it. */
.nx .nx-hero__top .nx-pill {
    background: var(--nx-white);
    color: var(--nx-black);
    border-color: var(--nx-white);
}
.nx-hero__top .nx-pill::before { background: var(--nx-black); }
.nx .nx-hero__top .nx-pill:hover { color: var(--nx-white); }

.nx .nx-hero__foot .nx-langwrap .lang-opt:hover,
.nx .nx-hero__foot .nx-langwrap .lang-opt.active { color: var(--nx-white); }

/* ── Statement + reel ───────────────────────────────────────────────────── */
.nx-say {
    display: grid;
    gap: clamp(38px, 6vw, 90px);
    grid-template-columns: 1fr;
}
@media (min-width: 900px) {
    .nx-say { grid-template-columns: 1fr 1fr; align-items: start; }
}
.nx-say__aside {
    display: flex;
    flex-direction: column;
    gap: 18px;
    max-width: 46ch;
}
.nx-say__body {
    font-size: clamp(.95rem, 1.15vw, 1.06rem);
    line-height: 1.5;
    color: var(--nx-mute);
}
.nx-dark .nx-say__body { color: var(--nx-mute-dark); }

/* Reel: a panel that grows toward full-bleed as it scrolls through */
.nx-reel {
    position: relative;
    margin-inline: auto;
    border-radius: 14px;
    overflow: hidden;
    background: var(--nx-black);
    /* js drives --nx-reel-p from 0 → 1 across the scroll range. It stops at
       94% rather than 100%: keeping a gutter on both sides means even the
       page's biggest panel still reads as a panel, not a full screen. */
    width: calc(70% + 24% * var(--nx-reel-p, 0));
    aspect-ratio: 16 / 9;
}
.nx-reel video,
.nx-reel img {
    width: 100%; height: 100%;
    object-fit: cover;
}


/* ── Stats ──────────────────────────────────────────────────────────────── */
.nx-stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    border-top: 1px solid var(--nx-line-dark);
}
@media (min-width: 800px) { .nx-stats { grid-template-columns: repeat(4, 1fr); } }

.nx-stat {
    padding: clamp(24px, 3vw, 42px) 18px clamp(24px, 3vw, 42px) 0;
    border-bottom: 1px solid var(--nx-line-dark);
}
@media (min-width: 800px) { .nx-stat { border-bottom: 0; } }

.nx-stat__n {
    display: block;
    font-size: clamp(2.2rem, 4vw, 3.6rem);
    font-weight: 600;
    letter-spacing: -.045em;
    line-height: 1;
}
.nx-stat__l {
    display: block;
    margin-top: 12px;
    font-family: var(--nx-mono);
    font-size: 10.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--nx-mute-dark);
    max-width: 22ch;
}

/* ── Work / service panels ──────────────────────────────────────────────── */
.nx-works { position: relative; }

.nx-work {
    position: relative;
    display: block;
    padding-block: clamp(50px, 8vw, 110px);
}

.nx-work__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 18px;
    padding-inline: var(--nx-gutter);
    padding-bottom: 22px;
}

.nx-work__idx {
    font-family: var(--nx-mono);
    font-size: 11px;
    letter-spacing: .1em;
    color: var(--nx-mute-dark);
}

.nx-work__name {
    font-size: clamp(1.6rem, 3.4vw, 3rem);
    font-weight: 600;
    letter-spacing: -.04em;
    line-height: 1;
}

.nx-work__media {
    position: relative;
    overflow: hidden;
    border-radius: 10px;
    margin-inline: var(--nx-gutter);
    aspect-ratio: 16 / 8;
    background: #141414;
}
.nx-work__media img {
    width: 100%; height: 100%;
    object-fit: cover;
    /* scale-in as the panel enters, driven by js via --sp */
    transform: scale(calc(1.22 - .22 * var(--sp, 1)));
    transition: transform .1s linear;
    will-change: transform;
}

.nx-work__go {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: 20px;
    padding-inline: var(--nx-gutter);
    font-family: var(--nx-mono);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--nx-mute-dark);
    transition: color .35s ease;
}
.nx-work:hover .nx-work__go { color: var(--nx-white); }
.nx-work__go svg { transition: transform .45s var(--nx-ease); }
.nx-work:hover .nx-work__go svg { transform: translateX(5px); }

/* ── Service picker ─────────────────────────────────────────────────────────
   Three full-bleed panels, one per craft. At rest they are equal thirds with
   straight edges and all three names showing. Point at one and the whole row
   shears: the edges go diagonal, the panel takes room off the other two, and
   their names step aside so only the chosen one is left standing.

   Mechanics: the skew lives on the row, so every divider stays parallel; the
   photo and the caption inside each panel carry the exact counter-skew, which
   keeps them upright while the panel they sit in is a parallelogram. The row
   is wider than the viewport by --nx-craft-bleed on each side so the sheared
   outer edges never wander into view, and the first and last panels carry that
   overhang as flex-basis so what you actually see stays even thirds. */
.nx-craft {
    /* Both live here rather than on the row so one media query can flatten the
       whole component, resting state and hover state together. */
    --nx-craft-bleed: 86px;
    --nx-craft-skew: 0deg;
    position: relative;
    overflow: hidden;                                /* clips the overhang */
    margin-top: clamp(30px, 3.6vw, 52px);
    margin-inline: calc(var(--nx-gutter) * -1);      /* out to the page edges */
}
.nx-craft.is-live { --nx-craft-skew: -7deg; }

.nx-craft__row {
    display: flex;
    gap: clamp(3px, .38vw, 6px);
    height: clamp(400px, 44vw, 620px);
    width: calc(100% + var(--nx-craft-bleed) * 2);
    margin-left: calc(var(--nx-craft-bleed) * -1);
    background: var(--nx-white);                     /* shows through the gaps */
    transform: skewX(var(--nx-craft-skew));
    transition: transform .8s var(--nx-ease);
}

.nx-craft__panel {
    position: relative;
    display: block;
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    transition: flex-grow .8s var(--nx-ease);
}
/* The outer two are the ones hanging off the edges, so they get the overhang
   as their basis — the grown part, i.e. the part on screen, stays equal. */
.nx-craft__panel:first-child,
.nx-craft__panel:last-child { flex-basis: var(--nx-craft-bleed); }

.nx-craft.is-live .nx-craft__panel { flex-grow: .85; }
.nx-craft.is-live .nx-craft__panel.is-on { flex-grow: 1.45; }

.nx-craft__shot {
    position: absolute;
    top: 0;
    bottom: 0;
    /* Same overhang again: the counter-skew swings the photo sideways by
       tan(7°)·height/2 at the top and bottom edges, and this covers it. */
    left: calc(var(--nx-craft-bleed) * -1);
    right: calc(var(--nx-craft-bleed) * -1);
    z-index: 0;
    transform: skewX(calc(var(--nx-craft-skew) * -1));
    transition: transform .8s var(--nx-ease);
}
.nx-craft__shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* Legibility scrim. Weighted to the left, where the name sits. */
.nx-craft__shot::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(100deg,
        rgba(0, 0, 0, .66) 0%,
        rgba(0, 0, 0, .34) 52%,
        rgba(0, 0, 0, .5) 100%);
}

/* Second scrim, on the panels that were not chosen. */
.nx-craft__panel::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background: var(--nx-ink);
    opacity: 0;
    pointer-events: none;
    transition: opacity .6s var(--nx-ease);
}
.nx-craft.is-live .nx-craft__panel:not(.is-on)::after { opacity: .42; }

.nx-craft__cap {
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    z-index: 2;
    padding-inline: clamp(18px, 2.3vw, 44px);
    /* Centred on the panel's own midline, which is also the row's skew origin,
       so this counter-skew cancels the row's exactly. */
    transform: translateY(-50%) skewX(calc(var(--nx-craft-skew) * -1));
    transition: transform .8s var(--nx-ease), opacity .5s var(--nx-ease);
}
/* The outer panels start and end off screen, so their names have to be pushed
   back in by the overhang or the first and last words get cut in half. At the
   caption's own height — the row's midline — the shear is zero, so insetting
   by exactly the overhang lands the name on the visible edge either way. */
.nx-craft__panel:first-child .nx-craft__cap { left: var(--nx-craft-bleed); }
.nx-craft__panel:last-child .nx-craft__cap { right: var(--nx-craft-bleed); }
/* Once a panel is chosen the other two are too narrow to hold their name
   without it breaking apart, so they hand the row over to it. */
.nx-craft.is-live .nx-craft__panel:not(.is-on) .nx-craft__cap { opacity: 0; }

.nx-craft__name {
    display: block;
    /* Wide enough for the longest single word (Development) and no wider, so
       every name breaks after its first word in both states. Left to wrap on
       its own the chosen panel would have room to pull its name back onto one
       line, and the name would jump a line up mid-shear. */
    max-width: 7.2em;
    font-size: clamp(1.7rem, 4.4vw, 4.25rem);
    font-weight: 800;
    line-height: .95;
    letter-spacing: -.035em;
    color: var(--nx-white);
}

.nx-craft__go {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: clamp(14px, 1.4vw, 22px);
    font-family: var(--nx-mono);
    font-size: 11px;
    font-weight: 400;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .82);
    opacity: 0;
    transform: translateY(7px);
    transition: opacity .5s var(--nx-ease), transform .5s var(--nx-ease);
}
.nx-craft.is-live .nx-craft__panel.is-on .nx-craft__go {
    opacity: 1;
    transform: none;
}

.nx-craft__panel:focus-visible {
    outline: 2px solid var(--nx-white);
    outline-offset: -6px;
}

/* Stacked, there is nothing to take room from and no pointer to take it with,
   so the whole shear comes off and all three names stay put. */
@media (max-width: 799px) {
    .nx-craft,
    .nx-craft.is-live {
        --nx-craft-bleed: 0px;
        --nx-craft-skew: 0deg;
    }
    .nx-craft__row {
        display: block;
        height: auto;
        width: 100%;
        margin-left: 0;
        background: none;
    }
    .nx-craft__panel { height: clamp(190px, 40vw, 270px); }
    .nx-craft__panel + .nx-craft__panel { margin-top: 4px; }
    .nx-craft.is-live .nx-craft__panel,
    .nx-craft.is-live .nx-craft__panel.is-on { flex-grow: 1; }
    .nx-craft.is-live .nx-craft__panel:not(.is-on)::after { opacity: 0; }
    .nx-craft.is-live .nx-craft__panel:not(.is-on) .nx-craft__cap { opacity: 1; }
    .nx-craft__go { opacity: 1; transform: none; }
    .nx-craft__name { font-size: clamp(1.7rem, 7vw, 2.5rem); }
}

/* ── Case study ─────────────────────────────────────────────────────────── */
/* Image and copy sit side by side, with the copy column dropped down the page
   — an editorial stagger. The image is a panel, never a full screen. */
.nx-case {
    display: grid;
    grid-template-columns: 1fr;
    gap: clamp(28px, 4vw, 44px);
    align-items: start;
}
@media (min-width: 900px) {
    .nx-case {
        grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
        gap: clamp(40px, 5vw, 76px);
    }
    .nx-case__body { padding-top: clamp(40px, 7vw, 110px); }
}

.nx-case__media {
    position: relative;
    overflow: hidden;
    border-radius: 10px;
    /* Close to the source shots' own 1.85 — a 4/3 frame threw away a sixth of
       the width of every one of them. */
    aspect-ratio: 16 / 9;
    background: #141414;
}
.nx-case__media img { width: 100%; height: 100%; object-fit: cover; }

.nx-case__badge {
    position: absolute;
    top: 16px; left: 16px;
    z-index: 2;
    padding: 7px 12px;
    border-radius: 999px;
    background: var(--nx-white);
    color: var(--nx-black);
    font-family: var(--nx-mono);
    font-size: 10px;
    letter-spacing: .12em;
    text-transform: uppercase;
}

.nx-case__body { display: block; }

.nx-case__title {
    font-size: clamp(1.5rem, 2.7vw, 2.4rem);
    font-weight: 500;
    line-height: 1.06;
    letter-spacing: -.035em;
}
.nx-case__desc {
    margin-top: 18px;
    font-size: .98rem;
    line-height: 1.55;
    color: var(--nx-mute-dark);
    max-width: 56ch;
}

.nx-metrics {
    display: flex;
    flex-wrap: wrap;
    gap: 34px;
    align-content: start;
    margin-top: clamp(30px, 4vw, 46px);
    padding-top: clamp(24px, 3vw, 34px);
    border-top: 1px solid var(--nx-line-dark);
}
.nx-metric__n {
    display: block;
    font-size: clamp(1.5rem, 2.4vw, 2.1rem);
    font-weight: 600;
    letter-spacing: -.04em;
    line-height: 1;
}
.nx-metric__l {
    display: block;
    margin-top: 9px;
    font-family: var(--nx-mono);
    font-size: 10px;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--nx-mute-dark);
}

/* ── Studio strip ───────────────────────────────────────────────────────── */
.nx-studio {
    display: grid;
    gap: clamp(34px, 5vw, 70px);
    grid-template-columns: 1fr;
    align-items: center;
}
@media (min-width: 900px) {
    .nx-studio { grid-template-columns: 1fr 1fr; }
}
.nx-studio__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}
.nx-studio__grid figure {
    overflow: hidden;
    border-radius: 8px;
    aspect-ratio: 4 / 5;
}
.nx-studio__grid figure:nth-child(2) { margin-top: clamp(20px, 5vw, 54px); }
/* Sizing and motion come from .nx-par on the figure — no hover zoom, the
   drift is the movement. */

/* ── FAQ ────────────────────────────────────────────────────────────────── */
.nx-faq { border-top: 1px solid var(--nx-line); }

.nx-faq__item { border-bottom: 1px solid var(--nx-line); }

.nx-faq__q {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 22px;
    padding: clamp(20px, 2.4vw, 30px) 0;
    background: none;
    border: 0;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    font-size: clamp(1.05rem, 1.7vw, 1.5rem);
    font-weight: 500;
    letter-spacing: -.028em;
    color: inherit;
    transition: opacity .3s ease;
}
.nx-faq__q:hover { opacity: .55; }

.nx-faq__q svg {
    flex: 0 0 auto;
    transition: transform .5s var(--nx-ease);
}
.nx-faq__q[aria-expanded="true"] svg { transform: rotate(180deg); }

.nx-faq__a {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .6s var(--nx-ease);
}
.nx-faq__a > div { overflow: hidden; }
.nx-faq__q[aria-expanded="true"] + .nx-faq__a { grid-template-rows: 1fr; }

.nx-faq__a p {
    padding-bottom: clamp(20px, 2.4vw, 30px);
    max-width: 62ch;
    font-size: .98rem;
    line-height: 1.6;
    color: var(--nx-mute);
}
.nx-dark .nx-faq__a p { color: var(--nx-mute-dark); }

/* ── Glitch band ────────────────────────────────────────────────────────── */
/* A band, not a full screen — inset by the page gutter so it sits in the
   same column as every other panel. The bottom margin matters: the section
   below it is dark, so without it the band's bottom edge (and its rounded
   corners) melt straight into that section instead of reading as a panel. */
.nx-glitch {
    position: relative;
    min-height: 72svh;
    margin-inline: var(--nx-gutter);
    margin-bottom: clamp(90px, 13vw, 190px);
    border-radius: 14px;
    display: grid;
    place-items: center;
    overflow: hidden;
    background: var(--nx-black);
}
.nx-glitch__bg {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    opacity: .55;
}
.nx-glitch__mid {
    position: relative;
    z-index: 2;
    font-size: clamp(1.1rem, 2vw, 1.7rem);
    font-weight: 500;
    letter-spacing: -.03em;
    color: var(--nx-white);
    text-align: center;
    padding-inline: var(--nx-gutter);
}

/* ── Contact ────────────────────────────────────────────────────────────── */
.nx-talk {
    display: grid;
    gap: clamp(46px, 7vw, 90px);
    grid-template-columns: 1fr;
}
@media (min-width: 940px) {
    .nx-talk { grid-template-columns: 1fr 1fr; }
}

.nx-talk__title {
    font-size: clamp(3.4rem, 11vw, 9rem);
    font-weight: 700;
    line-height: .84;
    letter-spacing: -.055em;
}

.nx-talk__mail {
    display: inline-block;
    margin-top: 30px;
    font-size: clamp(1rem, 1.7vw, 1.35rem);
    letter-spacing: -.02em;
    border-bottom: 1px solid currentColor;
    padding-bottom: 3px;
    transition: opacity .3s ease;
}
.nx-talk__mail:hover { opacity: .55; }

.nx-talk__where {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-top: 26px;
    font-family: var(--nx-mono);
    font-size: 11px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--nx-mute-dark);
}

/* Form — underline fields, no boxes */
.nx-form { display: flex; flex-direction: column; gap: 4px; }

.nx-field { position: relative; padding-block: 16px; border-bottom: 1px solid var(--nx-line-dark); }

.nx-field label {
    display: block;
    font-family: var(--nx-mono);
    font-size: 10px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--nx-mute-dark);
    margin-bottom: 9px;
}

.nx-field input,
.nx-field select,
.nx-field textarea {
    width: 100%;
    background: none;
    border: 0;
    padding: 0;
    color: var(--nx-white);
    font-family: inherit;
    font-size: 1.02rem;
    letter-spacing: -.015em;
    resize: none;
    outline: none;
}
.nx-field textarea { min-height: 96px; line-height: 1.5; }
.nx-field input::placeholder,
.nx-field textarea::placeholder { color: rgba(255, 255, 255, .28); }

.nx-field select { appearance: none; cursor: pointer; }
.nx-field select option { background: var(--nx-ink); color: var(--nx-white); }
.nx-field--sel::after {
    content: '';
    position: absolute;
    right: 2px; bottom: 24px;
    width: 7px; height: 7px;
    border-right: 1.5px solid var(--nx-mute-dark);
    border-bottom: 1.5px solid var(--nx-mute-dark);
    transform: rotate(45deg);
    pointer-events: none;
}

.nx-form .char-counter {
    margin: 8px 0 0;
    font-family: var(--nx-mono);
    font-size: 10px;
    letter-spacing: .1em;
    color: var(--nx-mute-dark);
    text-align: right;
}
.nx-form .char-counter.is-near-limit { color: #d8b26a; }
.nx-form .char-counter.is-at-limit   { color: #e06a5a; }

.nx-form__send { margin-top: 26px; align-self: flex-start; }

.nx-form .form-status {
    margin: 16px 0 0;
    font-family: var(--nx-mono);
    font-size: 11px;
    letter-spacing: .06em;
    line-height: 1.5;
    color: var(--nx-mute-dark);
}
.nx-form .form-status.success { color: #7fd1a3; }
.nx-form .form-status.error   { color: #e06a5a; }

.hp-field { display: none !important; }

/* ── Footer ─────────────────────────────────────────────────────────────── */
.nx-foot {
    background: var(--nx-ink);
    color: var(--nx-white);
    padding: clamp(60px, 8vw, 110px) 0 26px;
    border-top: 1px solid var(--nx-line-dark);
}

.nx-foot__cols {
    display: grid;
    gap: 40px;
    grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 900px) {
    .nx-foot__cols { grid-template-columns: 1.4fr repeat(3, 1fr); gap: 30px; }
}

.nx-foot__brand {
    font-size: clamp(1.4rem, 2.4vw, 2rem);
    font-weight: 700;
    letter-spacing: -.045em;
}
.nx-foot__desc {
    margin-top: 14px;
    max-width: 34ch;
    font-size: .92rem;
    line-height: 1.55;
    color: var(--nx-mute-dark);
}

.nx-foot h4 {
    font-family: var(--nx-mono);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--nx-mute-dark);
    margin-bottom: 16px;
}
.nx-foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.nx-foot li a {
    font-size: .95rem;
    letter-spacing: -.015em;
    transition: opacity .3s ease;
}
.nx-foot li a:hover { opacity: .55; }

.nx-foot__mail {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    margin-top: 4px;
    font-size: .95rem;
    border-bottom: 1px solid var(--nx-line-dark);
    padding-bottom: 3px;
}
.nx-foot__mail:hover { border-color: currentColor; }

.nx-foot__base {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin-top: clamp(50px, 7vw, 90px);
    padding-top: 22px;
    border-top: 1px solid var(--nx-line-dark);
    font-family: var(--nx-mono);
    font-size: 10px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--nx-mute-dark);
}
.nx-foot__base nav { display: flex; flex-wrap: wrap; gap: 20px; }
.nx-foot__base a:hover { color: var(--nx-white); }

/* ── Responsive trims ───────────────────────────────────────────────────── */
@media (max-width: 760px) {
    .nx-hero__intro { font-size: 1.05rem; }
    /* The gutter is a fixed 20px, so it eats a far bigger share of a narrow
       viewport — the wordmark has to give that width back or it overflows. */
    .nx-word { font-size: 18.5vw; letter-spacing: -.04em; }
    .nx-hero__foot { flex-direction: column; align-items: flex-start; gap: 14px; }
    .nx-reel { width: calc(88% + 12% * var(--nx-reel-p, 0)); }
    .nx-work__head { flex-direction: column; align-items: flex-start; gap: 6px; }
}

/* ── Reduced motion ─────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .nx *,
    .nx *::before,
    .nx *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
    .nx-rise { opacity: 1; transform: none; }
    .nx-lines .nx-line > span,
    .nx-word span,
    .nx-nav__row > span { transform: none; }
    .nx-work__media img { transform: none; }
    /* Transitions are already down to ~0ms here, so the shear would snap on
       and off under the pointer rather than travel. Leave the row square. */
    .nx-craft,
    .nx-craft.is-live { --nx-craft-skew: 0deg; }
    .nx .nx-par > img,
    .nx .nx-par > video { height: 100%; transform: none; }
    .nx-reel { width: 100%; }
}

/* ── Low-FX ──────────────────────────────────────────────────────────────────
   js/nx.js puts .lowfx on <html> when there is no GPU behind the browser —
   acceleration switched off, a blocklisted driver, a VM, a remote desktop — or
   when the frame times say the machine cannot keep up anyway.

   Everything below is close to free with a compositor and expensive without
   one. What comes off is the blurring, the permanent layer promotion and the
   second cursor. Layout, type, colour and the difference blending all stay:
   the blended areas are small, and they are what keeps the chrome legible
   against whatever scrolls under it.
   ────────────────────────────────────────────────────────────────────────── */

/* The scrim is the worst of them by a distance — fixed, full width, and
   visible exactly while the page is moving, so its backdrop is read back,
   blurred and masked on every scroll frame. Without the blur the tint has to
   separate the layers on its own, so it goes denser; the mask only ever
   existed to fade the blur's bottom edge, and the gradient already fades. */
.lowfx .nx-head__scrim {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    -webkit-mask-image: none;
    mask-image: none;
    background: linear-gradient(to bottom,
        rgba(8, 8, 8, .94) 0%,
        rgba(8, 8, 8, .74) 52%,
        rgba(8, 8, 8, 0) 100%);
}
.lowfx .nx-head__scrim.is-light {
    background: linear-gradient(to bottom,
        rgba(255, 255, 255, .96) 0%,
        rgba(255, 255, 255, .80) 52%,
        rgba(255, 255, 255, 0) 100%);
}

/* Shearing the row relayouts three flex children and re-rasterises three
   photos on every frame. The panels still open and close, just square. */
.lowfx .nx-craft,
.lowfx .nx-craft.is-live { --nx-craft-skew: 0deg; }

/* will-change hands each of these its own layer to hold for the life of the
   page. That is the right trade against a GPU and the wrong one against a CPU,
   which then has to composite every one of them by hand on every frame. */
.lowfx .nx .nx-pill,
.lowfx .nx .nx-par > video,
.lowfx .nx-work__media img,
.lowfx .nx-loader__reel { will-change: auto; }

/* js/nx.js never builds the dot in low-FX; this covers a demotion that lands
   after it already exists. The native cursor was never hidden, so nothing is
   left missing. */
.lowfx .nx-cursor { display: none; }
