/* ============================================================
   GRAPHICXO — Core stylesheet
   Dark luxury · purple energy → gold reward

   HOVER RULES GO INSIDE @media (hover:hover).
   Every :hover in this project is wrapped, here and in the other
   stylesheets. A touch browser has no hover: it fires the hover
   state on tap and then leaves it stuck until you tap something
   else, so a tapped card stays lit while you read the page it
   took you to. The query means those rules never apply on a
   device that cannot hover in the first place.

   Two things this is NOT for: .is-active and other real state
   classes, which must read on a phone and stay outside the
   query; and :focus-visible, which is the keyboard's business
   and unrelated to pointers.
   ============================================================ */

/* ---------- Tokens ---------- */
:root{
  --bg:#070707;
  --bg-2:#111111;
  --purple:#6D28FF;
  --purple-soft:#8b5cff;
  --gold:#D4AF37;
  --gold-soft:#e7c96b;
  --white:#F7F7F7;
  --grey:#BFBFBF;
  --grey-dim:#8a8a8a;      /* 5.84:1 on --bg. #6d6d6d was 3.89:1 — under AA for body text */
  --line:rgba(247,247,247,.10);

  --serif:"Fraunces", Georgia, serif;
  --sans:"Manrope", system-ui, sans-serif;

  --ease:cubic-bezier(.22,1,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --dur:.9s;

  --pad:clamp(1.25rem, 5vw, 6rem);
  --maxw:1600px;
}

/* ---------- Reset ---------- */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  background:var(--bg);
  color:var(--white);
  font-family:var(--sans);
  font-weight:400;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
body.is-locked{overflow:hidden;height:100vh}
/* The lightbox stops Lenis, but Lenis is not the only way to scroll: a native
   wheel or a trackpad still moves the document behind the picture. This is the
   same lock the preloader uses, kept separate so the two can never clear each
   other's state. The Scene 6 fan is fixed to the viewport, so it would slide
   about behind the backdrop without it. */
body.lb-open{overflow:hidden}
img{max-width:100%;display:block}
/* <picture> wraps several logos for the WebP sources. Left as the inline box
   it is by default, it becomes the grid/flex item in its parent's place —
   and every percentage width written against that parent then resolves
   against the picture instead. .preloader__icon at 52%% and .ab-portrait__mark
   at 56%% both collapse. display:contents takes the wrapper out of layout so
   the <img> stays the item it always was and none of the sizing CSS moves. */
picture{display:contents}
a{color:inherit;text-decoration:none}

/* One visible focus ring for everything that can take focus. There were only
   two :focus-visible rules on the whole site, both component-specific, which
   left the nav, the menu, every card link and every footer link relying on
   the browser default — and a default ring on a near-black background is
   somewhere between faint and invisible depending on the browser.
   :focus-visible rather than :focus, so a mouse click doesn't ring it. */
:focus-visible{
  outline:2px solid var(--gold-soft);
  outline-offset:3px;
  border-radius:2px;
}

/* Skip link. The nav is the same nine or so stops on every page; without this
   a keyboard user tabs through all of them before reaching the content, on
   every single page. Off-screen until focused, then it lands top-left. */
.skip{
  position:fixed;
  top:0;left:0;
  z-index:10000;
  transform:translateY(-120%);
  padding:.85rem 1.4rem;
  background:var(--gold);
  color:#0a0a0a;
  font-size:.85rem;font-weight:600;letter-spacing:.04em;
  border-bottom-right-radius:10px;
  transition:transform .25s var(--ease);
}
.skip:focus{transform:translateY(0)}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
::selection{background:var(--purple);color:#fff}

/* Lenis */
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto!important}
.lenis.lenis-stopped{overflow:hidden}

/* ---------- Typography ---------- */
h1,h2,h3,h4{font-family:var(--serif);font-weight:400;line-height:1.02;letter-spacing:-.02em}
h1 em,h2 em,h3 em{font-style:italic;font-weight:300}
.display{
  font-family:var(--serif);
  font-size:clamp(3rem, 11vw, 12rem);
  line-height:.92;
  letter-spacing:-.03em;
}
.eyebrow{
  font-family:var(--sans);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.28em;
  text-transform:uppercase;
  color:var(--grey);
}
.eyebrow--gold{color:var(--gold)}
.lead{font-size:clamp(1.05rem,1.6vw,1.35rem);color:var(--grey);max-width:46ch;font-weight:300}

/* ---------- Layout helpers ---------- */
.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad)}
.section{position:relative;padding-block:clamp(6rem,14vh,12rem)}
.main{position:relative;z-index:2}

/* Reveal base (JS adds .in) */
.reveal{opacity:0;transform:translateY(38px);transition:opacity 1.1s var(--ease),transform 1.1s var(--ease)}
.reveal.in{opacity:1;transform:none}
.reveal-2{transition-delay:.12s}
.reveal-3{transition-delay:.24s}
.reveal-4{transition-delay:.36s}
[data-split] .line{overflow:hidden;display:block}
[data-split] .line > span{display:block;transform:translateY(110%);transition:transform 1s var(--ease)}
[data-split].in .line > span{transform:none}
[data-split].in .line:nth-child(2) > span{transition-delay:.08s}
[data-split].in .line:nth-child(3) > span{transition-delay:.16s}
[data-split].in .line:nth-child(4) > span{transition-delay:.24s}

/* ============================================================
   CUSTOM CURSOR
   ============================================================ */
