:root {
  --bg: #1e1e1e;
  --fg: #fffaee;
  --fg-dim: rgba(255, 250, 238, 0.12);
  --fg-mid: rgba(255, 250, 238, 0.55);
  --accent: #6dff7a;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "42dot Sans", "Inter", system-ui, sans-serif;
  --s: 1;
}

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

html, body {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: var(--bg);
}

body {
  color: var(--fg);
  font-family: var(--sans);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
  position: relative;
}

a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
a:hover { color: #fff; }
ol, ul { list-style: none; }
img { display: block; }

/* =========================================================
   START-SCREEN — PLAY gate. Same dark bg as #splash so when
   the user clicks Play we can swap one for the other instantly
   (display:none → display:flex) without any fade or "modal
   change" flash — visually only the centered icon changes.
   ========================================================= */
#start-screen {
  position: fixed;
  inset: 0;
  z-index: 100001;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
}
#start-screen.hidden { display: none; }
/* =========================================================
   DESIGN-THINKING DIAL — concentric stage bands behind Play.
   Outer→inner: Empathize · Define · Ideate · Prototype · Test;
   Play (no border) is the innermost ring. Faint outlines show
   always; the band under the cursor fills with a tiled icon
   pattern of its stage. On Play the whole dial zooms toward the
   viewer and fades out (geometry + radii live in start-rings.js).
   ========================================================= */
.start-inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: scale(1);
  opacity: 1;
  transition: transform 0.6s cubic-bezier(0.66, 0, 0.85, 0.2),
              opacity 0.55s ease;
  will-change: transform, opacity;
}
#start-screen.zooming .start-inner {
  transform: scale(7);
  opacity: 0;
}

.start-play {
  position: relative;     /* sit above the rings layer */
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  background: transparent;
  border: none;           /* no border — the innermost dial ring IS its circle */
  padding: 0;
  color: var(--fg);
  cursor: none;
}
.start-play-icon {
  width: 22px;
  height: 22px;
  display: block;
  transition: transform 0.2s ease;
}
.start-play:hover .start-play-icon { transform: scale(1.12); }

/* Rings layer is centered on the Play button (left/top 50%); it must NOT
   clip — the viewport (body overflow:hidden) handles any overflow. */
.start-rings {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
}
.start-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  border: 1px solid var(--fg);
  border-radius: 50%;
  opacity: 0.07;
  will-change: transform;   /* parallax offset applied inline by JS */
}

/* Whole dial is clickable — Play fires from anywhere inside the rings. */
#start-screen { cursor: none; }
/* Annulus tiled with a stage-icon pattern. Hidden by default — icons appear
   ONLY for the band under the cursor. Ring-shaped mask + background image +
   parallax transform are set inline by JS. */
.start-band {
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 50%;
  background-repeat: repeat;
  opacity: 0;
  transition: opacity 0.35s ease;
}
.start-band.active { opacity: 0.45; }

.start-stage-label {
  position: absolute;
  left: 50%;
  top: calc(50% + 64px);
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 2.5px;
  text-transform: uppercase;
  color: var(--fg);
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
  white-space: nowrap;
}
.start-stage-label.show { opacity: 0.55; }


/* =========================================================
   SPLASH / LOADING SCREEN
   Full-viewport dark cover with 3 cycling icons (src/splash.js
   swaps the icon src on audio beats once Play is clicked).
   Starts invisible — `splash.js` adds `.active` to show it.
   ========================================================= */
#splash {
  position: fixed;
  inset: 0;
  z-index: 100000;
  background: var(--bg);
  display: none;                 /* no fade-in flash — toggled by JS */
  align-items: center;
  justify-content: center;
  opacity: 1;
  transition: opacity 0.45s ease;
}
#splash.active { display: flex; }
#splash.hidden { opacity: 0; pointer-events: none; }   /* fade-out only */
.splash-row {
  display: flex;
  align-items: center;
  gap: 18px;
}
.splash-icon {
  width: 28px;
  height: 28px;
  display: block;
  object-fit: contain;
}

/* =========================================================
   REVEAL — fade-in for main CV blocks once splash closes.
   reveal.js tags each block with .reveal-target + .reveal-hidden
   at page load, then strips .reveal-hidden with a per-element
   delay on the cv-splash-hidden event.
   ========================================================= */
