/* Self-hosted Inter variable font (weight axis 100–900), vendored like three/gsap */
@font-face {
  font-family: "Inter";
  src: url("/vendor/fonts/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* the theme: dark by default, light under html[data-theme="light"] (below).
     --fg-rgb / --bg-rgb are bare triplets for rgba(var(--fg-rgb), a) */
  --fg: #fff;
  --fg-rgb: 255, 255, 255;
  --bg-rgb: 14, 14, 15;
  --bg: #0e0e0f; /* Figma redesign frame bg */
  --panel-bg: #101013;
  --text: #ececec;
  --muted: #8b8b8b;
  --line: rgba(255, 255, 255, 0.08);
  --sans: "Inter", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --serif: Georgia, "Times New Roman", serif;

  /* == Figma foundation tokens, fluid ==
     Two fluid segments per token: 390→1440 here, 1440→1920 in the min-width
     block below. Exact at 390 / 1440 / 1920; ±a few px at 810 where his ramp
     bends (a straight line can't pass through all four anchors). */
  --margin: clamp(20px, 9.6px + 2.667vw, 48px);             /* Layout/Margin 20-32-48-64 */
  --type-display-1: clamp(56px, 17.37px + 9.905vw, 160px);  /* 56-90-160-213 */
  --type-display-2: clamp(44px, 15.77px + 7.238vw, 120px);  /* 44-68-120-160 */
  --type-display-3: clamp(36px, 16.69px + 4.952vw, 88px);   /* 36-52-88-117 */
  --type-headline-1: clamp(32px, 20.11px + 3.048vw, 64px);  /* 32-44-64-85 */
  --type-headline-2: clamp(28px, 17.6px + 2.667vw, 56px);   /* 28-38-56-75 */
  --type-title: clamp(20px, 18.51px + 0.381vw, 24px);       /* 20-22-24-32 */
  --type-lead: clamp(17px, 15.89px + 0.286vw, 20px);        /* 17-18-20-24 */
  --type-body: clamp(15px, 14.63px + 0.095vw, 16px);        /* 15-16-16-20 */
  --type-body-small: 14px;                                  /* 14-14-14-18 */
  --type-label: clamp(13px, 12.03px + 0.247vw, 14px);       /* 13-14-14-18 */
  --btn-h: 48px;      /* control height: 48 up to 1440, 56 at 1920 */
  /* liquid glass (from the Figma GLASS effect: blur 4, light -45deg) */
  --glass-fill: rgba(255, 255, 255, 0.02);
  --glass-fill-strong: rgba(255, 255, 255, 0.1);
  --glass-stroke: rgba(255, 255, 255, 0.19);
  --glass-blur: 4px;
  /* the -45deg light: a bright top-left inner rim, a faint bottom-right one */
  --glass-light:
    inset 0.75px 0.75px 0.5px rgba(255, 255, 255, 0.32),
    inset -0.75px -0.75px 0.5px rgba(255, 255, 255, 0.1);
}

/* the 1440→1920 segment of every fluid token */
@media (min-width: 1440px) {
  :root {
    --margin: clamp(48px, 3.333vw, 64px);
    --type-display-1: clamp(160px, 1px + 11.042vw, 213px);
    --type-display-2: clamp(120px, 8.333vw, 160px);
    --type-display-3: clamp(88px, 1px + 6.042vw, 117px);
    --type-headline-1: clamp(64px, 1px + 4.375vw, 85px);
    --type-headline-2: clamp(56px, -1px + 3.958vw, 75px);
    --type-title: clamp(24px, 1.667vw, 32px);
    --type-lead: clamp(20px, 8px + 0.833vw, 24px);
    --type-body: clamp(16px, 4px + 0.833vw, 20px);
    --type-body-small: clamp(14px, 2px + 0.833vw, 18px);
    --type-label: clamp(14px, 2px + 0.833vw, 18px);
    --btn-h: clamp(48px, 24px + 1.667vw, 56px);
  }
}

/* ---------- light mode: the same tokens, turned around (ui.js toggles it) ---------- */
html[data-theme="light"] {
  color-scheme: light;
  --fg: #0e0e0f;
  --fg-rgb: 14, 14, 15;
  --bg: #ecebe7;
  --bg-rgb: 236, 235, 231;
  --panel-bg: #f6f5f2;
  --text: #161617;
  --muted: #6c6c70;
  --line: rgba(14, 14, 15, 0.1);
  --glass-fill: rgba(255, 255, 255, 0.5);
  --glass-fill-strong: rgba(255, 255, 255, 0.82);
  --glass-stroke: rgba(14, 14, 15, 0.12);
  --glass-light:
    inset 0.75px 0.75px 0.5px rgba(255, 255, 255, 0.95),
    inset -0.75px -0.75px 0.5px rgba(14, 14, 15, 0.06),
    0 1px 3px rgba(14, 14, 15, 0.08);
}
/* a touch more grid on light: 2% dark reads fainter than 2% white */
html[data-theme="light"] .grid-bg {
  background-image:
    linear-gradient(rgba(14, 14, 15, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(14, 14, 15, 0.045) 1px, transparent 1px);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body { height: 100%; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-optical-sizing: auto;
  overflow: hidden;
  cursor: none;
  -webkit-font-smoothing: antialiased;
}

a, button { cursor: none; }

button {
  font-family: var(--sans);
  background: none;
  border: 0;
  color: inherit;
}

::selection { background: #39d98a; color: #06130c; }

/* ---------- background grid (2% white lines on a 40px pitch, per the redesign) ---------- */
/* the music's glow (src/gradient.js), transparent until sound is on. It rides
   over the spiral and its blurred edges, so they frost the cards and never the
   glow; the list, the pages, the keys and the header stay above it */
.glow-bg {
  position: fixed;
  inset: 0;
  z-index: 7;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

.bg-field,
.bg-flares {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
.bg-field { z-index: 0; }
.bg-flares { z-index: 1; }

/* the faint grid, over the backdrop, on every page */
.grid-bg {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(var(--fg-rgb), 0.02) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--fg-rgb), 0.02) 1px, transparent 1px);
  background-size: 40px 40px;
  background-position: center center;
}

/* ---------- canvas ---------- */
#scene {
  position: fixed;
  inset: 0;
  z-index: 5;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
}

body.is-detail #scene,
body[data-view="list"] #scene { pointer-events: none; }
/* the pages are see-through now (the backdrop and the music's glow show on all
   of them), so the spiral steps out from under them — paused, it would leave
   its last frame there */
body.is-about #scene,
body.is-contact #scene,
body.is-archive #scene { visibility: hidden; }

/* the spiral's soft edges (after inspiring.nk.studio's side blur): cards
   blur and darken as they go off the top or the bottom */
.spiral-edge {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 6; /* over the spiral, under the keys (9) and the header (10) */
  height: clamp(110px, 20vh, 220px);
  pointer-events: none;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  transition: opacity 0.6s ease;
}
.spiral-edge--top {
  top: 0;
  background: linear-gradient(180deg, rgba(var(--bg-rgb), 0.55) 0%, rgba(var(--bg-rgb), 0.18) 45%, rgba(var(--bg-rgb), 0) 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0.75) 35%, rgba(0, 0, 0, 0.35) 65%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0.75) 35%, rgba(0, 0, 0, 0.35) 65%, transparent 100%);
}
.spiral-edge--bottom {
  bottom: 0;
  background: linear-gradient(0deg, rgba(var(--bg-rgb), 0.55) 0%, rgba(var(--bg-rgb), 0.18) 45%, rgba(var(--bg-rgb), 0) 100%);
  -webkit-mask-image: linear-gradient(0deg, #000 0%, rgba(0, 0, 0, 0.75) 35%, rgba(0, 0, 0, 0.35) 65%, transparent 100%);
  mask-image: linear-gradient(0deg, #000 0%, rgba(0, 0, 0, 0.75) 35%, rgba(0, 0, 0, 0.35) 65%, transparent 100%);
}
/* only over the spiral itself */
body:not([data-view="spiral"]) .spiral-edge,
body.is-intro .spiral-edge,
body.is-detail .spiral-edge,
body.is-about .spiral-edge,
body.is-archive .spiral-edge,
body.is-contact .spiral-edge { opacity: 0; }

/* ---------- header chrome (liquid glass, per the redesign navbar) ---------- */
.chrome {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: clamp(16px, 2.6vh, 24px) var(--margin);
  pointer-events: none;
  transition: opacity 0.45s ease, transform 0.45s ease;
}

body.is-detail .chrome,
body.is-intro .chrome {
  opacity: 0;
  transform: translateY(-12px);
  pointer-events: none !important;
}

.chrome > * { pointer-events: auto; }
body.is-detail .chrome > * { pointer-events: none; }

/* TONYDSGN. wordmark — full mark at every size (96px at 390 per the frames) */
.logo { display: block; width: clamp(102px, 66.4px + 9.09vw, 140px); line-height: 0; text-decoration: none; color: var(--fg); }
.logo-mark { width: 100%; height: auto; display: block; overflow: visible; }
/* phones: the short mark, TD with the keycap full stop */
.logo-short { display: none; width: 100%; height: auto; overflow: visible; }
@media (max-width: 760px) {
  .logo { width: 46px; }
  .logo-mark { display: none; }
  .logo-short { display: block; }
}
/* the full stop is a keycap in the colour of the page's key (logodot.js) */
.lm-key { fill: var(--dot-key, #9cf03a); transition: fill 0.45s ease; }
.lm-face { fill: var(--dot-face, #c3f683); transition: fill 0.45s ease; }
/* a near-white key would vanish on the light theme: it reads as ink there */
html[data-theme="light"][data-dot-pale] .lm-key { fill: var(--fg); }
html[data-theme="light"][data-dot-pale] .lm-face { fill: #3b3b3e; }
@media (prefers-reduced-motion: reduce) { .lm-key, .lm-face { transition: none; } }

/* phones (390 frame): the navbar wraps if it has to */
@media (max-width: 560px) {
  .chrome { flex-wrap: wrap; row-gap: 8px; }
}

/* right cluster: status text, sound, menu */
.chrome-right { display: flex; align-items: center; gap: 8px; }
/* left cluster: the wordmark, then the status beside it */
.chrome-left { display: flex; align-items: center; gap: clamp(20px, 3.4vw, 56px); }

.status {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: var(--type-label);
  line-height: 1.2;
}

/* light / dark: a glass circle like the sound button; the disc turns over */
.theme-btn {
  display: grid;
  place-items: center;
  width: var(--btn-h);
  height: var(--btn-h);
  flex: none;
  border-radius: 50%;
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--glass-light);
  color: var(--fg);
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.3s ease;
}
.theme-btn svg { width: 22px; height: 22px; transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1); }
html[data-theme="light"] .theme-btn svg { transform: rotate(180deg); }
@media (hover: hover) { .theme-btn:hover { transform: scale(1.08); background: rgba(var(--fg-rgb), 0.06); } }
.theme-btn:focus-visible { outline: 2px solid rgba(var(--fg-rgb), 0.7); outline-offset: 3px; }

/* the switch itself: the new theme opens out of the button in a circle */
::view-transition-old(root),
::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
::view-transition-new(root) { animation: theme-reveal 0.75s cubic-bezier(0.65, 0, 0.35, 1); }
@keyframes theme-reveal {
  from { clip-path: circle(0 at var(--theme-x, 50%) var(--theme-y, 50%)); }
  to { clip-path: circle(var(--theme-r, 150vmax) at var(--theme-x, 50%) var(--theme-y, 50%)); }
}
.status-label { color: rgba(var(--fg-rgb), 0.4); }
.status-time { color: var(--fg); }

.menu-btn {
  display: flex;
  align-items: center;
  height: var(--btn-h);
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--glass-light);
  color: var(--fg);
  font-size: var(--type-body);
  font-weight: 500;
  padding: 0 24px;
  border-radius: 999px;
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.3s ease;
}
@media (hover: hover) { .menu-btn:hover { transform: scale(1.05); background: rgba(var(--fg-rgb), 0.06); } }
/* while the music's glow is up, glass that sits in it stops frosting it */
body.has-glow .cf-send { -webkit-backdrop-filter: none; backdrop-filter: none; }
@media (max-width: 900px) {
  body.has-glow .menu-btn { -webkit-backdrop-filter: none; backdrop-filter: none; }
  .menu-btn {
    position: fixed;
    bottom: 30px;
    inset-inline: 0;
    margin-inline: auto; /* centred without a transform, so hover/scale can't unseat it */
    width: fit-content;
    z-index: 11;
  }
  /* the chrome's hidden state translates it, and a transformed ancestor
     captures position:fixed — the menu pill would ride the header during the
     0.45s reveal. Stacked layouts hide the chrome with opacity alone. */
  body.is-detail .chrome,
  body.is-intro .chrome { transform: none; }
}

/* glass degrades honestly: engines without backdrop-filter get a slightly
   stronger fill so the controls still read, and reduced-transparency users get
   near-opaque surfaces (Apple's own Liquid Glass guidance) */
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .menu-btn, .sound-btn { background: rgba(var(--fg-rgb), 0.09); }
}
@media (prefers-reduced-transparency: reduce) {
  .menu-btn, .sound-btn, .intro-enter, .menu-close,
  .menu-panel {
    background: rgba(44, 44, 48, 0.95);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .menu-panel { background: rgba(var(--bg-rgb), 0.98); }
}

/* ---------- hover label (cursor-trailing card) + cursor ---------- */
.hover-label {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 30;
  pointer-events: none;
  will-change: transform;
}
.hl-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 22px 9px 9px;
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 16px 44px -14px rgba(0, 0, 0, 0.6);
  opacity: 0;                     /* hidden until first hover; gsap drives it */
  transform-origin: top left;
  will-change: transform, opacity;
}
.hl-thumb {
  width: 52px;
  height: 52px;
  border-radius: 12px;
  object-fit: cover;
  flex: none;
  background: #e9e9ec;            /* placeholder tint before the image paints */
}
.hl-text {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 22px;
  letter-spacing: -0.01em;
  color: #0d0d0d;
  white-space: nowrap;
}

/* external-link arrow. Was the ↗ character, which iOS and Android substitute
   with the colour emoji from their own font — an SVG renders the same everywhere. */
.ext-arrow {
  width: 0.72em;
  height: 0.72em;
  margin-left: 0.4em;
  flex: none;
  vertical-align: baseline;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cursor-dot {
  position: fixed;
  left: 0;
  top: 0;
  /* above the intro (z-50) too: the real cursor is hidden site-wide, so if the
     dot paints under the loading screen there is nothing to aim the gate
     buttons with. pointer-events:none, so sitting on top blocks nothing. */
  z-index: 60;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: #fff;
  mix-blend-mode: difference;
  pointer-events: none;
  transition: width 0.25s ease, height 0.25s ease, margin 0.25s ease, opacity 0.3s ease;
  will-change: transform;
}
.cursor-dot.is-active { width: 26px; height: 26px; margin: -13px 0 0 -13px; }
@media (hover: none) { .cursor-dot, .hover-label { display: none; } body, a, button { cursor: auto; } }

/* ---------- sound button (glass circle in the header cluster) ---------- */
.sound-btn {
  width: var(--btn-h);
  height: var(--btn-h);
  flex: none;
  border-radius: 50%;
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--glass-light);
  color: var(--fg);
  display: grid;
  place-items: center;
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.3s ease;
}
@media (hover: hover) { .sound-btn:hover { transform: scale(1.08); background: rgba(var(--fg-rgb), 0.06); } }
/* equaliser: four bars that dance while sound is on and flatten into a single
   line when muted (each bar collapses to a sliver, so they read as one rule) */
.sound-btn .eq { display: flex; align-items: center; justify-content: center; gap: 2.5px; width: 19px; height: 17px; }
.sound-btn .eq i {
  display: block;
  width: 2.5px;
  height: 100%;
  border-radius: 2px;
  background: currentColor;
  transform: scaleY(0.12);              /* muted = flat line */
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.sound-btn.is-on .eq i { animation: eqBar 1.1s ease-in-out infinite; }
.sound-btn.is-on .eq i:nth-child(1) { animation-delay: -0.9s; }
.sound-btn.is-on .eq i:nth-child(2) { animation-delay: -0.35s; }
.sound-btn.is-on .eq i:nth-child(3) { animation-delay: -0.65s; }
.sound-btn.is-on .eq i:nth-child(4) { animation-delay: -0.15s; }
@keyframes eqBar { 0%, 100% { transform: scaleY(0.22); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) {
  .sound-btn.is-on .eq i { animation: none; transform: scaleY(0.62); }
}

/* ---------- list view ---------- */
.list-view {
  position: fixed;
  inset: 0;
  z-index: 7; /* the glow's level; later in the markup, so over it */
  overflow-y: auto;
  padding: 16vh 11.18vw 14vh;
  visibility: hidden;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.4s ease;
  scrollbar-width: none;
  /* the rows fade out over the bottom 171px (was a dark overlay band; a mask
     reveals whatever sits behind — the page, or the glow when sound is on) */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 171px), rgba(0, 0, 0, 0.08) calc(100% - 20px));
  mask-image: linear-gradient(to bottom, #000 calc(100% - 171px), rgba(0, 0, 0, 0.08) calc(100% - 20px));
}
.list-view::-webkit-scrollbar { display: none; }
body[data-view="list"] .list-view { visibility: visible; pointer-events: auto; }
@media (max-width: 900px) { .list-view { padding-left: var(--margin); padding-right: var(--margin); } }
/* a project detail sits over the gallery — fade the list out beneath it */
body.is-detail .list-view { opacity: 0; pointer-events: none; }
body.is-detail .list-preview { opacity: 0 !important; }
/* About, Playground and Contact are see-through (the backdrop shows), so the
   list steps out while one is up; it fades back in when the page closes */
body.is-about .list-view,
body.is-archive .list-view,
body.is-contact .list-view { visibility: hidden; opacity: 0; pointer-events: none; }
body.is-about .list-preview,
body.is-archive .list-preview,
body.is-contact .list-preview { opacity: 0 !important; }

.list-rows { list-style: none; }

/* redesign anatomy: 01 · Title · right-aligned meta, 20% separators, 40% muted */
.row {
  display: flex;
  align-items: baseline;
  gap: clamp(16px, 2.5vw, 36px);
  padding: 3.6vh 0;
  border-bottom: 1px solid rgba(var(--fg-rgb), 0.2);
  color: var(--fg);
  transition: color 0.3s ease;
}
.row:first-child { border-top: 1px solid rgba(var(--fg-rgb), 0.2); }
.row-idx { font-size: var(--type-body); color: rgba(var(--fg-rgb), 0.4); min-width: 2.2em; }
.row-title {
  font-size: var(--type-headline-2);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.1;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
/* hover: the title turns serif italic and steps 53px right (per the frames) */
@media (hover: hover) { .row:hover .row-title { font-family: var(--serif); font-style: italic; font-weight: 400; letter-spacing: 0; transform: translateX(53px); } }
.row-meta { margin-left: auto; font-size: var(--type-body); color: rgba(var(--fg-rgb), 0.4); white-space: nowrap; }
/* phones (390 list frame): number, title and meta stack left in a column */
@media (max-width: 560px) {
  .row { display: block; padding: 24px 0; }
  .row-title { display: block; margin-top: 12px; }
  .row-meta { display: block; margin-left: 0; margin-top: 16px; white-space: normal; }
}
/* phones have no hover, so each row carries its picture instead of the
   cursor's preview (listview.js fills it in when the list first opens) */
.row-thumb { display: none; }
@media (max-width: 560px) {
  .row-thumb {
    display: block;
    margin-top: 16px;
    aspect-ratio: 16 / 10;
    border-radius: 12px;
    overflow: hidden;
    background: rgba(var(--fg-rgb), 0.05);
  }
  .row-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
}

.list-preview {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 7;
  width: 300px;
  aspect-ratio: 3 / 2;
  border-radius: 16px;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  will-change: transform;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.6);
}
.list-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- project overlay (modal-like detail view) ---------- */
/* the glow goes back under the project panel (it only shows round its edges) */
body.is-detail .glow-bg { z-index: 0; }
.overlay {
  position: fixed;
  inset: 0;
  z-index: 4;
  visibility: hidden;
  pointer-events: none;
}
.overlay.is-open { visibility: visible; }
.overlay.is-open .panel { pointer-events: auto; }
.overlay.measuring { visibility: visible !important; opacity: 0 !important; }

.panel {
  position: absolute;
  inset: 10px;
  border-radius: 26px;
  background: var(--panel-bg);
  border: 1px solid rgba(var(--fg-rgb), 0.07);
  overflow-y: auto;
  overflow-x: hidden;
  pointer-events: none;
  outline: none;
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--fg-rgb), 0.16) transparent;
}
.panel::-webkit-scrollbar { width: 5px; }
.panel::-webkit-scrollbar-thumb { background: rgba(var(--fg-rgb), 0.16); border-radius: 4px; }

.close-btn {
  position: absolute;
  top: 28px;
  right: 30px;
  z-index: 9;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--fg);
  color: var(--bg);
  display: grid;
  place-items: center;
  opacity: 0;
  pointer-events: none;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.close-btn.is-in { opacity: 1; pointer-events: auto; }
@media (hover: hover) { .close-btn:hover { transform: rotate(90deg) scale(1.07); } }
.close-btn svg { width: 20px; height: 20px; }

.panel-inner { padding: clamp(20px, 4vw, 56px) clamp(20px, 4.5vw, 64px) 0; }

.p-top {
  display: flex;
  gap: 18px;
  font-size: clamp(14px, 1.2vw, 16px);
  color: var(--muted);
  text-transform: lowercase;
  padding-top: 12px;
}
.p-top .sep { opacity: 0.5; }
.p-top .p-year { margin-left: auto; }

.p-title {
  font-size: clamp(44px, 8.5vw, 130px);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 0.98;
  margin: 4vh 0 5vh;
}
.p-title em { font-family: var(--serif); font-weight: 400; font-style: italic; letter-spacing: -0.01em; }
/* SplitText wraps each title line in an overflow:clip mask sized to the tight
   0.98 line-box; pad the bottom so descenders aren't cut, and pull the next
   line back up so the visual spacing is unchanged */
.p-title > div { padding-bottom: 0.16em; margin-bottom: -0.16em; }

.p-herowrap { display: flex; justify-content: center; }
.hero {
  width: 100%;          /* same width as the other detail images (.p-imgs img) */
  aspect-ratio: 2 / 1;  /* full width, 75% of the height of a 3/2 image */
  border-radius: 20px;
  overflow: hidden;
  background: rgba(var(--fg-rgb), 0.06);
  position: relative;
}
.hero img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: 0; }
.hero.is-reel::after {
  content: "▶";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 84px;
  height: 84px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  color: #111;
  font-size: 22px;
  display: grid;
  place-items: center;
  text-indent: 4px;
}

.p-cols {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: 6vw;
  margin: 9vh 0;
  max-width: 1080px;
}
.p-desc p { font-size: clamp(16px, 1.45vw, 21px); line-height: 1.55; color: rgba(var(--fg-rgb), 0.8); margin-bottom: 1.2em; }
.p-meta { font-size: 14px; color: var(--muted); }
.p-meta dt { text-transform: uppercase; letter-spacing: 0.12em; font-size: 11px; margin: 22px 0 8px; color: rgba(var(--fg-rgb), 0.44); }
.p-meta dd { color: rgba(var(--fg-rgb), 0.85); line-height: 1.7; }

.p-imgs { display: grid; gap: 18px; margin-bottom: 9vh; }
.p-imgs img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; border-radius: 20px; display: block; }

