/* ============================================================
   MERIDIAN WEB DESIGN
   ------------------------------------------------------------
   Dark only. The page is ONE surface: a single continuous
   background runs from the top of the hero to the last line of
   the footer. No section paints its own fill, no section draws a
   rule across the page, so nothing reads as a seam.

   Black and white, nothing else. The ground is a near-black neutral,
   the text is white, and white does every job that needs emphasis:
   the button, the link, the live section, the mark under a headline.
   A silver hairline, the meridian line, carries eyebrows, numbers
   and dividers. The only colour on the page is the work itself: the
   clients' sites in the portfolio.

   Depth comes from four fixed layers stacked behind the content:

     .aura    one soft light source, drifting with scroll
     .floor   a perspective grid under the hero
     #grain   a static film grain, kills gradient banding
     #field   the studio's mark as a live 3D point cloud

   Motion lives in three places: the [data-anim] reveal system at the
   bottom of this file (every box flies into place in 3D and replays
   each time it comes back into view); the field, which turns with the
   pointer and spins with the scroll; and the [data-tilt] cards, which
   turn toward the pointer and carry a sheen where it is. The hero's
   tiles hold still: they only arrive.
   ============================================================ */

/* ---------------- tokens ---------------- */
:root{
  --bg:        #080808;
  --bg-2:      #0f0f0f;
  --rule:      #2a2a2a;
  --rule-soft: #1c1c1c;
  --ink:       #f5f5f4;      /* white, a hair warm so it never goes blue on a cold screen */
  --ink-2:     #c4c4c2;
  --muted:     #8e8e8c;

  /* action colour: clickable things and the mark under a headline */
  --accent:     #ffffff;
  --accent-ink: #0a0a0a;

  /* hairlines: --hair rules between blocks (the meridian line), --rule
     and --rule-soft edge the furniture, --rule-ui edges what you press,
     so a ghost button or a chip has a visible rim at rest */
  --hair:       rgba(255,255,255,.12);
  --rule-ui:    rgba(255,255,255,.38);

  /* THE LIGHT. One light falls from above, so every surface's rim is
     brightest along its top edge and fades down its sides: --lit-top at
     the top, --lit-mid a third of the way down, --lit-foot at the foot.
     The pointer is a second, moving light (--lit-ptr at its centre,
     within --lit-r of it). These are edges catching light, never a glow:
     a blurred white on this ground reads as grey fog. */
  --lit-top:  .34;
  --lit-mid:  .10;
  --lit-foot: .06;
  --lit-ptr:  .68;
  --lit-r:    160px;

  --t-display: clamp(2.7rem, 6.2vw, 5.4rem);
  --t-2xl:     clamp(2rem, 4.2vw, 3.3rem);
  --t-xl:      clamp(1.4rem, 2.3vw, 1.9rem);
  --t-lg:      1.25rem;
  --t-md:      1.0625rem;
  --t-sm:      0.9375rem;
  --t-xs:      0.8125rem;
  --t-2xs:     0.72rem;

  --serif: "Playfair Display", Georgia, "Times New Roman", serif;
  --sans:  "Outfit", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --mono:  "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;

  --s-1:.25rem; --s-2:.5rem; --s-3:.75rem; --s-4:1rem;
  --s-5:1.5rem; --s-6:2rem;  --s-7:3rem;   --s-8:4.5rem; --s-9:7rem;

  --r-sm:6px; --r:14px; --r-pill:999px;
  --shadow: 0 24px 60px rgba(0,0,0,.55);

  --ease: cubic-bezier(.16,1,.3,1);
  --ease-back: cubic-bezier(.22,1.15,.36,1);
  --d-in: 760ms;
  --d-3d: 850ms;
  --d-ui: 160ms;

  --pad: clamp(1.25rem, 5vw, 5rem);
  --wrap: 1240px;
  --nav-h: 62px;          /* the bar itself */
  --nav-gap: 0px;         /* the bar meets the top edge */

}
/* Every colour above is a neutral, which sRGB hex states exactly, so
   the file carries no oklch() block: an older phone browser and a new
   one get the same page. */

/* On a phone the small sizes step up one notch: 15px body copy and
   11.5px labels read fine on a laptop and too small in the hand. */
@media (max-width:48rem){:root{--t-sm:1rem;--t-xs:.875rem;--t-2xs:.75rem}}
/* Written by landing.js on the layer that uses them, never on :root: a
   custom property on the root invalidates the style of every element on
   the page each time it changes, which at sixty writes a second was most
   of the style cost of a scroll. */
.aura{--scrollp:0}
.floor{--floor-a:1;--fx:0;--px:50vw;--py:40vh}
html:not(.js) .floor{display:none}   /* the grid belongs to a swirl hero, which needs the script */

*,*::before,*::after{box-sizing:border-box}
html{overflow-x:hidden;overflow-x:clip;-webkit-text-size-adjust:100%}
body{
  overflow-x:hidden;overflow-x:clip;margin:0;color:var(--ink);
  font-family:var(--sans);font-size:var(--t-md);line-height:1.6;
  font-synthesis-weight:none;-webkit-font-smoothing:antialiased;
  /* the one background of the whole page */
  background:var(--bg);
}
img{display:block;max-width:100%}
a,button,summary{-webkit-tap-highlight-color:rgba(255,255,255,.14)}
.btn,.menu-btn,summary,.nav a{touch-action:manipulation}
a{color:inherit;text-decoration:none}
p{margin:0 0 1em}
::selection{background:var(--accent);color:var(--accent-ink)}
:where(a,button,summary,[tabindex]):focus-visible{
  outline:2px solid var(--accent);outline-offset:3px;border-radius:var(--r-sm)}
.skip{position:absolute;left:-9999px;top:8px;background:var(--ink);color:var(--bg);
  padding:var(--s-2) var(--s-4);border-radius:var(--r-sm);z-index:120}
.skip:focus{left:8px}
.wrap{width:min(var(--wrap),100% - var(--pad)*2);margin-inline:auto}

h1,h2,h3{font-family:var(--serif);font-weight:500;font-style:normal;margin:0;
  line-height:1.06;overflow-wrap:anywhere;min-width:0;text-wrap:balance}
h1{font-size:var(--t-display);letter-spacing:-.012em;line-height:1.04}
h2{font-size:var(--t-2xl);letter-spacing:-.01em;line-height:1.08}
h3{font-size:var(--t-xl);letter-spacing:0;line-height:1.16}
.lede{font-size:var(--t-lg);line-height:1.5;color:var(--ink-2);max-width:34ch;margin:0}
.body{max-width:66ch}
.muted{color:var(--muted)}

.mark{position:relative;white-space:nowrap}
/* the line under a headline's words is lit in the middle, the way a
   polished edge catches the one light where it faces it squarely */