.reveal-target {
  transition: opacity 0.55s ease, transform 0.55s ease;
}
.reveal-target.reveal-hidden {
  opacity: 0;
  transform: translateY(4px);
}

/* =========================================================
   PAGE-WIDE ANIMATED GRAIN (rendered by noise.js)
   ========================================================= */
#noise {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: 9999;
  opacity: 0.08;
  mix-blend-mode: overlay;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}

/* =========================================================
   CROSSHAIR CURSOR
   System cursor is hidden everywhere; two 1px fixed lines
   follow the mouse via --cx / --cy custom props (updated by
   the script in index.html on each mousemove).
   ========================================================= */
html, body, * { cursor: none !important; }

.crosshair {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 100002;   /* above start-screen and splash */
}
.crosshair-h,
.crosshair-v {
  position: absolute;
  background: rgba(255, 250, 238, 0.18);
  pointer-events: none;
  will-change: transform;
}
.crosshair-h {
  left: 0;
  top: 0;
  width: 100vw;
  height: 1px;
  transform: translateY(var(--cy, 50vh));
}
.crosshair-v {
  left: 0;
  top: 0;
  width: 1px;
  height: 100vh;
  transform: translateX(var(--cx, 50vw));
}
.cursor-hidden .crosshair { opacity: 0; }

/* =========================================================
   CANVAS — outermost flex container per Figma:
   `flex gap-[215px] items-start p-[40px]` + border.
   Design space 1440 × 1024 uniform-scaled via zoom to viewport.
   ========================================================= */
.canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 1440px;
  /* min-height (not fixed height) so a taller-than-Figma content column makes
     the box grow — keeping the 40px bottom padding below the content instead
     of letting content overflow past it. The fit() script scales to this. */
  min-height: 1024px;
  zoom: var(--s, 1);
  /* Transparent so the 3D hero (z-index 0) shows through. The page base
     colour lives on <body> (same var(--bg)), so the left column looks
     identical while the 3D is no longer hidden behind a solid fill. */
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.12);
  overflow: visible;
  isolation: isolate;
  z-index: 1;   /* above the 3D hero backdrop (.hero-3d z-index: 0) */

  display: flex;
  gap: 215px;
  padding: 40px;
  align-items: flex-start;
}

/* =========================================================
   MAIN CONTENT COLUMN — 642 wide, flex-col gap-200
   ========================================================= */
.content {
  flex-shrink: 0;
  width: 642px;
  display: flex;
  flex-direction: column;
  gap: 200px;
  align-items: flex-start;
  z-index: 3;
}

.row { display: flex; flex-shrink: 0; }

/* ── Row 1: meta + name ── */
.row-top {
  gap: 97px;
  align-items: flex-end;
  width: 100%;
}