.cursor{position:fixed;top:0;left:0;z-index:9998;pointer-events:none;mix-blend-mode:difference;opacity:0;transition:opacity .4s}
.cursor.on{opacity:1}
.cursor__ring{position:absolute;top:0;left:0;width:38px;height:38px;border:1px solid var(--white);border-radius:50%;transform:translate(-50%,-50%);transition:width .35s var(--ease),height .35s var(--ease),background .35s,border-color .35s}
.cursor__dot{position:absolute;top:0;left:0;width:5px;height:5px;background:var(--white);border-radius:50%;transform:translate(-50%,-50%)}
.cursor__label{position:absolute;top:0;left:0;transform:translate(-50%,-50%);font-size:.62rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:#000;opacity:0;white-space:nowrap;transition:opacity .3s}
.cursor.hover .cursor__ring{width:74px;height:74px;background:var(--white)}
.cursor.hover .cursor__dot{opacity:0}
.cursor.label .cursor__ring{width:96px;height:96px;background:var(--white)}
.cursor.label .cursor__dot{opacity:0}
.cursor.label .cursor__label{opacity:1}
@media (hover:none){.cursor{display:none}}

/* ============================================================
   PRELOADER
   ============================================================ */
/* .gx-warm is stamped on <html> before first paint on every page after the
   one that played the intro, so this markup never flashes again. */
html.gx-warm .preloader{display:none!important}

.preloader{position:fixed;inset:0;z-index:9999;background:var(--bg);display:grid;place-items:center;overflow:hidden;will-change:transform}
/* Vignette: keeps the eye on the mark and stops the auras washing the edges out. */
.preloader::after{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(ellipse at 50% 48%,transparent 38%,rgba(7,7,7,.72) 100%)}

.preloader__aura{position:absolute;top:50%;left:50%;translate:-50% -50%;border-radius:50%;pointer-events:none;filter:blur(46px)}
.preloader__aura--purple{width:66vmax;height:66vmax;background:radial-gradient(circle,rgba(109,40,255,.30),rgba(109,40,255,.05) 46%,transparent 68%);animation:plAura 5.2s var(--ease) infinite}
.preloader__aura--gold{width:34vmax;height:34vmax;background:radial-gradient(circle,rgba(212,175,55,.20),transparent 65%);animation:plAura 5.2s var(--ease) infinite reverse;animation-delay:-1.4s}
@keyframes plAura{0%,100%{transform:scale(.82);opacity:.55}50%{transform:scale(1.12);opacity:1}}

.preloader__inner{position:relative;z-index:2;display:flex;flex-direction:column;align-items:center;gap:clamp(1.6rem,4vh,2.6rem)}

/* --- The mark, ringed by the progress arc --- */
.preloader__badge{position:relative;width:clamp(150px,25vmin,216px);aspect-ratio:1;display:grid;place-items:center}
.preloader__ring{position:absolute;inset:0;width:100%;height:100%;overflow:visible;transform:rotate(-90deg)}
.pl-track{fill:none;stroke:rgba(247,247,247,.09);stroke-width:1.5}
/* dasharray 553 ≈ 2πr at r=88 — JS drives the offset from the same 0→100 value
   as the counter, so the ring IS the progress read-out, not a second one. */
.pl-arc{fill:none;stroke:url(#plArcGrad);stroke-width:2.5;stroke-linecap:round;stroke-dasharray:553;stroke-dashoffset:553;filter:drop-shadow(0 0 9px rgba(212,175,55,.45))}
.pl-dash{fill:none;stroke:rgba(212,175,55,.20);stroke-width:1;stroke-dasharray:1.5 11;transform-origin:50% 50%;animation:plSpin 18s linear infinite}
@keyframes plSpin{to{transform:rotate(360deg)}}

.preloader__icon{width:52%;height:auto;opacity:0;transform:scale(.72);filter:drop-shadow(0 14px 34px rgba(0,0,0,.65)) drop-shadow(0 0 26px rgba(212,175,55,.22))}

/* --- Wordmark: letters rise out of a mask --- */
.preloader__word{display:flex;overflow:hidden;padding-block:.25em;font-family:var(--sans);font-size:clamp(.66rem,1.5vw,.8rem);font-weight:600;letter-spacing:.46em;padding-inline-start:.46em;color:var(--white)}
.preloader__word span{display:inline-block;transform:translateY(120%)}

/* --- Footer block: the progress rule, then label left / count right ---
   Inset to the page gutter and lifted off the viewport edge, so it reads as a
   ruled line under a caption rather than a browser loading bar. */
.preloader__foot{position:absolute;left:0;right:0;bottom:0;z-index:2;display:flex;flex-direction:column;gap:clamp(.9rem,2vh,1.4rem);padding:0 var(--pad) clamp(1.7rem,5vh,2.8rem);opacity:0}
.preloader__meta{display:flex;align-items:flex-end;justify-content:space-between;gap:1rem}
.preloader__tag{font-family:var(--sans);font-size:.6rem;font-weight:600;letter-spacing:.3em;text-transform:uppercase;color:var(--grey-dim);padding-bottom:.5rem}
.preloader__count{display:flex;align-items:baseline;gap:.35rem;font-family:var(--serif);color:var(--gold);line-height:.85}
.preloader__count i{font-style:normal;font-size:clamp(2.2rem,7vw,3.6rem);font-variant-numeric:tabular-nums;min-width:2.6ch;text-align:right}
.preloader__count em{font-style:normal;font-family:var(--sans);font-size:.58rem;font-weight:600;letter-spacing:.2em;color:var(--gold-soft);opacity:.75}

/* The rule keeps its glow outside its own box, so no overflow clipping here. */
.preloader__bar{position:relative;width:100%;height:2px;border-radius:2px;background:rgba(139,92,255,.13)}
.preloader__bar span{position:relative;display:block;height:100%;width:0;border-radius:inherit;
  background:linear-gradient(90deg,rgba(109,40,255,.28),var(--purple) 42%,var(--purple-soft));
  box-shadow:0 0 9px rgba(139,92,255,.8),0 0 24px rgba(109,40,255,.6),0 0 52px rgba(109,40,255,.32)}
/* Leading spark — the light the fill is being drawn out of. */
.preloader__bar span::after{content:"";position:absolute;top:50%;right:0;width:6px;height:6px;border-radius:50%;transform:translate(50%,-50%);
  background:#c4a6ff;box-shadow:0 0 10px 2px rgba(139,92,255,.95),0 0 28px 6px rgba(109,40,255,.55)}

/* No-JS / no-GSAP fallback — the panel must never be a permanent black wall. */
.preloader.done{transform:translateY(-100%);transition:transform 1.1s var(--ease);pointer-events:none}
html.no-js .preloader{display:none}

@media (max-width:640px){
  .preloader__tag{display:none}
  .preloader__meta{justify-content:flex-end}
}

/* ============================================================
   THE SIGNATURE MARK
   The studio signs its work, so the mark is written rather than set: a
   flourish that draws itself on when it comes into view. Two sizes — the
   full one that closes about.php, and the miniature after the footer line.

   app.js measures each [data-draw] path and writes its own length into
   stroke-dasharray/offset. Guessing a dash length instead makes the pen
   appear to stop moving before the end of the stroke. With JS off nothing is
   ever set and the default state of the markup is the finished drawing.
   ============================================================ */
.sig{width:100%;height:auto;overflow:visible;display:block}
.sig__stroke{
  fill:none;stroke:var(--gold);
  stroke-width:3.6;stroke-linecap:round;stroke-linejoin:round;
  filter:drop-shadow(0 0 16px rgba(212,175,55,.45));
  transition:stroke-dashoffset 2.6s var(--ease);
}
/* The underline arrives after the hand has lifted. */
.sig__rule{
  fill:none;stroke:var(--gold-soft);
  stroke-width:1.7;stroke-linecap:round;opacity:.5;
  transition:stroke-dashoffset 1.5s var(--ease) 1.15s;
}
/* And the full stop last, once the flourish has landed. */
.sig__dot{
  fill:var(--gold);opacity:0;
  filter:drop-shadow(0 0 10px rgba(212,175,55,.85));
  transition:opacity .55s var(--ease) 2.25s;
}
.is-drawn .sig__dot{opacity:1}

/* The miniature, set inline after a line of copy — the footer's copyright
   and the homepage sign-off both use it. Smaller box, so the stroke has to
   thicken or it disappears at this scale. */
.sign-inline{display:inline-block;width:62px;margin-left:.45rem;vertical-align:-.55em}
.sig--mini .sig__stroke{
  stroke-width:2.7;
  filter:drop-shadow(0 0 8px rgba(212,175,55,.4));
  transition-duration:1.5s;
}

/* ============================================================
   PAGE TRANSITION
   ============================================================ */
.transition{position:fixed;inset:0;z-index:9990;display:grid;grid-template-columns:repeat(4,1fr);pointer-events:none}
.transition__panel{background:var(--bg-2);transform:scaleY(0);transform-origin:bottom}
/* A warm page opens underneath the panels — the same ones the outgoing page
   closed with — and app.js retracts them, so navigation is one continuous
   wipe rather than a cut. The .cover / .reveal-out rules below outrank this. */
html.gx-warm .transition__panel{transform:scaleY(1);transform-origin:top}
.transition.cover .transition__panel{transform:scaleY(1);transform-origin:bottom;transition:transform .55s var(--ease)}
.transition.cover .transition__panel:nth-child(2){transition-delay:.06s}
.transition.cover .transition__panel:nth-child(3){transition-delay:.12s}
.transition.cover .transition__panel:nth-child(4){transition-delay:.18s}
.transition.reveal-out .transition__panel{transform:scaleY(0);transform-origin:top;transition:transform .55s var(--ease)}
.transition.reveal-out .transition__panel:nth-child(2){transition-delay:.06s}
.transition.reveal-out .transition__panel:nth-child(3){transition-delay:.12s}
.transition.reveal-out .transition__panel:nth-child(4){transition-delay:.18s}

/* ============================================================
   NAV
   ============================================================ */
.nav{position:fixed;top:0;left:0;width:100%;z-index:9000;display:flex;align-items:center;justify-content:space-between;padding:1.5rem var(--pad);transition:padding .5s var(--ease),background .5s}
.nav.scrolled{padding-block:1rem;background:rgba(7,7,7,.6);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.nav.hide{transform:translateY(-110%);transition:transform .5s var(--ease)}
.nav__logo,.footer__logo{font-family:var(--serif);font-size:1.4rem;letter-spacing:-.02em}
.nav__logo em,.footer__logo em{font-style:italic;color:var(--gold)}
.nav__links{display:flex;gap:2.2rem}
.nav__link{position:relative;font-size:.85rem;font-weight:500;letter-spacing:.02em;color:var(--grey);padding:.3rem 0;transition:color .4s}
.nav__link::after{content:"";position:absolute;left:0;bottom:0;width:0;height:1px;background:var(--gold);transition:width .4s var(--ease)}
/* Split from the hover half below: .is-active is the current page and has to
   read on a phone, where :hover never fires (or worse, sticks after a tap). */
.nav__link.is-active{color:var(--white)}
.nav__link.is-active::after{width:100%}
@media (hover:hover){
  .nav__link:hover{color:var(--white)}
  .nav__link:hover::after{width:100%}
}
/* ---- Services submenu ----
   No JavaScript: the panel opens on :hover and on :focus-within, which is
   what makes it reachable with a keyboard. The parent stays a real link, so
   on a touch device — where hover never fires — tapping Services simply goes
   to the services page and the panel never appears at all. */
.nav__item{position:relative;display:flex;align-items:center}
.nav__caret{
  display:inline-block;
  width:0;height:0;
  margin-left:.4rem;
  vertical-align:middle;
  border-left:3.5px solid transparent;
  border-right:3.5px solid transparent;
  border-top:4px solid currentColor;
  opacity:.6;
  transition:transform .35s var(--ease),opacity .35s var(--ease);
}
.nav__sub{
  position:absolute;
  top:calc(100% + .9rem);
  left:-1rem;
  min-width:16.5rem;
  padding:.5rem;
  border:1px solid var(--line);
  border-radius:14px;
  background:rgba(10,10,12,.94);
  backdrop-filter:blur(16px);
  -webkit-backdrop-filter:blur(16px);
  box-shadow:0 30px 70px -40px rgba(0,0,0,.9);
  opacity:0;visibility:hidden;
  transform:translateY(6px);
  transition:opacity .3s var(--ease),transform .3s var(--ease),visibility .3s;
}
/* The gap between the link and the panel would drop the hover halfway across
   it. This bridges it without moving the panel up onto the type. */
.nav__sub::before{content:"";position:absolute;left:0;right:0;top:-.9rem;height:.9rem}

.nav__sub-link{
  display:block;
  padding:.6rem .75rem;
  border-radius:9px;
  font-size:.85rem;font-weight:500;color:var(--grey);
  transition:background .3s var(--ease),color .3s var(--ease);
}
.nav__sub-all{
  display:block;
  margin-top:.35rem;padding:.6rem .75rem;
  border-top:1px solid var(--line);
  font-size:.76rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--gold);
}

/* Keyboard first, and outside the hover query on purpose: :focus-within is
   the keyboard's business, exactly like :focus-visible elsewhere here. */
.nav__item--has-sub:focus-within .nav__sub{opacity:1;visibility:visible;transform:none}
.nav__item--has-sub:focus-within .nav__caret{transform:rotate(180deg);opacity:1}

@media (hover:hover){
  .nav__item--has-sub:hover .nav__sub{opacity:1;visibility:visible;transform:none}
  .nav__item--has-sub:hover .nav__caret{transform:rotate(180deg);opacity:1}
  .nav__sub-link:hover{background:rgba(247,247,247,.06);color:var(--white)}
  .nav__sub-all:hover{color:var(--gold-soft)}
}
@media (prefers-reduced-motion:reduce){
  .nav__sub{transition:opacity .2s,visibility .2s;transform:none}
  .nav__caret{transition:none}
}

.nav__cta{position:relative;font-size:.82rem;font-weight:600;padding:.7rem 1.4rem;border:1px solid var(--line);border-radius:100px;overflow:hidden;transition:color .4s,border-color .4s}
.nav__cta span{position:relative;z-index:2}
.nav__cta::before{content:"";position:absolute;inset:0;background:linear-gradient(120deg,var(--purple),var(--gold));transform:translateY(101%);transition:transform .5s var(--ease);z-index:1}
@media (hover:hover){.nav__cta:hover{color:#080808;border-color:transparent}}
@media (hover:hover){.nav__cta:hover::before{transform:translateY(0)}}
.nav__toggle{display:none;flex-direction:column;gap:6px;width:44px;height:44px;justify-content:center;align-items:flex-end;padding:5px 0}
.nav__toggle span{display:block;height:1.5px;width:26px;background:var(--white);transition:transform .4s var(--ease),width .4s}
.nav__toggle span:last-child{width:18px}
body.menu-open .nav__toggle span:first-child{transform:translateY(3.75px) rotate(45deg);width:26px}
body.menu-open .nav__toggle span:last-child{transform:translateY(-3.75px) rotate(-45deg);width:26px}

/* ============================================================
   MOBILE MENU
   The whole navigation on a handset, so it has to behave like a
   page rather than a poster.

   --menu-top clears the fixed nav bar, which stays ABOVE this panel
   (z 9000 vs 8000) because it carries the button that closes it. The
   value is the bar's own worst case: its block padding twice plus the
   logo's height, both of which step down with the same breakpoints.
   ============================================================ */
.menu{
  --menu-top:8.75rem;
  position:fixed;
  inset:0;
  z-index:8000;
  background:var(--bg);
  /* The panel itself never scrolls — .menu__panel inside it does. That split
     is what keeps the list from sliding underneath the nav bar, which stays
     above this (z 9000) and is transparent while the menu is open. When the
     whole panel was the scroller, its padding-top scrolled with the content:
     a scrolled list ran up behind the logo and the close button and was
     legible straight through them. A scroller whose BOX starts below the bar
     cannot do that — content is clipped at the bar's lower edge instead. */
  overflow:hidden;
  clip-path:inset(0 0 100% 0);
  transition:clip-path .8s var(--ease);
  pointer-events:none;
}
.menu.open{clip-path:inset(0 0 0% 0);pointer-events:auto}

/* The panel's own light, so it is a designed surface rather than a black
   rectangle: the studio's purple entering top-right, gold settling
   bottom-left — the same journey the homepage makes, in one screen.
   It sits on .menu rather than inside the scroller, so it covers the full
   panel — including the strip behind the transparent nav bar — and holds
   still while the list moves over it. */
.menu::before{
  content:"";
  position:absolute;
  inset:0;
  pointer-events:none;
  background:
    radial-gradient(ellipse 96% 44% at 84% -8%, rgba(109,40,255,.22), transparent 68%),
    radial-gradient(ellipse 84% 40% at -6% 106%, rgba(212,175,55,.10), transparent 72%);
}

/* The scrolling half. Its top edge is the bar's lower edge, so the bar always
   has clean ground under it however far the list has been scrolled.

   The old rule was justify-content:center on the whole panel, with no escape
   hatch. Centred content that outgrows its box overflows in BOTH directions,
   and the half above the top edge cannot be scrolled back — on a short
   handset, or any phone held in landscape, the six links plus the services
   list plus the footer did exactly that and the last items were simply
   unreachable. margin-block:auto on the list below centres it the same way
   while there is room and collapses to nothing when there isn't. */
.menu__panel{
  position:absolute;
  top:calc(var(--menu-top) + env(safe-area-inset-top));
  right:0;
  bottom:0;
  left:0;
  z-index:1;
  display:flex;
  flex-direction:column;
  overflow-y:auto;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  /* Notches and home indicators — without these the footer sits under the
     gesture bar and the outer links under a rounded corner. */
  padding:
    0
    calc(var(--pad) + env(safe-area-inset-right))
    calc(2rem + env(safe-area-inset-bottom))
    calc(var(--pad) + env(safe-area-inset-left));
}

.menu__links{position:relative;z-index:1;display:flex;flex-direction:column;margin-block:auto;width:100%}

/* Each entry is a row on a ruled list, not a free-floating word. The hairline
   is what makes six items of different lengths read as one column, and it is
   what gives the services children something to hang under. */
.menu__group{display:flex;flex-direction:column;border-top:1px solid var(--line)}
.menu__group:last-child{border-bottom:1px solid var(--line)}

.menu__link{
  position:relative;
  display:flex;align-items:baseline;gap:1rem;
  /* .5rem of padding on a clamped 1.8-3.1rem serif keeps every row past 46px
     tall at any width — a real target, without the 11vw / 4.5rem type that
     used to push the last two items and the footer off a small screen with
     no way to scroll them back. */
  padding:.5rem 0;
  font-family:var(--serif);
  font-size:clamp(1.8rem,8vw,3.1rem);
  line-height:1.14;
  letter-spacing:-.02em;
  color:var(--white);
  opacity:0;transform:translateY(24px);
  transition:opacity .55s var(--ease),transform .55s var(--ease),color .3s;
}
.menu.open .menu__link{opacity:1;transform:none;transition-delay:calc(.1s * var(--i))}
.menu__link.is-active,
.menu__link.is-active .menu__index{color:var(--gold)}
@media (hover:hover){ .menu__link:hover{color:var(--gold)} }
.menu__link:focus-visible{outline:1px solid var(--gold);outline-offset:4px}

/* A fixed gutter, so 01..06 form an unbroken column down the left edge
   whatever the labels beside them do — and so the services children can be
   indented to the words rather than to the numbers. */
.menu__index{
  flex:none;width:2.2rem;
  font-family:var(--sans);font-size:.7rem;font-weight:600;letter-spacing:.14em;
  color:var(--grey-dim);
  transition:color .3s;
}

/* ---- Services children in the overlay ----
   Listed flat, as chips. They were plain text at .15rem of padding, which is
   a 22px tap target — half what a thumb needs, five of them in a row. A
   bordered pill is both the target and the signal that these are separate
   destinations from the parent above them. */
.menu__sub{
  display:flex;flex-wrap:wrap;gap:.5rem;
  margin:0 0 1rem 2.2rem;
  opacity:0;transform:translateY(16px);
  transition:opacity .55s var(--ease),transform .55s var(--ease);
}
.menu.open .menu__sub{opacity:1;transform:none;transition-delay:calc(.1s * var(--i) + .06s)}
.menu__sub-link{
  display:inline-flex;align-items:center;
  min-height:40px;
  padding:.3rem .9rem;
  border:1px solid var(--line);
  border-radius:100px;
  font-size:.82rem;font-weight:500;letter-spacing:.01em;
  color:var(--grey);
  transition:color .3s var(--ease),border-color .3s var(--ease),background .3s var(--ease);
}
@media (hover:hover){.menu__sub-link:hover{color:var(--gold);border-color:rgba(212,175,55,.4);background:rgba(212,175,55,.07)}}
.menu__sub-link:focus-visible{outline:1px solid var(--gold);outline-offset:3px}

/* The CTA the desktop bar carries and the overlay did not. .nav__cta is
   display:none from 1024px down, so until now the one action the site most
   wants a visitor to take was absent from the only navigation a phone has.
   Contact is still item 06 in the list; this is the same destination stated
   as an offer rather than as a page name. */
.menu__cta{
  position:relative;z-index:1;
  margin-top:2rem;
  align-self:flex-start;
  justify-content:center;
  opacity:0;transform:translateY(20px);
  transition:opacity .55s var(--ease),transform .55s var(--ease),box-shadow .45s var(--ease),color .45s;
}
.menu.open .menu__cta{opacity:1;transform:none;transition-delay:.72s}

.menu__footer{
  position:relative;z-index:1;
  margin-top:2.2rem;
  padding-top:1.5rem;
  display:flex;flex-wrap:wrap;
  gap:.9rem 1.5rem;
  align-items:center;justify-content:space-between;
  border-top:1px solid var(--line);
  opacity:0;
  transition:opacity .55s var(--ease);
}
.menu.open .menu__footer{opacity:1;transition-delay:.8s}
.menu__mail{color:var(--gold);font-size:.95rem;word-break:break-word}
.menu__socials{display:flex;flex-wrap:wrap;gap:.4rem}
.menu__socials a{
  display:inline-flex;align-items:center;
  min-height:38px;padding:.2rem .8rem;
  border:1px solid var(--line);border-radius:100px;
  color:var(--grey);font-size:.78rem;font-weight:500;
  transition:color .3s var(--ease),border-color .3s var(--ease);
}
@media (hover:hover){.menu__socials a:hover{color:var(--gold);border-color:rgba(212,175,55,.4)}}

/* Shorter bar, shorter clearance — matches the .nav step-down at this width. */
@media (max-width:600px){
  .menu{--menu-top:7.75rem}
}

/* A short viewport — a small handset, or any phone turned on its side. The
   panel scrolls now, so nothing is lost either way, but a menu you have to
   scroll to see the bottom of is a worse menu. The list gives way first: the
   headline type is the only thing here with room to spare, and the CTA and
   the contact details are the parts worth keeping on the first screen.
   --menu-top is deliberately untouched — the nav bar it clears does not get
   shorter just because the screen did. */
@media (max-height:700px){
  .menu__link{font-size:clamp(1.5rem,5.6vw,2.1rem);padding:.4rem 0}
  .menu__sub{margin-bottom:.7rem;gap:.4rem}
  .menu__sub-link{min-height:36px;padding:.2rem .75rem}
  .menu__cta{margin-top:1.4rem}
  .menu__footer{margin-top:1.4rem;padding-top:1.1rem}
}

/* The page must not scroll behind the panel. Only `overflow` — deliberately
   NOT body.is-locked, which also sets height:100vh: on a phone that shrinks
   the document mid-gesture and the browser clamps the scroll position, so
   closing the menu would drop the visitor somewhere else on the page. */
body.menu-open{overflow:hidden}

/* The bar stays on top of the panel because it holds the close button, but it
   must not also print its own ground over it — and it must never be the
   hidden-on-scroll state while it is the only way out. */
body.menu-open .nav,
body.menu-open .nav.scrolled{background:transparent;border-bottom-color:transparent;backdrop-filter:none}
body.menu-open .nav.hide{transform:none}

/* ============================================================
   BUTTONS
   ============================================================ */
.btn{position:relative;display:inline-flex;align-items:center;gap:.7rem;font-size:.9rem;font-weight:600;padding:1rem 2rem;border-radius:100px;overflow:hidden;border:1px solid var(--line);transition:color .45s}
.btn span{position:relative;z-index:2}
.btn::before{content:"";position:absolute;inset:0;background:linear-gradient(120deg,var(--purple),var(--gold));transform:translateY(101%);transition:transform .55s var(--ease);z-index:1}
@media (hover:hover){.btn:hover{color:#080808;border-color:transparent}}
@media (hover:hover){.btn:hover::before{transform:translateY(0)}}
/* Gold CTA — gradient body, glass highlight, soft gold cast shadow.
   ::before keeps the shared slide-up fill (brightened gold, not white);
   ::after carries the glass sheen plus a light sweep on hover. */
.btn--gold{
  color:#080808;
  border-color:transparent;
  background:linear-gradient(135deg,#e7c96b 0%,#d4af37 42%,#c9a227 72%,#e2c463 100%);
  box-shadow:
    0 10px 26px -10px rgba(212,175,55,.55),
    0 2px 6px rgba(0,0,0,.35),
    inset 0 1px 0 rgba(255,255,255,.55),
    inset 0 -1px 0 rgba(0,0,0,.16);
  transition:transform .45s var(--ease),box-shadow .45s var(--ease),color .45s;
}
.btn--gold::before{background:linear-gradient(135deg,#f2e0a0,#e7c96b 45%,#d4af37)}
.btn--gold::after{
  content:"";
  position:absolute;
  inset:0;
  z-index:1;
  border-radius:inherit;
  pointer-events:none;
  background:
    linear-gradient(180deg,rgba(255,255,255,.42),rgba(255,255,255,.05) 46%,transparent 68%),
    linear-gradient(105deg,transparent 32%,rgba(255,255,255,.5) 46%,transparent 60%);
  background-size:100% 100%,220% 100%;
  background-position:0 0,145% 0;
  transition:background-position .9s var(--ease);
}
@media (hover:hover){.btn--gold:hover{
  transform:translateY(-2px);
  box-shadow:
    0 18px 36px -12px rgba(212,175,55,.75),
    0 3px 10px rgba(0,0,0,.42),
    inset 0 1px 0 rgba(255,255,255,.65),
    inset 0 -1px 0 rgba(0,0,0,.18);
}}
@media (hover:hover){.btn--gold:hover::after{background-position:0 0,-45% 0}}
.btn--gold:active{transform:translateY(0)}
.btn--gold:focus-visible{outline:2px solid var(--gold-soft);outline-offset:3px}

/* ============================================================
   HERO
   ============================================================ */
.hero{position:relative;height:100svh;min-height:640px;display:flex;align-items:center;overflow:hidden}
.hero__canvas{position:absolute;inset:0;z-index:1}
.hero__canvas canvas{display:block;width:100%;height:100%}
.hero__grain{position:absolute;inset:0;z-index:2;opacity:.5;pointer-events:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.05'/%3E%3C/svg%3E")}
.hero__content{position:relative;z-index:3;width:100%}
.hero__eyebrow{display:flex;align-items:center;gap:.9rem;margin-bottom:1.6rem}
.hero__eyebrow::before{content:"";width:46px;height:1px;background:var(--gold)}
.hero__title{font-size:clamp(3.2rem,12vw,13rem);line-height:.9;letter-spacing:-.035em}
.hero__title em{color:var(--gold)}
.hero__sub{margin-top:2rem;display:flex;flex-wrap:wrap;gap:1.6rem 3rem;align-items:flex-end;justify-content:space-between}
.hero__scroll{position:absolute;bottom:2rem;left:50%;transform:translateX(-50%);z-index:3;display:flex;flex-direction:column;align-items:center;gap:.6rem;color:var(--grey);font-size:.68rem;letter-spacing:.24em;text-transform:uppercase}
.hero__scroll i{display:block;width:1px;height:44px;background:linear-gradient(var(--gold),transparent);animation:scrollLine 2.2s var(--ease) infinite}
@keyframes scrollLine{0%{transform:scaleY(0);transform-origin:top}45%{transform:scaleY(1);transform-origin:top}55%{transform:scaleY(1);transform-origin:bottom}100%{transform:scaleY(0);transform-origin:bottom}}

/* Marquee */
.marquee{border-block:1px solid var(--line);padding-block:1.4rem;overflow:hidden;white-space:nowrap;background:var(--bg-2)}
.marquee__track{display:inline-flex;gap:3rem;will-change:transform}
.marquee__track span{font-family:var(--serif);font-size:clamp(1.4rem,3vw,2.4rem);color:var(--grey);display:inline-flex;align-items:center;gap:3rem}
.marquee__track span::after{content:"✳";color:var(--gold);font-size:.8em}

/* ============================================================
   MANIFESTO
   ============================================================ */
.manifesto{text-align:left}
.manifesto__text{font-family:var(--serif);font-size:clamp(1.8rem,4.6vw,4.2rem);line-height:1.12;letter-spacing:-.02em;max-width:20ch}
.manifesto__text .dim{color:var(--grey-dim)}
.manifesto__text .g{color:var(--gold);font-style:italic}
.manifesto__meta{margin-top:3rem;display:flex;gap:3rem;flex-wrap:wrap}
.manifesto__stat .n{font-family:var(--serif);font-size:clamp(2.2rem,4vw,3.4rem);color:var(--white)}
.manifesto__stat .l{font-size:.8rem;color:var(--grey);letter-spacing:.04em}

/* ============================================================
   SERVICES (pillars)
   ============================================================ */
.services__head{display:flex;justify-content:space-between;align-items:flex-end;gap:2rem;flex-wrap:wrap;margin-bottom:3.5rem}
.services__title{font-size:clamp(2.4rem,6vw,5rem)}
.pillars{border-top:1px solid var(--line)}
.pillar{position:relative;display:grid;grid-template-columns:6rem 1fr auto;gap:2rem;align-items:center;padding:clamp(1.6rem,3.5vw,2.8rem) 0;border-bottom:1px solid var(--line);cursor:pointer;transition:padding-left .5s var(--ease)}
.pillar__no{font-family:var(--serif);font-size:1rem;color:var(--grey-dim);transition:color .4s}
.pillar__main{display:flex;flex-direction:column;gap:.5rem}
.pillar__title{font-size:clamp(1.8rem,4.4vw,3.2rem);line-height:1;transition:color .4s,transform .5s var(--ease)}
.pillar__lead{font-size:.95rem;color:var(--grey);max-width:42ch;opacity:.001;max-height:0;overflow:hidden;transition:opacity .5s,max-height .5s var(--ease),margin .5s}
.pillar__tags{display:flex;flex-wrap:wrap;gap:.5rem;max-width:340px;justify-content:flex-end}
.pillar__tag{font-size:.72rem;color:var(--grey);border:1px solid var(--line);border-radius:100px;padding:.35rem .8rem;transition:border-color .4s,color .4s}
.pillar__glow{position:absolute;left:0;top:50%;transform:translateY(-50%);width:0;height:70%;background:radial-gradient(ellipse at left,rgba(109,40,255,.16),transparent 70%);transition:width .6s var(--ease);pointer-events:none}
@media (hover:hover){.pillar:hover{padding-left:1.5rem}}
@media (hover:hover){.pillar:hover .pillar__no{color:var(--gold)}}
@media (hover:hover){.pillar:hover .pillar__title{color:var(--white)}}
@media (hover:hover){.pillar:hover .pillar__lead{opacity:1;max-height:6rem;margin-top:.4rem}}
@media (hover:hover){.pillar:hover .pillar__glow{width:60%}}
@media (hover:hover){.pillar:hover .pillar__tag{border-color:rgba(212,175,55,.4);color:var(--gold-soft)}}

/* ============================================================
   WORK
   ============================================================ */
.work__head{display:flex;justify-content:space-between;align-items:flex-end;gap:2rem;flex-wrap:wrap;margin-bottom:3rem}
.work__grid{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(1.5rem,3vw,3rem)}
.work__grid.is-single{grid-template-columns:1fr}
.card{position:relative;display:block;overflow:hidden;border-radius:14px;background:var(--bg-2);aspect-ratio:4/3;border:1px solid var(--line)}
.card:nth-child(odd){transform:translateY(0)}
.card__media{position:absolute;inset:0;transition:transform .9s var(--ease)}
.card__media::before{content:"";position:absolute;inset:0;z-index:2;background:linear-gradient(180deg,transparent 40%,rgba(7,7,7,.85))}
.card__ph{position:absolute;inset:0;background:linear-gradient(135deg,var(--accent,#6D28FF) 0%,#0c0c0c 65%);opacity:.9}
.card__ph::after{content:"";position:absolute;inset:0;background:radial-gradient(circle at 30% 25%,rgba(255,255,255,.14),transparent 55%)}
.card__index{position:absolute;top:1.4rem;left:1.6rem;z-index:3;font-family:var(--serif);font-size:.9rem;color:rgba(255,255,255,.6)}
.card__body{position:absolute;left:0;bottom:0;z-index:3;width:100%;padding:1.8rem;display:flex;justify-content:space-between;align-items:flex-end;gap:1rem}
.card__title{font-size:clamp(1.5rem,3vw,2.4rem);line-height:1}
.card__cat{font-size:.78rem;color:var(--grey);letter-spacing:.02em;margin-top:.4rem}
.card__year{font-size:.78rem;color:var(--grey)}
.card__arrow{position:absolute;top:1.4rem;right:1.6rem;z-index:3;width:44px;height:44px;border:1px solid rgba(247,247,247,.25);border-radius:50%;display:grid;place-items:center;opacity:0;transform:translate(-6px,6px) scale(.9);transition:opacity .5s var(--ease),transform .5s var(--ease),background .4s}
.card__arrow svg{width:16px;height:16px;stroke:var(--white)}
@media (hover:hover){.card:hover .card__media{transform:scale(1.06)}}
@media (hover:hover){.card:hover .card__arrow{opacity:1;transform:none;background:var(--gold)}}
@media (hover:hover){.card:hover .card__arrow svg{stroke:#080808}}

/* ============================================================
   ABOUT TEASER
   ============================================================ */
.about-t{display:grid;grid-template-columns:1.1fr .9fr;gap:clamp(2rem,6vw,7rem);align-items:center}
.about-t__title{font-size:clamp(2rem,5vw,4rem);line-height:1.05}
.about-t__title .g{color:var(--gold);font-style:italic}
.about-t__body{color:var(--grey);font-weight:300;font-size:1.05rem;max-width:44ch;display:flex;flex-direction:column;gap:1.2rem}
.about-t__visual{position:relative;aspect-ratio:3/4;border-radius:14px;overflow:hidden;border:1px solid var(--line);background:radial-gradient(circle at 50% 30%,rgba(109,40,255,.35),#0a0a0a 70%)}
.about-t__visual .ring{position:absolute;inset:0;margin:auto;width:60%;aspect-ratio:1;border:1px solid rgba(212,175,55,.4);border-radius:50%}
.about-t__visual .ring:nth-child(2){width:42%;border-color:rgba(139,92,255,.5)}
.about-t__visual .ring:nth-child(3){width:24%;border-color:rgba(212,175,55,.6)}
.about-t__visual .xo{position:absolute;inset:0;display:grid;place-items:center;font-family:var(--serif);font-size:clamp(3rem,8vw,6rem);color:var(--gold);font-style:italic}

/* ============================================================
   FOOTER
   ============================================================ */
.footer{position:relative;padding:clamp(4rem,10vh,8rem) var(--pad) 2.5rem;overflow:hidden;border-top:1px solid var(--line)}

/* Ambient dust — the same accent-mote recipe as the homepage story's
   orbiting artifacts (story.css .dot: white-hot core fading to purple or
   gold, twinkling), reused here in plain CSS so it works on every page,
   not just the homepage build story.css is scoped to. Positions/timing
   are randomised server-side per dot so the field never repeats a pattern. */
.footer__particles{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0}
.footer__particle{
  position:absolute;
  left:var(--x);
  top:var(--y);
  width:var(--s);
  height:var(--s);
  border-radius:50%;
  background:radial-gradient(circle,#fff 0%,#c9b3ff 40%,rgba(139,92,255,.9) 100%);
  box-shadow:0 0 6px 1px rgba(255,255,255,.35),0 0 14px 3px rgba(139,92,255,.5);
  opacity:0;
  animation:footerDust var(--dur) ease-in-out infinite;
  animation-delay:var(--delay);
}
.footer__particle.is-gold{
  background:radial-gradient(circle,#fff 0%,#ffeeb8 40%,rgba(212,175,55,.95) 100%);
  box-shadow:0 0 6px 1px rgba(255,255,255,.35),0 0 14px 3px rgba(212,175,55,.5);
}
@keyframes footerDust{
  0%,100%{opacity:0;transform:translateY(6px) scale(.7)}
  50%{opacity:.75;transform:translateY(-10px) scale(1)}
}
/* .footer__particles is positioned (z-index:0), so without an explicit
   z-index here these two would paint UNDER it — positioned descendants
   stack above non-positioned in-flow ones regardless of source order. */
.footer__grid,.footer__bottom{position:relative;z-index:1}

.footer__grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:2rem;padding-block:3.5rem;border-top:1px solid var(--line);margin-top:3rem}
.footer__col{display:flex;flex-direction:column;gap:.7rem}
.footer__col h3{font-family:var(--sans);font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;color:var(--grey-dim);margin-bottom:.5rem;font-weight:600}
/* :not(.social-row__link) so the Connect column keeps its brand colours. This
   rule is (0,1,1) and the social row's own colour is (0,1,0), so without the
   exclusion the footer repaints every mark grey and gold on hover. */
.footer__col a:not(.social-row__link){color:var(--grey);font-size:.92rem;transition:color .3s}
@media (hover:hover){.footer__col a:not(.social-row__link):hover{color:var(--gold)}}
.footer__desc{color:var(--grey);font-size:.92rem;font-weight:300;max-width:34ch}
.footer__bottom{display:flex;justify-content:space-between;flex-wrap:wrap;gap:1rem;padding-top:2rem;border-top:1px solid var(--line);color:var(--grey-dim);font-size:.8rem}
.footer__bottom em{color:var(--gold);font-style:italic}

/* ============================================================
   SOCIAL ROW
   The studio's own accounts as brand marks, rendered by
   social_row() in functions.php.

   Lives in main.css rather than beside any one of its three call
   sites, because it appears in all of them: scene 7 of the
   homepage story, the contact CTA band on the inner pages, and
   the aside on contact.php. One rule set, three modifiers for
   the alignment each context already has.

   The footer keeps listing the same accounts as words. That is
   the right shape for a column of links; this is the right shape
   next to a call to action, and they are not redundant so much
   as aimed at two different moments.
   ============================================================ */
/* The fallback lives on the row, not on the link: custom properties inherit,
   and the per-network modifiers below carry the same one-class specificity as
   .social-row__link itself. Declared on the link, the fallback would come
   later in the file and quietly win over every brand colour. */
.social-row{
  display:flex;flex-wrap:wrap;gap:.6rem;align-items:center;
  --brand:var(--gold); --brand-rgb:212,175,55;
}

/* Brand colours, on at rest — the same --brand / --brand-rgb pairing the
   article share buttons use in pages.css, for the same reason: CSS cannot pull
   an alpha out of a hex custom property, so the ring, the wash and the glow
   need the RGB form separately.

   Measured against the #070707 background before being used. The floor for a
   non-text graphic is 3:1 (WCAG 1.4.11). Facebook and LinkedIn keep the values
   already lifted for the share rail — their official blues read 4.76:1 and
   3.54:1, which survive behind a hover wash but go muddy sitting unlit on
   near-black. The rest are authentic.

   Instagram is flat rather than its gradient on purpose: the ring, the wash and
   the glow are all alpha variations of one hue, and a gradient has no single
   hue to vary. Every icon set solves it the same way. */
.social-row__link--instagram { --brand:#E1306C; --brand-rgb:225,48,108 }  /* 4.64:1 — official */
.social-row__link--facebook  { --brand:#4C9AFF; --brand-rgb:76,154,255 }  /* 7.07:1 — lifted from #1877F2 */
.social-row__link--linkedin  { --brand:#3A9BE8; --brand-rgb:58,155,232 }  /* 6.75:1 — lifted from #0A66C2 */
.social-row__link--behance   { --brand:#4C8DFF; --brand-rgb:76,141,255 }  /* 6.29:1 — lifted from #1769FF */
.social-row__link--dribbble  { --brand:#EA4C89; --brand-rgb:234,76,137 }  /* 5.65:1 — official */
.social-row__link--whatsapp  { --brand:#25D366; --brand-rgb:37,211,102 }  /* 10.16:1 — official */

.social-row__link{
  display:grid;place-items:center;
  /* 42px, not the 17px glyph — a mark this small needs a target
     around it that a thumb can actually hit. */
  width:42px;height:42px;
  border-radius:50%;
  color:var(--brand);
  border:1px solid rgba(var(--brand-rgb),.42);
  background:rgba(var(--brand-rgb),.07);
  /* The glow is the point: these sit in the footer's particle field and under
     a CTA, both busy. Outer bloom to lift the mark off the background, inner
     to keep the ring from reading as a flat hairline. Tight radii and negative
     spread so it stays a halo rather than a smear. */
  box-shadow:0 0 13px -4px rgba(var(--brand-rgb),.55),
             inset 0 0 10px -6px rgba(var(--brand-rgb),.85);
  transition:color .35s var(--ease),border-color .35s var(--ease),
             background .35s var(--ease),transform .35s var(--ease),
             box-shadow .35s var(--ease);
}
/* Colour already arrived at rest, so hover has to do something else: the same
   hue, further up and brighter. */
@media (hover:hover){.social-row__link:hover{
  border-color:rgba(var(--brand-rgb),.85);
  background:rgba(var(--brand-rgb),.18);
  transform:translateY(-2px);
  box-shadow:0 8px 26px -10px rgba(var(--brand-rgb),.95),
             inset 0 0 12px -5px rgba(var(--brand-rgb),.95);
}}
.social-row__link:focus-visible{
  border-color:rgba(var(--brand-rgb),.85);
  background:rgba(var(--brand-rgb),.18);
}
.social-row__icon{width:17px;height:17px;display:block}

/* Behance and Dribbble have no brand mark in gx_share_icon(), so
   social_row() falls back to the label. A pill, not an empty
   circle with a word overflowing it. */
.social-row__link.is-word{
  width:auto;padding:0 .95rem;border-radius:100px;
  font-size:.85rem;letter-spacing:.01em;
}

.social-row--scene{justify-content:center;margin-top:1.9rem}
.social-row--cta{margin-top:.5rem}
.social-row--aside{margin-top:1.15rem}
/* The footer column's other children are 24px text rows on a .7rem gap; the
   42px targets need a little less on top so the row optically aligns with
   "Home" in the Navigate column beside it. */
.social-row--footer{margin-top:.15rem;gap:.5rem}

@media (prefers-reduced-motion:reduce){
  @media (hover:hover){.social-row__link:hover{transform:none}}
}

/* ============================================================
   WHATSAPP CHAT BUTTON
   components/whatsapp.php, fixed bottom-right on every page.

   z-index 70: above the story layers (0–3) and the scene rail
   (60), below the nav (9000) and the mobile menu (8000) so it
   never floats over an open menu. Same right offset as .rail,
   which sits vertically centred, so the two share an edge
   without ever meeting.
   ============================================================ */
.wa{
  position:fixed;
  right:clamp(1rem,2.4vw,2.4rem);
  bottom:clamp(1rem,2.4vw,2.4rem);
  z-index:70;
  display:flex;align-items:center;gap:.6rem;
  /* The disc is the button; the row only exists to give the label
     somewhere to come from. The row is ~178px wide on desktop even
     while the label is invisible, and as a plain anchor that whole
     box swallowed clicks on the footer line beneath it. Events are
     handed to the disc alone — :hover still reaches .wa, because a
     hovered child hovers its ancestors. */
  pointer-events:none;
}
.wa__disc{pointer-events:auto}
.wa__disc{
  position:relative;
  display:grid;place-items:center;
  width:56px;height:56px;
  border-radius:50%;
  color:#25D366;
  border:1px solid rgba(37,211,102,.34);
  background:rgba(10,14,11,.82);
  /* backdrop-filter so it stays readable over the particle field
     and over a photograph on a portfolio card alike. */
  backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);
  /* Same halo recipe as .social-row__link, so the two read as one family:
     outer bloom, inner rim, then the drop shadow that lifts it off the page. */
  box-shadow:0 0 16px -4px rgba(37,211,102,.55),
             inset 0 0 12px -6px rgba(37,211,102,.85),
             0 10px 30px rgba(0,0,0,.45);
  transition:color .35s var(--ease),border-color .35s var(--ease),
             background .35s var(--ease),transform .35s var(--ease),
             box-shadow .35s var(--ease);
}
.wa__icon{width:25px;height:25px;display:block}

/* The ring that breathes. Behind the disc, so it never washes the
   mark out, and it is the first thing prefers-reduced-motion
   turns off below. */
.wa__pulse{
  position:absolute;inset:-1px;
  border-radius:50%;
  border:1px solid rgba(37,211,102,.5);
  animation:waPulse 2.8s var(--ease) infinite;
  pointer-events:none;
}
@keyframes waPulse{
  0%   {transform:scale(1);opacity:.65}
  70%  {transform:scale(1.42);opacity:0}
  100% {transform:scale(1.42);opacity:0}
}

/* The label is desktop-only chrome: it slides out of the disc on
   hover, so it needs a pointer that can hover to make sense, and
   the room to sit beside the disc without crowding the edge. */
.wa__label{
  display:none;
  pointer-events:none;
  font-size:.82rem;font-weight:600;letter-spacing:.01em;
  color:var(--white);
  padding:.6rem .95rem;
  border-radius:100px;
  border:1px solid var(--line);
  background:rgba(10,10,10,.86);
  backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);
  white-space:nowrap;
  opacity:0;transform:translateX(10px);
  transition:opacity .35s var(--ease),transform .35s var(--ease);
}
@media (hover:hover) and (min-width:721px){
  .wa__label{display:block}
  .wa:hover .wa__label{opacity:1;transform:none}
  .wa:hover .wa__disc{
    color:#0a0f0b;
    border-color:transparent;
    background:#25D366;
    transform:translateY(-3px);
    box-shadow:0 14px 34px rgba(37,211,102,.32);
  }
  /* The ring has done its job once the pointer is on the button. */
  .wa:hover .wa__pulse{animation-play-state:paused;opacity:0}
}

/* An open mobile menu is the one moment the button is in the way:
   it is a full-screen panel and this would sit under it, visible
   through nothing but still catching the eye at the edge. */
body.menu-open .wa{opacity:0;pointer-events:none;transition:opacity .3s}

@media (max-width:720px){
  .wa__disc{width:52px;height:52px}
  .wa__icon{width:23px;height:23px}
}

@media (prefers-reduced-motion:reduce){
  .wa__pulse{animation:none;opacity:.4}
  .wa__label{transition:none}
  @media (hover:hover){ .wa:hover .wa__disc{transform:none} }
}

/* ============================================================
   INNER PAGE HEADER (shared by About/Services/Work/Blog/Contact)
   ============================================================ */
.phead{position:relative;padding-top:clamp(9rem,18vh,13rem);padding-bottom:clamp(3rem,7vh,6rem);overflow:hidden}
.phead__glow{position:absolute;left:-10%;top:-10%;width:60vw;height:60vw;max-width:800px;max-height:800px;background:radial-gradient(circle,rgba(109,40,255,.18),transparent 60%);pointer-events:none}
.phead__eyebrow{display:flex;align-items:center;gap:.9rem;margin-bottom:1.4rem}
.phead__eyebrow::before{content:"";width:40px;height:1px;background:var(--gold)}
.phead__title{font-size:clamp(2.8rem,10vw,9rem);line-height:.92;letter-spacing:-.03em}
.phead__title em{color:var(--gold)}
.phead__desc{margin-top:1.8rem;max-width:52ch;color:var(--grey);font-size:clamp(1rem,1.5vw,1.25rem);font-weight:300}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:1024px){
  .nav__links,.nav__cta{display:none}
  .nav__toggle{display:flex}
  .about-t{grid-template-columns:1fr;gap:3rem}
  .footer__grid{grid-template-columns:1fr 1fr}
}
@media (max-width:720px){
  .work__grid{grid-template-columns:1fr}
  .pillar{grid-template-columns:3rem 1fr;gap:1rem}
  .pillar__tags{display:none}
  .pillar__lead{opacity:1;max-height:none;margin-top:.4rem}
  .footer__grid{grid-template-columns:1fr}
  .hero__sub{flex-direction:column;align-items:flex-start}
  .manifesto__text{max-width:none}
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001s!important;transition-duration:.001s!important}
  .reveal{opacity:1;transform:none}
  [data-split] .line>span{transform:none}
  .preloader{display:none}
  .sig__stroke,.sig__rule,.sig__dot{transition:none}
  .sig__dot{opacity:1}
  /* Without this the crushed-duration rule above turns the infinite
     twinkle into a strobe instead of stopping it — same fix story.css
     applies to .dot. */
  .footer__particle{animation:none;opacity:.3}
}

/* ---------- Brand logo (real logo integration) ---------- */
.nav__brand{display:inline-flex;align-items:center}
.nav__logo-full{height:56px;width:auto;display:block;transition:height .5s var(--ease)}
.footer__logo-img{width:200px;height:auto;max-width:100%;object-fit:contain;display:block;margin-bottom:.4rem}
.nav.scrolled .nav__logo-full{height:44px}
@media (max-width:1024px){
  .nav{padding-block:2.1rem}
  /* backdrop-filter repaints the full nav strip on every scroll frame —
     too expensive on mobile GPUs, so use a solid bar instead. */
  .nav.scrolled{padding-block:1.5rem;backdrop-filter:none;background:rgba(7,7,7,.94)}
  .nav__logo-full{height:60px}
  .nav.scrolled .nav__logo-full{height:50px}
}
@media (max-width:600px){
  .nav{padding-block:1.9rem}
  .nav.scrolled{padding-block:1.35rem}
  .nav__logo-full{height:52px}
  .nav.scrolled .nav__logo-full{height:44px}
}
/* A phone on its side. The bar is sized off the logo and 2.1rem of block
   padding, which is 127px — a third of a 390px-tall screen, standing over
   everything, and the menu overlay has to clear all of it before its first
   item. Height is the scarce dimension here, so the bar gives some back.
   .menu reads --menu-top for its own clearance, so the two stay in step. */
@media (max-height:520px){
  .nav{padding-block:1rem}
  .nav.scrolled{padding-block:.85rem}
  .nav__logo-full{height:40px}
  .nav.scrolled .nav__logo-full{height:36px}
  .menu{--menu-top:5.25rem}
}

/* ============================================================
   PHASE 3 — about mark, process, testimonials, contact CTA
   ============================================================ */
.about-t__mark{position:absolute;inset:0;margin:auto;width:52%;height:auto;filter:drop-shadow(0 20px 50px rgba(109,40,255,.35))}

/* Process */
/* overflow-x:clip is doing real work here, not tidying.
   .process is a .wrap, which runs the full width of the viewport (its gutter
   is padding, inside). So .process__glow's right:-10% below is not 10% of a
   content column that has room to spare — it is 10% of the viewport, pushed
   past the viewport's own right edge, at every width up to the point the wrap
   hits its 1600px cap and starts leaving a margin. Measured: 39px past on a
   390px phone, 144px on a 1440px desktop.

   That is a horizontal scroll on the about page, and on a phone it is worse
   than it sounds — the browser widens the layout viewport to take in the
   overflow, so every piece of type on the page renders about 10% smaller
   than it was designed to.

   Clipping rather than moving the glow, because the section IS the viewport's
   width: everything clipped here was off-screen anyway, so this costs nothing
   visually at any size. `clip` rather than `hidden` so it stays a non-scrolling
   clip and overflow-y is free to remain visible. */
.process{position:relative;overflow-x:clip}
.process__glow{position:absolute;right:-10%;top:20%;width:50vw;height:50vw;max-width:700px;max-height:700px;background:radial-gradient(circle,rgba(212,175,55,.10),transparent 60%);pointer-events:none}
.process__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(1.5rem,3vw,2.5rem);margin-top:3.5rem}
.process__step{position:relative;padding-top:2rem;border-top:1px solid var(--line)}
.process__no{font-family:var(--serif);font-size:1rem;color:var(--gold)}
.process__title{font-size:clamp(1.4rem,2.4vw,2rem);margin:1rem 0 .8rem}
.process__text{color:var(--grey);font-size:.95rem;font-weight:300;line-height:1.65}

/* Testimonials */
.testi__title{font-size:clamp(2rem,5vw,4rem);line-height:1.05;margin-top:1rem}
.testi__title em{color:var(--gold)}
.testi__grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(1.5rem,4vw,3.5rem);margin-top:2.5rem}
.testi{border:1px solid var(--line);border-radius:16px;padding:clamp(1.8rem,3vw,2.8rem);background:linear-gradient(160deg,rgba(17,17,17,.6),rgba(7,7,7,.2))}
.testi__quote{font-family:var(--serif);font-size:clamp(1.25rem,2.1vw,1.75rem);line-height:1.4;letter-spacing:-.01em;color:var(--white)}
.testi__by{display:flex;flex-direction:column;gap:.2rem;margin-top:1.8rem}
.testi__name{color:var(--gold);font-weight:600;font-size:.95rem}
.testi__role{color:var(--grey);font-size:.85rem}

/* Contact CTA */
.contact-cta__inner{display:grid;grid-template-columns:1.2fr .8fr;gap:clamp(2rem,5vw,5rem);align-items:center;padding:clamp(2.5rem,6vw,5rem);border:1px solid var(--line);border-radius:20px;position:relative;overflow:hidden;background:radial-gradient(ellipse at 20% 0%,rgba(109,40,255,.18),transparent 60%)}
.contact-cta__title{font-size:clamp(2rem,5vw,4rem);line-height:1.05;margin-top:1rem}
.contact-cta__title em{color:var(--gold)}
.contact-cta__right{display:flex;flex-direction:column;gap:.8rem;align-items:flex-start}
.contact-cta__mail{font-family:var(--serif);font-size:clamp(1.2rem,2.2vw,1.8rem);color:var(--white);border-bottom:1px solid var(--line);padding-bottom:.3rem;transition:color .3s,border-color .3s}
@media (hover:hover){.contact-cta__mail:hover{color:var(--gold);border-color:var(--gold)}}
.contact-cta__note{color:var(--grey);font-size:.9rem}

@media (max-width:900px){
  .process__grid{grid-template-columns:1fr 1fr}
  .testi__grid{grid-template-columns:1fr}
  .contact-cta__inner{grid-template-columns:1fr;gap:2rem}
}
@media (max-width:560px){ .process__grid{grid-template-columns:1fr} }

/* ============================================================
   LIGHTBOX
   Opt-in with data-zoom on anything: an <img>, or an element carrying the
   picture as a background (the Scene 6 cards). app.js builds the dialog once,
   on demand, so a page with nothing zoomable ships no extra markup.

   The whole point is UNCROPPED. Every place an image appears on this site it
   is cropped to fit a shape the layout wanted — a 3:4 card, a square tile, a
   page-section strip. object-fit:contain inside a viewport-sized stage is the
   one view that shows the file as it was made.

   z-index 9500 puts it over the nav (9000) and the menu (8000), and under the
   cursor (9998), the transition curtain (9990) and the preloader (9999) —
   three things that must never end up behind a picture.
   ============================================================ */
.lb{
  position:fixed;
  inset:0;
  z-index:9500;
  display:grid;
  place-items:center;
  padding:clamp(1rem,4vw,3.5rem);
  background:rgba(4,4,4,.93);
  backdrop-filter:blur(14px);
  opacity:0;
  visibility:hidden;
  transition:opacity .38s var(--ease),visibility .38s;
}
.lb.is-open{opacity:1;visibility:visible}
/* The backdrop is the close target, so it must not swallow the buttons and
   the picture sitting on it — those opt back in. */
.lb__stage{
  position:relative;
  display:flex;
  align-items:center;
  justify-content:center;
  max-width:100%;
  max-height:100%;
  margin:0;
  pointer-events:none;
}
.lb__img{
  display:block;
  max-width:min(100%,1500px);
  /* Leaves room for the count strip below, so a tall 9:16 card never pushes it
     off the bottom of the screen. */
  max-height:calc(100svh - clamp(6rem,13vh,8rem));
  width:auto;
  height:auto;
  object-fit:contain;
  border-radius:6px;
  background:#0b0b0b;
  box-shadow:0 40px 120px -30px rgba(0,0,0,.9);
  opacity:0;
  transform:scale(.965);
  transition:opacity .42s var(--ease),transform .42s var(--ease);
}
.lb.is-open .lb__img{opacity:1;transform:none}
/* Swapping to a neighbour: fade the old frame out rather than letting the
   new file pop in line by line as it decodes. */
.lb.is-swapping .lb__img{opacity:0;transform:scale(.99);transition-duration:.16s}
.lb__close,.lb__step{
  position:absolute;
  display:grid;place-items:center;
  width:48px;height:48px;
  border:1px solid var(--line);
  border-radius:100px;
  color:var(--grey);
  background:rgba(7,7,7,.55);
  backdrop-filter:blur(6px);
  cursor:pointer;
  pointer-events:auto;
  transition:color .35s,border-color .35s,background .35s,opacity .35s;
}
.lb__close{top:clamp(1rem,3vw,2rem);right:clamp(1rem,3vw,2rem)}
.lb__step--prev{left:clamp(1rem,3vw,2rem);top:50%;translate:0 -50%}
.lb__step--next{right:clamp(1rem,3vw,2rem);top:50%;translate:0 -50%}
.lb__close svg,.lb__step svg{width:18px;height:18px}
@media (hover:hover){
  .lb__close:hover,.lb__step:hover{color:var(--white);border-color:var(--gold);background:rgba(30,22,6,.8)}
}
.lb__close:focus-visible,.lb__step:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
/* One image in the group: the arrows would only ever return you to where you
   already are. */
.lb--single .lb__step{display:none}

.lb__count{
  position:absolute;
  bottom:clamp(1rem,3vw,2rem);
  left:50%;
  translate:-50% 0;
  display:flex;align-items:baseline;gap:.4rem;
  font-family:var(--serif);font-size:.9rem;color:var(--grey-dim);
}
.lb__count em{font-style:normal;color:var(--gold)}
.lb__count span{opacity:.5}
.lb--single .lb__count{display:none}

/* Anything that opens a lightbox says so on the way in. */
[data-zoom]{cursor:zoom-in}

@media (max-width:700px){
  /* Thumb-reachable, and off the picture: at this width side arrows sit on
     top of the image rather than beside it. */
  .lb__step{top:auto;bottom:clamp(1rem,3vw,2rem);translate:none}
  .lb__step--prev{left:clamp(1rem,3vw,2rem)}
  .lb__step--next{right:clamp(1rem,3vw,2rem)}
  .lb__img{max-height:calc(100svh - clamp(9rem,21vh,11.5rem))}
}
@media (prefers-reduced-motion:reduce){
  .lb,.lb__img{transition-duration:.01ms}
  .lb__img{transform:none}
}