.mark::after{content:"";position:absolute;left:0;right:0;bottom:-.04em;
  height:clamp(1.5px,.03em,3px);border-radius:2px;
  background:linear-gradient(90deg,rgba(255,255,255,.55),#fff 50%,rgba(255,255,255,.55))}

/* ---------------- headlines in metal ----------------
   The display headlines are white at the top of each line and shade to
   a cool silver at its foot, as a polished letter would under a light
   from above. One gradient per line (background-size is the line
   height, repeated down the block), so a second line is lit like the
   first rather than sitting in the shadow of it. The white holds to
   the middle of the line and the silver is reached by the baseline, so
   a descender running into the line below stays silver. Inside
   @supports, so an engine that cannot clip a background to text keeps
   plain white letters; nothing here may carry a text-shadow, which
   would show through the transparent glyphs. */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .hero-h,.page-hero h1,.sec-head h2,.close-grid h2{
    --lh:1.08em;
    color:transparent;
    background-image:linear-gradient(180deg,#fff 0%,#fff 46%,#d2d2d2 86%,#d2d2d2 100%);
    background-size:100% var(--lh);background-repeat:repeat-y;
    -webkit-background-clip:text;background-clip:text}
  .hero-h{--lh:1.03em}
  .page-hero h1{--lh:1.04em}
  /* the clip to text ends at the box, and a last line's descenders hang
     below it: room under the box for them, taken back by the margin so
     nothing moves */
  .hero-h,.page-hero h1,.sec-head h2,.close-grid h2{padding-bottom:.26em;margin-bottom:-.26em}
  .page-hero h1{margin-bottom:calc(var(--s-4) - .26em)}
  /* The marked words are a positioned span (for their underline), and
     a positioned box paints apart from its parent, outside the parent's
     clip to text: on its own they would vanish. So they carry the metal
     themselves. An inline's background spans its font's content area
     (Playfair's ascent to descent, 1.333em) rather than the line, so
     the stops are moved to land where the line's do. */
  :is(.hero-h,.page-hero h1,.sec-head h2,.close-grid h2) .mark{
    color:transparent;
    background-image:linear-gradient(180deg,#fff 0%,#fff 46.9%,#d2d2d2 79.1%,#d2d2d2 100%);
    background-size:100% 100%;background-repeat:no-repeat;
    -webkit-box-decoration-break:clone;box-decoration-break:clone;
    -webkit-background-clip:text;background-clip:text}
}

/* ---------------- the technical voice ----------------
   Eyebrows, numbers and labels are set in the mono, uppercase and
   letterspaced. It is the one gesture both references share. */
.eyebrow{display:flex;align-items:center;gap:var(--s-3);
  font-family:var(--mono);font-size:var(--t-2xs);font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
  margin:0 0 var(--s-4)}
.eyebrow .dot{width:6px;height:6px;border-radius:50%;background:var(--accent);flex:none}

/* ============================================================
   THE CONTINUOUS BACKGROUND
   Fixed layers. Because they are fixed they cannot seam: there is
   no edge to line up as the content scrolls over them. The light
   moves with --scrollp instead, so the page still travels through
   a changing light rather than sitting on a flat wall.
   ============================================================ */
.aura{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  background:
    /* a faint answer low left, so the ground is not one flat tone */
    radial-gradient(80% 60% at 10% 92%, rgba(255,255,255,.035) 0%, transparent 60%),
    linear-gradient(180deg, #0b0b0b 0%, #070707 50%, #090909 100%)}
/* The one light source, high and to the right, sliding down the page as
   you scroll. It is its own layer moved by transform, so the slide costs
   the compositor a translate rather than the painter a full-screen
   gradient every frame. */
.aura::after{content:"";position:absolute;left:28%;top:-40%;width:110%;height:120%;
  background:radial-gradient(50% 50% at 50% 50%, rgba(255,255,255,.085) 0%, transparent 70%);
  transform:translate3d(0, calc(var(--scrollp) * 40vh), 0);will-change:transform}

/* The floor. A fine grid on a plane tilted away from the camera, with
   its far edge at a horizon just above the middle of the screen. Its
   lines fade at the horizon and at both sides so it never reads as a
   box. A soft pool of light follows the pointer across it. */
.floor{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  perspective:820px;perspective-origin:50% 42%;
  opacity:var(--floor-a);transition:opacity 300ms linear}
.floor::before{content:"";position:absolute;left:-70%;right:-70%;top:50%;height:160%;
  background-image:
    repeating-linear-gradient(90deg, rgba(255,255,255,.085) 0 1px, transparent 1px 96px),
    repeating-linear-gradient(0deg,  rgba(255,255,255,.085) 0 1px, transparent 1px 96px);
  transform-origin:50% 0;
  transform:rotateX(74deg) translate3d(calc(var(--fx,0) * 1px), 0, 0);
  -webkit-mask-image:
    linear-gradient(180deg, transparent 0%, #000 22%, #000 68%, transparent 100%),
    linear-gradient(90deg, transparent 12%, #000 38%, #000 62%, transparent 88%);
  -webkit-mask-composite:source-in;
  mask-image:
    linear-gradient(180deg, transparent 0%, #000 22%, #000 68%, transparent 100%),
    linear-gradient(90deg, transparent 12%, #000 38%, #000 62%, transparent 88%);
  mask-composite:intersect}
.floor::after{content:"";position:absolute;left:-420px;top:-420px;width:840px;height:840px;
  border-radius:50%;
  background:radial-gradient(circle at 50% 50%, rgba(255,255,255,.055) 0%, transparent 60%);
  transform:translate3d(var(--px), var(--py), 0);will-change:transform}

/* Grain is the difference between a gradient and a surface, and on a
   neutral ground banding shows more than it ever did in colour. One
   small tile of noise, drawn once by the browser and repeated. */
#grain{position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.06;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:180px 180px}

#field{position:fixed;inset:0;z-index:0;pointer-events:none;
  opacity:0;transition:opacity 900ms var(--ease)}
#field.ready{opacity:1}

/* Everything the reader reads sits above all of them. */
#smooth,.nav,.foot{position:relative;z-index:1}

/* ---------------- the cursor ring ----------------
   A companion to the native cursor, not a replacement: it follows a
   beat behind and opens over anything you can press. Only shown once
   the script has confirmed a fine pointer (html.ptr). */
.cursor{position:fixed;left:0;top:0;z-index:200;pointer-events:none;
  width:26px;height:26px;margin:-13px 0 0 -13px;border-radius:50%;
  border:1.5px solid var(--accent);opacity:0;mix-blend-mode:screen;
  transition:width 260ms var(--ease),height 260ms var(--ease),margin 260ms var(--ease),
    opacity 300ms var(--ease),border-color 260ms var(--ease),background 260ms var(--ease)}
html.ptr.ptr-seen .cursor{opacity:.85}
html.ptr.hov-link .cursor{width:46px;height:46px;margin:-23px 0 0 -23px;
  border-color:var(--accent);background:rgba(255,255,255,.10)}

/* ---------------- buttons ---------------- */
.btn{position:relative;overflow:hidden;isolation:isolate;
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  font-family:var(--sans);font-size:var(--t-sm);font-weight:600;
  padding:var(--s-3) var(--s-5);min-height:48px;border-radius:var(--r-pill);
  border:1px solid transparent;cursor:pointer;white-space:nowrap;
  transition:background var(--d-ui) var(--ease),color var(--d-ui) var(--ease),
    border-color var(--d-ui) var(--ease),transform var(--d-ui) var(--ease),
    box-shadow var(--d-ui) var(--ease)}
.btn svg{width:18px;height:18px;fill:currentColor;flex:none}
.btn .arrow{fill:none;width:16px;height:16px;transition:transform 220ms var(--ease)}
@media (hover:hover){.btn:hover .arrow{transform:translateX(4px)}}
/* the sheen: a pool of light where the pointer is, written as --mx/--my */
.btn::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;opacity:0;
  background:radial-gradient(120px circle at var(--mx,50%) var(--my,50%),
    rgba(255,255,255,.22), transparent 70%);
  transition:opacity 260ms var(--ease)}
.btn.hov::after{opacity:1}
/* The white button is a lit object, not a flat chip: its fill falls off
   a shade from top to foot, its top edge is a crisp specular line and
   its foot a hair darker, so it reads as something that stands up off
   the page under the same light as the rims. */
.btn-go{background:linear-gradient(180deg,#fff 0%,#fff 40%,#f0f0f0 100%);color:var(--accent-ink);
  box-shadow:inset 0 1px 0 #fff,inset 0 -1px 0 rgba(0,0,0,.06)}
@media (min-width:48rem){.btn-go{background:linear-gradient(180deg,#fff 0%,#f7f7f7 35%,#e9e9e9 100%)}}
/* on the white button the pool is a shade, not a light */
.btn-go::after{background:radial-gradient(120px circle at var(--mx,50%) var(--my,50%),
    rgba(0,0,0,.09), transparent 70%)}
/* lifted by a shadow, not lit by a glow: white light on black makes fog */
@media (hover:hover){.btn-go:hover{transform:translateY(-1px);
  box-shadow:inset 0 1px 0 #fff,inset 0 -1px 0 rgba(0,0,0,.06),0 12px 32px rgba(0,0,0,.55)}}
.btn-go:active{transform:translateY(0)}
/* The sweep: a narrow diagonal band of light that crosses the white
   button once, on hover, and once on load for the hero's button. White
   on white is all but invisible, so the band carries a faint shade on
   its leading and trailing sides; that edge is what the eye reads as a
   glint passing over glass. It sits under the label, like the pool. */
.btn-go::before{content:"";position:absolute;top:-2px;bottom:-2px;left:0;width:60%;z-index:-1;
  pointer-events:none;transform:translate3d(-110%,0,0);
  background:linear-gradient(105deg,transparent 0 22%,rgba(0,0,0,.035) 38%,#fff 49%,#fff 51%,
    rgba(0,0,0,.035) 62%,transparent 78% 100%);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 20% 80%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 20% 80%,transparent)}
@keyframes sweep{from{transform:translate3d(-110%,0,0)}to{transform:translate3d(260%,0,0)}}
@keyframes sweep-in{from{transform:translate3d(-110%,0,0)}to{transform:translate3d(260%,0,0)}}
@media (prefers-reduced-motion:no-preference){
  /* the hero's button, once its tile has arrived; landing.js marks it
     .swept when that pass ends, so a tile that replays its arrival
     never replays the sweep */
  .tile-a.in .btn-go:not(.swept)::before{animation:sweep-in 700ms cubic-bezier(.45,0,.25,1) 900ms 1 both}
  @media (hover:hover){.btn-go:hover::before{animation:sweep 700ms cubic-bezier(.45,0,.25,1) 1 both}}
}
.btn-line{border-color:var(--rule-ui);color:var(--ink);
  background:rgba(14,14,14,.45);
  /* the top of its rim catches the light: a crescent inside the rim,
     widest at the top and gone by the sides, which a pill's curve gives
     for free */
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2)}
@media (hover:hover){.btn-line:hover{border-color:rgba(255,255,255,.75);color:var(--accent);transform:translateY(-1px)}}
.btn-line:active{transform:translateY(0)}
.btn-line::after{background:radial-gradient(140px circle at var(--mx,50%) var(--my,50%),
    rgba(255,255,255,.07), transparent 70%)}
.btn[disabled]{opacity:.45;pointer-events:none}
/* a link is white like a headline, so it carries a faint underline at
   rest, which fills to a solid one under the pointer */
.link{color:var(--accent);position:relative;padding-bottom:2px;font-weight:500;font-size:var(--t-sm);
  display:inline-block;padding-block:var(--s-2);box-shadow:inset 0 -1px 0 rgba(255,255,255,.28)}
.link::after{content:"";position:absolute;left:0;bottom:0;height:1px;width:100%;
  background:currentColor;transform:scaleX(0);transform-origin:left;
  transition:transform var(--d-ui) var(--ease)}
.link:focus-visible::after{transform:scaleX(1)}
@media (hover:hover){.link:hover::after{transform:scaleX(1)}}

/* ---------------- nav ---------------- */
/* A floating bar rather than a band across the top: it sits centred on
   the same measure as the page content with its own rounded shell. The
   full-width <header> keeps the bar fixed but is itself transparent and
   click-through, so only the bar catches clicks.

   A pill, sitting against the top edge with no gap beneath it. The
   header itself fades the top few pixels of the screen into the ground,
   so nothing scrolling under the pill's two top corners shows through
   the arcs. Its rim is a touch brighter along the top, where the page's
   one light falls. Once the page has moved under it the bar firms up
   and drops a few pixels in height, so it takes less of the screen. */
.nav{position:fixed;top:0;left:0;right:0;z-index:100;
  padding:var(--nav-gap) var(--pad) 0;pointer-events:none}
.nav::before{content:"";position:absolute;left:0;right:0;top:0;height:calc(var(--nav-h) * .55);
  background:linear-gradient(180deg,rgba(8,8,8,.96),rgba(8,8,8,0));
  pointer-events:none}
.nav-in{pointer-events:auto;position:relative;
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);
  height:var(--nav-h);padding:0 var(--s-3) 0 var(--s-5);
  width:min(var(--wrap),100%);margin-inline:auto;
  border-radius:var(--r-pill);
  background:linear-gradient(180deg,rgba(20,20,20,.62),rgba(10,10,10,.50));
  border:1px solid rgba(255,255,255,.07);
  border-bottom-color:rgba(255,255,255,.10);
  -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
  box-shadow:0 8px 30px rgba(0,0,0,.30),
             inset 0 1px 0 rgba(255,255,255,.07);
  transition:background 320ms var(--ease),border-color 320ms var(--ease),
    box-shadow 320ms var(--ease),height 320ms var(--ease)}
.nav.stuck .nav-in{height:calc(var(--nav-h) - 6px);
  background:linear-gradient(180deg,rgba(14,14,14,.90),rgba(8,8,8,.86));
  border-color:rgba(255,255,255,.10);border-bottom-color:rgba(255,255,255,.14);
  box-shadow:0 14px 44px rgba(0,0,0,.48),
             inset 0 1px 0 rgba(255,255,255,.08)}
.brand{display:inline-flex;align-items:center;gap:var(--s-3);white-space:nowrap;
  font-family:var(--serif);font-size:1.15rem;line-height:1;letter-spacing:.005em;color:var(--ink)}
.brand img{width:30px;height:30px;flex:none}   /* crisp: the silver mark needs no halo */
.nav .btn{min-height:40px;padding:var(--s-2) var(--s-4)}
.nav .btn-go{box-shadow:inset 0 1px 0 #fff,inset 0 -1px 0 rgba(0,0,0,.10),
  0 0 0 1px rgba(255,255,255,.08),0 6px 18px rgba(0,0,0,.45)}
.nav-end{display:flex;align-items:center;gap:var(--s-2)}
.brand{padding-block:var(--s-2)}   /* a full tap target inside the bar */

/* the menu button: two lines that become a cross */
.menu-btn{display:none;position:relative;width:44px;height:44px;border-radius:50%;
  border:1px solid var(--rule-ui);background:rgba(14,14,14,.35);cursor:pointer;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2);   /* the lit top of its rim, as on every pill */
  padding:0;flex:none;transition:border-color var(--d-ui) var(--ease)}
.menu-btn span{position:absolute;left:13px;right:13px;height:1.5px;background:var(--ink);
  border-radius:2px;transition:transform 260ms var(--ease),top 260ms var(--ease)}
.menu-btn span:first-child{top:18px}
.menu-btn span:last-child{top:25px}
@media (hover:hover){.menu-btn:hover{border-color:rgba(255,255,255,.75)}}
html.menu-open .menu-btn span:first-child{top:21px;transform:rotate(45deg)}
html.menu-open .menu-btn span:last-child{top:21px;transform:rotate(-45deg)}
@media (max-width:63.99rem){.menu-btn{display:block}}

/* the sheet: the whole screen under the bar, blurred over the page */
.menu{position:fixed;inset:0;z-index:90;padding:calc(var(--nav-h) + var(--s-6)) var(--pad) var(--s-7);
  background:rgba(6,6,6,.86);-webkit-backdrop-filter:blur(22px);backdrop-filter:blur(22px);
  overflow:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  opacity:0;transition:opacity 280ms var(--ease)}
.menu[hidden]{display:none}
html:not(.menu-open) .menu{pointer-events:none}
html.menu-open .menu{opacity:1}
.menu-in{display:grid;gap:var(--s-2);width:min(var(--wrap),100%);margin-inline:auto}
.menu-in a{display:flex;align-items:baseline;gap:var(--s-4);
  font-family:var(--serif);font-size:clamp(1.9rem,7vw,2.6rem);line-height:1.15;color:var(--ink);
  padding:var(--s-3) 0;border-bottom:1px solid var(--hair);
  opacity:0;transform:translate3d(0,14px,0);
  transition:opacity 420ms var(--ease) var(--delay,0ms),transform 420ms var(--ease-back) var(--delay,0ms)}
html.menu-open .menu-in a{opacity:1;transform:none}
.menu-in a:nth-child(1){--delay:40ms} .menu-in a:nth-child(2){--delay:90ms}
.menu-in a:nth-child(3){--delay:140ms} .menu-in a:nth-child(4){--delay:190ms}
.menu-in a:nth-child(5){--delay:240ms} .menu-in a:nth-child(6){--delay:300ms}
.menu-in .menu-wa,.menu-in .menu-mail{margin-top:var(--s-5);border:0;font-family:var(--sans);font-weight:600;
  font-size:var(--t-lg);color:var(--accent);flex-wrap:wrap}
.menu-in .menu-mail{margin-top:0;--delay:340ms}
.menu-in .menu-wa span,.menu-in .menu-mail span{font-family:var(--mono);font-weight:400;font-size:var(--t-sm);
  color:var(--muted);overflow-wrap:anywhere}
html.menu-open{overflow:hidden}
html.menu-open .nav-in{background:rgba(10,10,10,.9)}
@media (max-width:36rem){
  /* measured at 320 to 575px: the wordmark cannot shrink and the two round
     buttons cannot either, so the only give is the gap and the padding.
     Below about 350px the wordmark trims to an ellipsis rather than
     pushing the menu button out of the pill. */
  .nav-in{padding:0 var(--s-2) 0 var(--s-4);gap:var(--s-2)}
  .brand{font-size:.95rem;gap:var(--s-2);min-width:0;padding-block:var(--s-3)}
  .brand span{min-width:0;overflow:hidden;text-overflow:ellipsis;line-height:1.3}
  .brand img{width:24px;height:24px}
  .nav .btn-label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .nav .btn{width:44px;min-width:44px;height:44px;padding:0;gap:0;border-radius:50%}
  .nav .btn svg{width:20px;height:20px}
  /* the FAQ question gets the column back: smaller side padding and marker gap */
  .faq summary{padding:var(--s-4);font-size:1.125rem;gap:var(--s-3)}
  .faq details p{padding:0 var(--s-4) var(--s-4)}
}
.nav-links{display:none;gap:var(--s-6);
  font-family:var(--mono);font-size:var(--t-2xs);letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted)}
.nav-links a{position:relative;padding:var(--s-2) 0;transition:color var(--d-ui) var(--ease)}
.nav-links a::after{content:"";position:absolute;left:0;right:0;bottom:2px;height:1px;
  background:var(--accent);transform:scaleX(0);transform-origin:left;
  transition:transform 240ms var(--ease),opacity var(--d-ui) var(--ease)}
/* under the pointer a link gets a ghost of the line; the section you
   are reading keeps the solid one, so the two never read the same */
@media (hover:hover){.nav-links a:hover{color:var(--ink)}}
@media (hover:hover){.nav-links a:hover:not(.on)::after{transform:scaleX(1);opacity:.4}}
.nav-links a.on{color:var(--accent)}
.nav-links a.on::after{transform:scaleX(1);opacity:1}
@media (min-width:64rem){.nav-links{display:flex}}

/* ============================================================
   HERO
   One block of four tiles under the bar: who we are and how to start
   (A), the work (B), what we offer (C) and the short version of why
   it is easy (D). Read in that order on a phone, one column, A then
   C then B then D, which is also the source order.

   On a laptop the four fill the first screen and interlock along ONE
   straight diagonal, from the top of the block to its foot: A and C
   stand to its left, B and D to its right, and the level seam on the
   right runs lower than the one on the left, so the tiles lock together
   rather than sitting in a slanted grid. The shapes, and the rows'
   heights, come from landing.js (.is-cut, see THE BENTO there), which
   measures what A, C and D hold; until it has, and without it, every
   tile is the plain rounded card below. The seam's position and lean
   are the two numbers on .bento, --seam and --slant.

   The gap is one number everywhere: between tiles, across the seam
   (measured square to it) and between the bar and the block, so the
   bar reads as one more piece of the same set.
   ============================================================ */
.hero{--g:clamp(.625rem,.35rem + .55vw,.875rem);
  --tp:clamp(1.25rem,.8rem + 1.6vw,2.25rem);        /* inside a text tile */
  --bp:clamp(.5rem,.3rem + .4vw,.6875rem);          /* inside the work tile: a passe-partout */
  --ri:max(4px,calc(var(--r) - var(--bp)));         /* concentric with the tile's corner */
  --t-hero:clamp(2.125rem,1.25rem + 3.6vw,3.25rem);
  position:relative;z-index:1;
  padding:calc(var(--nav-h) + var(--nav-gap) + var(--g)) var(--pad) var(--s-6)}

.bento{position:relative;width:min(var(--wrap),100%);margin-inline:auto;
  display:grid;gap:var(--g);grid-template-columns:minmax(0,1fr);
  grid-template-areas:"a" "c" "b" "d"}
.tile-a{grid-area:a}.tile-b{grid-area:b}.tile-c{grid-area:c}.tile-d{grid-area:d}

/* the card every tile is, until it is cut */
.tile{position:relative;isolation:isolate;min-width:0;
  background:rgba(12,12,12,.92);border:1px solid var(--hair);border-radius:var(--r)}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}

/* ---- A: the headline, the mark and the two ways in ---- */
.tile-a{display:flex;flex-direction:column;padding:var(--tp)}
.hero-eyebrow{margin:0 0 var(--s-4)}
.hero-eyebrow .nowrap{white-space:nowrap}
.hero-h{font-size:var(--t-hero);line-height:1.03;letter-spacing:-.015em;max-width:15ch;margin:0}
.a-foot{margin-top:auto;padding-top:var(--s-5);
  display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start;
  column-gap:var(--s-4);row-gap:var(--s-5)}
/* the studio's mark, still: the field is the moving version of it */
.thumb{width:clamp(2.75rem,2rem + 2.2vw,4.25rem);aspect-ratio:1;flex:none;
  display:grid;place-items:center;border-radius:calc(var(--r) - 3px);
  background:linear-gradient(180deg,#141414,#0c0c0c);border:1px solid var(--hair)}
.thumb img{width:66%;height:auto}
.hero-lede{margin:0;max-width:44ch;color:var(--ink-2);font-size:var(--t-md);line-height:1.55;
  text-wrap:pretty}
.hero-cta{grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3) .625rem}
.hero-cta .btn{padding-inline:1.25rem}
/* in the hand, two full-width buttons: one for each thumb, no guessing */
@media (max-width:39.99rem){.hero-cta .btn{flex:1 1 100%}}

/* ---- C: the six services, every one a door ----
   A head row (the label, and the way to all of them), then the six as
   rows of a table: the name set like a small headline, its one line
   under it, and the arrow in a column of its own at the row's end, so
   every row reads as something to tap. Hairlines between the rows. */
.tile-c{display:flex;flex-direction:column;
  padding:calc(var(--tp) - var(--s-3)) var(--tp) calc(var(--tp) - var(--s-3))}
.c-head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  min-height:2rem;margin-bottom:var(--s-1)}
.c-head .eyebrow{margin:0}
.c-all{display:inline-flex;align-items:center;gap:var(--s-2);min-height:44px;margin-block:-6px;padding-left:var(--s-2);
  font-size:var(--t-xs);font-weight:600;color:var(--ink-2);white-space:nowrap;
  transition:color var(--d-ui) var(--ease)}
.c-all .arrow{width:14px;height:14px;flex:none;transition:transform 220ms var(--ease)}
@media (hover:hover){.c-all:hover{color:var(--ink)}.c-all:hover .arrow{transform:translateX(3px)}}
.svc-list{list-style:none;margin:0;padding:0;flex:1 1 auto;
  display:grid;grid-template-columns:minmax(0,1fr);grid-auto-rows:1fr;column-gap:var(--s-5)}
.svc-list li{display:flex;min-width:0}
.svc-list li + li{border-top:1px solid var(--hair)}
/* the row: the text block and the arrow. Its fill runs a little past
   the text on either side and stops short of the hairlines, so under
   the pointer it reads as one soft key, not a highlighted table cell */
.svc{position:relative;flex:1;min-width:0;display:grid;grid-template-columns:minmax(0,1fr) auto;
  column-gap:var(--s-3);align-content:center;min-height:44px;margin-inline:calc(-1 * var(--s-3));
  padding:var(--svc-py,.75rem) var(--s-3);border-radius:calc(var(--r) - 5px)}
.svc::before{content:"";position:absolute;inset:3px 0;z-index:-1;border-radius:inherit;pointer-events:none;
  transition:background var(--d-ui) var(--ease)}
.svc-name{font-family:var(--serif);font-weight:500;font-size:clamp(1.125rem,.95rem + .35vw,1.25rem);
  line-height:1.2;letter-spacing:-.005em;color:var(--ink)}
.svc-line{grid-column:1;margin-top:3px;font-size:var(--t-xs);line-height:1.4;color:var(--muted);
  text-wrap:balance;transition:color var(--d-ui) var(--ease)}
.svc .arrow{grid-column:2;grid-row:1 / span 2;align-self:center;width:16px;height:16px;
  color:var(--muted);transition:transform 220ms var(--ease),color var(--d-ui) var(--ease)}
.svc:focus-visible{outline-offset:0;border-radius:calc(var(--r) - 5px)}
@media (hover:hover){
  .svc:hover::before{background:rgba(255,255,255,.025)}
  .svc:hover .svc-line{color:var(--ink-2)}
  .svc:hover .arrow{transform:translateX(3px);color:var(--ink)}
}

/* ---- B: the work ----
   Each site is its picture with a caption under it, in a band of the
   tile's own fill: the client's name, an arrow out for a live site, and
   what it is. Nothing sits on the picture, so the clients' own headlines
   and buttons stay clear. Narrower than a laptop only the newest keeps
   its picture; the other two are named rows (the Work section below
   shows all three at full size). */
.tile-b{display:grid;grid-template-columns:minmax(0,1fr);align-content:start;padding:var(--bp)}
.work-shot{position:relative;display:flex;flex-direction:column;min-width:0;color:var(--ink)}
.ws-pic{position:relative;display:block;overflow:hidden;min-height:0;
  aspect-ratio:1916/940;border-radius:var(--ri);background:var(--bg-2)}
/* Overscan, hinged on the top edge: a captured page carries stray
   pixels at its edges (a cursor in one corner, the first rows of the
   next section along the foot), so the picture runs a little past the
   bottom of its frame and a hair past the sides. Where it is cropped,
   --focus keeps the client's headline in view. */
.ws-pic img{position:absolute;left:0;top:0;width:100%;height:109%;
  object-fit:cover;object-position:var(--focus,50% 0);
  transform:scale(1.03);transform-origin:50% 0;transition:transform 700ms var(--ease)}
.work-shot[data-shot="0"]{--focus:50% 25%}   /* the window never starts below VIVACÉ's accent */
/* a taller screen has room for the whole of the site's own top: its
   bar, then its headline, the way the page really opens */
@media (min-width:70rem) and (min-height:50rem){.work-shot[data-shot="0"]{--focus:50% 0}}
.work-shot[data-shot="1"]{--focus:0 42%}
.work-shot[data-shot="2"]{--focus:50% 45%}
/* a hairline over the picture's edge, so a dark screenshot still has one */
.ws-pic::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  border:1px solid rgba(255,255,255,.10);transition:border-color var(--d-ui) var(--ease)}
.ws-cap{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0;
  padding:var(--s-3) var(--s-2) var(--s-2)}
.ws-name{display:block;max-width:100%;
  font-family:var(--serif);font-weight:500;font-size:1.1875rem;line-height:1.25;color:var(--ink)}
.ws-name .nowrap{white-space:nowrap}
.ws-name .out{display:inline-block;width:.75em;height:.75em;margin-left:.35em;vertical-align:-.02em;color:var(--ink-2);
  transition:transform 220ms var(--ease),color var(--d-ui) var(--ease)}
.ws-kind{font-family:var(--mono);font-size:var(--t-2xs);font-weight:500;line-height:1.45;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted);text-wrap:balance}
@media (hover:hover){
  /* under the pointer the site leans in a touch, slowly: a door, not a jolt */
  a.work-shot:hover .ws-pic img{transform:scale(1.055)}
  a.work-shot:hover .ws-pic::after{border-color:rgba(255,255,255,.28)}
  a.work-shot:hover .ws-name .out{transform:translate(2px,-2px);color:var(--ink)}
}
/* the two beneath the lead, as rows: name and kind, a hairline between */
@media (max-width:69.99rem){
  .work-shot:not(.lead) .ws-pic{display:none}
  /* the words line up with the other tiles' copy, a padding in */
  .work-shot:not(.lead){border-top:1px solid var(--hair);justify-content:center;min-height:56px;
    margin-inline:calc(var(--tp) - var(--bp))}
  .work-shot:not(.lead) .ws-cap{padding:var(--s-3) 0}
  .work-shot.lead .ws-cap{padding:var(--s-3) calc(var(--tp) - var(--bp))}
}

/* ---- D: the short version ---- */
.tile-d{display:flex;flex-direction:column;padding:var(--tp) var(--tp) calc(var(--tp) - var(--s-3))}
.tile-d > .eyebrow{margin:0 0 var(--s-3)}
/* B's padding is the thin passe-partout round its pictures; its label
   steps in to the same line as the other tiles' copy */
.tile-b > .eyebrow{margin:calc(var(--tp) - var(--bp)) calc(var(--tp) - var(--bp)) var(--s-3)}
.d-facts{list-style:none;margin:0;padding:0;flex:1 1 auto;display:grid;grid-auto-rows:1fr}
.d-facts li{display:grid;align-content:center;gap:2px;padding-block:var(--s-3)}
.d-facts li + li{border-top:1px solid var(--hair)}
.d-facts strong{font-weight:600;font-size:var(--t-md);line-height:1.3;color:var(--ink)}
.d-facts span{font-size:var(--t-xs);line-height:1.45;color:var(--muted);text-wrap:balance}

/* a narrow phone: the eyebrow's tracking closes a little, so the place
   name stays on the line instead of breaking off on its own */
@media (max-width:25rem){.hero-eyebrow{letter-spacing:.11em}}
/* a phone gives the lede the mark's column back: the same mark sits in
   the bar just above, and the room puts a whole service row on the
   first screen */
@media (max-width:39.99rem){.thumb{display:none}.a-foot{grid-template-columns:minmax(0,1fr)}}

/* ---- a tablet: the headline and the services across, the work and
   the facts side by side under them ---- */
@media (min-width:40rem){
  .bento{grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);grid-template-areas:"a a" "c c" "b d"}
  /* the headline tile runs the full width here: two long lines, not
     three short ones beside an empty half */
  .hero-h{max-width:26ch}
  /* enough air between the columns that each arrow reads as the end of
     its own row, not the start of the next column's */
  .svc-list{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:clamp(2.5rem,1rem + 3vw,4rem)}
  .svc-list li + li{border-top:0}
  .svc-list li:nth-child(n+3){border-top:1px solid var(--hair)}
}

/* a tablet's facts sit as a centred group in the row's rhythm rather
   than stretched to the height of the work beside them */
@media (min-width:40rem) and (max-width:69.99rem){
  .tile-d{justify-content:center}
  .d-facts{flex:0 0 auto;grid-auto-rows:auto}
  .d-facts li{padding-block:var(--s-4)}
}

/* ---- a laptop: the block, filling the first screen ----
   (a short laptop screen, such as 1080p at 150% with the browser's own
   bars, gets a compact tier at the end of this block) ----
   From 1120px, where the headline tile is wide enough for its two
   buttons side by side. Without the script (or before it has measured)
   the block is a plain grid of the same proportions: A beside B over C
   beside D, three column tracks so the two rows split in different
   places. */
@media (min-width:70rem){
  .hero-h{max-width:15ch}
  .hero{--vh:1vh;--t-hero:clamp(2.375rem,min(4.1vw,6.1 * var(--vh)),4.25rem);
    --tpy:clamp(1.25rem,3.4 * var(--vh),2.25rem);     /* a text tile's top and bottom: less on a short screen */
    padding-bottom:var(--g)}
  @supports (height:1svh){.hero{--vh:1svh}}
  /* --bh0 is the first screen under the bar, less a gap above and below,
     within sensible limits; the block takes that height, or more if
     what it holds needs more */
  .bento{--seam:.455;--slant:.27;
    --bh0:clamp(31rem,calc(100 * var(--vh) - var(--nav-h) - var(--nav-gap) - var(--g) * 2),62rem);
    grid-template-columns:minmax(0,46fr) minmax(0,20fr) minmax(0,34fr);
    grid-template-rows:minmax(min-content,1.4fr) minmax(min-content,1fr);
    grid-template-areas:"a b b" "c c d";
    min-height:var(--bh0)}
  .tile-a{padding-block:var(--tpy)}
  .tile-c{padding-top:calc(var(--tpy) - var(--s-3));padding-bottom:calc(var(--tpy) - var(--s-3))}
  .tile-d{padding-block:calc(var(--tpy) - .5rem)}
  .hero-eyebrow{margin-bottom:clamp(.75rem,2 * var(--vh),1.5rem)}
  .a-foot{padding-top:clamp(.625rem,1.6 * var(--vh),2rem);row-gap:clamp(.75rem,1.8 * var(--vh),1.5rem)}
  .hero-lede{line-height:1.5}
  .svc{--svc-py:clamp(.3125rem,.75 * var(--vh),.75rem)}
  /* the facts carry no label here: beside the services and under the
     work they read as what they are, and the room goes to the work */
  .tile-d > .eyebrow,.tile-b > .eyebrow{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);
    white-space:nowrap;margin:0}
  .d-facts li{padding-block:clamp(.375rem,.9 * var(--vh),.75rem)}
  /* the services keep their one-liners on one line here, so the gap
     between the columns grows with the screen instead */
  .svc-list{column-gap:clamp(1.5rem,4vw - 1.75rem,3rem)}

  /* the work: the lead across the top, two beneath it, each picture
     with its caption band under it. The pictures take whatever height
     the row leaves and are cropped to it; the captions keep theirs, the
     same for the two beneath (a subgrid, so neither picture is shorter
     because its neighbour's name wrapped). A row of --bp between the
     two pairs; --b-in is how far the lower pair stands in from the
     left, set by landing.js so the lower-left picture starts on the
     seam. Once the block is cut the script writes the settled tracks
     back in whole device pixels (--b-rows, --b-cols), so the pictures'
     rims are as crisp as the tiles'. */
  .tile-b{grid-template-columns:var(--b-cols,var(--b-in,0px) var(--b-l,minmax(0,1fr)) var(--bp) minmax(0,1fr));
    grid-template-rows:var(--b-rows,minmax(0,1.85fr) auto var(--bp) minmax(0,1fr) auto);gap:0;
    align-content:stretch}
  .work-shot{display:grid;grid-template-rows:subgrid;grid-row:span 2;row-gap:0}
  .work-shot.lead{grid-column:1 / -1;grid-row:1 / span 2}
  .work-shot[data-shot="1"]{grid-column:2;grid-row:4 / span 2}
  .work-shot[data-shot="2"]{grid-column:4;grid-row:4 / span 2}
  .ws-pic{aspect-ratio:auto}
  .ws-cap{padding:.5rem var(--s-2) .375rem calc(var(--s-2) + var(--cl,0px))}
  .work-shot.lead .ws-cap{flex-direction:row;flex-wrap:wrap;align-items:baseline;column-gap:var(--s-4)}
  .work-shot.lead .ws-name{font-size:1.25rem}
  .work-shot:not(.lead) .ws-name{font-size:1.0625rem}
  .work-shot:not(.lead) .ws-kind{font-size:.6875rem;letter-spacing:.1em}
}
/* below 1280px the seam stands a little further right, so the services
   keep their one line where they can; from 1280px they always do */
@media (min-width:70rem) and (max-width:79.99rem){
  .bento{--seam:.47}
  /* and the two captions under the lead are set a size smaller */
  .work-shot:not(.lead) .ws-name{font-size:.9375rem}
  .work-shot:not(.lead) .ws-kind{font-size:.625rem;letter-spacing:.08em}
}
@media (min-width:80rem){
  .svc-line{text-wrap:wrap}
}

/* ---- the cut: set by landing.js once it has measured the block ----
   Every tile is placed by the script at its shape's bounding box. The
   tile's own fill and border step aside for a skin drawn on the same
   path as its clip: one div for the fill and one SVG stroke for the
   rim, a hair inside the edge so it is never half cut away. The copy
   keeps clear of the seam through the padding and offsets below, which
   the script works out from where the seam passes each line. */
.bento.is-cut{display:block;height:var(--bh)}
.bento.is-cut > .tile{position:absolute;background:none;border-color:transparent}
.tile-skin{position:absolute;z-index:-1;pointer-events:none;background:rgba(12,12,12,.92)}
.tile-skin svg,.shot-rim{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}
.tile-skin path{fill:none;stroke-width:1}   /* the strokes are gradients, see THE LIGHT */
.bento.is-cut .tile-a{padding-right:calc(var(--tp) + var(--a-r,0px))}
.bento.is-cut .a-foot{margin-right:calc(-1 * var(--a-ext,0px))}
.bento.is-cut .tile-c{padding-right:calc(var(--tp) + var(--c-r,0px))}
.bento.is-cut .svc-list li{margin-right:calc(-1 * var(--x,0px))}
.bento.is-cut .tile-d{padding-left:calc(var(--tp) + var(--d-l,0px))}
.bento.is-cut .d-facts li{margin-left:calc(-1 * var(--x,0px));padding-left:var(--x,0px)}
.bento.is-cut .ws-pic{border-radius:0}
.bento.is-cut .ws-pic::after{display:none}
.shot-rim{z-index:1}
.shot-rim path{fill:none;stroke-width:1}
.shot-rim .ring{stroke:none}
/* a clipped box would cut a ring drawn outside it: the focus ring is
   drawn round the picture and its caption instead, in white */
.bento.is-cut a.work-shot:focus-visible{outline:none}
.bento.is-cut a.work-shot:focus-visible .shot-rim .ring{stroke:var(--accent);stroke-width:2}
.bento:not(.is-cut) .tile-skin,.bento:not(.is-cut) .shot-rim{display:none}

@media (max-width:48rem){
  /* a taller crop of the work screenshots: a phone shows the middle of
     the hero rather than a thin strip of it */
  .shot{aspect-ratio:3/2}
}

/* ---------------- sections ----------------
   No fill and no rule. The background behind them is the page's,
   and it never restarts. */
.sec{position:relative;z-index:1;background:none;border:0;
  padding-block:clamp(4rem,9vw,var(--s-9));
  scroll-margin-top:calc(var(--nav-h) + var(--nav-gap) + var(--s-5))}
.sec-head{display:grid;gap:var(--s-4);margin-bottom:var(--s-8);max-width:52ch}
.sec-head .eyebrow{margin:0}
.sec-head h2{max-width:20ch}

/* ---------------- surfaces that turn toward the pointer ----------------
   Any [data-tilt] box turns a few degrees toward the pointer while it
   is over it (landing.js writes --tx/--ty) and carries a pool of light
   where the pointer is (--mx/--my). The transform lives on the same
   element as the reveal, so a settled box uses this rule and an
   arriving one uses the reveal's. */
[data-tilt]{position:relative}
[data-tilt]::after{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;opacity:0;z-index:2;
  background:radial-gradient(320px circle at var(--mx,50%) var(--my,50%),
    rgba(255,255,255,.045), transparent 65%);
  transition:opacity 320ms var(--ease)}
[data-tilt].hov::after{opacity:1}
[data-tilt].hov{transition:transform 200ms var(--ease),
  border-color var(--d-ui) var(--ease),box-shadow var(--d-ui) var(--ease),
  background var(--d-ui) var(--ease)}
[data-tilt]:not([data-anim]),[data-anim][data-tilt].in{
  transform:perspective(900px) rotateX(var(--ty,0deg)) rotateY(var(--tx,0deg))}

/* ---------------- work ---------------- */
.work{display:grid;gap:clamp(3rem,7vw,6rem)}
.piece{display:grid;grid-template-columns:minmax(0,6fr) minmax(0,5fr);
  gap:clamp(1.5rem,4vw,4rem);align-items:center}
.piece:nth-child(even){grid-template-columns:minmax(0,5fr) minmax(0,6fr)}
.piece:nth-child(even) .shot{order:2}
.piece:nth-child(2){margin-left:clamp(0px,6vw,5rem)}
.shot{display:block;position:relative;aspect-ratio:11/5;border-radius:var(--r);overflow:hidden;background:var(--bg-2);
  border:1px solid var(--rule-soft);box-shadow:var(--shadow);
  transition:border-color var(--d-ui) var(--ease),box-shadow var(--d-ui) var(--ease)}
/* a cover in the card's own colour that lifts once the image has loaded */
.shot::after{content:"";position:absolute;inset:0;background:var(--bg-2);opacity:1;pointer-events:none;
  transition:opacity 700ms var(--ease)}
.shot.ld::after{opacity:0}
html:not(.js) .shot::after{display:none}
@media (hover:hover){a.shot:hover{border-color:rgba(255,255,255,.28);box-shadow:0 30px 70px rgba(0,0,0,.6)}}
.shot img{width:100%;height:100%;object-fit:cover;object-position:top center;
  transform-origin:center;will-change:transform,opacity}
.shot-empty{display:grid;place-items:center;background:var(--bg-2);border-style:dashed}
.piece .eyebrow{margin-bottom:var(--s-3)}
.piece h3{margin-bottom:var(--s-3)}
.piece p{color:var(--muted);font-size:var(--t-sm);max-width:52ch}
.piece ul{list-style:none;padding:0;margin:0 0 var(--s-5);display:grid;gap:var(--s-1)}
.piece li{font-size:var(--t-sm);color:var(--ink-2);padding-left:var(--s-4);position:relative}
.piece li::before{content:"";position:absolute;left:0;top:.62em;width:5px;height:5px;
  border-radius:50%;background:var(--accent)}
.slot{display:inline-block;font-size:var(--t-xs);color:var(--muted);
  border:1px dashed var(--rule);border-radius:var(--r-sm);padding:var(--s-1) var(--s-2)}
@media (max-width:56rem){
  .piece,.piece:nth-child(even){grid-template-columns:minmax(0,1fr)}
  .piece:nth-child(even) .shot{order:0}
  .piece:nth-child(2){margin-left:0}
}

/* ---------------- process ----------------
   Three beats, three cards. The order is carried by the reading order
   and the copy, not by numbers stamped on the boxes. */
.steps{list-style:none;padding:0;margin:0;position:relative;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-5)}
.step{position:relative;padding:var(--s-6);border-radius:var(--r);
  background:rgba(12,12,12,.92);border:1px solid var(--rule-soft);
  box-shadow:var(--shadow);
  transition:border-color var(--d-ui) var(--ease),box-shadow var(--d-ui) var(--ease)}
@media (hover:hover){.step:hover{border-color:var(--rule)}}   /* not a link: it stirs, it does not invite */
.step h3{font-size:var(--t-xl);margin-bottom:var(--s-3)}
.step p{margin:0;font-size:var(--t-sm);line-height:1.55;color:var(--ink-2)}
@media (max-width:56rem){
  .steps{grid-template-columns:minmax(0,1fr)}
}

/* ---------------- what we do ----------------
   Shared with the services page: the same card there and here. */
.svc-grid{display:grid;gap:var(--s-5);
  grid-template-columns:repeat(auto-fit,minmax(min(19rem,100%),1fr))}
.svc-card{display:grid;align-content:start;gap:var(--s-3);
  padding:var(--s-6);border-radius:var(--r);
  background:rgba(12,12,12,.92);border:1px solid var(--rule-soft);
  box-shadow:var(--shadow);
  transition:border-color var(--d-ui) var(--ease),box-shadow var(--d-ui) var(--ease)}
@media (hover:hover){.svc-card:hover{border-color:rgba(255,255,255,.28)}}
.svc-card .eyebrow{margin:0 0 var(--s-2)}
.svc-card h2,.svc-card h3{font-size:var(--t-xl)}
.svc-card p{color:var(--muted);font-size:var(--t-sm);margin:0}
.svc-more{display:inline-flex;align-items:center;gap:var(--s-2);
  font-size:var(--t-sm);font-weight:600;color:var(--accent)}
.svc-more svg{fill:none;stroke:currentColor;transition:transform 220ms var(--ease)}
@media (hover:hover){.svc-card:hover .svc-more svg{transform:translateX(4px)}}

/* ---------------- about ----------------
   One paragraph on a hairline, set large. The sentence an answer
   engine should lift, so it is the plainest thing on the page. */
.sec-about{padding-block:clamp(2rem,5vw,3.5rem)}
.about{max-width:62rem;padding:var(--s-6) 0;
  border-top:1px solid var(--hair);border-bottom:1px solid var(--hair)}
.about .eyebrow{margin-bottom:var(--s-4)}
.define{font-size:clamp(1.2rem,1.9vw,1.55rem);line-height:1.45;color:var(--ink);
  max-width:38em;margin:0 0 var(--s-5);text-wrap:pretty}
.about-facts{margin:0;display:grid;gap:var(--s-4) var(--s-6);
  grid-template-columns:repeat(auto-fit,minmax(min(12rem,100%),1fr))}
.about-facts dt{font-family:var(--mono);font-size:var(--t-2xs);letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);font-weight:500}
.about-facts dd{margin:2px 0 0;font-size:var(--t-sm);color:var(--ink-2)}

/* ---------------- faq ---------------- */
.faq{max-width:62rem;margin-left:auto;display:grid;gap:var(--s-3)}
.faq details{position:relative;border:1px solid var(--rule-soft);border-radius:var(--r);
  background:rgba(12,12,12,.92);
  transition:background var(--d-ui) var(--ease),
    border-color var(--d-ui) var(--ease),box-shadow var(--d-ui) var(--ease)}
@media (hover:hover){.faq details:hover{border-color:rgba(255,255,255,.28)}}
.faq details[open]{background:rgba(18,18,18,.95);border-color:var(--rule)}
.faq summary{cursor:pointer;list-style:none;display:flex;gap:var(--s-4);
  justify-content:space-between;align-items:center;padding:var(--s-4) var(--s-5);
  font-family:var(--serif);font-size:var(--t-lg);line-height:1.3;
  transition:color var(--d-ui) var(--ease)}
.faq summary::-webkit-details-marker{display:none}
@media (hover:hover){.faq summary:hover{color:var(--accent)}}
.faq summary::after{content:"+";flex:none;
  display:grid;place-items:center;width:32px;height:32px;
  border:1px solid var(--rule-ui);border-radius:50%;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2);
  font-family:var(--sans);font-size:1.15rem;font-weight:500;line-height:1;
  color:var(--ink-2);
  transition:background var(--d-ui) var(--ease),color var(--d-ui) var(--ease),
    border-color var(--d-ui) var(--ease)}
@media (hover:hover){.faq summary:hover::after{border-color:rgba(255,255,255,.75);color:var(--accent)}}
.faq details[open] summary::after{content:"\2013";
  background:var(--accent);border-color:transparent;color:var(--accent-ink)}
.faq details p{margin:0;padding:0 var(--s-5) var(--s-5);
  color:var(--ink-2);font-size:var(--t-sm);line-height:1.6;max-width:64ch}

/* ---------------- close + footer ---------------- */
.close-grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);
  gap:clamp(2rem,5vw,4rem);align-items:start}
