/* ─────────────────────────────────────────────────────────────────────────
   Portfolio page styles, on top of the vendored Nocturne system.

   Two layout modes share one DOM:

     flow mode (default)  A normal scrolling document. This is what you get
                          with JavaScript off, on phones and tablets, and
                          under prefers-reduced-motion. The laptop chrome
                          flattens away and its two screens become sections.

     deck mode (.mode-deck on <html>, added by the inline head script and
                owned by app.js)  The scroll-jacked original: a 600vh track
                with a sticky viewport, six cross-faded panels, and the
                laptop lid opening for panels 2 and 3.

   Everything here is mode-agnostic unless it sits under .mode-deck.
   ───────────────────────────────────────────────────────────────────── */

:root {
  --ground: #121422;
  --gutter: 56px;
  --nav-h: 75px;
  /* The accent the canvas backgrounds draw with. app.js reads this, so
     retinting the effects is a one-line change here. */
  --bg-accent: #9184d9;
}

html { background: var(--ground); }
body { background: var(--ground); overflow-x: hidden; }

a { text-decoration: none; color: var(--color-accent-300); }
a:hover { color: var(--color-accent); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 10px 16px;
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: 0 0 var(--radius-md) 0;
}
.skip-link:focus { left: 0; }

/* ── animated ground ─────────────────────────────────────────────────── */

.bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(120% 100% at 12% 0%, #1d2036 0%, #161826 55%, #121422 100%);
}
.bg-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 500ms ease;
}
/* In flow mode only the constellation runs, softened behind the content. */
.bg-layer[data-bg="a"] { opacity: 0.55; }
.bg-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(70% 65% at 50% 50%, rgba(22, 24, 38, 0) 0%, rgba(18, 20, 34, 0.5) 100%);
}

/* ── 404 ─────────────────────────────────────────────────────────────── */
/* 404.html reuses the panel column; it just needs centring in the viewport. */

.error-page {
  position: relative;
  z-index: 20;
  min-height: 100vh;
  display: flex;
  align-items: center;
}

/* ── chrome: nav, dots, progress ─────────────────────────────────────── */

.topnav {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 18px var(--gutter);
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(8px);
}
.topnav-brand {
  font-family: var(--font-heading);
  font-size: 15px;
  letter-spacing: 0.02em;
  color: var(--color-text);
}
.topnav-brand:hover { color: var(--color-accent); }
.topnav-links { display: flex; gap: 26px; font-size: 14px; }
.topnav-links a { color: var(--color-neutral-300); }
.topnav-links a:hover { color: var(--color-accent); }

.dots,
.progress { display: none; }

/* ── shared content pieces ───────────────────────────────────────────── */

/* Anchor targets clear the sticky nav. Deck mode does its own jumping, so
   this only matters in flow mode — where it is exactly what is needed. */
.panel,
.lscreen { scroll-margin-top: 84px; }

.panel { position: relative; z-index: 20; }
.panel-col {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding: 76px var(--gutter);
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: flex-start;
}

.kicker {
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-accent-300);
}

.display {
  margin: 0;
  font-weight: 500;
  font-size: clamp(34px, min(6vw, 8.6vh), 82px);
  line-height: 1.02;
  letter-spacing: -0.032em;
  max-width: 15ch;
  text-wrap: pretty;
}
.display-sm {
  font-size: clamp(30px, min(4.4vw, 7vh), 54px);
  letter-spacing: -0.026em;
  max-width: 24ch;
}
.title {
  margin: 0;
  font-weight: 500;
  font-size: clamp(26px, min(3.9vw, 6.4vh), 52px);
  line-height: 1.04;
  letter-spacing: -0.028em;
  max-width: 22ch;
  text-wrap: pretty;
}

