/* ==========================================================================
   John Luna — Personal
   Palette: Ink #0A0F1A · Bone #EDE7DB · Orange #F26B1D · Peach #FFB36B · Rust #B5501A
   Type:    Syne (display) · Instrument Sans (body) · JetBrains Mono (data)

   Built from the Claude Design file "Personal Launch". The professional site
   next door is Forest and Linen and set in Petrona; this one is deliberately
   not. The two share a folder, a favicon and the switch in the top right.

   Orange appears once per surface: the dot on the button that opens the deck,
   and the ring on the one slot that has a page. Peach is for the number that
   is live and the metadata cell that says what happens next.
   ========================================================================== */

:root {
  --ink:    #0A0F1A;
  --bone:   #EDE7DB;
  --orange: #F26B1D;
  --peach:  #FFB36B;
  --rust:   #B5501A;

  --bone-80: rgba(237, 231, 219, 0.8);
  --bone-60: rgba(237, 231, 219, 0.6);
  --bone-55: rgba(237, 231, 219, 0.55);
  --bone-45: rgba(237, 231, 219, 0.45);
  --bone-30: rgba(237, 231, 219, 0.3);
  --bone-18: rgba(237, 231, 219, 0.18);
  --bone-15: rgba(237, 231, 219, 0.15);
  --bone-14: rgba(237, 231, 219, 0.14);
  --bone-12: rgba(237, 231, 219, 0.12);
  --ink-78:  rgba(10, 15, 26, 0.78);
  --ink-72:  rgba(10, 15, 26, 0.72);
  --ink-70:  rgba(10, 15, 26, 0.7);
  --ink-45:  rgba(10, 15, 26, 0.45);
  --ink-35:  rgba(10, 15, 26, 0.35);

  --f-display: Syne, "Arial Black", Impact, sans-serif;
  --f-body:    "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --f-mono:    "JetBrains Mono", Consolas, ui-monospace, monospace;

  /* The countdown face, on the launch's digit and nowhere else. Still being
     chosen between Orbitron, Share Tech Mono, Major Mono Display and Doto,
     all of which are loaded; swap it here and nowhere else. */
  --count-font: 'Orbitron', sans-serif;

  --side: 6vw;                 /* the inset every edge-aligned thing shares */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }

html { background: var(--ink); }
body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--f-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--peach); }
:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }
h1, h2, p, dl, dd { margin: 0; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 16px; top: -100px; z-index: 100;
  padding: 10px 14px; background: var(--bone); color: var(--ink);
  font: 500 13px/1 var(--f-body); border-radius: 6px;
}
.skip-link:focus { top: 16px; }

.noscript { padding: 24px var(--side); font-size: 14px; color: var(--bone-60); }
.noscript a { color: var(--bone); text-decoration: underline; }

/* --- the small type --- */

.p-label {
  font: 500 12px/1 var(--f-body);
  letter-spacing: 0.3em;
  text-transform: uppercase;
}
.p-label-sm {
  font: 500 11px/1 var(--f-body);
  letter-spacing: 0.25em;
  text-transform: uppercase;
  opacity: 0.7;
}
.p-mono {
  font: 400 13px/1 var(--f-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
}

/* --- the staggered arrivals ---
   Anything with data-stagger starts a little low and invisible; personal.js
   sets the delay from the attribute and adds is-in when its scene is up. */
[data-stagger] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}
[data-stagger].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  [data-stagger] { transform: none; transition-duration: 0.3s; }
}

/* ==========================================================================
   THE HEADER, on every route
   Brand left, the Professional | Personal switch right. On the landing it is
   ink on the bright frame and flips to bone once the frame goes dark; the
   route sets --nav-fg and --nav-bg and the parts read from those.
   ========================================================================== */

/* One route on the page at a time. The slot page is display: flex, which would
   outrank the browser's own hidden rule, so hidden is restated here. */
.route[hidden] { display: none !important; }

.route {
  --nav-fg:   var(--bone);
  --nav-bg:   var(--ink);
  --nav-line: var(--bone-30);
}
.route-landing:not(.is-light) {
  --nav-fg:   var(--ink);
  --nav-bg:   var(--bone);
  --nav-line: var(--ink-35);
}

.p-nav {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 36px var(--side) 0;
  color: var(--nav-fg);
  transition: color 500ms ease;
}
.p-brand { display: flex; flex-direction: column; gap: 4px; color: inherit; }
.p-brand:hover { color: inherit; }
.p-brand-name { font: 800 18px/1 var(--f-display); letter-spacing: -0.02em; }
.p-brand-sub {
  font: 500 10px/1 var(--f-body);
  letter-spacing: 0.25em;
  text-transform: uppercase;
  opacity: 0.7;
}