.card{background:rgba(12,12,12,.92);border:1px solid var(--rule-soft);
  border-radius:var(--r);padding:var(--s-6);
  box-shadow:var(--shadow);transition:border-color var(--d-ui) var(--ease)}
@media (hover:hover){.card:hover{border-color:var(--rule)}}
.card h3{font-size:var(--t-lg);margin-bottom:var(--s-4)}
.card dl{margin:0;display:grid;gap:var(--s-4)}
.card dt{font-family:var(--mono);font-size:var(--t-2xs);letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);font-weight:500}
.card dd{margin:2px 0 0;font-size:var(--t-sm)}
@media (max-width:62rem){.close-grid{grid-template-columns:minmax(0,1fr)}}

.foot{position:relative;z-index:1;background:none;
  border-top:1px solid var(--hair);padding:var(--s-7) 0 var(--s-6)}
.foot-top{display:flex;flex-wrap:wrap;gap:var(--s-5);justify-content:space-between;
  align-items:baseline;padding-bottom:var(--s-5);border-bottom:1px solid var(--hair)}
.foot-links{display:flex;flex-wrap:wrap;gap:var(--s-2) var(--s-5);
  font-family:var(--mono);font-size:var(--t-2xs);letter-spacing:.14em;text-transform:uppercase}