.meta-col {
  flex-shrink: 0;
  width: 307px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.meta {
  font-family: var(--sans);
  font-weight: 300;
  font-size: 12px;
  line-height: normal;
  letter-spacing: -0.48px;
  color: var(--fg);
}

.meta-icons {
  width: 142.327px;
  height: 17px;
}

.status-on {
  color: var(--accent);
  filter: drop-shadow(0 0 6px rgba(109, 255, 122, 0.45));
}

/* Name column — volume widget (top) + name+dots stack (bottom).
   Stretches to row height so justify-between pushes the two children apart. */
.name-col {
  flex-shrink: 0;
  width: 240px;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
}

/* ── Volume widget ── */
.volume-control {
  display: flex;
  align-items: center;
  width: 240px;
  gap: 0;
}
.volume-mute,
.volume-plus,
.volume-minus {
  background: transparent;
  border: 0;
  padding: 0;
  color: var(--fg);
  cursor: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.volume-mute       { width: 32px; height: 44px; }
.volume-plus,
.volume-minus      { width: 44px; height: 44px; }
.volume-mute img   { width: 32px; height: 44px; display: block; }
.volume-plus img,
.volume-minus img  { width: 44px; height: 44px; display: block; }
.volume-mute.muted img { opacity: 0.35; }

.volume-bars {
  flex: 1 0 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 0 8px;
}
.vol-bar {
  height: 22px;
  background: var(--fg);
  flex-shrink: 0;
  opacity: 0.18;
  transition: opacity 0.18s ease;
}
.vol-bar.active { opacity: 1; }

.volume-buttons {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* ── Name + dots (positioned absolutely within their own stack) ── */
.name-stack {
  position: relative;
  width: 240px;
  z-index: 3;
}
.name-dots {
  position: absolute;
  left: 121px;
  top: 0;
  width: 108.037px;
  height: 108.037px;
  transform: rotate(180deg);
  opacity: 0.6;
  pointer-events: none;
}
.name {
  position: relative;
  margin-top: 24.03px;
  font-family: var(--sans);
  font-weight: 800;
  font-size: 32px;
  line-height: 1.05;
  letter-spacing: -0.96px;
  color: var(--fg);
  white-space: nowrap;
  z-index: 2;
}

/* ── Bottom wrapper: skills/awards row + general/contacts row ── */
.bottom-wrap {
  display: flex;
  flex-direction: column;
  gap: 80px;
  align-items: flex-start;
  width: 100%;
}

/* ── Row 2a: skills/awards + roles (full width) ── */
.row-skills {
  gap: 80px;
  align-items: flex-start;
  width: 100%;
}

.skills-col {
  flex-shrink: 0;
  width: 307.09px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.skills {
  font-size: 12px;
  line-height: normal;
  letter-spacing: -0.48px;
  white-space: nowrap;
}

.skills-icon {
  width: 24px;
  height: 16px;
}

.awards-wrap {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-end;
  width: 100%;
}
.awards {
  font-size: 12px;
  line-height: normal;
  letter-spacing: -0.48px;
  text-align: right;
}
.award-icons {
  width: 98.52px;
  height: 14px;
}

/* Roles wheel-picker — driven by wheel scroll (src/roles.js).
   Each column clips its inner list and translateY's based on --idx
   set by JS, so the active role lines up with a fixed "active" row
   (y = 27.685px from the top of the column). */
.roles {
  flex-shrink: 0;
  display: flex;
  gap: 28px;
  font-family: var(--sans);
  font-weight: 800;
  font-size: 23.027px;
  line-height: 1.2;
  letter-spacing: -0.6908px;
}
.roles-col {
  overflow: hidden;
  height: 110px;
}
.roles-col-1 {
  width: 104px;
  text-align: right;
}
.roles-col-2 {
  width: 125.185px;
  align-self: stretch;
}
.roles-list {
  display: flex;
  flex-direction: column;
  list-style: none;
  transition: transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}
/* Col 1 starts with Product at y=27.685 (one row of empty space above);
   --idx counts up as we scroll, sliding the list UP by one row each step. */
.roles-col-1 .roles-list {
  padding-top: 27.685px;
  transform: translateY(calc(var(--idx, 0) * -27.685px));
}
/* Col 2 starts with Designer at y=27.685 (Developer above at y=0).
   --idx=0 → Designer active (no shift), --idx=1 → list shifted DOWN by one
   row so Developer reaches the active y=27.685 line. */
.roles-col-2 .roles-list {
  transform: translateY(calc(var(--idx, 0) * 27.685px));
}
.role {
  height: 27.685px;
  width: 100%;
  flex-shrink: 0;
  color: var(--fg-dim);
  transition: color 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.role.role-active { color: var(--fg); }

/* =========================================================
   Row 2b: general panel + contacts panel
   gap-80 items-end
   ========================================================= */
.row-bottom {
  gap: 80px;
  align-items: flex-start;   /* general aligns to top, matches Figma */
  width: 100%;
}

/* ───── GENERAL panel ─────
   Figma uses one grid cell with overlapping children at margin offsets:
     general-head     ml:0       mt:4.51    w:307 h:70
     compass-v        ml:122.84  mt:0       8.6 × 141.5
     compass-h        ml:0       mt:71.13   306.8 × 8.6
     compass-d        ml:79.02   mt:29.51   94.8 × 95.5
     compass-wave     ml:1.19    mt:90.51   59.8 × 10.4
     lang-edu         ml:147.09  mt:92      160 × auto
     stats            ml:1       mt:158.51  306 × auto
   Natural cell height = 158.51 + stats line height ≈ 190.
*/
.general {
  flex-shrink: 0;
  position: relative;
  width: 307px;
  height: 190px;
}

.compass {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.compass .line {
  position: absolute;
  display: block;
}
.line-h {
  left: 0;
  top: 71.13px;
  width: 306.808px;
  height: 8.621px;
}
.line-v {
  left: 122.84px;
  top: 0;
  width: 8.621px;
  height: 141.529px;
}
.line-d {
  left: 79.02px;
  top: 29.51px;
  width: 94.785px;
  height: 95.496px;
}

.general-head {
  position: absolute;
  left: 0;
  top: 0;
  display: flex;
  width: 307px;
  height: 70px;
  align-items: flex-end;           /* logos sit at the bottom of the head */
  justify-content: space-between;
  padding-bottom: 12px;
}
.general-title {
  align-self: flex-start;          /* but the title pops up to the top */
  font-family: var(--sans);
  font-weight: 800;
  font-size: 23.027px;
  line-height: 1;
  letter-spacing: -0.6908px;
  color: var(--fg);
  width: 143px;
}
.general-logos {
  display: flex;
  gap: 14px;
  align-items: center;
}
.general-logos img { display: block; }
.general-logos img:nth-child(1) { width: 20px;     height: 20px; } /* image16 */
.general-logos img:nth-child(2) { width: 19.548px; height: 20px; } /* spalah */
.general-logos img:nth-child(3) { width: 57px;     height: 20px; } /* image14 */

.lang-edu {
  position: absolute;
  left: 147.09px;
  top: 92px;
  width: 160px;
  font-size: 12px;
  line-height: normal;
  letter-spacing: -0.48px;
}

.compass-wave {
  position: absolute;
  left: 1.19px;
  top: 90.51px;
  width: 59.821px;
  height: 10.354px;
}

.stats {
  position: absolute;
  left: 1px;
  top: 158.51px;
  display: flex;
  width: 306px;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  line-height: normal;
  letter-spacing: -0.48px;
  white-space: nowrap;
}
.stats > div { display: block; }
.stats dt, .stats dd { display: block; }

/* ───── CONTACTS panel — fills remaining row width ───── */
.contacts {
  flex: 1 0 0;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-start;
}

.cv-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  height: 38px;
  border: 1px solid var(--fg);
  border-radius: 50px;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: -0.48px;
  color: var(--fg);
  text-decoration: none;
  white-space: pre;
  align-self: flex-start;
  transition: background 0.2s, color 0.2s;
}
.cv-btn:hover { background: var(--fg); color: var(--bg); }
.cv-btn:hover img { filter: invert(1); }
.cv-btn img { width: 7px; height: 9px; }

.contacts-title {
  font-family: var(--mono);
  font-weight: 300;
  font-size: 12px;
  letter-spacing: -0.48px;
  text-transform: uppercase;
  color: var(--fg);
}

.contacts-block {
  font-family: var(--sans);
  font-weight: 300;
  font-size: 12px;
  line-height: normal;
  letter-spacing: -0.48px;
  text-transform: uppercase;
}
.contacts-block p { white-space: nowrap; }
.contacts-block-mono {
  width: 230px;
  font-family: var(--mono);
  letter-spacing: -0.84px;
  text-align: justify;
}

.contacts-and {
  display: flex;
  gap: 8px;
  align-items: center;
  width: 100%;
  font-size: 12px;
  letter-spacing: -0.48px;
}
.contacts-and-arrow {
  flex: 1 0 0;
  min-width: 0;
  height: 5px;
  transform: scaleY(-1) rotate(180deg);
}

.contacts-burst {
  position: absolute;
  left: 96%;
  top: -4%;
  width: 32px;
  height: 32px;
  pointer-events: none;
}

/* =========================================================
   MODEL META — author + license, top-right of the viewport.
   Anonymous-looking link (no underline).
   ========================================================= */
.model-meta {
  position: fixed;
  top: 40px;
  left: 760px;
  right: 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  z-index: 50;
  pointer-events: none;
}
.model-author,
.model-cc {
  font-family: var(--sans);
  font-weight: 300;
  font-size: 12px;
  line-height: normal;
  letter-spacing: -0.48px;
  text-transform: uppercase;
  color: var(--fg);
  text-decoration: none;
  pointer-events: auto;
  white-space: nowrap;
}
.model-author:hover { color: #fff; }

/* DOS-style blinking caret while typing (and a brief linger after).
   Added/removed by roles.js → typeInto() via the `.typing` class. */
.model-author.typing::after,
.model-cc.typing::after {
  content: "_";
  margin-left: 1px;
  animation: dos-caret-blink 0.55s steps(1) infinite;
}
@keyframes dos-caret-blink {
  0%, 50%   { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}

/* =========================================================
   HERO 3D — fixed on the viewport, anchored to the right edge,
   full viewport height. Detached from the canvas flex so its
   width and right inset are tied to vw/vh, not the design-space
   1440 × 1024 canvas. Width tunable via --hero-w (default 56vw).
   ========================================================= */
.hero-3d {
  position: fixed;
  top: 0;
  right: 0;
  width: var(--hero-w, 56vw);
  height: 100vh;
  pointer-events: none;
  z-index: 0;   /* behind the content frame (.canvas z-index: 1) */
}
.hero-3d canvas {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--bg);
}

/* =========================================================
   MOBILE — reflow the fixed 1440×1024 design-space into a
   natural, vertically-scrolling column. The 3D hero becomes a
   dim, fixed ambient backdrop; text sits on a translucent card.
   ========================================================= */
@media (max-width: 760px) {
  /* Allow the page to scroll instead of locking to one viewport. */
  html, body {
    height: auto;
    min-height: 100%;
    overflow-x: hidden;
    overflow-y: auto;
    -webkit-text-size-adjust: 100%;
  }

  /* Drop the design-space zoom + fixed 1440×1024 box — flow normally.
     `zoom` is what shrinks the whole design to fit desktop; on mobile we
     reflow instead, so it MUST be reset or everything renders tiny. */
  .canvas {
    position: static;
    width: 100%;
    height: auto;
    min-height: 100vh;
    zoom: 1;
    transform: none;
    border: none;
    padding: 0;
    display: block;
    z-index: 1;
  }

  /* The text column becomes a full-width, naturally-flowing card. */
  .content {
    width: 100%;
    max-width: 100%;
    gap: 0;
    margin: 0;
    padding: 64px 22px 52px;
    background: rgba(18, 18, 18, 0.78);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
  }

  /* Rows flow vertically with even spacing instead of the desktop gaps. */
  .row, .row-top, .row-skills, .row-bottom { width: 100%; }
  .bottom-wrap { gap: 48px; }
  .row-top { flex-direction: column; gap: 30px; align-items: flex-start; }
  .meta-col { width: 100%; }

  /* 3D models become a very dim, fixed backdrop behind everything. */
  .hero-3d {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    opacity: 0.22;
    z-index: 0;
  }

  /* ── Row 1: name block drops below the meta block ── */
  .row-top { position: relative; }
  .name-col {
    position: static;
    width: 100%;
    align-items: flex-start;
    margin-top: 30px;
    gap: 26px;
  }
  .volume-control { align-items: flex-start; }
  .name {
    text-align: left;
    font-size: 44px;
    letter-spacing: -1.4px;
  }

  /* ── Skills row: hide the wheel-picker (needs a scroll wheel) ── */
  .row-skills { margin-top: 40px; }
  .roles { display: none; }

  /* ── Bottom row: general + contacts stack vertically ── */
  .row-bottom {
    flex-direction: column;
    gap: 46px;
    margin-top: 52px;
  }
  .general, .contacts { width: 100%; }
  .compass { display: none; }
  /* Mono contacts block has a fixed 230px width on desktop — let it flow. */
  .contacts-block-mono { width: 100%; text-align: left; }

  /* Attribution drops into normal flow at the very bottom. */
  .model-meta {
    position: static;
    inset: auto;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    max-width: 560px;
    margin: 0 auto;
    padding: 0 22px 40px;
  }
  .model-author, .model-cc { white-space: normal; }

  /* Start-screen dial keeps its natural size — do NOT scale it down or it
     shrinks to a dot. Outer rings simply overflow off-screen (body clips). */
}

@media (max-width: 380px) {
  .name { font-size: 38px; }
}

/* Touch devices: no mouse → drop the custom crosshair + restore the
   normal cursor (the design-wide `cursor: none` is mouse-only UX). */
@media (pointer: coarse) {
  html, body, * { cursor: auto !important; }
  .crosshair { display: none !important; }
}