/* the password form of a locked project (laid out with the story, .os-lock) */
.lock-form { display: flex; gap: 10px; }
.lock-input {
  flex: 1;
  min-width: 0;
  background: rgba(var(--fg-rgb), 0.06);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px 16px;
  color: var(--fg);
  font-size: 15px;
  font-family: var(--sans);
  outline: none;
  transition: border-color 0.2s ease;
}
.lock-input::placeholder { color: rgba(var(--fg-rgb), 0.42); }
.lock-input:focus { border-color: rgba(var(--fg-rgb), 0.4); }
.lock-btn {
  flex: none;
  background: var(--fg);
  color: var(--bg);
  border: none;
  border-radius: 12px;
  padding: 0 24px;
  font-size: 15px;
  font-weight: 600;
  font-family: var(--sans);
}
.lock-error { color: #ff7a7a; font-size: 13px; margin-top: 14px; min-height: 1em; }

/* ---------- case study (long-form project detail) ---------- */
/* Full-width panel, but the content + images stay in a centered column at the
   size they were in the drawer — the extra width becomes side margins. Below
   ~1120px panel width it just fills, so mobile/tablet stay full-width. */
.panel-inner.cs { max-width: 1120px; margin-inline: auto; }
.panel-inner.cs ~ .p-next { max-width: 1120px; margin-inline: auto; }
.cs-lede {
  font-size: clamp(20px, 2vw, 24px);
  line-height: 1.38;
  letter-spacing: -0.015em;
  color: rgba(var(--fg-rgb), 0.93);
  max-width: 20em;
  margin: 7vh 0 6vh; /* extra top space to separate the lede from the at-a-glance above it */
}
.cs-glance {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 24px 40px;
  margin: 0 0 2vh;
  padding-top: 4vh; /* the rule that used to sit under the hero image was removed */
}
.cs-g-k { font-size: clamp(12px, 1.1vw, 14px); text-transform: uppercase; letter-spacing: 0.13em; color: rgba(var(--fg-rgb), 0.44); margin-bottom: 7px; }
.cs-g-v { font-size: clamp(14px, 1.2vw, 16px); line-height: 1.5; color: rgba(var(--fg-rgb), 0.9); }

.cs-section { margin: 8.5vh 0; } /* heading rules + figures run to the column edge; text stays narrow via .cs-body/.cs-point */
.cs-h {
  font-size: clamp(12px, 1.1vw, 14px);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgba(var(--fg-rgb), 0.54);
  margin: 0 0 28px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
.cs-body { max-width: 40em; }
.cs-body p { font-size: clamp(16px, 1.4vw, 18px); line-height: 1.62; color: rgba(var(--fg-rgb), 0.8); margin: 0 0 1.15em; }
.cs-body p:last-child { margin-bottom: 0; }

.cs-point { max-width: 44em; margin-top: 44px; } /* approach point text stays narrow; each point is followed by its own full-width figure */
.cs-point-lead { display: block; font-size: clamp(18px, 1.5vw, 20px); font-weight: 600; letter-spacing: -0.01em; color: var(--fg); margin-bottom: 9px; }
.cs-point-body { display: block; font-size: clamp(16px, 1.4vw, 18px); line-height: 1.6; color: rgba(var(--fg-rgb), 0.7); }

.cs-figs { display: grid; gap: 26px; margin-top: 24px; }
.cs-figrow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cs-fig { margin: 0; }
.cs-fig img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; border-radius: 18px; display: block; background: rgba(var(--fg-rgb), 0.06); }
.cs-figcap { margin-top: 13px; font-size: clamp(12px, 1.1vw, 14px); line-height: 1.5; color: rgba(var(--fg-rgb), 0.54); max-width: 46em; }

/* title row with the live-site CTA on the right */
.cs-titlerow { display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; margin: 4vh 0 5vh; }
.cs-titlerow .p-title { margin: 0; font-size: clamp(40px, 4.6vw, 66px); } /* keep the revised (smaller) title size in the centered column */
/* secondary button — ghost pill that inverts to solid on hover */
.cs-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  padding: 12px 22px;
  border: 1px solid rgba(var(--fg-rgb), 0.28);
  border-radius: 100px;
  font-size: clamp(14px, 1.2vw, 16px);
  font-weight: 500;
  color: var(--fg);
  white-space: nowrap;
  transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}
