/* ---------- Inter ---------- */

@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/inter-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/inter-var-ita.woff2") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

/* ---------- JetBrains Mono ---------- */

@font-face {
  font-family: "JetBrains Mono";
  src: url("../assets/fonts/jetbrains-var.woff2") format("woff2");
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "JetBrains Mono";
  src: url("../assets/fonts/jetbrains-var-ita.woff2") format("woff2");
  font-weight: 100 800;
  font-style: italic;
  font-display: swap;
}


:root{
  --background:#F5F5F5;
  --surface:#EEECEB;
  --text-primary:#4b4749;
  --text-muted:#A4A4A4;
  --line: rgba(198,191,191,0.4);
  --accent:#A73E2E;
}
*{
  box-sizing:border-box;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
}
input,
textarea,
[contenteditable="true"]{
  -webkit-user-select:text;
  user-select:text;
  -webkit-touch-callout:default;
}
img,
video,
canvas,
svg{
  -webkit-user-drag:none;
  user-drag:none;
}
/* stops the mobile browser's overscroll/rubber-band bounce (and Android's
   pull-to-refresh) — without this, dragging past the top or bottom of the
   page stretches and springs back the whole viewport, including the fixed
   background layers below, which reads as the entire background moving
   around. This is separate from the hero/index touch-action:none work
   (that's about specific sections capturing a drag instead of scrolling;
   this is about the page's own edges bouncing regardless of section). */
/* overflow-x:hidden was only ever on body — but <html> (the
   documentElement) is what mobile browsers actually treat as the
   scrolling/bouncing surface, so body's own clipping alone didn't stop a
   horizontal drag from rubber-banding the whole page. Scoped to the X
   axis only (overscroll-behavior-x, not the shorthand) — the shorthand
   also disables Y-axis overscroll at the document root, which on some
   desktop browser/OS combinations broke normal scroll momentum entirely
   instead of just suppressing the bounce. The actual bug was horizontal
   only, so only that axis needs touching. */
html{
  overflow-x:hidden;
  overscroll-behavior-x:none;
}
html,body{margin:0;padding:0;}
body{
  background:
    radial-gradient(ellipse 1000px 700px at 15% -5%, rgba(255,255,255,0.7), transparent 55%),
    radial-gradient(ellipse 900px 800px at 100% 30%, rgba(198,191,191,0.16), transparent 60%),
    radial-gradient(ellipse 1200px 900px at 50% 120%, rgba(164,164,164,0.12), transparent 55%),
    var(--background);
  color:var(--text-primary);
  font-family:'Inter',sans-serif;
  font-weight:300;
  position:relative;
  overflow-x:hidden;
  overscroll-behavior-x:none;
}
body::after{
  content:'';
  position:fixed; inset:0;
  pointer-events:none;
  z-index:999;
  opacity:0.025;
  mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}
.cursor-light-layer{
  position:fixed;
  inset:0;
  pointer-events:none;
  overflow:hidden;
  z-index:2;
}
.cursor-light-orb{
  position:absolute;
  width:2px;
  height:2px;
  margin:-1px 0 0 -1px;
  border-radius:50%;
  opacity:0;
  background:rgba(255,253,242,0.46);
  box-shadow:
    0 0 18px 12px rgba(255,253,242,0.30),
    0 0 54px 38px rgba(250,244,224,0.14),
    0 0 118px 82px rgba(227,220,201,0.07);
  will-change:transform,opacity;
}
.hero-copy,
.corner-label,
#index > .index-eyebrow,
#index > .index-grid,
#contact > *,
footer > *,
.legal-sheet > *{
  position:relative;
  z-index:3;
}
section{ position:relative; overflow:hidden; }
#hero{ overflow:visible; }

/* ---------------- HERO ---------------- */
#hero{
  min-height:100vh;
  padding:0 64px;
  /* Let the page follow a normal vertical swipe outside the sphere. The
     sphere keeps touch-action:none on its own hitbox for direct interaction. */
  touch-action:pan-y;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
}
#hero::before{
  content:'';
  position:absolute; inset:0;
  background: radial-gradient(ellipse 900px 700px at 68% 45%, rgba(255,255,255,0.5), transparent 60%);
  pointer-events:none;
  z-index:0;
}
#hero::after{
  content:'';
  position:absolute;
  left:0;
  right:0;
  bottom:-1px;
  height:340px;
  z-index:2;
  pointer-events:none;
  background:linear-gradient(
    to bottom,
    rgba(215,217,213,0) 0,
    rgba(215,217,213,0.03) 18%,
    rgba(215,217,213,0.12) 36%,
    rgba(215,217,213,0.30) 54%,
    rgba(215,217,213,0.58) 72%,
    rgba(215,217,213,0.84) 88%,
    #d7d9d5 100%
  );
}
.corner-label{
  position:absolute;
  font-family:'JetBrains Mono',monospace;
  font-size:11px;
  letter-spacing:0.12em;
  color:var(--text-muted);
  text-transform:uppercase;
  z-index:1000;
          text-decoration: none !important;
}
.corner-label.tl{ top:32px; left:64px; position: fixed;}
/* hero layer switch — the only nav ZenMaker has, deliberately placed in
   the corner-label language rather than a conventional header bar, since
   the hero has never had one. Building = this site; Studio = the
   separate locked-viewport archive page at /studio. */
.layer-toggle{
  display:inline-flex;
  align-items:center;
  gap:2px;
  border:1px solid var(--line);
  border-radius:999px;
  padding:3px;
  background:rgba(255,255,255,0.5);
      width: 171px;
}
.layer-toggle a{
  padding:5px 12px;
  border-radius:999px;
  color:var(--text-muted);
  text-decoration:none;
  transition:background .2s ease, color .2s ease;
}
.layer-toggle a.on{ background:#cdceca; color:#ffffff; }
.layer-toggle a:not(.on):hover{ color:var(--text-primary); }
.layer-toggle a:focus-visible{ outline:1px solid var(--accent); outline-offset:2px; }
.corner-label.tr{ top:55px; right:88px; }
.corner-label.tr{
  text-decoration:none;
  transition:color 220ms cubic-bezier(0.16,1,0.3,1);
}
/* once scrolled past the hero (see js/main.js), it switches from absolute
   (scrolls away with #hero) to fixed so "Start a Project" stays reachable
   the rest of the page — reuses whatever top/right this breakpoint already
   defines above, just changes how those offsets are anchored. */
.corner-label.tr.is-sticky{ position:fixed; }
.corner-label.tr:hover,
.corner-label.tr:focus-visible{ color:var(--text-primary); }
.corner-label.tr:focus-visible{
  outline:1px solid currentColor;
  outline-offset:5px;
}
.corner-label.bl{
  bottom:32px; left:64px;
  display:flex; align-items:center; gap:8px;
}
.corner-label.bl .dot{
  width:5px; height:5px; background:#FDFBF4;
  animation:status-pulse 3.6s cubic-bezier(0.45,0,0.55,1) infinite;
  transform-origin:center;
}
@keyframes status-pulse{
  0%, 100%{ opacity:0.42; transform:scale(0.82); box-shadow:0 0 0 rgba(253,251,244,0); }
  50%{ opacity:0.92; transform:scale(1); box-shadow:0 0 7px rgba(253,251,244,0.38); }
}

/* full-bleed hero video background — fills the hero section edge to edge.
   inset:0 + 100%/100% on a position:relative ancestor is equivalent to
   100vw/100vh here without the horizontal-scrollbar overflow that a literal
   100vw causes on viewports with a vertical scrollbar. */
.hero-video-layer{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  z-index:1;
  overflow:hidden;
  pointer-events:none;
}
#hero-korwerk-video{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:61% 50%;
  background:transparent;
  display:block;
  pointer-events:none;
  /* native "drag this image/video" ghost + text-selection highlight are
     separate browser behaviors from touch-action/scrolling — this is what
     stops a drag from picking the video up like a draggable image or
     painting a selection highlight across it. */
  -webkit-user-drag:none;
  user-drag:none;
  -webkit-user-select:none;
  user-select:none;
}
/* invisible circular hover/click target sized to the visible orb only —
   not the full hero panel — so the video reverses the instant the cursor
   leaves the sphere itself instead of the surrounding empty background.
   left/top/width are set precisely by JS (layoutSphereHitbox in main.js),
   computed from the video's actual object-fit:cover crop math, so it
   tracks the sphere exactly at any viewport size instead of the old
   per-breakpoint percentage guesses. pointer-events stays off until JS
   has positioned it at least once, so it can never sit somewhere wrong
   and eat clicks before layout runs. */