.foot-links a{color:var(--muted);transition:color var(--d-ui) var(--ease);
  display:inline-block;padding-block:var(--s-2)}
@media (hover:hover){.foot-links a:hover{color:var(--ink)}}
/* the accounts, set as real links with their marks rather than a row of
   grey words, so they read as places to go */
.foot-social{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-4) var(--s-6);
  padding:var(--s-5) 0;border-bottom:1px solid var(--hair)}
.foot-social .eyebrow{margin:0}
.socials{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.socials a{display:inline-flex;align-items:center;gap:var(--s-2);min-height:44px;
  padding:var(--s-2) var(--s-4);border:1px solid var(--rule-ui);border-radius:var(--r-pill);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2);
  font-size:var(--t-sm);font-weight:600;color:var(--ink);
  transition:border-color var(--d-ui) var(--ease),color var(--d-ui) var(--ease),
    box-shadow var(--d-ui) var(--ease)}
@media (hover:hover){.socials a:hover{border-color:rgba(255,255,255,.75);color:var(--accent)}}
.socials svg{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.socials .handle{font-family:var(--mono);font-weight:400;font-size:var(--t-xs);color:var(--muted);
  margin-left:var(--s-1)}
@media (max-width:34rem){.socials .handle{display:none}}
.foot-note{font-size:var(--t-xs);color:var(--muted);max-width:64ch;margin:var(--s-5) 0 0;line-height:1.6}
.foot-bot{display:flex;flex-wrap:wrap;gap:var(--s-4) var(--s-6);justify-content:space-between;
  font-size:var(--t-xs);color:var(--muted);margin-top:var(--s-5)}
.foot-bot p{margin:0}

/* ============================================================
   INERTIAL SCROLL
   The whole page is one surface that eases toward the real scroll
   position rather than snapping to it. The native scrollbar, keyboard
   scrolling and anchor links all still drive the real scroller.
   Off by default in the markup, so with no JS the page scrolls normally.
   ============================================================ */
#smooth{position:relative;z-index:1}
html.smooth #smooth{position:fixed;top:0;left:0;right:0;will-change:transform}
html.smooth body{overflow-x:clip}
#spacer{pointer-events:none}

/* ============================================================
   THE 3D REVEAL SYSTEM
   ------------------------------------------------------------
   Every box on the page carries a [data-anim] value naming how it
   arrives. The element starts rotated and pushed back in Z inside a
   perspective set on its parent, then flies to identity.

   Two things make it cinematic rather than a fade:

     1. real perspective: .stage gives the group a camera, so a card
        rotating about Y genuinely turns, near edge growing, far edge
        shrinking, instead of squashing flat;
     2. it replays: the class comes OFF once the element is well clear
        of the viewport, so scrolling back up and down plays the move
        again every time.
   ============================================================ */
.stage{perspective:1400px;perspective-origin:50% 50%}

/* No preserve-3d on these. The box's own transform already carries its
   perspective, and putting the children into a 3D context of their own
   made a settled row's bullet list project to a box thousands of pixels
   wide that sat over the screenshot beside it and took its hover. */
[data-anim]{
  opacity:0;will-change:transform,opacity;backface-visibility:hidden;
  transition:opacity var(--d-3d) var(--ease) var(--delay,0ms),
             transform var(--d-3d) var(--ease-back) var(--delay,0ms),
             filter var(--d-3d) var(--ease) var(--delay,0ms);
  filter:blur(4px)}

/* A phone gets the same arrival without the blur and without 40-odd
   permanently promoted layers: the transition itself promotes a layer
   for as long as it runs, which is all a phone can afford. */
@media (pointer:coarse){
  /* the floor's fade is written every frame of the hero's scroll; a
     transition on each write kept a phone starting one 60 times a
     second over a large masked layer. The ring is never shown. */
  /* The floor grid is a tilted, masked layer two and a half screens
     wide whose fade was rewritten every frame of the hero's scroll: on
     an iPhone that was the lag at the top of the page. A phone has no
     floor. */
  .floor{display:none}
  .cursor{display:none}
  /* The bar blurred whatever was behind it. On a phone that blur is
     recomputed every frame the stars move under it, which was most of
     the lag while scrolling: the bar is simply a shade darker instead. */
  .nav-in{-webkit-backdrop-filter:none;backdrop-filter:none;
    background:linear-gradient(180deg,rgba(20,20,20,.9),rgba(10,10,10,.86))}
  [data-anim]{will-change:auto;filter:none;
    transition:opacity var(--d-3d) var(--ease) var(--delay,0ms),
               transform var(--d-3d) var(--ease-back) var(--delay,0ms)}
}

/* spins in from the left, hinged on its own left edge */
[data-anim="spin-left"]{transform:perspective(1400px) translate3d(-16%,8%,-340px) rotateY(-62deg) rotateZ(-4deg);
  transform-origin:left center}
/* and the mirror, for the alternating rows */
[data-anim="spin-right"]{transform:perspective(1400px) translate3d(16%,8%,-340px) rotateY(62deg) rotateZ(4deg);
  transform-origin:right center}
/* tips up off the floor, like a card being stood upright */
[data-anim="tilt-up"]{transform:perspective(1400px) translate3d(0,26%,-260px) rotateX(52deg);
  transform-origin:center bottom}
/* falls forward from the back of the scene */
[data-anim="drop-in"]{transform:perspective(1400px) translate3d(0,-18%,-420px) rotateX(-38deg);
  transform-origin:center top}
/* the camera dollies onto it */
[data-anim="dolly"]{transform:perspective(1400px) translate3d(0,6%,-520px) scale(.94)}
/* swings in on a hinge above it, the way a sign swings */
[data-anim="swing"]{transform:perspective(1400px) translate3d(0,-10%,-200px) rotateX(-58deg) rotateZ(-3deg);
  transform-origin:center -40%}
/* the plain one, for running text */
[data-anim="rise"]{transform:translate3d(0,28px,0);filter:none}

[data-anim].in{opacity:1;transform:none;filter:none}
/* A box waiting for its reveal is invisible but still there, and in its
   hidden pose (swung 62 degrees toward the camera) it projects to a box
   several screens wide. That invisible box sat over visible cards and
   took their pointer, which is what made a card flicker when the pointer
   was barely on it. Until it has arrived, it takes no pointer at all. */
html.js [data-anim]:not(.in){pointer-events:none}

/* no JS, no observer: everything is simply present */
html:not(.js) [data-anim]{opacity:1;transform:none;filter:none}

/* The older single-shot class is kept so any markup still using it
   behaves, now routed through the same 3D arrival. */
.up{opacity:0;transform:translate3d(0,20px,0);
  transition:opacity var(--d-in) var(--ease) var(--delay,0ms),
             transform var(--d-in) var(--ease) var(--delay,0ms)}
.up.in{opacity:1;transform:none}
html:not(.js) .up{opacity:1;transform:none}

/* Scroll-linked depth: while a group is on screen it leans a few
   degrees with the scroll, so the whole page reads as a space being
   travelled through rather than a stack of flat panels. */
@property --lean{syntax:"<number>";inherits:false;initial-value:0}
.lean{transform:perspective(1600px) rotateX(calc(var(--lean,0) * 2.4deg)) translateZ(calc(var(--lean,0) * -18px))}

/* A short laptop screen: about 610 to 660px under the browser's bars
   on a 1080p laptop at 150%. The headline runs two lines instead of
   three, the lede takes the mark's column, and the spacing tightens, so
   the last service and the last fact still sit above the fold. */
@media (min-width:70rem) and (max-height:45rem){
  .hero-h{max-width:24ch}
  .thumb{display:none}
  .a-foot{grid-template-columns:minmax(0,1fr);padding-top:.5rem;row-gap:.75rem}
  .hero-eyebrow{margin-bottom:.625rem}
  .svc{--svc-py:.125rem}
  .svc-name{font-size:1.0625rem}
  .svc-line{margin-top:1px;font-size:.78125rem}
  .c-head{min-height:1.5rem}
  .tile-d{padding-block:.375rem}
  .d-facts li{padding-block:.25rem}
}

/* ============================================================
   INNER PAGES
   Service pages, project pages and the location page. Same
   continuous background, same reveal system; only the furniture
   is different: a breadcrumb, a page head, and running prose.
   ============================================================ */
.page-hero{position:relative;z-index:1;
  padding:calc(var(--nav-h) + var(--nav-gap) + var(--s-8)) 0 var(--s-7)}
.page-hero h1{max-width:18ch;margin-bottom:var(--s-4)}
.page-lede{font-size:var(--t-lg);line-height:1.5;color:var(--ink-2);
  max-width:46ch;margin:0 0 var(--s-5)}
.ctas{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-4);margin-top:var(--s-6)}