.p-switch {
  display: flex;
  gap: 3px;
  padding: 3px;
  border: 1px solid var(--nav-line);
  border-radius: 6px;
  font: 500 11px/1 var(--f-body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  transition: border-color 500ms ease;
}
.p-switch a,
.p-switch span {
  padding: 10px 16px;
  border-radius: 4px;
  white-space: nowrap;
}
.p-switch a { color: inherit; opacity: 0.75; transition: opacity 150ms ease; }
.p-switch a:hover { opacity: 1; color: inherit; }
.p-switch [aria-current] {
  background: var(--nav-fg);
  color: var(--nav-bg);
  transition: background 500ms ease, color 500ms ease;
}

@media (max-width: 479px) {
  .p-switch a, .p-switch span { padding: 8px 10px; letter-spacing: 0.12em; }
}

/* ==========================================================================
   LANDING
   ========================================================================== */

.l-track { position: relative; height: 300vh; height: 300svh; }
.l-stage {
  position: sticky;
  top: 0;
  height: 100vh; height: 100svh;
  overflow: hidden;
  background: var(--ink);
}

/* The clip. On a wide screen it is the whole background and carries the hero
   parallax; on a small one the still holds the hero and the clip fades in as
   the hero hands over. Both layers start invisible and personal.js drives them. */
.l-clip {
  position: absolute;
  inset: -4%;
  transform-origin: 50% 50%;
  will-change: transform;
}
.l-vid, .l-cv {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.l-vid { object-fit: cover; object-position: 50% 28%; }

/* The still, and the same still masked to the bottom of the frame and set in
   front of the copy, so the smoke sits over the words during the hero. */
.l-bg, .l-fg {
  position: absolute;
  inset: -6%;
  background: url("/personal/media/hero.jpg") center 50% / cover no-repeat;
  transform-origin: 50% 50%;
  will-change: transform;
}
.l-bg { transition: opacity 500ms ease; }
.l-fgwrap {
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 14%, transparent 34%);
          mask-image: linear-gradient(to top, #000 0%, #000 14%, transparent 34%);
}
.l-fg { filter: brightness(0.7) saturate(1.1); }

/* Darkens the right of the frame under the last scene's copy. */
.l-scrim3 {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(to left, rgba(10, 15, 26, 0.88) 0%, rgba(10, 15, 26, 0.6) 45%, rgba(10, 15, 26, 0.3) 100%);
}

/* The three scenes share the frame; the scroll decides which is up. */
.l-s {
  position: absolute;
  inset: 0;
  display: flex;
  transition: opacity 0.1s ease-out;
}
.l-s1 {
  flex-direction: column;
  justify-content: flex-end;
  padding: 0 var(--side) 22vh;
}
.l-eyebrow { margin-bottom: 20px; color: var(--ink); }
.l-h1 {
  font: 800 clamp(44px, 7.4vw, 140px)/0.92 var(--f-display);
  letter-spacing: -0.035em;
  color: var(--ink);
  max-width: 14ch;
  text-wrap: balance;
  will-change: transform;
}
.l-lede {
  margin-top: 28px;
  max-width: 38ch;
  font: 500 clamp(16px, 1.3vw, 20px)/1.5 var(--f-body);
  color: var(--ink-78);
}

.l-s2 {
  align-items: center;
  justify-content: center;
  padding: 0 8vw;
  opacity: 0;
}
.l-h2 {
  max-width: 22ch;
  text-align: center;
  font: 700 clamp(28px, 4.2vw, 72px)/1.15 var(--f-display);
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: balance;
}
.l-dim { color: var(--ink-70); }
.l-dimmer { color: var(--ink-45); }

.l-s3 {
  align-items: center;
  justify-content: flex-end;
  padding: 0 var(--side);
  opacity: 0;
}
.l-s3-inner { max-width: 560px; }
.l-kicker { margin-bottom: 16px; font: 400 18px/1.3 var(--f-body); color: var(--bone-60); }
.l-h2-deck {
  margin-bottom: 36px;
  font: 800 clamp(40px, 5.4vw, 92px)/0.95 var(--f-display);
  letter-spacing: -0.03em;
  color: var(--bone);
}
.l-cta {
  display: inline-flex;
  align-items: center;
  gap: 18px;
  font: 500 13px/1 var(--f-body);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--bone);
}
.l-cta:hover { color: var(--bone); }
.l-cta-label { white-space: nowrap; }
.l-cta-dot {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--orange);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.3s var(--ease);
}
.l-cta:hover .l-cta-dot { transform: translateX(4px); }