.sphere-hitbox{
  position:absolute;
  top:0;
  left:0;
  width:0;
  height:0;
  border-radius:50%;
  background:transparent;
  pointer-events:none;
  z-index:2;
  cursor:pointer;
  /* touch only — lets touchstart/touchend on the sphere drive the video
     scrub (see main.js) instead of the browser treating the drag as a
     page pan. Has zero effect on mouse/pen input. */
  touch-action:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
}
.sphere-hitbox.positioned{ pointer-events:auto; }
/* toggle with `document.body.classList.add('debug-hitbox')` in devtools —
   never enable this class in markup/production. */
body.debug-hitbox .sphere-hitbox{
  outline:1px solid rgba(255,0,0,0.5);
  background:rgba(255,0,0,0.05);
}

/* right-edge safety valve so touch users can always scroll the page even
   though the sphere/preview objects capture drags with touch-action:none.
   Only rendered (via JS toggling touch-scroll-edge-active on <html>) while
   #hero or #index is actually in view — once scrolled past into contact/
   footer it disappears, so it never sits over unrelated controls like the
   footer's to-top button. Desktop/mouse: doesn't exist at all. */
.touch-scroll-edge{ display:none; }
@media (pointer:coarse){
  html.touch-scroll-edge-active .touch-scroll-edge{
    display:block;
    position:fixed;
    top:0; right:0; bottom:0;
    width:clamp(40px, 10vw, 64px);
    z-index:50;
    touch-action:pan-y;
    background:transparent;
  }
}

/* one-shot tap ripple for touch devices — a lightweight substitute for
   desktop hover feedback where there's no persistent pointer. */
.tap-pulse{
  position:fixed;
  z-index:9999;
  width:14px;
  height:14px;
  margin:-7px 0 0 -7px;
  border-radius:50%;
  background:radial-gradient(circle, rgba(255,253,242,0.55), rgba(255,253,242,0) 70%);
  pointer-events:none;
  animation:tap-pulse-anim 550ms cubic-bezier(0.16,1,0.3,1) forwards;
}
@keyframes tap-pulse-anim{
  0%{ transform:scale(0.4); opacity:0.85; }
  100%{ transform:scale(9); opacity:0; }
}

.hero-copy{
  position:relative;
  z-index:3;
  height:100vh;
  display:flex;
  flex-direction:column;
  justify-content:center;
  max-width:none;
  pointer-events:none;
}
.hero-title{
  line-height:0;
  margin:0;
}
.hero-wordmark{
  display:block;
  width:clamp(620px, 76vw, 1120px);
  max-width:none;
  height:auto;
  user-select:none;
  -webkit-user-drag:none;
}
/* a single fluid offset drives the asymmetric indent for both lines,
   instead of a fixed 9vw plus a one-off +10vw hack that only kicked in
   past 2000px — that combination is what made the block drift inconsistently
   instead of holding one deliberate offset across sizes. */
.hero-copy .hero-statement{
  font-size:1.1rem;
  font-weight:400;
  margin:24px 0 14px;
  letter-spacing:0.01em;
  max-width:550px;
  padding-left:clamp(24px, 8vw, 150px);
}
.hero-copy p{
  font-size:14.5px;
  line-height:1.7;
  letter-spacing:0.01em;
  color:#7d7b7b;
  margin:0;
  max-width:min(35vw, 550px);
  /* offset a touch further than the statement above it — the staggered
     indent between the two lines is the "asymmetric" part, not just the
     block's position against the page edge. */
  padding-left:clamp(36px, 10vw, 150px);
  opacity:0;
  transform:translateY(12px);
}
@media (prefers-reduced-motion: reduce){
  .hero-copy p{ opacity:1; transform:none; }
}

/* ---------------- INDEX ---------------- */
#index{
  --index-text-primary:#4b4749;
  --index-text-muted:#858889;
  --index-meta:#777b7c;
  --index-line:rgba(75,71,73,0.14);
  min-height:100vh;
  margin-top:0;
  padding:120px 100px 120px 50px !important;
  border-top:0;
  background:#d7d9d5;
  overflow:hidden;
}
#index::before{
  content:'';
  position:absolute;
  z-index:1;
  left:0;
  right:0;
  top:0;
  bottom:0;
  pointer-events:none;
  background:linear-gradient(
    to bottom,
    #d7d9d5 0,
    #d8dad6 80px,
    #ddddd8 170px,
    #e2e0da 240px,
    #e7e4dc 340px
  );
}
#index::after{
  content:'';
  position:absolute;
  z-index:1;
  inset:0;
  pointer-events:none;
  background:url("../assets/index_bg.webp") center / cover no-repeat;
  -webkit-mask-image:linear-gradient(
    to bottom,
    transparent 0,
    transparent 80px,
    #000 300px,
    #000 calc(100% - 240px),
    transparent 100%
  );
  mask-image:linear-gradient(
    to bottom,
    transparent 0,
    transparent 80px,
    #000 300px,
    #000 calc(100% - 240px),
    transparent 100%
  );
}
.index-eyebrow{
  font-family:'JetBrains Mono',monospace;
  font-size:11px; letter-spacing:0.14em; color:var(--index-meta);
  text-transform:uppercase; margin-bottom:64px;
}
.index-grid{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:40px;
  align-items:start;
}

#arrowup,
#arrowdown {
  display: inline-block;
}

#arrowup {
  display: none;
}

#arrowdown {
  display: inline-block;
}

@media (min-width:1440px){
  #index{ padding-left:0; padding-right:100px; }
  .index-eyebrow, .index-grid{
    max-width:1555px;
    margin-left:auto;
    margin-right:0;
  }
}
.row-list{ display:flex; flex-direction:column; }
.index-eyebrow-secondary{
  margin-bottom:0;
  padding:54px 0 18px;
}
.proj-row{
  display:flex;
  align-items:center;
  gap:18px;
  padding:22px 0;
  border-bottom:1px dashed var(--index-line);
  cursor:pointer;
}
.proj-row:first-child{ border-top:1px dashed var(--index-line); }
.proj-row .dot{
  width:5px; height:5px; background:var(--accent);
  opacity:0; transition:opacity .3s ease;
  flex-shrink:0; display: none;
}
.proj-row.active .dot{ opacity:1; }
.proj-row .name{
  font-size:1.8rem;
  font-weight:500;
  color:var(--index-text-muted);
  transition:color .35s ease;
  flex:1;
  text-transform: lowercase;
  letter-spacing: -0.05rem;
}
.proj-row.active .name{ color:var(--index-text-primary); }
.proj-row .status{
  font-family:'JetBrains Mono',monospace;
  font-size:10px;
  letter-spacing:0.1em;
  color:var(--index-meta);
  text-transform:uppercase;
  opacity:0.72;
  margin-left:auto;
}
.proj-row.active .status{ opacity:1; color:var(--index-meta); }
.pixel-text{
  font-family:'Press Start 2P', monospace !important;
  font-size:16px !important;
  letter-spacing:0 !important;
}
.name-pixel{
  width:130px;
  height:30px;
  flex:0 0 auto;
  image-rendering:pixelated;
  opacity:0.55;
  transition:opacity .35s ease;
}
.proj-row.active .name-pixel{ opacity:0.85; }
.info-panel .redacted-value{
  image-rendering:pixelated;
  height:11px;
  display:inline-block;
  vertical-align:middle;
}