.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);
  font-family:var(--mono);font-size:var(--t-2xs);color:var(--muted);margin-bottom:var(--s-5);
  letter-spacing:.14em;text-transform:uppercase;font-weight:500}
.crumbs a{transition:color var(--d-ui) var(--ease)}
@media (hover:hover){.crumbs a:hover{color:var(--ink)}}
.crumbs span[aria-hidden]{color:var(--muted);opacity:.7}
.crumbs > span:last-child{color:var(--ink-2)}   /* where you are, set apart from where you can go */
.kind{font-family:var(--mono);font-size:var(--t-2xs);letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);font-weight:500;margin-bottom:var(--s-3);display:block}

.prose{max-width:68ch}
.prose-block{margin-bottom:var(--s-7)}
.prose-block h2{font-size:var(--t-xl);margin-bottom:var(--s-4)}
.prose-block p{color:var(--ink-2);font-size:var(--t-md)}
.page-intro{font-size:var(--t-lg);line-height:1.55;color:var(--ink-2);
  margin-bottom:var(--s-7);max-width:56ch}
.check{list-style:none;padding:0;margin:0;display:grid;gap:var(--s-2)}
.check li{font-size:var(--t-sm);color:var(--ink-2);padding-left:var(--s-5);
  position:relative;line-height:1.5}