@media (hover: hover) { .cs-cta:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); } }

/* ---------- project story, after charlieosborne.co ---------- */
/* a sticky column of small text (grey labels, values in caps) beside a column of
   big rounded images; the four steps sit at the foot of the column and the one
   whose images cross the middle of the panel shows its words (overlay.js) */
.panel-inner.os {
  --os-pad: clamp(14px, 1.3vw, 22px);
  --os-gap: clamp(12px, 1.1vw, 18px);
  --os-fs: clamp(12px, 0.84vw, 13.5px);
  padding: var(--os-pad) var(--os-pad) 0;
}
.os-grid {
  display: grid;
  grid-template-columns: minmax(300px, 31%) minmax(0, 1fr);
  gap: clamp(20px, 2.4vw, 44px);
  align-items: start;
}
.os-aside {
  position: sticky;
  top: var(--os-pad);
  height: calc(100vh - 20px - 2 * var(--os-pad)); /* the panel sits 10px in from each edge */
  height: calc(100dvh - 20px - 2 * var(--os-pad));
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 24px;
  min-width: 0;
  font-size: var(--os-fs);
  line-height: 1.45;
  font-weight: 500;
  color: rgba(var(--fg-rgb), 0.88);
}
.os-title { margin: 6px 0 14px; font-size: clamp(30px, 2.5vw, 44px); line-height: 1; letter-spacing: -0.03em; }
.os-lede { margin: 0; max-width: 34em; color: rgba(var(--fg-rgb), 0.5); }
.os-meta { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; margin-top: clamp(22px, 3.4vh, 40px); }
.os-facts { display: grid; gap: 14px; align-content: start; margin: 0; }
.os-facts dt,
.os-k { display: block; margin-bottom: 3px; color: rgba(var(--fg-rgb), 0.42); }
.os-facts > div { min-width: 0; }
.os-facts dd { margin: 0; text-transform: uppercase; letter-spacing: 0.01em; color: var(--fg); overflow-wrap: anywhere; }
.os-live { color: var(--fg); text-decoration: none; }
.os-live .ext-arrow { display: inline-block; width: 0.85em; height: 0.85em; margin-left: 4px; vertical-align: -0.06em; }
.os-nb { white-space: nowrap; }
@media (hover: hover) { .os-live:hover { color: #f0461f; } }
.os-deliv ul { margin: 0; padding: 0; list-style: none; text-transform: uppercase; letter-spacing: 0.01em; line-height: 1.6; color: var(--fg); }

/* the four steps */
.os-steps { min-height: 0; }
.os-tabs { display: flex; flex-wrap: wrap; gap: 6px 18px; padding-bottom: 11px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.os-tabs a { display: inline-flex; gap: 6px; color: rgba(var(--fg-rgb), 0.42); text-decoration: none; transition: color 0.3s ease; }
.os-tabs a span { font-variant-numeric: tabular-nums; transition: color 0.3s ease; }
.os-tabs a.is-current { color: var(--fg); }
.os-tabs a.is-current span { color: #f0461f; }
@media (hover: hover) { .os-tabs a:hover { color: var(--fg); } }
.os-bodies { display: grid; align-items: start; }
.os-body,
.os-note { grid-area: 1 / 1; opacity: 0; visibility: hidden; transition: opacity 0.35s ease, visibility 0s linear 0.35s; }
.os-body.is-current,
.os-note.is-current { opacity: 1; visibility: visible; transition: opacity 0.5s ease 0.12s, visibility 0s; }
.os-body p,
.os-note p { margin: 0 0 0.7em; }
.os-body > p:first-child { color: var(--fg); }
.os-points { margin: 0.9em 0 0; padding: 0; list-style: none; display: grid; gap: 0.6em; }
.os-points li { color: rgba(var(--fg-rgb), 0.62); }
.os-points b { font-weight: 500; color: var(--fg); }
.os-reflect { margin-top: 1em; padding-top: 0.9em; border-top: 1px solid var(--line); }
.os-reflect p { color: rgba(var(--fg-rgb), 0.62); }
.os-notes { display: grid; margin-top: 0.9em; padding-top: 0.9em; border-top: 1px solid var(--line); }
/* a step with no words of its own opens straight on its points or its notes */
.os-body > .os-points:first-child { margin-top: 0; }
.os-body > .os-notes:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.os-note .os-k { color: var(--fg); }
.os-note p { color: rgba(var(--fg-rgb), 0.62); }

/* the images */
.os-media { display: grid; gap: var(--os-gap); min-width: 0; padding-bottom: var(--os-gap); }
.os-sec { position: relative; display: grid; gap: var(--os-gap); scroll-margin-top: var(--os-pad); }
.os-fig {
  margin: 0;
  aspect-ratio: 16 / 10;
  border-radius: clamp(12px, 1.05vw, 16px);
  overflow: hidden;
  background: rgba(var(--fg-rgb), 0.05);
}
.os-fig img,
.os-fig video { display: block; width: 100%; height: 100%; object-fit: cover; }
.os-fig.is-flat { background: #fff; }
.os-fig.is-flat img { object-fit: contain; padding: 5% 6%; }
.os-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--os-gap); }
.os-pair .os-fig { aspect-ratio: 4 / 5; }
/* images rise in as they scroll into view */
.os-rv { opacity: 0; transform: translateY(34px); transition: opacity 0.9s ease, transform 1.05s cubic-bezier(0.22, 1, 0.36, 1); }
.os-rv.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .os-rv { opacity: 1; transform: none; transition: none; } .os-body, .os-note { transition: none; } }

/* a locked project: the story's layout, the password at the foot of the side
   column (where the steps go) and the project's cover beside it, as tall as the
   column. The lede and facts come from the project's own fields. */
.os-lock { max-width: 30em; }
.os-lock-k { display: flex; align-items: center; gap: 7px; margin: 0 0 14px; padding-bottom: 11px; border-bottom: 1px solid var(--line); color: var(--fg); }
.os-lock-ico { width: 1.15em; height: 1.15em; flex: none; color: #f0461f; }
.os-lock-sub { margin: 0 0 14px; color: rgba(var(--fg-rgb), 0.62); }
.os-lock .lock-form { gap: 8px; }
.os-lock .lock-input { padding: 11px 14px; border-radius: 10px; font-size: 13px; }
.os-lock .lock-btn { padding: 0 18px; border-radius: 10px; font-size: 13px; }
.os-lock .lock-error { margin-top: 10px; font-size: 12px; }
.os-lock-fig {
  position: relative;
  aspect-ratio: auto;
  height: calc(100vh - 20px - 2 * var(--os-pad));
  height: calc(100dvh - 20px - 2 * var(--os-pad));
}
.os-lock-chip {
  position: absolute;
  top: 14px;
  left: 14px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.92);
  color: #111;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
}
.os-lock-chip .os-lock-ico { width: 12px; height: 12px; color: currentColor; }

/* the words again, beside their images: for screen readers on desktop (the
   side column is aria-hidden), and the only copy on phones */
.os-inline { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* short screens: the side column drops the lede and the deliverables first */
/* when the words run long for the window (overlay.js _fitAside): tighter type
   first, then the compact head, which short windows always get */
.panel-inner.os.is-snug,
.panel-inner.os.is-compact { --os-fs: 12px; }
.is-snug .os-aside,
.is-compact .os-aside { line-height: 1.4; gap: 16px; }
.is-compact .os-lede, .is-compact .os-deliv { display: none; }
.is-compact .os-meta { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 18px; }
.is-compact .os-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-column: 1 / -1; gap: 12px 20px; }
.is-compact .os-facts > .is-live { grid-column: 1 / -1; }
.is-compact .os-points { gap: 0.45em; }

/* tablets and phones: one column, the words of each step above its images */
@media (max-width: 900px) {
  .panel-inner.os { --os-fs: 13px; padding-top: 22px; }
  .os-grid { grid-template-columns: minmax(0, 1fr); gap: 34px; }
  .os-aside { position: static; height: auto; display: block; }
  .os-title { padding-right: 58px; } /* clear of the close button */
  .os-item.is-lead { order: -1; }    /* the first picture comes before the words */
  .os-steps { display: none; }
  .os-lock { margin-top: 28px; }
  .os-lock-fig { aspect-ratio: 16 / 10; height: auto; }
  .os-inline { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; }
  .os-sec { gap: 14px; }
  .os-sec + .os-sec { margin-top: 30px; }
  .os-inline { font-size: 15px; line-height: 1.55; color: rgba(var(--fg-rgb), 0.74); }
  .os-inline-h { display: flex; gap: 8px; margin: 0 0 12px; padding-bottom: 10px; border-bottom: 1px solid var(--line); font-size: 13px; font-weight: 500; color: var(--fg); }
  .os-inline-h span { color: #f0461f; font-variant-numeric: tabular-nums; }
  .os-inline p { margin: 0 0 0.85em; }
  .os-inline:not(.os-inline-note) > p:first-of-type { font-size: 17px; line-height: 1.45; letter-spacing: -0.005em; color: var(--fg); }
  .os-inline .os-points { gap: 0.8em; margin-top: 1em; }
  .os-inline .os-points li { color: rgba(var(--fg-rgb), 0.7); }
  .os-inline-note { margin-top: 10px; }
  .os-inline-note h3 { margin: 0 0 6px; font-size: 15px; font-weight: 600; color: var(--fg); }
}
@media (max-width: 560px) {
  .os-meta { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; }
  .os-pair { gap: 10px; }
}

/* ---------- side drawer (Culture Summit) — opens as a half-screen panel ---------- */
.overlay.is-drawer { z-index: 6; background: rgba(8, 8, 10, 0.55); pointer-events: auto; } /* sit above the spiral canvas (z-5) and dim it as the backdrop */
.overlay.is-drawer .panel {
  left: 0;
  right: 0;
  width: 75vw;
  margin-inline: auto;        /* centered, with an equal dimmed gallery strip on each side */
  box-shadow: 0 40px 120px -36px rgba(0, 0, 0, 0.8);
}
.overlay.is-drawer .panel-inner { padding: clamp(18px, 3vw, 40px) clamp(18px, 3vw, 44px) 0; }
.overlay.is-drawer .p-title { font-size: clamp(34px, 4.6vw, 66px); } /* scale the title to the narrower drawer */
.overlay.is-drawer .cs-figrow { grid-template-columns: 1fr; }        /* stack EN/AR in the narrow drawer */

@media (max-width: 760px) {
  /* on phones the drawer just takes the full screen, like the normal panel */
  .overlay.is-drawer { background: transparent; }
  .overlay.is-drawer .panel { left: 10px; right: 10px; width: auto; margin-inline: 0; box-shadow: none; }
}

/* "Next project" — centered words that split apart on hover to reveal a cluster
   of the next project's images in the middle (exoape/Apple-style footer) */
.p-next {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 10vh clamp(20px, 4.5vw, 64px) 12vh;
  color: var(--text);
  background: none;
  border: none;
}
.pn-row { display: flex; align-items: center; justify-content: center; gap: clamp(10px, 1.4vw, 26px); }
.pn-word { font-size: clamp(40px, 7vw, 100px); font-weight: 500; letter-spacing: -0.025em; line-height: 1; }
.pn-media {
  position: relative;
  flex: 0 0 auto;
  width: 0; /* collapsed at rest so the words read together; grows on hover */
  height: clamp(120px, 15vw, 220px);
  transition: width 0.55s cubic-bezier(0.76, 0, 0.24, 1);
}
.pn-img {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(170px, 19vw, 300px);
  aspect-ratio: 16 / 10; /* landscape, like the pictures on the pages */
  object-fit: cover;
  border-radius: 12px;
  box-shadow: 0 24px 60px -24px rgba(0, 0, 0, 0.75);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.7);
  transition: transform 0.55s cubic-bezier(0.34, 1.45, 0.5, 1), opacity 0.4s ease;
}
/* open: the media slot widens and the pictures fan out (one picture just sits) */
.p-next:focus-visible .pn-media { width: clamp(200px, 23vw, 360px); }
.p-next:focus-visible .pn-img { opacity: 1; }
.p-next:focus-visible .pn-img-a { transform: translate(-50%, -50%) rotate(-3deg) scale(1); }
.p-next:focus-visible .pn-img-c { transform: translate(-36%, -62%) rotate(6deg) scale(0.9); }
.p-next:focus-visible .pn-img-b { transform: translate(-64%, -38%) rotate(-9deg) scale(0.82); }
.p-next:focus-visible .pn-n1 .pn-img-a { transform: translate(-50%, -50%) rotate(-2deg) scale(1); }

/* phones and touch: just the words, no pictures (he asked); a tap navigates.
   overlay.js doesn't render the pictures there either, so none are fetched */
@media (hover: none), (max-width: 760px) {
  .p-next .pn-media { display: none; }
}
/* pointer devices keep the plain hover trigger */
@media (hover: hover) {
  .p-next:hover .pn-media { width: clamp(200px, 23vw, 360px); }
  .p-next:hover .pn-img { opacity: 1; }
  .p-next:hover .pn-img-a { transform: translate(-50%, -50%) rotate(-3deg) scale(1); }
  .p-next:hover .pn-img-c { transform: translate(-36%, -62%) rotate(6deg) scale(0.9); }
  .p-next:hover .pn-img-b { transform: translate(-64%, -38%) rotate(-9deg) scale(0.82); }
  .p-next:hover .pn-n1 .pn-img-a { transform: translate(-50%, -50%) rotate(-2deg) scale(1); }
}
/* full-bleed divider before the next-project footer — like the detail-page rules
   (1px var(--line)) but spanning the whole panel, to set the section apart */
.p-next-rule {
  width: 100%;
  height: 0;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--line);
}

@media (max-width: 760px) {
  .p-cols { grid-template-columns: 1fr; gap: 4vh; }
  .cs-figrow { grid-template-columns: 1fr; } /* stack the EN/AR comparison */
  .close-btn { top: 20px; right: 20px; width: 44px; height: 44px; }
}

/* ---------- about page: the name, four keycaps beside it, and the panel the
   pressed key prints (tony / loves / design; the ♥ goes to Contact) ---------- */
.about {
  position: fixed;
  inset: 0;
  z-index: 8; /* above the spiral canvas, under the keycap layer (9), chrome and menu */
  display: flex;
  flex-direction: column;
  background: transparent; /* the backdrop shows through (background.js) */
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.55s ease, visibility 0s linear 0.55s;
  overflow: hidden; /* never scrolls: about.js scales the stage to fit (phones scroll, below) */
}
.about.is-open { visibility: visible; opacity: 1; transition: opacity 0.55s ease; }


/* a centred stage (after the Studio A reference): a small eyebrow, the name,
   the keys tumbled across the bottom of it, the pressed key's panel. Centred
   in the viewport; about.js scales it down when a short window can't hold it */
.about-inner {
  position: relative;
  z-index: 1;
  width: 100%;
  margin: auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: clamp(88px, 11vh, 112px) var(--margin) clamp(28px, 5vh, 56px);
  text-align: center;
  transform-origin: 50% 0;
}
.about-eyebrow { margin: 0 0 clamp(10px, 1.8vh, 18px); font-size: var(--type-label); font-weight: 500; color: rgba(var(--fg-rgb), 0.45); }

/* the lowercase name on one line, centred */
.about-name {
  display: flex;
  justify-content: center;
  gap: 0.24em;
  margin: 0;
  font-size: clamp(34px, min(7.6vw, 12vh), 136px);
  line-height: 1;
}
/* each word swings up through its own mask; the padding leaves room for
   ascenders and descenders (p, g, y), and about.js lets go of the clip
   altogether once the word has landed */
.an-mask { display: inline-block; overflow: hidden; padding: 0.1em 0 0.3em; margin: -0.1em 0 -0.3em; }
.an-mask.is-landed { overflow: visible; }
.an-line {
  display: block;
  font-size: 1em;
  font-weight: 500;
  letter-spacing: -0.06em;
  color: var(--fg);
  white-space: nowrap;
}

/* the keycaps' placeholder: four keys in a row, pulled up over the bottom of
   the name. The keys are drawn on the shared keycap layer, which follows this box. */
.about-keys {
  --ak: clamp(68px, min(8.6vw, 14.5vh), 150px);
  width: calc(var(--ak) * 4.54); /* four keys and three 18% gaps */
  height: var(--ak);
  margin-top: calc(var(--ak) * -0.2); /* over the bottom of the second line, like the reference */
  flex: none;
}

/* the panels share one cell; the pressed key's panel shows */
.about-panels {
  display: grid;
  width: 100%;
  max-width: 1000px;
  margin-top: clamp(24px, 4.2vh, 52px);
}
.ap {
  grid-area: 1 / 1;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}
.ap.is-active { visibility: visible; opacity: 1; transition: opacity 0.4s ease 0.05s; }
.ap-head { margin: 0 0 24px; font-size: var(--type-label); font-weight: 500; letter-spacing: -0.06em; color: rgba(var(--fg-rgb), 0.4); }

/* tony: the positioning line, then Who I am | Recognition */
.about-lede {
  margin: 0 auto;
  max-width: clamp(340px, 248px + 17.08vw, 600px);
  font-size: var(--type-lead);
  line-height: 1.2;
  font-weight: 400;
  color: var(--fg);
}
.ap-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(24px, 2.8vw, 56px);
  max-width: 900px;
  margin: clamp(30px, 4.6vh, 52px) auto 0;
  text-align: left;
}
.ap-col { font-size: var(--type-label); letter-spacing: -0.06em; color: var(--fg); }
.ap-col p { margin: 0; }
.ap-col p + p { margin-top: 8px; }
.ap-col .ap-head { margin-bottom: 24px; }
.ap-links { display: flex; justify-content: center; gap: 24px; margin: clamp(26px, 4vh, 42px) 0 0; font-size: var(--type-label); }
.ap-links a { color: var(--fg); text-decoration: none; border-bottom: 1px solid rgba(var(--fg-rgb), 0.3); padding-bottom: 2px; transition: border-color 0.3s ease; }
@media (hover: hover) { .ap-links a:hover { border-color: var(--fg); } }