.preview-zone{
  position:relative;
  display:flex;
  flex-direction:column;
  perspective:1000px;
}
.preview-stage{
  height:220px;
  position:relative;
}
.preview-canvas-holder{
  position:absolute; inset:0;
  opacity:0;
  transition:opacity .4s ease;
  pointer-events:none;
  cursor:pointer;
}
.preview-canvas-holder > div{
  width:100%;
  height:100%;
}
.preview-canvas-holder.visible{ opacity:1; pointer-events:auto; }
.preview-canvas-holder.preview-empty{ pointer-events:none; }
/* touch only, real 3D objects (BuzzSpark/BuzzCraft) — lets a drag rotate/
   glow the object (see attachCursorTilt/setupScene in main.js) instead of
   scrolling the page. Not applied to the anonymous pixel-art
   previews, which aren't 3D objects and already work fine via tap.
   user-select/user-drag/touch-callout stop the same native "mark this
   like text" / "drag this canvas like an image" behavior the hero video
   had — the WebGL canvas is just as susceptible to it as the video is. */
#preview-buzzspark, #preview-buzzcraft, #preview-halolayer{
  touch-action:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
  -webkit-user-drag:none;
  user-drag:none;
}
#preview-buzzspark canvas, #preview-buzzcraft canvas, #preview-halolayer canvas{
  -webkit-user-drag:none;
  user-drag:none;
}
.preview-canvas-holder canvas{
  width:100%;
  height:100%;
  display:block;
  filter:drop-shadow(0 18px 20px rgba(82,76,70,0.24));
}
.preview-canvas-holder.pixel canvas{ image-rendering:pixelated; filter:blur(0.4px); opacity:0.85; object-fit:contain; object-position:top; }

/* live crop — deliberately no border, no chrome, no device frame.
   just a plain rectangle showing the interface itself. */
.preview-window{
  position:relative;
  height:200px;
  margin-top:56px;
  overflow:hidden;
  background:var(--surface);
  cursor:pointer;
  color:inherit;
  text-decoration:none;
  filter:saturate(0.08) contrast(0.96);
  opacity:0;
  transform:translateY(8px);
  transition:opacity .5s ease, transform .5s ease, filter .45s cubic-bezier(0.16,1,0.3,1);
}
.preview-window.revealed{ opacity:1; transform:translateY(0); }
/* Anonymous projects have no real site to preview — the whole box
   (and its "Open" link) is hidden rather than shown empty or pointing
   nowhere. Set/cleared in setActive() in main.js. */
.preview-window.no-preview{ display:none; }
.preview-window:hover,
.preview-window:focus-visible,
.preview-window:active{ filter:saturate(1) contrast(1); }
.preview-window:focus-visible{
  outline:1px solid var(--index-text-primary);
  outline-offset:4px;
}
.crop-holder{
  position:absolute; inset:0;
  opacity:0;
  transition:opacity .4s ease;
}
.crop-holder.visible{ opacity:1; }
.crop-holder iframe{
  position:absolute;
  width:228%; height:280%;
  top:-8%; left:0;
  border:none;
  transform:scale(0.44);
  transform-origin:top left;
  pointer-events:none;
}

.crop-holder img{
    width:100%;
    height:100%;
    object-fit:cover;
    display:block;
     object-position: top center;
    transition:transform .6s cubic-bezier(.22,.61,.36,1);
}



.pixel-crop canvas{
  width:100%; height:100%;
  image-rendering:pixelated;
  opacity:0.7;
}
.open-btn{
  position:absolute;
  bottom:10px; right:12px;
  font-family:'JetBrains Mono',monospace;
  font-size:10px; letter-spacing:0.08em;
  text-transform:uppercase;
  color:var(--index-meta);
  background:rgba(245,245,245,0.85);
  border:none; padding:6px 12px;
  cursor:pointer;
  pointer-events:none;
}
.open-btn:hover{ color:var(--accent); }

.info-panel{
  font-family:'JetBrains Mono',monospace;
  font-size:11px;
  letter-spacing:0.03em;
  color:var(--text-muted);
  padding-top:0;
  margin-top:26px;
  opacity:0;
  transform:translateY(8px);
  transition:opacity .5s ease, transform .5s ease;
}
.info-panel.revealed{ opacity:1; transform:translateY(0); }
.info-panel .row{
  display:flex;
  justify-content:space-between;
  padding:3px 0;
}
.info-panel .row .k{ color:var(--index-text-muted); text-transform:uppercase; letter-spacing:0.1em; }
.info-panel .row .v{ color:var(--index-text-primary); text-transform:uppercase; letter-spacing:-0.03em; text-align:right; max-width:75%; }

@media (min-width:1330px){

.preview-window{
  height: 22dvw;
  margin-top: 5dvh;

}

/* the 3D previews (BuzzSpark/BuzzCraft) render at whatever size their
   container is — Three.js's resize() reads .preview-stage's own
   clientWidth/clientHeight, so growing the container here is what makes
   the rendered object itself look bigger, not a CSS transform:scale. */
.preview-stage{
  height: 340px;
}

}

@media (min-width:2000px){

.preview-window{
  height: 33dvh;
  margin-top: 5dvh;

}

.preview-stage{
  height: 440px;
}

.corner-label.bl{
    margin-left: 10vw;
}

/* on very wide viewports the hero video's subject sits well right of
   center — the wordmark and copy need to shift with it (and grow) so the
   title actually reads as covering the video rather than floating small
   and left against a much wider frame. */
.hero-copy{
  padding-left:8vw;
}
.hero-wordmark{
  width:clamp(620px, 62vw, 1520px);
}

}


@media (max-width:860px){
  .cursor-light-layer{ display:none; }
  #hero{ min-height:100svh; padding:0 24px; overflow:visible; }
  #hero-korwerk-video{ object-position:50% 36%; }
  .hero-copy{
    height:100svh;
    justify-content:center;
  }

  .hero-copy p{
        max-width: 75dvw;
        padding-left: clamp(24px, 8vw, 150px);
        margin-left: 0px;
              }
  .hero-wordmark{
    width:min(92vw, 760px);
  }
  .hero-copy .hero-statement{ margin-top:18px; }
  .corner-label.tr{ left:auto; right:24px; top:33px; }
  .corner-label.bl{ bottom:24px; left:24px; }
  .corner-label.tl{ top:24px; left:24px; width: 160px;}
  .layer-toggle a{ padding:5px 10px; }

  #index{ margin-top:0; padding:80px 24px !important; }
  .index-grid{ grid-template-columns:1fr; }
  .proj-row .name{ font-size:24px; }
  .preview-zone{ margin-top:24px; }
  .preview-stage{ height:220px; }
  .preview-window{ margin:auto; width: 80dvw; height: 40dvw; }

  #arrowup {
    display: inline-block;
  }

  #arrowdown {
    display: none;
  }

 .as-modeline { 
    display: none !important;
}
}

@media (min-width:861px) and (max-width:1180px){
  #hero{ padding:0 40px; }
  #hero-korwerk-video{ object-position:58% 44%; }
  .hero-wordmark{ width:min(82vw, 940px); }
  .corner-label.tr{ top:36px; right:40px; }
  .corner-label.bl{ left:40px; }
}

/* ---------------- CONTACT / FOOTER ---------------- */
.contact-footer-surface{
  position:relative;
  overflow:visible;
  background:#e7e4dc;
}
.contact-footer-surface::before{
  content:'';
  position:absolute;
  z-index:1;
  inset:0;
  pointer-events:none;
  background:url("../assets/index_bg.webp") center bottom / cover no-repeat;
  -webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 220px);
  mask-image:linear-gradient(to bottom, transparent 0, #000 220px);
}
/* capy mascot — sits above the shared background texture but below all
   real content (email, footer text/links). Invisible at rest; only
   appears as a one-shot "flash" triggered from JS, never a permanent
   watermark. Sized by height and flush to the wrapper's bottom edge (no
   negative bottom offset) so it fits inside .contact-footer-surface's
   own box without needing overflow:visible — only a small horizontal
   bleed off the right edge. mix-blend-mode:screen exists for the mp4
   fallback's solid black background — capy_transparent.webm already
   carries real alpha, so the blend is a no-op there. */