.lede {
  margin: 0;
  font-size: 18px;
  line-height: 1.55;
  color: var(--color-neutral-300);
  max-width: 46ch;
  text-wrap: pretty;
}
.body-lg {
  margin: 0;
  font-size: 17px;
  line-height: 1.62;
  color: var(--color-neutral-300);
  max-width: 56ch;
  text-wrap: pretty;
}

.actions { display: flex; flex-wrap: wrap; gap: 12px; padding-top: 6px; }
.tags { display: flex; flex-wrap: wrap; gap: 7px; }
.links { display: flex; flex-wrap: wrap; gap: 20px; font-size: 14px; padding-top: 4px; }

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: 54px;
  padding-top: 34px;
  margin-top: 8px;
  border-top: 1px solid var(--color-neutral-800);
  font-size: 13px;
  color: var(--color-neutral-400);
}
.stats strong {
  display: block;
  font-family: var(--font-heading);
  font-size: 26px;
  font-weight: 500;
  color: var(--color-text);
}

.meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 40px;
  padding-top: 22px;
  border-top: 1px solid var(--color-neutral-800);
  font-size: 14px;
  color: var(--color-neutral-400);
}
.meta-row-plain {
  gap: 26px;
  padding-top: 48px;
  border-top: 0;
  font-size: 13px;
  color: var(--color-neutral-500);
}

/* image slots — swap the file in assets/img, keep the ratio.
   These are classes rather than inline `style="--shot-ratio: …"` attributes
   so the strict CSP in _headers can keep style-src at 'self': inline style
   ATTRIBUTES fall under style-src-attr, which inherits style-src, so an
   inline ratio would be dropped and every image would collapse to 4/3. */
.shot-4x3 { --shot-ratio: 4 / 3; }
.shot-3x4 { --shot-ratio: 3 / 4; }
.shot-16x10 { --shot-ratio: 16 / 10; }

.shot {
  position: relative;
  width: 100%;
  aspect-ratio: var(--shot-ratio, 4 / 3);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.shot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* two-column panels — single column until there is room for two */
.split { display: grid; gap: 40px; align-items: center; }
.split-body { display: flex; flex-direction: column; gap: 20px; align-items: flex-start; }
.split-image-first .shot { max-width: 340px; }

/* ── the laptop ──────────────────────────────────────────────────────── */
/* In flow mode every layer below is inert: the frame, hinge and browser
   chrome collapse and the two screens read as ordinary sections. */

.laptop-stage { position: relative; z-index: 22; }
.chrome,
.base { display: none; }

.lscreen-inner {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding: 40px var(--gutter);
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.lscreen-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.lscreen-head h2 {
  margin: 0;
  font-weight: 500;
  font-size: 28px;
  letter-spacing: -0.024em;
}
.lscreen-head span { font-size: 13px; }
.lscreen-foot { margin: 0; padding-top: 6px; font-size: 13px; color: var(--color-neutral-600); }

.project {
  display: grid;
  grid-template-columns: 1fr;
  gap: 22px;
  padding: 18px;
  align-items: start;
}
.project .shot { max-width: 420px; }
.project-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.project-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}
.project-meta .card-kicker { font-size: 11px; }
.project-meta .text-muted { font-size: 12px; }
.project .card-title { font-size: 21px; line-height: 1.12; }
.project .card-body { font-size: 14px; line-height: 1.55; opacity: 1; color: var(--color-neutral-300); }
.project .tag { font-size: 11px; }
.project .links { gap: 18px; font-size: 13px; }

.lscreen-inner-split { gap: 40px; }
.stack-intro { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; }
.stack-intro .kicker { font-size: 12px; }
.stack-intro p { margin: 0; font-size: 15px; line-height: 1.6; }
.stack-intro .tag { font-size: 11px; }
.stack-title {
  margin: 0;
  font-weight: 500;
  font-size: clamp(28px, 3.4vw, 38px);
  line-height: 1.03;
  letter-spacing: -0.028em;
}

.stack-rows { display: flex; flex-direction: column; gap: 16px; }
.stack-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--color-neutral-800);
}
.stack-row-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--font-heading);
  font-size: 18px;
}
.stack-row-head .text-muted { font-family: var(--font-body); font-size: 13px; }
.stack-row-items { font-size: 14px; line-height: 1.5; color: var(--color-neutral-300); text-wrap: pretty; }