/* loves: the snapshot cards fanned out in a hand. GSAP owns their transforms
   (the fan, the deal out of the loves key, the lift on hover, the flip) */
.ap-hint { margin: 18px 0 0; font-size: var(--type-label); color: rgba(var(--fg-rgb), 0.4); }
.love-row {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  padding: 26px 0 10px;
  perspective: 1400px;
}
.love-card {
  --cw: clamp(150px, 12.6vw, 204px);
  position: relative;
  flex: none;
  width: var(--cw);
  aspect-ratio: 4 / 5;
  margin: 0 calc(var(--cw) * -0.07);
  padding: 0;
  border: 0;
  background: none;
  transform-style: preserve-3d;
  cursor: none;
  opacity: 0;
}
/* two faces back to back: the colour side, and the photo side it flips over to */
.lc-face {
  position: absolute;
  inset: 0;
  border-radius: 18px;
  overflow: hidden;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  box-shadow: 0 22px 54px -18px rgba(0, 0, 0, 0.85), inset 0 1px 0 rgba(255, 255, 255, 0.28), inset 0 0 0 1px rgba(255, 255, 255, 0.1);
  transition: filter 0.35s ease;
}
.lc-front { background: var(--lc-bg); }
.lc-back { transform: rotateY(180deg); background: #1a1a1c; }
.lc-back img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
/* the photo's label rides a dark fade along its bottom */
.lc-back::after {
  content: "";
  position: absolute;
  inset: 45% 0 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.72));
}
.lc-emoji {
  position: absolute;
  inset: 0 0 18%;
  display: grid;
  place-items: center;
  font-size: calc(var(--cw) * 0.36);
  line-height: 1;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.28));
}
.lc-title {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 12px;
  z-index: 1;
  font-size: var(--type-label);
  font-weight: 600;
  color: #fff;
  text-align: left;
}
.love-card:focus-visible { outline: 2px solid rgba(var(--fg-rgb), 0.8); outline-offset: 4px; }
/* while one card tells its story, the rest of the hand dims (the faces, so the
   3D flip isn't flattened) */