.capy-video{
  position:absolute;
  z-index:2;
  right:-2vw;
  bottom:0;
  width:auto;
  height:min(75vh, 900px);
  aspect-ratio:1062 / 1242;
  object-fit:contain;
  object-position:bottom right;
  mix-blend-mode:screen;
  opacity:0;
  pointer-events:none;
  transition:opacity .9s ease;
}
.capy-video.visible{ opacity:0.6; }
@media (prefers-reduced-motion: reduce){
  .capy-video{ display:none; }
}
@media (max-width:860px){
  .capy-video{ height:min(55vh, 550px); right:-4vw; }
}
#contact{
  position:relative;
  z-index:3;
  padding:100px 64px 60px;
  text-align:left;
  overflow:visible;
  background:transparent;
  width: 65vw;
}
.email-copy{
font-size: clamp(55px, 8vw, 198px);
  font-weight: 700;
  color: #e3e3de;
  cursor: pointer;
  display: inline-block;
  position: relative;
  letter-spacing: -0.03em;
  max-width:100%;
  text-decoration:none !important;
  -webkit-text-decoration:none;
  -webkit-tap-highlight-color:transparent;
}

.email-copy:hover{ color: #f1f1ec; }
.email-copy:link,
.email-copy:visited,
.email-copy:hover,
.email-copy:active,
.email-copy:focus,
.email-copy.copied,
.email-copy *,
.email-copy a,
.email-copy a:link,
.email-copy a:visited,
.email-copy a:hover,
.email-copy a:active
.email-compose{
  text-decoration:none !important;
  -webkit-text-decoration:none !important;
}
.email-copy:focus-visible{ outline:1px solid rgba(164,164,164,0.38); outline-offset:5px; }
.email-copy{ transition: filter .5s ease, opacity .5s ease, color .35s cubic-bezier(0.16, 1, 0.3, 1); }
.email-copy.obscured{ filter: blur(9px); opacity:0.45; }
.email-copy .copied-tag{
  position:absolute;
  top:-11px; right:0; left:auto;
  font-family:'JetBrains Mono',monospace;
  font-size:11px; letter-spacing:0.1em;
  color:var(--text-primary);
  text-transform:uppercase;
  opacity:0;
  transform:translateY(4px);
  transition:opacity .3s ease, transform .3s ease;
  pointer-events:none;
  font-weight: 100;
  display:flex;
  align-items:center;
  gap:8px;
}
.email-copy.copied .copied-tag{ opacity:1; transform:translateY(0); pointer-events:auto; }
.email-compose{
  width:22px;
  height:22px;
  display:grid;
  place-items:center;
  color:var(--text-muted);
  border-radius:50%;
  transition:color .25s ease, background-color .25s ease;
}
.email-compose:hover,
.email-compose:focus-visible{
  color:var(--accent);
  background:rgba(255,255,255,0.28);
}
.email-compose:focus-visible{ outline:1px solid currentColor; outline-offset:2px; }
.email-compose svg{
  width:14px;
  height:14px;
  fill:none;
  stroke:currentColor;
  stroke-width:1.5;
  stroke-linecap:round;
  stroke-linejoin:round;
}

footer{
  position:relative;
  z-index:3;
  padding:28px 64px 40px;
  display:flex;
  justify-content:space-between;
  align-items:center;
  font-family:'JetBrains Mono',monospace;
  font-size:11px;
  letter-spacing:0.08em;
  color:var(--text-muted);
  text-transform:uppercase;
  flex-wrap:wrap;
  gap:16px;
}
@media (min-width:1440px){
  footer{
    max-width:1100px;
    margin:0 auto;
    padding-left:0;
    padding-right:0;
  }
}
.footer-left{ display:flex; align-items:center; gap:6px; position:relative; }
.footer-legal-links{ display:inline-flex; align-items:center; gap:6px; margin-left:10px; }
.footer-left button, .footer-left a{
  background:none; border:none; padding:0;
  font-family:inherit; font-size:inherit; letter-spacing:inherit;
  color:var(--text-muted); text-transform:inherit;
  text-decoration:none;
  cursor:pointer;
}
.footer-left button:hover, .footer-left a:hover{ color:var(--text-primary); }

.bio-trigger{
  cursor:pointer;
  color:var(--text-muted);
  transition:color .2s ease;
}
.bio-trigger:hover,
.bio-trigger.open,
.bio-trigger:focus-visible{
  color:var(--text-primary);
}

.zm-name{ position:relative; cursor:pointer; color:var(--text-muted); }

#bio-panel{
  position:absolute;
  bottom:100%; left:40px;
  margin-bottom:20px;
  width:min(560px, 60vw);
  font-family:'Inter', sans-serif;
  font-size:1.1rem;
  line-height:1.3rem;
    color: var(--text-primary);
  background:transparent;
  opacity:0;
  transform:translateY(10px);
  transition:opacity .45s ease, transform .45s ease;
  pointer-events:none;
      text-transform: lowercase;
    letter-spacing: -0.03rem;
}
#bio-panel.open{ opacity:1; transform:translateY(0); pointer-events:auto; }
#bio-panel .line{ display:block; }
#bio-panel .line:nth-child(1){ padding-left:0; }
#bio-panel .line:nth-child(2){ padding-left:34px; }
#bio-panel .line:nth-child(3){ padding-left:68px; }
#bio-panel .line:nth-child(4){ padding-left:102px; }
#bio-panel .line.signoff{ text-align:right; padding-left:0; margin-top:10px; }
#bio-panel strong{ color:var(--text-primary); font-weight:400; }

  @media (max-width:2000px){

#contact{ padding:70px 5px 40px; }

.email-copy {
  font-size: clamp(55px, 8vw, 177px);
  font-weight: 700;
     letter-spacing: -0.03em;
}

}


@media (max-width:860px){
  .email-copy{
    display:block;
    width:100%;
    font-size:clamp(42px, 13.5vw, 88px);
    line-height:0.98;
  }
  .email-domain{ display:block; }
  .email-copy .copied-tag{ top:-32px; }
  #bio-panel{ width:min(380px, 78vw); left:0; font-size: 1rem; }
  #bio-panel .line:nth-child(2){ padding-left:18px; }
  #bio-panel .line:nth-child(3){ padding-left:36px; }
  #bio-panel .line:nth-child(4){ padding-left:54px; }
}

@media (prefers-reduced-motion:reduce){
  .corner-label.bl .dot{ animation:none; opacity:0.72; }
}
.footer-social{ display:flex; align-items:center; gap:18px; }
.footer-social a{
  color:var(--text-muted); text-decoration:none;
  font-family:'JetBrains Mono',monospace; font-size:0.8rem; letter-spacing:0.1em;
}
.footer-social a:hover{ color:var(--text-primary); }
footer .to-top{
  background:none; border:none;
  font-family:'JetBrains Mono',monospace;
  font-size:11px; letter-spacing:0.08em;
  color:var(--text-muted);
  text-transform:uppercase;
  cursor:pointer;
  display:flex; align-items:center; gap:6px;
}
footer .to-top:hover{ color:var(--text-primary); }

@media (max-width:860px){
  #contact{ padding:70px 24px 40px; }
  footer{
    padding:24px 24px 56px;
    display:grid;
    grid-template-columns:minmax(0, 1fr) auto;
    align-items:center;
    gap:24px 16px;
  }
  .footer-left{
    grid-column:1;
    grid-row:2;
    flex-wrap:wrap; row-gap:10px;
    position:relative;
    bottom:auto; left:auto; right:auto;
  }
  /* force onto its own line instead of leaving it to flex-wrap, which
     could otherwise split "Imprint", "·" and "Privacy" apart from each
     other mid-group */
  .footer-legal-links{
    flex-basis:100%;
    margin-left:0;
  }
  /* left-aligned rather than right — the touch-scroll-edge strip lives
     at the right edge of the viewport (fixed, full height, needed so a
     drag anywhere in #hero/#index can still scroll the page instead of
     being captured by the sphere/preview objects). During the scroll
     transition into the footer there's a moment where the strip and the
     top of the footer are both on screen, so anything right-aligned here
     could get its taps swallowed by the strip above it. */
  .footer-social{
    grid-column:1 / -1;
    grid-row:1;
          flex-direction: column;
        align-items: flex-end;
    gap:12px;
            pointer-events: auto;
        padding-right: 5px;
  }

  .footer-social a {

    font-size: 1.1rem;
   
}

  footer .to-top{
    grid-column:2;
    grid-row:2;
    position:static;
    align-self:flex-end;
    background:rgba(245,245,245,0.55);
    padding:8px 12px;
  }
}