/* ── flow-mode responsive steps ──────────────────────────────────────── */

@media (max-width: 720px) {
  :root { --gutter: 24px; }
  .panel-col { padding-block: 56px; gap: 20px; }
  .stats { gap: 28px; }
  .meta-row { gap: 20px; }
  /* The ch-based measures are a typographic limit for wide viewports. On a
     phone the container is already narrower than 15ch, so keeping them just
     forces ragged wrapping. */
  .display,
  .display-sm,
  .title { max-width: none; }
}

@media (min-width: 760px) {
  .project { grid-template-columns: 232px 1fr; }
  .project .shot { max-width: none; }
}

@media (min-width: 940px) {
  .split { grid-template-columns: 1fr 1fr; gap: 56px; }
  .split-image-first .shot { max-width: none; }
  .lscreen-inner-split { display: grid; grid-template-columns: 0.85fr 1.15fr; }
}

/* ══════════════════════════════════════════════════════════════════════
   DECK MODE — the scroll-jacked original
   ══════════════════════════════════════════════════════════════════ */

.mode-deck .track {
  position: relative;
  height: 600vh;
}
.mode-deck .stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background: var(--ground);
}
.mode-deck .bg { position: absolute; }
/* Which layer leads is driven per panel by app.js. */
.mode-deck .bg-layer[data-bg="a"] { opacity: 1; }

.mode-deck .topnav {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  max-width: 1400px;
  margin: 0 auto;
  padding: 26px var(--gutter);
  flex-wrap: nowrap;
  background: transparent;
  backdrop-filter: none;
}

.mode-deck .dots {
  position: absolute;
  right: 34px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.mode-deck .dots button {
  width: 9px;
  height: 9px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--color-neutral-600);
  background: transparent;
  cursor: pointer;
  transition: background 200ms, border-color 200ms;
}
.mode-deck .dots button:hover { border-color: var(--color-accent); }

.mode-deck .progress {
  position: absolute;
  left: var(--gutter);
  bottom: 42px;
  z-index: 30;
  display: flex;
  align-items: baseline;
  gap: 14px;
  pointer-events: none;
}
.mode-deck .progress [data-progress-step] {
  font-family: var(--font-heading);
  font-size: 13px;
  letter-spacing: 0.18em;
  color: var(--color-neutral-500);
}
.mode-deck .progress [data-progress-label] {
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-neutral-600);
}

/* Panels stack on top of each other and cross-fade. */
.mode-deck .panel {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  padding: 100px 0 86px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translate3d(0, 26px, 0);
  transition: opacity 320ms ease, transform 320ms ease;
}
.mode-deck .panel[data-panel="5"] { z-index: 25; padding: 126px 0 60px; }
.mode-deck .panel-col { padding-block: 0; gap: 26px; }
.mode-deck .panel[data-panel="1"] .panel-col,
.mode-deck .panel[data-panel="4"] .panel-col { display: grid; }
.mode-deck .panel[data-panel="1"] .panel-col { grid-template-columns: 1.05fr 0.95fr; }
.mode-deck .panel[data-panel="4"] .panel-col { grid-template-columns: 0.8fr 1.2fr; }
.mode-deck .panel[data-panel="4"] .shot { max-height: 62vh; }
.mode-deck .panel[data-panel="5"] .panel-col { max-width: 1180px; }
.mode-deck .panel[data-panel="5"] .lede { max-width: 480px; }
.mode-deck .display { max-width: 15ch; }
.mode-deck .panel[data-panel="5"] .display { max-width: 700px; }

/* ── the laptop, assembled ───────────────────────────────────────────── */