.love-row.is-reading .lc-face { filter: brightness(0.32) saturate(0.6); }
/* the "flip" key's spot, under the hand */
.love-flip { --fk: clamp(46px, 4.6vw, 64px); width: var(--fk); height: var(--fk); margin: clamp(8px, 1.4vh, 16px) auto 0; }
/* the fact the fourth key dealt, read out but not shown */
.about-fact { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* design: what I do, big, and the tools key beside it */
.ap-design {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(48px, 7vw, 120px);
  text-align: left;
}
.ap-list { display: inline-block; list-style: none; margin: 0; padding: 0; text-align: left; }
/* one keycap working through the tools (about.js draws it over .tool-key) */
.ap-tools {
  --tk: clamp(84px, min(7vw, 12vh), 124px);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.tool-key { width: var(--tk); height: var(--tk); margin-top: 4px; }
.tool-name { min-width: 8ch; margin: 14px 0 0; font-size: var(--type-body); font-weight: 500; color: var(--fg); }
.tool-all { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.ap-list li {
  display: flex;
  align-items: baseline;
  gap: clamp(16px, 2vw, 32px);
  font-size: var(--type-headline-2);
  font-weight: 500;
  letter-spacing: -0.04em;
  line-height: 1.18;
  color: var(--fg);
}
.ap-list span { min-width: 2ch; font-size: var(--type-label); font-weight: 400; letter-spacing: 0; color: rgba(var(--fg-rgb), 0.4); }

/* cursor image trail — travel shots pop along the pointer path, over the text
   (z-3) but under the story card (z-6) */
.about-trail {
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  overflow: hidden;
}
.about-trail img {
  position: absolute;
  top: 0;
  left: 0;
  width: clamp(118px, 11vw, 188px);
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 14px;
  opacity: 0;
  will-change: transform, opacity;
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.55);
}

/* the story a snapshot card grows into: top level so it opens over the keycaps.
   No heavy scrim: the rest of the hand dims instead, so you keep your place */
.story {
  position: fixed;
  inset: 0;
  z-index: 12;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(8, 8, 9, 0);
  visibility: hidden;
}
.story.is-open { visibility: visible; }
.story-card {
  position: relative;
  width: min(clamp(470px, 335px + 9.375vw, 515px), calc(100vw - 48px));
  max-height: calc(100vh - 48px);
  overflow: auto;
  padding: 12px 16px 24px;
  border-radius: clamp(24px, 2.083vw, 40px);
  background: var(--fg);
  color: var(--bg);
  box-shadow: 0 40px 110px -30px rgba(0, 0, 0, 0.7);
}
.story-close {
  position: absolute;
  top: 22px;
  right: 26px;
  z-index: 1;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.88);
  color: #000;
  cursor: none;
}
.story-close svg { width: 18px; height: 18px; }
.story-close:focus-visible { outline: 2px solid #000; outline-offset: 2px; }
.sc-photo {
  width: 100%;
  aspect-ratio: 445 / 297;
  border-radius: 16px;
  overflow: hidden;
}
.sc-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sc-title {
  margin: 22px 0 0;
  font-size: var(--type-title);
  font-weight: 600;
  line-height: 1.2;
  color: #000;
}
.sc-body { margin-top: 13px; }
.sc-body p { margin: 0; font-size: 15px; line-height: 1.2; color: #000; }
.sc-body p + p { margin-top: 8px; }

/* tablet and phones: the page scrolls, the columns stack */
@media (max-width: 900px) {
  .about-inner { padding-top: 104px; }
}
@media (max-width: 560px) {
  /* the stage sits lower, so the keys come up nearer the middle. Only the
     panel that's up takes room, so tony's (the long one) scrolls while loves
     and design fit the screen (a phone too short for them still scrolls) */
  .about { overflow: hidden auto; }
  .ap:not(.is-active) { height: 0; overflow: hidden; }
  /* scrolled, the stage slides up under the header and fades out there; the
     keys too (they're drawn on their own layer, so it takes the same fade) */
  .about,
  body.is-about .kc-layer {
    -webkit-mask-image: linear-gradient(to bottom, transparent 52px, #000 100px);
    mask-image: linear-gradient(to bottom, transparent 52px, #000 100px);
  }
  .about[data-panel="tony"] {
    /* its text fades out along the bottom as it scrolls too: a mask, so the
       backdrop and the music's glow behind the page stay as they are */
    -webkit-mask-image: linear-gradient(to bottom, transparent 52px, #000 100px, #000 calc(100% - 150px), transparent calc(100% - 46px));
    mask-image: linear-gradient(to bottom, transparent 52px, #000 100px, #000 calc(100% - 150px), transparent calc(100% - 46px));
  }
  /* the stage starts right under the header, and the page ends with room to
     scroll its last links up past the faded bottom edge and the Menu pill */
  .about-inner { margin: 0; padding-top: clamp(96px, 13vh, 120px); padding-bottom: 170px; }
  .about-name { font-size: clamp(30px, 8.4vw, 48px); }
  .about-keys { --ak: clamp(58px, 17vw, 76px); margin-top: calc(var(--ak) * -0.06); }
  .ap-cols { grid-template-columns: 1fr; gap: 32px; }
  .love-card { --cw: 23vw; margin: 0 calc(var(--cw) * -0.12); border-radius: 12px; }
  .lc-title { left: 8px; right: 8px; bottom: 8px; font-size: 11px; }
  /* design: the list, then the tools key with its name beside it */
  .ap-design { flex-direction: column; align-items: center; gap: 22px; }
  .ap-tools { --tk: 64px; display: grid; grid-template-columns: var(--tk) auto; align-items: center; column-gap: 16px; text-align: left; }
  .ap-tools .ap-head { grid-column: 1 / -1; margin-bottom: 10px; text-align: center; }
  .tool-key, .tool-name { margin: 0; }
}

/* ---------- playground (the archive page) ---------- */
/* after gionatannese.com/projects: a short centred heading over a grid of tall
   covers, the title and category under each. The page scrolls (Lenis on it) and
   stays see-through like About and Contact, so the backdrop shows between cards */
.archive {
  position: fixed;
  inset: 0;
  z-index: 8;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  background: transparent; /* the backdrop shows through (background.js) */
  /* the grid fades out as it slides up under the header, gone behind its words */
  -webkit-mask-image: linear-gradient(to bottom, transparent clamp(48px, 6vh, 64px), #000 clamp(112px, 15vh, 150px));
  mask-image: linear-gradient(to bottom, transparent clamp(48px, 6vh, 64px), #000 clamp(112px, 15vh, 150px));
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.55s ease, visibility 0s linear 0.55s;
}
.archive::-webkit-scrollbar { display: none; }
.archive.is-open { visibility: visible; opacity: 1; transition: opacity 0.55s ease; }

.archive-inner { padding: clamp(128px, 21vh, 210px) 10px clamp(56px, 10vh, 120px); }
.archive-head { max-width: 36em; margin: 0 auto clamp(52px, 9vh, 100px); text-align: center; }
.archive-eyebrow {
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1.3em;
}
.archive-title {
  font-size: clamp(34px, 3.6vw, 56px);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.02;
  color: var(--fg);
}
.archive-title em { font-family: var(--serif); font-style: italic; font-weight: 400; }
.archive-note {
  margin: 1.3em auto 0;
  max-width: 30em;
  font-size: clamp(14px, 1.05vw, 16px);
  line-height: 1.55;
  color: var(--muted);
}

/* the grid: three across, two on tablets, one on phones */
.archive-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(30px, 4.6vh, 48px) clamp(6px, 0.6vw, 10px);
}
@media (max-width: 1000px) { .archive-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) {
  .archive-inner { padding-left: var(--margin); padding-right: var(--margin); }
  .archive-grid { grid-template-columns: minmax(0, 1fr); }
}
.ag-card {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.ag-media {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  border-radius: clamp(10px, 0.9vw, 14px);
  overflow: hidden;
  background: rgba(var(--fg-rgb), 0.05);
}
.ag-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1s cubic-bezier(0.22, 1, 0.36, 1);
}
/* a hairline keeps the covers' own grey from melting into the light page */
.ag-media::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px rgba(var(--fg-rgb), 0.07); pointer-events: none; }
@media (hover: hover) { .ag-card:hover .ag-media img { transform: scale(1.035); } }
.ag-chip {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 1;
  padding: 5px 9px;
  border-radius: 7px;
  background: rgba(255, 255, 255, 0.92);
  color: #111;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.01em;
}
.ag-cap { display: grid; gap: 2px; margin-top: 12px; font-size: 13px; line-height: 1.35; }
.ag-title { color: var(--fg); font-weight: 500; }
.ag-cat { color: rgba(var(--fg-rgb), 0.5); }
.ag-card:focus-visible { outline: none; }
.ag-card:focus-visible .ag-media { outline: 2px solid rgba(var(--fg-rgb), 0.7); outline-offset: 4px; }

/* a clicked cover grows into a card beside its words (under them on phones);
   it lives on <body>, so it holds still however far down the page it came from */
.aw-lightbox {
  position: fixed;
  inset: 0;
  z-index: 12; /* over the chrome (10), under the menu (15) */
  visibility: hidden;
  pointer-events: none;
  background: rgba(var(--bg-rgb), 0);
  transition: background 0.45s ease, visibility 0s linear 0.45s;
}
.aw-lightbox.is-open { visibility: visible; pointer-events: auto; background: rgba(var(--bg-rgb), 0.97); transition: background 0.45s ease; }
.aw-lb-img { position: absolute; object-fit: cover; box-shadow: 0 40px 110px -30px rgba(0, 0, 0, 0.55); }
/* the words: under the card, centred (JS sets `top` so the pair centres as a
   unit); beside it, left aligned, on wide windows (.is-side) */
.aw-lb-info {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: min(86vw, 640px);
  text-align: center;
  opacity: 0;
}
.aw-lb-info.is-side { transform: none; text-align: left; }
.aw-lb-meta {
  margin: 0;
  color: var(--muted);
  font-size: clamp(11px, 0.85vw, 12px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.aw-lb-title {
  margin: 0.7em 0 0;
  color: var(--fg);
  font-size: clamp(22px, 2.4vw, 32px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
}
.aw-lb-note {
  margin: 0.75em 0 0;
  color: var(--muted);
  font-size: clamp(13px, 1.05vw, 15px);
  line-height: 1.6;
}
.aw-lb-visit {
  display: inline-block;
  margin-top: 1.4em;
  padding: 0.72em 1.5em;
  border-radius: 999px;
  background: var(--fg);
  color: var(--bg);
  font-size: clamp(13px, 1vw, 14px);
  font-weight: 500;
  text-decoration: none;
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
}
@media (hover: hover) { .aw-lb-visit:hover { transform: translateY(-2px); } }
.aw-lb-visit:focus-visible { outline: 2px solid rgba(var(--fg-rgb), 0.7); outline-offset: 3px; }
/* touch needs a 44px target — the desktop padding lands at ~39px */
@media (hover: none) { .aw-lb-visit { padding: 0.95em 1.6em; } }
.aw-lb-gate {
  margin: 0.9em 0 0;
  color: var(--muted);
  font-size: clamp(11px, 0.85vw, 12px);
  letter-spacing: 0.08em;
}
.aw-lb-gate[hidden] { display: none; }
.aw-lb-visit[hidden] { display: none; }
.aw-lb-close {
  position: absolute;
  top: 24px;
  right: 28px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--fg);
  color: var(--bg);
  display: grid;
  place-items: center;
  opacity: 0;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@media (hover: hover) { .aw-lb-close:hover { transform: rotate(90deg) scale(1.07); } }
/* the lightbox × sits where the menu button lives — hide the chrome while it's up */
body.aw-lb-open .menu-btn,
body.aw-lb-open .logo { opacity: 0; pointer-events: none; }
.aw-lb-close svg { width: 20px; height: 20px; }

/* ---------- menu overlay ---------- */
.menu-close {
  position: absolute;
  top: 24px;
  right: 28px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--fg);
  color: var(--bg);
  display: grid;
  place-items: center;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@media (hover: hover) { .menu-close:hover { transform: rotate(90deg) scale(1.07); } }
.menu-close svg { width: 20px; height: 20px; }

/* the menu — a right-side liquid-glass drawer over the frosted page.
   Scrim: black 40% + 32px backdrop blur (Figma Rectangle 208). It lives on a
   ::before so the drawer's own backdrop-filter never sits inside a faded
   ancestor (opacity on a parent would isolate the backdrop and kill the glass). */
.menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 15;
  visibility: hidden;
  pointer-events: none;
  transition: visibility 0s linear 0.65s;
}
.menu-overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  -webkit-backdrop-filter: blur(32px);
  backdrop-filter: blur(32px);
  opacity: 0;
  transition: opacity 0.5s ease;
}
.menu-overlay.is-open { visibility: visible; pointer-events: auto; transition: none; }
.menu-overlay.is-open::before { opacity: 1; }
/* light mode: a lighter scrim, and the drawer near-white instead of grey glass */
html[data-theme="light"] .menu-overlay::before { background: rgba(14, 14, 15, 0.16); }

/* the drawer: 612px glass panel inset from the edges (Figma Frame 15816) */
.menu-panel {
  position: absolute;
  top: clamp(12px, 4.7vh, 42px);
  right: clamp(12px, 2.78vw, 40px);
  bottom: clamp(12px, 5.4vh, 49px);
  width: min(clamp(612px, 330px + 19.58vw, 706px), calc(100vw - 24px)); /* 612@1440 → 706@1920 */
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 1.667vw, 32px);
  border-radius: clamp(24px, 1.667vw, 32px);
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--glass-light), 0 40px 110px -40px rgba(0, 0, 0, 0.7);
  overflow: hidden;
  transform: translateX(calc(100% + 64px));
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
html[data-theme="light"] .menu-panel { background: rgba(251, 250, 247, 0.86); box-shadow: var(--glass-light), 0 40px 110px -40px rgba(14, 14, 15, 0.3); }
.menu-overlay.is-open .menu-panel { transform: translateX(0); }

/* close: 56px glass circle pinned top-right inside the drawer */
.menu-close {
  position: absolute;
  top: 24px;
  right: 24px;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--glass-light);
  color: var(--fg);
  display: grid;
  place-items: center;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.3s ease;
}
@media (hover: hover) { .menu-close:hover { transform: rotate(90deg) scale(1.06); background: rgba(var(--fg-rgb), 0.06); } }
.menu-close svg { width: 20px; height: 20px; }

/* items sit vertically centred; number rides near the label's baseline */
.menu-nav {
  margin: auto 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}
.menu-link {
  display: flex;
  align-items: baseline;
  gap: 24px;
  color: var(--fg);
  transition: transform 0.35s ease;
}
@media (hover: hover) { .menu-link:hover { transform: translateX(8px); } }
.menu-link .ml-idx { font-size: var(--type-body); color: rgba(var(--fg-rgb), 0.4); min-width: 2.1em; text-align: left; }
.menu-link .ml-word {
  display: inline-block;
  font-size: var(--type-headline-1);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.2;
  transition: font-family 0.05s;
}
@media (hover: hover) { .menu-link:hover .ml-word { font-family: var(--serif); font-style: italic; font-weight: 400; } }

/* footer: credit left, the socials' keycaps right (menukeys.js draws them
   over this slot: three keys, two 22% gaps) */
.menu-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font-size: var(--type-label);
}
.menu-credit { font-weight: 500; color: rgba(var(--fg-rgb), 0.4); }
.menu-socials { display: flex; gap: 24px; }
.menu-socials a { color: var(--fg); text-decoration: none; }
@media (hover: hover) { .menu-socials a:hover { text-decoration: underline; text-underline-offset: 3px; } }

/* stacked layouts (810/390 frames): the drawer becomes a BOTTOM SHEET that
   slides up, hugging its content; the close circle hangs below it, centred in
   the same slot the floating Menu pill occupies */
@media (max-width: 900px) {
  .menu-panel {
    top: auto;
    left: var(--margin);
    right: var(--margin);
    bottom: 98px; /* clears the 48px close circle at bottom 30 + a 20px gap */
    width: auto;
    height: fit-content;
    padding: clamp(20px, 2.96vw, 24px);
    border-radius: clamp(20px, 2.96vw, 24px);
    overflow: visible; /* the close circle lives outside the sheet's box */
    transform: translateY(calc(100% + 130px));
  }
  .menu-overlay.is-open .menu-panel { transform: translateY(0); }
  .menu-nav { margin: 0 0 40px; }
  .menu-close {
    top: auto;
    bottom: -68px; /* 48px circle + 20px gap below the sheet */
    right: auto;
    left: 50%;
    margin-left: -24px;
    width: 48px;
    height: 48px;
  }
  /* the pill and the close share the slot — swap them while the menu is up */
  body.menu-open .menu-btn { opacity: 0; pointer-events: none; }
}
@media (max-width: 560px) {
  .menu-foot { flex-direction: column; align-items: flex-start; gap: 12px; }
  .menu-socials { gap: 18px; }
}

/* ---------- page-switch veil (huyml-style cover with the destination name) ---------- */
.page-veil {
  position: fixed;
  inset: 0;
  z-index: 45; /* over everything but the intro (50) and the cursor (60) */
  background: var(--bg);
  display: grid;
  place-items: center;
  visibility: hidden;
  pointer-events: none;
  /* no CSS transform here — gsap owns it (yPercent), and a CSS translateY
     would STACK with gsap's, parking the veil at 200% between runs */
  will-change: transform;
}
.page-veil.is-on { visibility: visible; pointer-events: auto; } /* eats clicks mid-flight */
.pv-mask { display: block; overflow: hidden; padding-bottom: 0.14em; margin-bottom: -0.14em; }
.pv-word {
  display: block;
  font-size: var(--type-display-3);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--fg);
}

/* ---------- intro (loading screen: project cards tumbling past the camera) ---------- */
.intro {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--bg);
  overflow: hidden;
}
/* the card stream: spawned + flown entirely by main.js (GSAP owns transforms).
   The layer pins z-index 0 so flying cards can never paint over the type. */
.intro-cards {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.intro-cards .ic {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(120px, 14vw, 280px);
  aspect-ratio: 3 / 2;
  border-radius: clamp(8px, 0.83vw, 16px);
  overflow: hidden;
  opacity: 0;
  will-change: transform;
}
.intro-cards .ic img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* the centre block: the wordmark with the heart line right under it, in the
   middle of the ring. main.js solves its size and position against the ring so
   no card ever crosses it (it sets top, --core-logo, --core-tag, --core-gap). */
.intro-core {
  position: absolute;
  left: 0;
  right: 0;
  top: 45%;
  z-index: 1;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--core-gap, 14px);
  text-align: center;
  pointer-events: none;
}
.intro-wordmark {
  margin: 0;
  font-size: var(--core-logo, 56px);
  font-weight: 500;
  letter-spacing: -0.06em;
  line-height: 1;
  color: var(--fg);
  white-space: nowrap;
  user-select: none;
  opacity: 0; /* lifted by main.js the moment the letters exist */
}
.intro-wordmark .iw-ch { display: inline-block; will-change: transform, opacity; }
.intro-wordmark .iw-key { width: 0.3em; height: 0.3em; margin-left: 0.17em; vertical-align: baseline; }
.intro-wordmark .iw-key svg { display: block; width: 100%; height: 100%; }
/* the loader's keycap, over the wordmark (main.js's ring solver counts it in) */
.intro-key { width: calc(var(--core-logo, 56px) * 1.15); height: calc(var(--core-logo, 56px) * 1.15); flex: none; }
.intro-tag { opacity: 0; } /* fades in under the wordmark once its letters land */
.intro-tag p { margin: 0; color: var(--fg); white-space: nowrap; }
.it-heart { font-size: calc(var(--type-lead) * var(--core-tag, 1)); line-height: 1.2; font-weight: 400; }
.it-role { margin-top: calc(8px * var(--core-tag, 1)) !important; font-size: calc(var(--type-label) * var(--core-tag, 1)); line-height: 1.2; font-weight: 400; opacity: 0.4; }
@media (max-width: 560px) {
  .it-heart { font-size: calc(19px * var(--core-tag, 1)); }
  .it-role { font-size: calc(15px * var(--core-tag, 1)); }
}

/* stacked layouts: the gate and the counter sit a little lower */
@media (max-width: 900px) {
  .intro-cta { bottom: 48px; }
  .intro-count { bottom: 48px; }
}
@media (max-width: 560px) {
  .intro-cta { bottom: 40px; }
  .intro-count { bottom: 40px; }
}

/* the entry gate — appears once the count hits 100: two equal glass pills,
   stacked and centred (per the finished-state frames) */
.intro-cta {
  position: absolute;
  left: 50%;
  bottom: clamp(40px, 6.2vh, 64px);
  transform: translate(-50%, 14px);
  width: max-content;
  max-width: 82vw;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  opacity: 0;
  transition: opacity 0.6s ease, transform 0.6s ease;
  pointer-events: none;   /* flipped to auto by .is-in, so the gate can't be clicked early */
}
.intro-cta.is-in { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }

.intro-enter,
.intro-skip {
  display: inline-flex;
  align-items: center;
  height: var(--btn-h);
  padding: 0 24px;
  border: none;
  border-radius: 999px;
  color: var(--fg);
  font-size: var(--type-body);
  font-weight: 500;
  cursor: none;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.3s ease;
}
/* primary carries the glass; the secondary is a bare pill (no fill, per Figma) */
.intro-enter {
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--glass-light);
}
.intro-skip { background: none; }
@media (hover: hover) {
  .intro-enter:hover { transform: translateY(-2px); background: rgba(var(--fg-rgb), 0.08); }
  .intro-skip:hover { background: rgba(var(--fg-rgb), 0.05); }
}
.intro-enter:active, .intro-skip:active { transform: scale(0.97); }
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .intro-enter { background: rgba(var(--fg-rgb), 0.12); }
}
.intro-enter:focus-visible,
.intro-skip:focus-visible { outline: 2px solid rgba(var(--fg-rgb), 0.7); outline-offset: 4px; }
.intro-count {
  position: absolute;
  left: 0;
  right: 0;
  bottom: clamp(40px, 6.2vh, 64px); /* the gate lands in this exact spot once it fades */
  text-align: center;
  font-size: var(--type-label);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--fg);
  line-height: 1;
  font-variant-numeric: tabular-nums; /* digits tick without the number jittering */
  pointer-events: none;
}
/* (the gate stays centred on every breakpoint — the counter fades out before it
   arrives, and the captions glide up out of its way on stacked layouts) */