/* ---------------- LEGAL PAGES (imprint / privacy) ----------------
   Real, directly-linkable pages — not a modal. The homepage White Smoke
   background + grain (from body / body::after above) stays visible behind
   and above the sheet; <main> is just a tall Parchment-colored block that
   starts a little way down the page and animates up into place on load.
   No JS is required to reach or read the content — js/legal.js only
   smooths the entrance (implicitly, via the CSS animation already
   running) and the exit-to-homepage transition. */
body.legal-page{
  min-height:100vh;
  background-color:#e7e4dc;
  background-image:url("../assets/index_bg.webp");
  background-size:cover;
  background-position:center;
  background-attachment:fixed;
}
.legal-sheet{
  position:relative;
  z-index:2;
  min-height:100vh;
  background:transparent;
  transform:translateY(0);
  opacity:1;
  transition:transform 420ms cubic-bezier(0.16,1,0.3,1), opacity 420ms cubic-bezier(0.16,1,0.3,1);
}
.legal-sheet.leaving{
  transform:translateY(12px);
  opacity:0;
}
@media (prefers-reduced-motion: reduce){
  .legal-sheet{ animation:none; transition:none; }
}

.legal-sheet-header{
  position:absolute;
  z-index:10;
  display:flex;
  align-items:center;
  justify-content:flex-start;
  padding:32px 48px;
  background:transparent;
      bottom: 10dvh;
    left: 5dvw;
}
.legal-close{
  position:relative;
  z-index:1;
  display:inline-flex;
  align-items:center;
  min-height:44px;
  padding:0 4px;
  pointer-events:auto;
  font-family:'JetBrains Mono',monospace;
  font-size:11px;
  letter-spacing:0.1em;
  color:#777b7c;
  text-transform:uppercase;
  text-decoration:none;
}
.legal-close:hover{ color:var(--accent); }
.legal-close:focus-visible,
.legal-sheet a:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:3px;
}

.legal-main{
  width:min(100% - 96px, 980px);
  margin:0 auto;
  padding:clamp(72px, 12vh, 150px) 0 140px;
}

/* fixed to the viewport bottom, same idea as .corner-tr's is-sticky —
   now the same footer-left/footer-social content (bio reveal + capy
   mascot + legal links) as Building/Studio, reusing those exact classes
   rather than a separate legal-only style. Centered to the same
   max-width:1100px as Building's own footer and Studio's .as-footline. */
.legal-footer{
  position:fixed; bottom:0; left:50%; z-index:5;
  transform:translateX(-50%);
  width:min(100% - 96px, 1100px);
  display:flex; align-items:center; justify-content:space-between;
  padding:28px 64px 40px;
  font-family:'JetBrains Mono',monospace; font-size:11px; letter-spacing:0.08em;
  color:var(--text-muted); text-transform:uppercase;
  pointer-events:none;
}
.legal-footer :is(a,button,[tabindex]){ pointer-events:auto; }
/* Building's capy-video is positioned absolute against a tall
   .contact-footer-surface section it lives inside; legal pages have no
   equivalent tall ancestor, so it's fixed directly to the viewport here —
   same size, same bottom-right corner, just anchored to the page instead
   of a section. */
.legal-page .capy-video{ position:fixed; pointer-events:none; }
@media (max-width:860px){
  .legal-footer{ 
            padding: 24px 24px 56px;
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
        gap: 24px 16px;
        width: 100%;
  }
}
.legal-main h1{
  font-family:'Inter',sans-serif;
  font-size:clamp(48px, 7vw, 92px);
  font-weight:300;
  line-height:0.95;
  margin:0 0 clamp(70px, 10vw, 120px);
  letter-spacing:-0.03em;
  text-wrap:balance;
}
.legal-block{
  
  gap:36px;
  padding:24px 0 28px;
  border-top:1px dashed rgba(75,71,73,0.16);
}
.legal-block h2{
  font-family:'Inter',sans-serif;
  font-size:13px;
  font-weight:400;
  margin-bottom:15px;
  color:#656263;
}
/* .legal-block is a 2-column grid (180px label + content); with more than
   one paragraph/list per block, default grid auto-flow wraps the extra
   items into column 1 on a new row instead of continuing in column 2 —
   pinning every non-heading child to column 2 keeps them stacked under
   each other in the content column regardless of how many there are. */
.legal-block > p,
.legal-block > ul{
  grid-column:2;
}
.legal-main p,
.legal-main li,
.legal-main a,
.legal-main a:link,
.legal-main a:visited,
.legal-main a:hover,
.legal-main a:active {
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  line-height: 1.75;
  color: #696667;
  margin: 0;
  max-width: 68ch;
  text-wrap: pretty;

  -webkit-text-decoration: none !important;
  text-decoration: none !important;
  -webkit-text-fill-color: currentColor;
}
.legal-main ul{ padding-left:20px; margin:0 0 14px; }
.legal-main a {
    color: var(--text-primary);
    text-decoration: none;
}
.legal-note{
  margin:56px 0 0 216px !important;
  font-size:12px !important;
  color:#858182 !important;
}

@media (max-width:860px){
  body.legal-page{ background-attachment:scroll; }
  .legal-sheet-header{ padding:22px 24px; gap:24px; bottom:max(220px, 24dvh);
        left: 0px; }
  .legal-sheet{ padding-bottom: 50px;
 }

  .legal-main{ width:calc(100% - 48px); padding:84px 0 100px; margin-bottom: 200px; }
  .legal-main h1{ margin-bottom:64px; }
  .legal-block{ grid-template-columns:1fr; gap:12px; padding:20px 0 24px; }
  .legal-note{ margin:42px 0 0 !important; }
}

/* ---------------- ZENSTUDIO ----------------
   A separate layer from the main site, not a sub-page of it — cooler,
   heavier "instrument" stone instead of the homepage's White Smoke,
   deliberately not a dark-mode inversion. Same clay accent as the rest of
   ZenMaker for continuity. Desktop/tablet: the page itself is locked
   (no scroll/swipe) and only the work list + thumbnail rail scroll,
   independently, in their own bounded panels — mirroring the same
   touch-action:none technique already used for the hero sphere. Mobile
   drops the lock entirely and becomes a normal scrolling single column,
   matching this codebase's existing convention of reserving pinned/locked
   behavior for desktop and falling back to plain scroll below 861px (see
   the ScrollTrigger index pin in js/main.js for the precedent). */
:root{
  --as-bg:#EBEDE7;
  --as-surface:#DFE2DA;
  --as-ink:#2E3130;
  --as-muted:#868C84;
  --as-line: rgba(46,49,48,0.14);
  --as-accent:#A73E2E;
}
html.studio-page,
body.studio-page{
  margin:0; padding:0;
  background:var(--as-bg);
  color:var(--as-ink);
}
body.studio-page{
  font-family:'Inter',sans-serif;
  font-weight:300;
  position:relative;
}
html.studio-page:has(.studio-frame.as-open),
body.studio-page:has(.studio-frame.as-open){
  overflow:hidden;
  touch-action:none;
  overscroll-behavior:none;
}
body.studio-page::after{
  /* same grain treatment as the rest of the site, just over the cooler
     ground */
  content:'';
  position:fixed; inset:0;
  pointer-events:none;
  z-index:999;
  opacity:0.025;
  mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}