.mode-deck .laptop-stage {
  position: absolute;
  inset: 0;
  z-index: 22;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 2400px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 300ms ease;
}
.mode-deck .laptop {
  position: relative;
  width: 1080px;
  transform-style: preserve-3d;
  transform: rotateX(10deg);
}
.mode-deck .lid {
  position: relative;
  width: 100%;
  height: 660px;
  transform-origin: 50% 100%;
  transform: rotateX(-90deg);
  transform-style: preserve-3d;
  transition: transform 620ms cubic-bezier(0.33, 0.9, 0.2, 1);
}
.mode-deck .bezel {
  position: absolute;
  inset: 0;
  border-radius: 16px 16px 5px 5px;
  padding: 14px 14px 28px;
  background: linear-gradient(165deg, #4549644d 0%, #1a1d2e 45%, #3a3e57 100%);
  box-shadow: 0 46px 92px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(255, 255, 255, 0.05);
}
.mode-deck .screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 7px;
  background: #0c0e18;
  overflow: hidden;
  box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.8);
}
.mode-deck .base {
  display: block;
  position: relative;
  width: 106%;
  left: -3%;
  height: 22px;
  border-radius: 4px 4px 15px 15px;
  background: linear-gradient(180deg, #3a3e57 0%, #23263a 55%, #14172a 100%);
  box-shadow: 0 44px 74px rgba(0, 0, 0, 0.5);
}
.mode-deck .base-notch {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  width: 120px;
  height: 5px;
  border-radius: 0 0 6px 6px;
  background: #10121e;
}

.mode-deck .chrome {
  display: flex;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 36px;
  align-items: center;
  gap: 11px;
  padding: 0 16px;
  border-bottom: 1px solid var(--color-neutral-800);
  background: #0e1120;
  font-size: 12px;
  color: var(--color-neutral-500);
  z-index: 4;
}
.mode-deck .chrome-lights { display: flex; gap: 6px; }
.mode-deck .chrome-lights span {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #47506b;
}
.mode-deck .chrome-tab {
  padding: 4px 12px;
  border-radius: 5px;
  background: #171a29;
  color: var(--color-neutral-300);
}
.mode-deck .chrome-hint {
  margin-left: auto;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 10.5px;
  color: var(--color-neutral-600);
}

/* Screen contents scroll inside the lid. */
.mode-deck .lscreen {
  position: absolute;
  inset: 36px 0 0;
  overflow-y: auto;
  overflow-x: hidden;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  scrollbar-width: thin;
  scrollbar-color: #2b2f47 transparent;
  transition: opacity 200ms linear;
}
.mode-deck .lscreen-inner { max-width: none; padding: 26px 34px 34px; }
.mode-deck .lscreen-inner-split { display: grid; grid-template-columns: 0.85fr 1.15fr; padding: 30px 36px 36px; }
.mode-deck .lscreen-head {
  position: sticky;
  top: -26px;
  padding: 26px 0 10px;
  margin: -26px 0 0;
  background: linear-gradient(#0c0e18 72%, rgba(12, 14, 24, 0));
  z-index: 2;
}
.mode-deck .stack-intro { position: sticky; top: 0; align-self: start; }
.mode-deck .project { grid-template-columns: 232px 1fr; }
.mode-deck .project .shot { max-width: none; }
.mode-deck .split-image-first .shot { max-width: none; }

[data-scroller]::-webkit-scrollbar { width: 8px; }
[data-scroller]::-webkit-scrollbar-track { background: transparent; }
[data-scroller]::-webkit-scrollbar-thumb { background: #2b2f47; border-radius: 4px; }
[data-scroller]::-webkit-scrollbar-thumb:hover { background: #3b4060; }

/* ── motion preferences ──────────────────────────────────────────────── */
/* Deck mode is already off under reduced-motion (see the head script), so
   this only has to quiet the transitions that remain in flow mode. */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