/* ---------- contact page: the ♥ key over the multilingual marquee, the form
   below; pressing the key picks the icon the note arrives with. It all fits
   one screen: the hero takes whatever height the form leaves it ---------- */
.contact {
  position: fixed;
  inset: 0;
  z-index: 8; /* above the spiral canvas, under the keycap layer (9), chrome and menu */
  display: flex;
  flex-direction: column;
  background: transparent; /* the backdrop shows through (background.js) */
  overflow: hidden auto; /* a window too short for it all still scrolls */
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.55s ease, visibility 0s linear 0.55s;
}
.contact.is-open { visibility: visible; opacity: 1; transition: opacity 0.55s ease; }


/* the hero: the marquee band across the middle, the key's placeholder on top.
   It flexes into the room the form leaves, and the marquee and the key size
   themselves to it (container units) */
.contact-hero {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
  min-height: 150px;
  max-height: 460px;
  margin-top: clamp(64px, 9vh, 96px);
  container-type: size;
}
.contact-marquee {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  overflow: hidden;
  pointer-events: none;
}
.cm-track {
  display: flex;
  align-items: center;
  width: max-content;
  white-space: nowrap;
  animation: cm-scroll 34s linear infinite;
  will-change: transform;
}
.cm-item,
.cm-sep {
  font-size: clamp(60px, 12.5vw, 215px);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.3; /* room for descenders (g, y, p) — the marquee clips overflow */
  color: var(--fg);
}
.cm-sep { margin: 0 clamp(18px, 3vw, 58px); color: #c75a2a; } /* warm accent dots */
.cm-ar { font-weight: 400; }
@keyframes cm-scroll { to { transform: translateX(-50%); } }

.contact-key {
  --kw: clamp(150px, 26vh, 240px);
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--kw);
  height: var(--kw);
  margin: calc(var(--kw) / -2) 0 0 calc(var(--kw) / -2);
}
/* sized by the hero they sit in, where the browser can (container units) */
@supports (height: 1cqh) {
  .cm-item, .cm-sep { font-size: clamp(40px, min(12.5vw, 56cqh), 215px); }
  .contact-key { --kw: clamp(92px, 70cqh, 240px); }
}