.studio-frame{
  position:relative;
  /* top/left match #hero's .corner-label.tl{top:32px;left:64px} exactly —
     the toggle pill is position:static here (vs. absolute on the hero) so
     this padding is what actually places it; any mismatch reads as a jump
     the instant the page repaints after clicking Studio. */
  padding:32px 64px 20px;
  display:flex;
  flex-direction:column;
}
.studio-frame::before{
  content:'';
  position:absolute; inset:0; pointer-events:none;
  background:repeating-linear-gradient(
    to bottom, rgba(46,49,48,0.02) 0px, rgba(46,49,48,0.02) 1px,
    transparent 1px, transparent 3px
  );
  mix-blend-mode:multiply;
}
@media (prefers-reduced-motion: reduce){
  .studio-frame::before{ display:none; }
}

.as-topline{
  display:flex; justify-content:space-between; align-items:flex-start;
  font-family:'JetBrains Mono',monospace; font-size:11px; letter-spacing:0.08em;
  color:var(--as-muted); text-transform:uppercase; line-height:1.7;
  position:relative; z-index:1; flex-shrink:0;
}
.as-topline .as-back{
  color:var(--as-ink); text-decoration:none; border-bottom:1px solid var(--as-line);
  padding-bottom:2px;
}
.as-topline .as-back:hover{ border-color:var(--as-accent); color:var(--as-accent); }
.as-topline .as-back:focus-visible{ outline:1px solid var(--as-accent); outline-offset:3px; }
.as-topline .as-telemetry{ text-align:right; max-width:300px; }
/* positioned like .corner-label.tr (top:55px/right:88px, same breakpoints)
   rather than inline in .as-topline's flex row — this is what makes it
   land in the exact same screen position as the Building tab's corner
   link, in both Macro and Micro. Typography intentionally NOT overridden
   here (no custom font-family/size/text-transform/min-width like before) —
   it inherits straight from .corner-label so the two pages' corner links
   are the same element visually, not just the same coordinates. */
.as-contact{
  display:inline-block;
  color:#626762;
  white-space:nowrap;
  transition:color .2s ease;
}
.as-contact #corner-tr-display{
  display:inline-block;
  transition:border-color .2s ease;
}
.as-contact:hover #corner-tr-display{ border-color:#626762; }
.as-contact:focus-visible{ outline:1px solid var(--as-accent); outline-offset:4px; }

.as-mission{ max-width:520px; margin:26px 0 0; position:relative; z-index:1; flex-shrink:0; }
.as-mission h1{
  font-family:'JetBrains Mono',monospace;
  font-size:16px; font-weight:700; letter-spacing:0.01em;
  margin:0 0 10px; text-transform:uppercase;
}
.as-mission p{
  font-family:'Inter',sans-serif; font-size:13px; line-height:1.7;
  color:var(--as-muted); margin:0; max-width:46ch;
  /* reserved for the longest of the mission copy / any work description,
     so swapping the text while projected (see js/studio.js) never
     reflows .as-body and shifts .as-list up or down */
  min-height:calc(1.7em * 4);
}

.as-body{
  display:grid;
  grid-template-columns:333px 1fr 140px;
  gap:28px;
  margin-top:28px;
  position:relative; z-index:1;
  flex:1;
  min-height:0;
}

.as-list{ display:flex; flex-direction:column; overflow-y:auto; min-height:0; touch-action:pan-y; }
.as-item{
  display:flex; align-items:baseline; gap:12px;
  padding:14px 0; border-top:1px dashed var(--as-line);
  cursor:pointer; background:none; border-left:none; border-right:none; border-bottom:none;
  text-align:left; width:100%; font-family:inherit; color:inherit; flex-shrink:0;
  transition:filter .45s ease, opacity .45s ease;
}
/* artemartemartem.com-style focus: hovering one row blurs/dims the rest
   rather than just highlighting the hovered one, so the list reads as one
   blended block that resolves around wherever attention is. */
.as-list.is-hovering .as-item:not(.is-hovered){
  filter:blur(3px);
  opacity:0.42;
}
@media (prefers-reduced-motion: reduce){
  .as-item{ transition:none; }
  .as-list.is-hovering .as-item:not(.is-hovered){ filter:none; opacity:1; }
}
.as-list .as-item:last-child{ border-bottom:1px dashed var(--as-line); }
.as-item .as-idx{
  font-family:'JetBrains Mono',monospace; font-size:11px; color:var(--as-muted);
  font-variant-numeric:tabular-nums;
}
.as-item .as-name{
  font-family:'Inter',sans-serif; font-size:15px; font-weight:400;
  color:var(--as-muted); transition:color .2s ease; flex:1;
}
.as-item.active .as-name{ color:var(--as-ink); }
.as-item.active .as-idx{ color:var(--as-ink); }
.as-item .as-medium{
  font-family:'JetBrains Mono',monospace; font-size:1rem; letter-spacing:0.06em;
  color:var(--as-muted); text-transform:uppercase; opacity:0.7;
}
.as-item:focus-visible{ outline:1px solid var(--as-accent); outline-offset:-2px; }

.as-viewer{
  position:relative;
  border-radius:3px;
  overflow:hidden;
  width:100%;
  height:100%;
  min-height:360px;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:10px;
  padding-bottom:24px;
  touch-action:none;
}
.as-viewer .as-frametag{
  position:relative; order:2; z-index:2;
  width:100%; justify-content:center;
  font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:0.06em;
  color:var(--as-muted); text-transform:uppercase;
  display:flex; align-items:center; gap:6px;
}
.as-viewer .as-frametag .as-led{
  width:5px; height:5px; border-radius:50%; background:#ffffff;
  box-shadow:0 0 6px #ffffff, 0 0 6px #ffffff; 
}
.as-art{
  order:1;
  position:relative;
  width:min(46vh, 330px);
  aspect-ratio:19 / 24.4;
  flex-shrink:0;
  box-shadow:0 22px 48px -24px rgba(46,49,48,0.4);
  touch-action:none;
  cursor:pointer;
  perspective:900px;
  transition:box-shadow .5s ease, border-radius .5s ease;
}
.studio-frame:not(.as-open) .as-art,
.studio-frame:not(.as-open) .as-frametag{ transform:translateY(-28px); }
.as-art canvas,
.as-art img,
.as-art video{
  display:block; width:100%; height:100%;
  object-fit:cover;
  object-position:center top;
  transition:transform .45s cubic-bezier(.16,1,.3,1), opacity .6s ease, filter .6s ease;
  will-change:transform, opacity, filter;
}
.as-art video{
  position:absolute; inset:0; object-fit:cover; z-index:4;
}
.as-art img{
  position:absolute; inset:0; z-index:2;
}
.as-art canvas{
  position:absolute; inset:0; z-index:3;
  opacity:0;
  pointer-events:none;
}
.studio-frame:not(.as-open) .as-art.is-morphing canvas{ opacity:1; transition:none; }
.as-art video[hidden],
.as-art img[hidden],
.as-art canvas[hidden]{ display:none; }
@media (prefers-reduced-motion: reduce){
  .as-art canvas, .as-art video{ transition:none; }
}

/* ---- expanded detail state: the artwork dissolves into the frame as a
   full-bleed background (position/size jump instantly; the canvas itself
   fades up from a blur so it *materializes* rather than sliding/stretching
   into place) — a minimal HUD panel centers over it. Triggered by clicking
   the artwork; `.is-settled` is added a frame later by JS so the opacity/
   filter transition actually has a "from" state to animate out of.

   Targeted via a class on .as-art itself (.is-projected), NOT a
   `.studio-frame.as-open .as-art` descendant selector — JS physically
   reparents .as-art to a direct child of <body> when this is active (see
   js/studio.js openMicroMode/closeDetail). That's deliberate: nested
   inside .as-viewer > .as-body > .studio-frame, this element's
   position:fixed geometry kept getting visually clipped/contained by
   ancestor overflow/grid/stacking behavior that varied by browser (the
   element's own computed rect was correct in testing, but what actually
   painted wasn't) — moving it out of that ancestor chain entirely removes
   the whole class of bug instead of chasing each ancestor property. ---- */