/* The countdown in the corner. The readout grows steadily from T-20 to T-04,
   where it turns Orange and stops: the last three seconds belong to the
   button. The rule and Scroll beside it go as soon as the scroll starts. */
.l-readout {
  position: absolute;
  left: var(--side);
  bottom: 36px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--nav-fg);
  transition: color 500ms ease;
  pointer-events: none;
}
.l-readout-n {
  white-space: nowrap;
  transition: color 300ms ease;
}
.l-readout-aux {
  display: flex;
  align-items: center;
  gap: 14px;
  transition: opacity 300ms ease;
}
.l-rule { width: 64px; height: 1px; background: currentColor; opacity: 0.5; }


@media (max-width: 639px) {
  .l-s1 { padding-bottom: 18vh; }
  .l-s3 { justify-content: flex-start; }
  .l-kicker { font-size: 16px; }
  .l-readout { bottom: 28px; }
}

/* ==========================================================================
   THE LAUNCH
   Four seconds of ink between the button and the deck: the glow comes up, the
   column lands and counts three, two, one; at zero the digit turns Orange and
   the frame shakes; after a hold the burst swallows the frame and the column
   lifts away; the deck is underneath when the overlay fades. personal.js adds
   is-in, is-lit, is-launch and is-out in that order, and snaps each digit in
   with inline styles.
   ========================================================================== */

.flash {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.flash[hidden] { display: none; }
.flash.is-out { opacity: 0; transition: opacity 700ms ease; }
.flash-glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 60% 50% at 50% 55%, rgba(242, 107, 29, 0.55), rgba(242, 107, 29, 0) 70%);
  opacity: 0;
}
.flash.is-in .flash-glow { opacity: 1; transition: opacity 900ms ease-out; }
.flash-text {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  opacity: 0;
  transform: scale(0.7);
}
.flash.is-in .flash-text {
  opacity: 1;
  transform: scale(1);
  transition: opacity 260ms var(--ease), transform 700ms var(--ease);
}
.flash.is-launch .flash-text {
  opacity: 0;
  transform: translateY(-60vh) scale(0.8);
  transition: opacity 350ms ease, transform 900ms cubic-bezier(0.4, 0, 1, 1);
}

/* The light. A circle that starts as a point and, at launch, grows past the
   edges of the frame. */
.flash-burst {
  position: absolute;
  left: 50%; top: 50%;
  width: 40vmax; height: 40vmax;
  margin: -20vmax 0 0 -20vmax;
  border-radius: 50%;
  background: radial-gradient(circle, #FFF4E0 0%, #FFB36B 30%, rgba(242, 107, 29, 0.8) 55%, rgba(242, 107, 29, 0) 72%);
  opacity: 0;
  transform: scale(0.1);
  pointer-events: none;
}
.flash.is-launch .flash-burst {
  opacity: 1;
  transform: scale(6);
  transition: transform 900ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 300ms ease;
}
.flash-label {
  font: 500 13px/1 var(--f-body);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  white-space: nowrap;
}
.flash-eyebrow { color: var(--peach); }
.flash-foot { color: var(--bone-55); }
.flash-num {
  font-family: var(--count-font);
  font-weight: 700;
  font-size: clamp(160px, 34vw, 560px);
  line-height: 0.85;
  letter-spacing: -0.02em;
  color: var(--bone);
  font-variant-numeric: tabular-nums;
  will-change: transform;
}
.flash.is-lit .flash-num {
  color: var(--orange);
  text-shadow: 0 0 80px rgba(242, 107, 29, 0.6);
}

/* ==========================================================================
   DECK
   ========================================================================== */

.d-track { position: relative; height: 1500vh; height: 1500svh; }
.d-stage {
  position: sticky;
  top: 0;
  height: 100vh; height: 100svh;
  overflow: hidden;
  background: var(--ink);
}
.d-bg {
  position: absolute;
  inset: -4%;
  background: url("/personal/media/hero.jpg") center 50% / cover no-repeat;
  opacity: 0.38;
  filter: saturate(0.85);
  will-change: transform;
}
.d-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 60% at 50% 45%, rgba(10, 15, 26, 0) 0%, rgba(10, 15, 26, 0.7) 60%, rgba(10, 15, 26, 0.95) 100%);
}