.contact-body {
  position: relative;
  z-index: 1;
  flex: none;
  width: min(560px, calc(100vw - 2 * var(--margin)));
  margin: clamp(4px, 1.4vh, 16px) auto 0;
  padding-bottom: clamp(20px, 4vh, 44px);
  text-align: center;
}
.ck-caption { margin: 0; font-size: var(--type-body); line-height: 1.45; color: rgba(var(--fg-rgb), 0.55); }
.ck-name { display: inline-block; color: var(--fg); }

/* the form: glass fields, the primary glass pill to send */
.cf { display: grid; gap: 10px; margin-top: clamp(14px, 2.4vh, 26px); text-align: left; }
.cf-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cf-field { display: grid; gap: 6px; }
.cf-label { padding-left: 4px; font-size: var(--type-label); color: rgba(var(--fg-rgb), 0.45); }
.cf-field input,
.cf-field textarea {
  width: 100%;
  padding: 14px 16px;
  border: 1px solid var(--glass-stroke);
  border-radius: 16px;
  background: var(--glass-fill);
  color: var(--fg);
  font: inherit;
  font-size: var(--type-body);
  outline: none;
  transition: border-color 0.25s ease, background 0.25s ease;
}
.cf-field textarea { height: 84px; resize: none; } /* it scrolls inside; growing would push the page past the screen */
.cf-field input::placeholder,
.cf-field textarea::placeholder { color: rgba(var(--fg-rgb), 0.32); opacity: 1; }
.cf-field input:focus,
.cf-field textarea:focus { border-color: rgba(var(--fg-rgb), 0.55); background: rgba(var(--fg-rgb), 0.05); }
.cf-field.is-invalid input,
.cf-field.is-invalid textarea { border-color: #ff6b5a; }
.cf-send {
  justify-self: center;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--btn-h);
  margin-top: 8px;
  padding: 0 28px;
  border: 0;
  border-radius: 999px;
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--glass-light);
  color: var(--fg);
  font: inherit;
  font-size: var(--type-body);
  font-weight: 500;
  cursor: none;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.3s ease;
}
.cf-emoji { display: inline-block; }
@media (hover: hover) { .cf-send:hover { transform: translateY(-2px); background: rgba(var(--fg-rgb), 0.08); } }
.cf-send:active { transform: scale(0.97); }
.cf-send:focus-visible { outline: 2px solid rgba(var(--fg-rgb), 0.7); outline-offset: 4px; }
.cf-note { min-height: 1.3em; margin: 2px 0 0; text-align: center; font-size: var(--type-label); color: rgba(var(--fg-rgb), 0.55); }
.cf-note a { color: var(--fg); text-decoration: underline; text-underline-offset: 3px; }
.cf-trap { display: none; }
/* while the note is on its way the button rests */
.cf.is-sending .cf-send { opacity: 0.55; pointer-events: none; }