.as-art.is-projected{
  position:fixed; inset:0; width:100vw; aspect-ratio:auto;
  /* 100vh doesn't account for mobile Safari/Chrome's dynamic address bar —
     leaves a real gap at the bottom that reads as "not actually full
     screen". 100dvh (dynamic viewport height) tracks the true visible
     area; the vh line stays first as a fallback for browsers without dvh
     support, and gets overridden by the second declaration where it is
     supported. */
  height:100vh; height:100dvh;
  z-index:40; box-shadow:none; border-radius:0;
}
.as-art.is-projected canvas,
.as-art.is-projected img,
.as-art.is-projected video{
  transform:none !important;
  opacity:0;
  filter:blur(26px) saturate(1.2);
}
.as-art.is-projected.is-settled canvas,
.as-art.is-projected.is-settled img,
.as-art.is-projected.is-settled video{
  opacity:1;
  filter:none;
}
@media (prefers-reduced-motion: reduce){
  .as-art.is-projected canvas,
  .as-art.is-projected img,
  .as-art.is-projected video{ opacity:1; filter:none; }
}

.studio-frame.as-open .as-rail,
.studio-frame.as-open .as-frametag,
.studio-frame.as-open .as-list,
.studio-frame.as-open .as-mission{
  opacity:0; visibility:hidden; pointer-events:none; transition:opacity .3s ease;
}
/* .as-viewer's own overflow:hidden (needed to clip the docked/Macro
   artwork's rounded corners) also clips its position:fixed child once
   Micro mode makes .as-art fullscreen — the element's own geometry is
   still correctly viewport-sized (fixed positioning doesn't care about
   ancestor overflow for *placement*), but this ancestor still visually
   clips it to its own small grid-cell box during paint. That's what was
   rendering as "not actually full screen" — geometry was right, paint
   wasn't. */
.studio-frame.as-open .as-viewer{ overflow:visible; }
/* Let the Micro-mode children join the frame's stacking order directly:
   artwork 40, detail layer 41, navigation/list 42. The default z-index on
   .as-body would otherwise trap the list below the full-screen detail hit area. */
.studio-frame.as-open .as-body{ z-index:auto; }
/* Site controls remain available over the projected artwork. Work copy is
   owned by the fixed editorial detail layer below. */
.studio-frame.as-open .as-topline,
.studio-frame.as-open .as-footline{
  position:relative; z-index:42;
}
.studio-frame.as-open .as-topline,
.studio-frame.as-open .as-footline{
  color:rgba(235,237,231,0.39);
  transition:color .3s ease;
}
.studio-frame.as-open .layer-toggle{
  background:rgb(0 0 0 / 50%);
  border-color:var(--line);
}
.studio-frame.as-open .layer-toggle a{ color:rgba(237,237,237,0.68); }
.studio-frame.as-open .layer-toggle a.on{
  background:#ededed;
  color:var(--text-primary);
}
.studio-frame.as-open .layer-toggle a:not(.on):hover{
  background:rgb(237 237 237 / 0%);
  color:#ededed;
}
.studio-frame.as-open .as-topline a{ color:rgba(235,237,231,0.85); }
.studio-frame.as-open .as-contact{ color:rgba(235,237,231,0.82); }
.studio-frame.as-open #corner-tr-display{ border-color:rgba(235,237,231,0.24); }
.studio-frame.as-open .as-contact:hover{ color:var(--as-bg); }
.studio-frame.as-open .as-contact:hover #corner-tr-display{ border-color:rgba(235,237,231,0.65); }
.studio-frame.as-open .as-footline{ border-top-color:rgba(235,237,231,0.16); }
.studio-frame.as-open .as-footline .as-modeline button.on{ color:rgba(235,237,231,0.9); }

.as-detail{
  position:fixed; inset:0; z-index:41;
  display:flex; align-items:center; justify-content:flex-start;
  padding:96px clamp(24px, 4.5vw, 72px);
  opacity:0; pointer-events:none;
  transition:opacity .4s ease;
  padding-left: 5vw;
}
.as-detail::before{
  content:'';
  position:absolute; inset:0;
  background:linear-gradient(90deg, rgba(12,13,12,0.62) 0%, rgba(20,22,21,0.38) 28%, rgba(20,22,21,0.08) 58%, rgba(12,13,12,0.22) 100%);
  pointer-events:none;
}
.studio-frame.as-open .as-detail{ opacity:1; pointer-events:auto; }
.as-detail-copy{
  position:relative; z-index:1;
  max-width:none; width:min(55vw, 1500px);
  text-align:left;
  color:var(--as-bg);
  transform:translateX(-18px);
  opacity:0;
  transition:transform .5s cubic-bezier(.16,1,.3,1) .1s, opacity .5s ease .1s;
}
.studio-frame.as-open .as-detail-copy{ transform:translateX(0); opacity:1; }
.as-detail-index{
  font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:0.14em;
  color:rgba(235,237,231,0.55); text-transform:uppercase; margin-bottom:12px; display:none;
}
.as-detail-copy h2{
  margin:0 0 8px; font-family:'Inter',sans-serif; font-weight: 800;
    font-size: clamp(26px, 31vw, 300px);
    letter-spacing: -0.03em;
    text-transform: lowercase; line-height: 0.9;
}
.as-detail-medium{
  margin:0 0 16px; font-family:'JetBrains Mono',monospace; font-size:1rem;
  letter-spacing:0.08em; text-transform:uppercase; display: none;
}
.as-detail-desc{
  margin:0 0 20px; font-size:1rem; line-height:1.65; color:rgba(235,237,231,0.82); font-weight:300; font-family: monospace;
}
.as-detail-images{
  position:fixed;
  left:50%; bottom:clamp(68px, 8vh, 96px);
  z-index:2;
  display:flex; justify-content:center; align-items:flex-end; gap:10px;
  transform:translateX(-50%);
  pointer-events:auto;
}
.as-detail-images[hidden]{ display:none; }
.as-detail-images img,
.as-detail-images video{
  display:block;
  width:clamp(72px, 11vw, 152px);
  height:clamp(48px, 8vw, 122px);
  object-fit:cover;
  border-radius:2px;
  opacity:0.78;
  filter:saturate(0.8) contrast(1.04);
  box-shadow:0 5px 8px rgba(0,0,0,0.18);
  user-select:none;
  cursor:default;
}
/* fixed to the viewport, not inside .as-detail-copy — that panel gets a
   scroll-scrubbed translate3d() every frame while scrolling/swiping in
   Micro mode (see setMicroDetailProgress in js/studio.js), which used to
   drag the nav buttons along with it. Centered directly under
   .as-detail-images, same horizontal anchor (left:50%), sitting at a
   smaller bottom offset so it reads as belonging to that row. */
.as-detail-nav{
  position:fixed;
  left:50%; bottom:28px;
  transform:translateX(-50%);
  z-index:2;
  display:flex; justify-content:center; gap:18px;
  font-family:'JetBrains Mono',monospace; font-size:9px; letter-spacing:0.1em;
  text-transform:uppercase; color:rgba(235,237,231,0.6);
}
.as-detail-nav button{
  all:unset; cursor:pointer; color:rgba(235,237,231,0.6);
  transition:color .2s ease;
}
.as-detail-nav button:hover,
.as-detail-nav button:focus-visible{ color:var(--as-bg); }

.as-rail{
  display:flex; flex-direction:column; gap:9px; overflow-y:auto; min-height:0; touch-action:pan-y;
  -ms-overflow-style:none; scrollbar-width:none;
}
.as-rail::-webkit-scrollbar{ width:0; height:0; display:none; }
.studio-frame:not(.as-open) .as-rail{ margin-top:0; align-self:start; width:100%; }
.as-thumb{
  position:relative;
  border-radius:2px; overflow:hidden; cursor:pointer;
  height:130px; flex-shrink:0;
  filter:grayscale(0.35) contrast(1.02);
  border:1px solid transparent;
  transition:border-color .2s ease, filter .2s ease;
}
.as-thumb img{ display:block; width:100%; height:100%; object-fit:cover; }
.as-thumb.active{ border-color:var(--as-ink); }
.as-thumb:hover{ filter:grayscale(0) contrast(1.08); }
.as-thumb:focus-visible{ outline:1px solid var(--as-accent); outline-offset:2px; }