.d-persp {
  position: absolute;
  inset: 0;
  perspective: 900px;
  perspective-origin: 50% 50%;
}
.d-cam {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  will-change: transform;
}

/* A slot. 260 by 320, centred on the stage and pushed out along the line by
   personal.js. A slot with a page is an <a>; the rest are inert. */
.d-card {
  --w: 260px; --h: 320px;
  position: absolute;
  left: 50%; top: 50%;
  width: var(--w); height: var(--h);
  margin: calc(var(--h) / -2) 0 0 calc(var(--w) / -2);
  padding: 20px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-radius: 12px;
  border: 1px solid var(--bone-14);
  background: var(--ink-72);
  color: var(--bone);
  backface-visibility: hidden;
  will-change: transform, opacity;
  transition: border-color 200ms ease, background 200ms ease;
}
.d-card.is-open { border-color: rgba(242, 107, 29, 0.7); }
a.d-card:hover { border-color: var(--orange); background: rgba(18, 24, 40, 0.9); color: var(--bone); }
.d-card-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font: 400 11px/1 var(--f-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-55);
}
.d-card-n {
  font: 800 96px/0.9 var(--f-display);
  letter-spacing: -0.05em;
  color: rgba(237, 231, 219, 0.92);
}
.d-card.is-open .d-card-n { color: var(--peach); }
.d-card-hint { color: var(--peach); }

.d-foot-l {
  position: absolute;
  left: var(--side);
  bottom: 36px;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 12px;
  pointer-events: none;
}
.d-kicker { color: var(--peach); }
.d-title {
  font: 800 clamp(28px, 3vw, 44px)/1 var(--f-display);
  letter-spacing: -0.03em;
  white-space: nowrap;
}
.d-readout { margin-top: 8px; color: var(--bone-60); }
.d-foot-r {
  position: absolute;
  right: var(--side);
  bottom: 36px;
  z-index: 5;
  max-width: 30ch;
  text-align: right;
  font: 400 13px/1.5 var(--f-body);
  color: var(--bone-55);
  pointer-events: none;
}

@media (max-width: 639px) {
  .d-card { --w: 200px; --h: 250px; padding: 16px; }
  .d-card-n { font-size: 72px; }
  .d-foot-r { display: none; }
  .d-foot-l { bottom: 28px; }
}

/* ==========================================================================
   SLOT PAGE
   ========================================================================== */

.route-project {
  min-height: 100vh;
  padding: 36px var(--side) 96px;
  display: flex;
  flex-direction: column;
  gap: 80px;
}
.pr-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.pr-back {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font: 500 11px/1 var(--f-body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.8;
}
.pr-back:hover { opacity: 1; color: var(--bone); }

.pr-grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
  gap: 64px;
  align-items: start;
}
.pr-index {
  margin-bottom: 24px;
  display: flex;
  gap: 18px;
  align-items: center;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-55);
}
.pr-n { color: var(--peach); }
.pr-title {
  margin-bottom: 24px;
  font: 800 clamp(48px, 7vw, 120px)/0.92 var(--f-display);
  letter-spacing: -0.035em;
}
.pr-lede {
  margin-bottom: 48px;
  max-width: 44ch;
  font: 400 clamp(18px, 1.5vw, 22px)/1.5 var(--f-body);
  color: var(--bone-80);
}
.pr-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 14px 32px;
  font: 400 15px/1.4 var(--f-body);
  border-top: 1px solid var(--bone-15);
  padding-top: 24px;
}
.pr-meta dt { color: var(--bone-55); }
.pr-next { color: var(--peach); }

.pr-side { display: flex; flex-direction: column; gap: 12px; }
.pr-frame {
  aspect-ratio: 4 / 3;
  border-radius: 10px;
  border: 1px solid var(--bone-12);
  display: flex;
  align-items: center;
  justify-content: center;
  font: 800 clamp(64px, 8vw, 140px)/1 var(--f-display);
  letter-spacing: -0.04em;
  color: var(--bone-18);
}
.pr-note { font-size: 12px; line-height: 1.5; letter-spacing: 0.06em; color: var(--bone-45); }
.pr-foot { font: 400 13px/1.5 var(--f-body); color: var(--bone-45); }

@media (max-width: 899px) {
  .pr-grid { grid-template-columns: 1fr; gap: 40px; }
  .route-project { gap: 56px; padding-bottom: 64px; }
}