/* the details, quiet, under the form */
.contact-info { display: grid; justify-items: center; gap: 6px; margin-top: clamp(12px, 2.4vh, 28px); }
.ci-link {
  font-size: clamp(20px, 2.86vw, 26px);
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid rgba(236, 236, 236, 0.28);
  padding-bottom: 2px;
  transition: border-color 0.3s ease, color 0.3s ease;
}
@media (hover: hover) { .ci-link:hover { color: var(--fg); border-color: var(--fg); } }
.ci-where { margin: 0; font-size: var(--type-label); color: rgba(var(--fg-rgb), 0.45); }
.ci-socials { display: flex; justify-content: center; gap: 18px; margin: 0; }
.ci-socials a { font-size: clamp(13px, 1.1vw, 15px); color: var(--muted); text-decoration: none; letter-spacing: 0.04em; transition: color 0.3s ease; }
@media (hover: hover) { .ci-socials a:hover { color: var(--fg); } }

/* hide the spiral/list toggle on contact (keep logo + menu) */

@media (prefers-reduced-motion: reduce) {
  .cm-track { animation: none; }
}
/* the Menu pill floats at the bottom from 900 down: the details end above it */
@media (max-width: 900px) {
  .contact-body { padding-bottom: calc(30px + var(--btn-h) + 16px); }
}
@media (max-width: 560px) {
  .cm-item, .cm-sep { font-size: clamp(46px, 17vw, 120px); }
  .contact-hero { margin-top: 92px; } /* clears the header */
  .contact-key { --kw: clamp(120px, 40vw, 170px); }
  @supports (height: 1cqh) {
    .cm-item, .cm-sep { font-size: clamp(40px, min(17vw, 56cqh), 120px); }
    .contact-key { --kw: clamp(92px, 70cqh, 170px); }
  }
}
/* name and email share a row down to the narrowest phones */
@media (max-width: 359px) {
  .cf-row { grid-template-columns: 1fr; }
}

/* ---------- keycaps: the shared 3D layer over About, Contact and Work ---------- */
.kc-layer { position: fixed; inset: 0; z-index: 9; pointer-events: none; visibility: hidden; }
/* the loader's key rides above the loader (50) until it has flown off (introkey.js) */
body.kc-over-intro .kc-layer { z-index: 52; }
.kc-layer.is-on { visibility: visible; }
.kc-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.kc-hit {
  position: absolute;
  left: 0;
  top: 0;
  padding: 0;
  border: 0;
  border-radius: 22%;
  background: none;
  pointer-events: auto;
  cursor: none;
  /* quick repeat taps never zoom the page, and a long press never pops the callout */
  touch-action: manipulation;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.kc-hit[hidden] { display: none; }
.kc-hit:focus-visible { outline: 2px solid rgba(var(--fg-rgb), 0.75); outline-offset: 6px; }

/* ---------- the Work keys: the number key + the view key at the bottom ---------- */
/* the caps are drawn on the keycap layer over the two slots (workkey.js) */
.work-keys {
  --wk: clamp(76px, 6.4vw, 100px);
  --wk-gap: 12px;
  position: fixed;
  left: 0;
  right: 0;
  bottom: clamp(20px, 3.2vh, 36px);
  z-index: 9;
  display: flex;
  justify-content: center;
  pointer-events: none;
  visibility: hidden;
}
.work-keys.is-on { visibility: visible; }
.wk-row { display: flex; }
.wk-slot {
  flex: none;
  width: var(--wk);
  height: var(--wk);
  transition: width 0.55s cubic-bezier(0.65, 0, 0.35, 1), margin 0.55s cubic-bezier(0.65, 0, 0.35, 1);
}
.wk-num { margin-right: var(--wk-gap); }
/* in the list only the view key stays: the number's slot folds away and it slides to the middle */
body[data-view="list"] .wk-num { width: 0; margin-right: 0; }
/* the Menu pill floats at the bottom centre from 900 down: the keys sit just above it */
@media (max-width: 900px) {
  .work-keys { bottom: calc(30px + var(--btn-h) + 16px); }
}
@media (max-width: 560px) {
  .work-keys { --wk: clamp(68px, 19vw, 84px); }
}