@media (min-width:861px){
  .studio-frame:not(.as-open) .as-rail{ margin-top:111px; }
  .as-rail{
    height:clamp(448px, calc(50vh + 68px), 548px);
    max-height:100%;
    padding-block:16px;
    overscroll-behavior:contain;
    scroll-snap-type:y proximity;
    scroll-padding-block:16px;
    -webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
    mask-image:linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
  }
  .as-thumb{
    height:clamp(76px, 10vh, 96px);
    scroll-snap-align:start;
  }
}

.as-footline{
  display:flex; justify-content:space-between; align-items:center;
  margin-top:20px; padding-top:16px; border-top:0px solid var(--as-line);
  font-family:'JetBrains Mono',monospace; font-size:10px; letter-spacing:0.08em;
  color:var(--as-muted); text-transform:uppercase;
  position:relative; z-index:1; flex-shrink:0;
  width:100%;
}
@media (min-width:1440px){
  .as-footline{ max-width:1100px; margin-left:auto; margin-right:auto; }
}
.as-footline-links{ display:flex; align-items:center; gap:8px; }
.as-footline-links a{
  color:inherit; text-decoration:none; transition:color .2s ease;
}
.as-footline-links a:hover{ color:var(--as-ink); }
.studio-frame.as-open .as-footline-links a:hover{ color:rgba(235,237,231,0.9); }
.as-footline-sep{ opacity:0.5; margin:0 2px; }
.as-footline .as-modeline{ display:flex; gap:14px; }
.as-footline .to-top{
  appearance:none; border:0; padding:0; background:none;
  color:inherit; font:inherit; letter-spacing:inherit; text-transform:inherit;
  cursor:pointer; transition:color .2s ease; display:none;
}
.as-footline .to-top:hover{ color:var(--as-ink); }
.as-footline .to-top:focus-visible{ outline:1px solid currentColor; outline-offset:4px; }
.as-footline .as-modeline button{
  appearance:none; border:0; padding:0; background:none;
  color:inherit; font:inherit; letter-spacing:inherit; text-transform:inherit;
  cursor:pointer; transition:color .2s ease;
}
.as-footline .as-modeline button:hover{ color:var(--as-ink); }
.as-footline .as-modeline button.on{ color:var(--as-ink); }
.as-footline .as-modeline button:focus-visible{ outline:1px solid currentColor; outline-offset:4px; }
.studio-frame.as-open .as-footline .as-modeline button:hover{ color:rgba(235,237,231,0.9); }

/* Micro mode: let the quiet footer brighten as one surface on hover or
   keyboard focus, including children that normally own a muted color. */
.studio-frame.as-open .as-footline:hover,
.studio-frame.as-open .as-footline:focus-within{
  color:rgba(235,237,231,0.78);
}
.studio-frame.as-open .as-footline:hover :is(.footer-left button, .footer-left a, .bio-trigger, .zm-name, .footer-social a, .to-top, .as-modeline button),
.studio-frame.as-open .as-footline:focus-within :is(.footer-left button, .footer-left a, .bio-trigger, .zm-name, .footer-social a, .to-top, .as-modeline button){
  color:rgba(235,237,231,0.78);
}
.studio-frame.as-open .as-footline:hover .as-modeline button.on,
.studio-frame.as-open .as-footline:focus-within .as-modeline button.on{
  color:rgba(235,237,231,0.96);
}

/* the actual page lock — desktop/tablet only. overflow:hidden on a fixed
   100vh html+body blocks scroll for every input method (wheel, touch,
   keyboard); touch-action:none is the belt-and-suspenders layer already
   proven elsewhere in this codebase for touch-specific gesture handling.
   .as-list/.as-rail's own touch-action:pan-y above is what lets those two
   panels keep scrolling normally despite the ancestor lock. */
@media (min-width:861px){
  html.studio-page, body.studio-page{
    height:100vh;
    overflow:hidden;
    touch-action:none;
    overscroll-behavior:none;
  }
  .studio-frame{ height:100vh; }
}

@media (max-width:2000px){


.as-detail-copy{
  width:min(55vw, 888px);
  }

.as-detail-copy h2{
    font-size: clamp(55px, 11vw, 222px);
 line-height: 0.9;
}

}



@media (max-width:860px){
  /* Mobile Studio scrolls naturally, with the footer at the end of the page. */
  .studio-frame{ padding:24px 24px 0; }
  .as-body{ grid-template-columns:1fr; gap:22px; }
  .as-list, .as-rail{ overflow-y:visible; }
  .as-viewer{ min-height:0; padding:28px 0 36px; gap:8px; }
  .as-art{
    width:min(70vw, 240px);
    touch-action:pan-y;
  }
  .studio-frame:not(.as-open) .as-art,
  .studio-frame:not(.as-open) .as-frametag{ transform:translateY(-6px); }
  .studio-frame:not(.as-open) .as-rail{
    margin-top:0;
    width:min(74vw, 286px);
    justify-self:center;
  }
  .as-viewer::before,
  .as-viewer::after{
    position:absolute; top:calc(50% - 14px); z-index:3;
    font-family:'JetBrains Mono',monospace; font-size:18px; line-height:1;
    color:var(--as-muted); opacity:0.82; pointer-events:none;
  }
  .as-viewer::before{
    content:'←'; left:calc(50% - min(35vw, 120px) - 28px);
  }
  .as-viewer::after{
    content:'→'; right:calc(50% - min(35vw, 120px) - 28px);
  }
  .studio-frame.as-open .as-viewer::before,
  .studio-frame.as-open .as-viewer::after{ opacity:0; }
  .as-rail{
    flex-direction:row;
    overflow-x:auto;
    overflow-y:hidden;
    align-items:center;
    justify-content:flex-start;
    padding-inline:24px;
    touch-action:pan-x pan-y;
    overscroll-behavior-inline:contain;
    scroll-snap-type:x proximity;
    scroll-padding-inline:24px;
    -webkit-overflow-scrolling:touch;
    -webkit-mask-image:linear-gradient(to right, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
    mask-image:linear-gradient(to right, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
  }
  .as-thumb{
    height:64px;
    width:96px;
    flex-shrink:0;
    scroll-snap-align:center;
  }
  .as-topline{ flex-direction:column; gap:10px; }
  .as-topline .as-telemetry{ text-align:left; }
  /* Match Building's mobile grid: socials on the right above Back to top. */
  .as-footline{
    position:relative; left:auto; right:auto; bottom:auto; z-index:5;
    width:calc(100% + 48px); max-width:none;
    margin:0 -24px;
    padding:24px 24px 56px;
    background:var(--as-bg);
    border-top:0px solid var(--as-line);
    display:grid;
    grid-template-columns:minmax(0, 1fr) auto;
    align-items:center;
    gap:24px 16px;
  }
  .as-footline .footer-social{
    grid-column:2;
    grid-row:1;
    justify-self:end;
  }
  .as-footline .to-top{
    display:flex;
    grid-column:2;
    grid-row:2;
    justify-self:end;
    align-self:end;
    background:rgba(245,245,245,0.55);
    padding:8px 12px;
  }
  .studio-frame.as-open .as-footline{
    opacity:0; visibility:hidden; pointer-events:none;
  }
  .as-footline-sep{ display:none; }
  .as-detail{ padding:88px 24px 76px; }
  .as-detail::before{
    background:linear-gradient(90deg, rgba(12,13,12,0.72) 0%, rgba(20,22,21,0.52) 58%, rgba(12,13,12,0.18) 100%);
  }
  .as-detail-copy{ width:min(78vw, 455px); }
  .as-detail-images{ bottom:72px; }
  .as-detail-images img,
  .as-detail-images video{ width:76px; height:52px; }
  .as-detail-copy h2{
    font-size: clamp(45px, 30vw, 55px);
 line-height: 1;
}
}