.check li::before{content:"";position:absolute;left:0;top:.46em;width:10px;height:6px;
  border-left:1.5px solid var(--accent);border-bottom:1.5px solid var(--accent);
  transform:rotate(-45deg)}

.related-links{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.related-links a{font-size:var(--t-sm);padding:var(--s-2) var(--s-4);
  border:1px solid var(--rule-ui);border-radius:var(--r-pill);color:var(--ink-2);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.2);
  transition:border-color var(--d-ui) var(--ease),color var(--d-ui) var(--ease)}
@media (hover:hover){.related-links a:hover{border-color:rgba(255,255,255,.75);color:var(--ink)}}

/* the inner-page FAQ reuses the homepage one, just narrower */
.prose .faq{max-width:none;margin-left:0}

/* ============================================================
   THE LIGHT
   ------------------------------------------------------------
   One idea, carried through the page: a single light falls from
   above, and the pointer is a second light that moves. Nothing here
   glows. Light only lands on edges (a rim, a hairline, the top of a
   letter, the top of a button), crisp and specular, the way a dark UI
   on glass catches it. A soft white haze on this ground reads as grey
   fog, which is why the page has none.

   EDGE LIGHT. Every surface's 1px rim is brightest along its top and
   fades down its sides (--lit-top, --lit-mid, --lit-foot). On a CSS box
   the rim is a masked ::before, one pixel of gradient over the border,
   with the fill left as it was; on the cut tiles of the hero it is the
   SVG stroke landing.js draws (THE BENTO there). Where a box already
   uses its ::after (the [data-tilt] sheen, a screenshot's load cover)
   the rim takes ::before, which none of them used.
   ============================================================ */
@property --lit{syntax:"<number>";inherits:false;initial-value:1}
@property --glint{syntax:"<number>";inherits:false;initial-value:0}

@supports (mask-composite:exclude) or (-webkit-mask-composite:xor){
  /* the border steps aside for the lit rim; :is() takes its strongest
     argument's weight, so a hover or open state's border colour cannot
     bring the flat border back over it */
  :is(.step,.svc-card,.card,.faq details,.nav-in,.bento:not(.is-cut) > .tile){border-color:transparent}
  .shot:not(.shot-empty){border-width:0}

  :where(.step,.svc-card,.card,.faq details,.nav-in,.bento:not(.is-cut) > .tile,.shot:not(.shot-empty))::before{
    content:"";position:absolute;inset:-1px;z-index:3;border-radius:inherit;pointer-events:none;
    padding:1px;
    background:linear-gradient(180deg,
      rgba(255,255,255,calc(var(--lit-top) * var(--lit))) 0%,
      rgba(255,255,255,calc(var(--lit-mid) * var(--lit))) 33%,
      rgba(255,255,255,calc(var(--lit-foot) * var(--lit))) 100%);
    -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
    -webkit-mask-composite:xor;
    mask:linear-gradient(#000 0 0) content-box exclude,linear-gradient(#000 0 0)}
  @media (hover:hover){
    :where(.step,.svc-card,.card,.faq details,.nav-in,.bento:not(.is-cut) > .tile,.shot:not(.shot-empty))::before{
      transition:--lit 220ms var(--ease),--glint 320ms var(--ease)}
  }
  /* a screenshot clips to its own rounded box, so its rim is drawn just
     inside it, where its border was */
  .shot:not(.shot-empty)::before{inset:0}

  /* THE POINTER AS A LIGHT. Over a [data-tilt] card the rim brightens
     where the pointer is, within --lit-r of it, on the rim alone: a
     second gradient layer at --mx/--my (written by landing.js), faded
     in and out with --glint. landing.js only sets .hov for a fine
     pointer with motion allowed. */
  [data-tilt]::before{
    background:
      radial-gradient(var(--lit-r) circle at var(--mx,50%) var(--my,50%),
        rgba(255,255,255,calc(var(--lit-ptr) * var(--glint))) 0%,
        rgba(255,255,255,calc(var(--lit-ptr) * .35 * var(--glint))) 35%,
        transparent 100%),
      linear-gradient(180deg,
        rgba(255,255,255,calc(var(--lit-top) * var(--lit))) 0%,
        rgba(255,255,255,calc(var(--lit-mid) * var(--lit))) 33%,
        rgba(255,255,255,calc(var(--lit-foot) * var(--lit))) 100%)}
  @media (hover:hover){
    [data-tilt].hov::before{--glint:1}
    /* where the flat border used to brighten under the pointer, the
       whole rim does now */
    :is(.svc-card,.faq details,a.shot):hover::before{--lit:1.45}
    :is(.step,.card):hover::before{--lit:1.2}
  }
  .faq details[open]::before{--lit:1.25}

  /* the bar: its rim a touch quieter than a card's, its foot a hair
     brighter than its sides, so its lower edge still reads over the
     page scrolling under it */
  .nav-in{--lit-top:.24;--lit-mid:.07;--lit-foot:.11}
  .nav.stuck .nav-in{--lit-top:.28}
  /* the bar's inner top line was its only light; the rim carries it */
  .nav-in{box-shadow:0 8px 30px rgba(0,0,0,.30)}
  .nav.stuck .nav-in{box-shadow:0 14px 44px rgba(0,0,0,.48)}
}

/* THE CUT TILES. The rim is an SVG stroke on the tile's own path (see
   shapeTile() in landing.js), painted with a vertical gradient whose
   stops are styled here, so the numbers above tune both kinds of rim.
   The pointer's light on them is a second stroke on the same path,
   painted with a radial gradient that landing.js moves with the
   pointer while it is over the block, and shown only then. */
.lit-g .s0{stop-color:#fff;stop-opacity:calc(var(--lit-top) * var(--lk,1))}
.lit-g .s1{stop-color:#fff;stop-opacity:calc(var(--lit-mid) * var(--lk,1))}
.lit-g .s2{stop-color:#fff;stop-opacity:calc(var(--lit-foot) * var(--lk,1))}
.glint-g .s0{stop-color:#fff;stop-opacity:var(--lit-ptr)}
.glint-g .s1{stop-color:#fff;stop-opacity:calc(var(--lit-ptr) * .35)}
.glint-g .s2{stop-color:#fff;stop-opacity:0}
.tile-skin .glint,.shot-rim .glint{fill:none;stroke-width:1;opacity:0;transition:opacity 320ms var(--ease)}
.bento.lit-on .glint{opacity:1}
/* a picture under the pointer: its whole rim brightens, as before */
@media (hover:hover){.bento.is-cut a.work-shot:hover .shot-rim{--lk:1.7}}

/* ONE LIGHT SOURCE BEHIND THE HERO. A wide, faint pool from above the
   block, gone by about the middle of the hero, so the tiles lift off
   the black. At its brightest it adds about a dozen levels to the
   ground; any more and it would read as fog rather than light. */
.hero::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(90% 70% at 50% -6%,
    rgba(255,255,255,.062) 0%,rgba(255,255,255,.035) 40%,rgba(255,255,255,.01) 75%,transparent 100%)}

/* LIGHT LINES BETWEEN SECTIONS. Above each section's head, a short
   hairline, brightest left of its middle and gone at both ends: the
   meridian line, caught by the light. */
.sec .sec-head::before,.close-grid > :first-child::before{content:"";display:block;
  width:clamp(160px,16vw,240px);height:1px;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.45) 30%,rgba(255,255,255,.12) 70%,transparent)}
.sec .sec-head::before{margin-bottom:var(--s-2)}
.close-grid > :first-child::before{margin-bottom:var(--s-5)}

/* SERVICE ROWS. In the hero's list, the row under the pointer or the
   keyboard is marked by a 2px white bar sliding in at its left edge. */
.svc::after{content:"";position:absolute;left:0;top:24%;bottom:24%;width:2px;border-radius:1px;
  background:var(--accent);pointer-events:none;
  transform:scaleY(0);transition:transform 220ms var(--ease)}
.svc:focus-visible::after{transform:scaleY(1)}
@media (hover:hover){.svc:hover::after{transform:scaleY(1)}}
@media (hover:none){.svc:active::after{transform:scaleY(1);transition-duration:90ms}}

/* ============================================================
   REDUCED MOTION: a real path. The field renders one still frame,
   the layers sit where they land, nothing follows the pointer.
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  html.smooth #smooth{position:relative;transform:none!important}
  [data-anim],.up{opacity:1!important;transform:none!important;filter:none!important;transition:none}
  [data-tilt]{transform:none!important}
  [data-tilt]::after,.btn::after,.floor::after{display:none}
  .cursor{display:none}
  .menu-in a{opacity:1;transform:none}
  .lean{transform:none!important}
  .shot img{transform:none!important;opacity:1!important;will-change:auto}
  a.work-shot:hover .ws-pic img{transform:scale(1.03)}
  .aura{background:linear-gradient(180deg,#0b0b0b,#070707)}
  *,*::before,*::after{transition-duration:.01ms!important;animation-duration:.01ms!important}
}

/* Windows high contrast drops backgrounds and shadows, which is all the
   menu lines, bullets, the eyebrow dot and the link underline are made of */
@media (forced-colors:active){
  .menu-btn span,.eyebrow .dot,.piece li::before,.mark::after,.svc::after,
  .sec .sec-head::before,.close-grid > :first-child::before{background:CanvasText}
  .link{text-decoration:underline;text-underline-offset:.2em}
  /* the metal headlines are transparent letters over a gradient: give
     them back real ink. The lit rims and the sweep are light, which a
     high contrast theme has no use for; the surfaces get a plain
     system border instead. */
  .hero-h,.page-hero h1,.sec-head h2,.close-grid h2,
  :is(.hero-h,.page-hero h1,.sec-head h2,.close-grid h2) .mark{color:CanvasText;background:none}
  :is(.step,.svc-card,.card,.faq details,.nav-in,.bento:not(.is-cut) > .tile){border-color:CanvasText}
  :is(.step,.svc-card,.card,.faq details,.nav-in,.bento:not(.is-cut) > .tile,.shot)::before,
  .btn-go::before,.hero::before,.tile-skin .glint,.shot-rim .glint{display:none}
  .tile-skin .rim,.shot-rim .pic{stroke:CanvasText}
}

/* ============================================================
   THUMBS, NOT POINTERS. On a phone every link in the furniture is
   at least 44px tall. Last in the file so nothing outranks it.
   ============================================================ */
@media (max-width:48rem){
  .foot-links a{display:inline-block;padding-block:var(--s-3)}
  .crumbs a{display:inline-block;padding-block:var(--s-3);margin-block:calc(-1 * var(--s-3))}
  .related-links a{padding-block:var(--s-3)}
  .link{display:inline-block;padding-top:var(--s-2)}
}
