/* ============================================================
   Alex Stark — Director Portfolio
   styles.css
   ----
   Sections:
     1. Design tokens
     2. Reset + base
     3. Loader
     4. Stage & video
     5. Topbar (logo + desktop nav + hamburger)
     6. Menu overlay
     7. Footer
     8. Slot machine
   ============================================================ */


/* -------------------------------------------------------
   1. Design tokens
   ------------------------------------------------------- */
:root {
  --bg:        #0a0908;
  --fg:        #ece6d9;
  --fg-2:      #cfc6b4;
  --muted:     rgba(236, 230, 217, 0.42);
  --line:      rgba(236, 230, 217, 0.10);
  --hair:      rgba(236, 230, 217, 0.22);
  --accent:    #ff5924;

  --serif:     "Cinzel", "Times New Roman", serif;
  --mono:      "Montserrat", ui-sans-serif, sans-serif;
}


/* -------------------------------------------------------
   2. Reset + base
   ------------------------------------------------------- */
* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  background: var(--bg);
  color: var(--fg);
  height: 100vh;
  width: 100vw;
  overflow: hidden;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
}


/* -------------------------------------------------------
   3. Loader
   Default: invisible. Only shown if the first video takes
   longer than ~280ms to load — keeps fast loads from
   flashing the loader.
   ------------------------------------------------------- */
.loader {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.45s ease;
  pointer-events: none;
}
.loader img {
  height: 56px;
  width: auto;
  animation: loader-pulse 1.8s ease-in-out infinite;
}
.loader.show { opacity: 1; pointer-events: auto; }
.loader.hide { opacity: 0; pointer-events: none; }

@keyframes loader-pulse {
  0%, 100% { opacity: 0.45; transform: scale(1); }
  50%      { opacity: 1;    transform: scale(1.04); }
}


/* -------------------------------------------------------
   4. Stage & video
   Two stacked <video> elements act as A/B buffers — z-index
   is controlled in JS so the incoming clip sits on top of
   the outgoing one during the cross-fade (no black gap).
   ------------------------------------------------------- */
.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: #000;
}

.bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: 0;
  /* 0.45s matches FADE_MS in js/video.js — change in lockstep */
  transition: opacity 0.45s ease;
}
.bg-video.active { opacity: 1; }


/* -------------------------------------------------------
   5. Topbar
   Logo + desktop nav + hamburger toggle. The hamburger is
   shown only on mobile (≤ 820px), where it replaces the
   inline desktop nav.
   ------------------------------------------------------- */
.topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 32px;
  pointer-events: none;
}
.topbar > * { pointer-events: auto; }

/* Logo */
.logo {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.logo img {
  display: block;
  height: 36px;
  width: auto;
}

/* Desktop nav */
.topnav {
  display: flex;
  align-items: center;
  gap: 28px;
}
.topnav a {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
  text-decoration: none;
  padding: 4px 2px;
  position: relative;
  transition: color 0.3s ease;
}
/* Underline grows from the left on hover, retracts to the
   right on un-hover. transform-origin flips between right
   (default) and left (hover) so only scaleX animates but
   the visual direction reverses naturally. */
.topnav a::after {
  content: '';
  position: absolute;
  left: 2px;
  right: 2px;
  bottom: 0;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
.topnav a:hover { color: #fff; }
.topnav a:hover::after {
  transform: scaleX(1);
  transform-origin: left center;
}

/* Hamburger toggle — three lines that fold around each other
   into an X on hover. Hidden on desktop, shown on mobile. */
.menu-toggle {
  display: none;
  width: 32px;
  height: 22px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  flex-direction: column;
  justify-content: space-between;
}
.menu-toggle span {
  display: block;
  width: 100%;
  height: 1px;
  background: var(--fg);
  transform-origin: center center;
  transition:
    transform 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.3s ease,
    background 0.3s ease;
}
.menu-toggle:hover span:nth-child(1) { transform: translateY(10.5px) rotate(45deg); }
.menu-toggle:hover span:nth-child(2) { transform: scaleX(0); opacity: 0; }
.menu-toggle:hover span:nth-child(3) { transform: translateY(-10.5px) rotate(-45deg); }
.menu-toggle:hover span               { background: var(--accent); }

/* Mobile breakpoint: swap inline nav for hamburger */
@media (max-width: 820px) {
  .topnav      { display: none; }
  .menu-toggle { display: flex; }
}
@media (max-width: 760px) {
  .topbar      { padding: 24px 24px; }
  /* Close X must match the hamburger's horizontal position when
     the topbar padding shrinks, otherwise the X looks like it
     jumps to the left when the menu opens. */
  .menu-close  { right: 24px; }
}


/* -------------------------------------------------------
   6. Menu overlay
   Solid accent panel that slides down on open and back up
   on close. Sequenced so items can fade in *after* the
   panel arrives on open, and the panel waits for items to
   fade out before sliding away on close.
   ------------------------------------------------------- */
/* Native <dialog> element. Browsers render dialogs in the top layer,
   above the page's stacking context — which means iOS Safari sizes them
   against the *visual* viewport (above the URL bar) rather than the
   *layout* viewport (which extends behind it). showModal() also pauses
   background scroll natively, so we don't need any position-fixed body
   hacks. */
.menu-overlay {
  /* Reset default <dialog> styling */
  border: 0;
  margin: 0;
  padding: 0 8vw;
  /* Fill the entire visible viewport */
  width: 100vw;
  height: 100vh;
  max-width: 100vw;
  max-height: 100vh;
  inset: 0;
  z-index: 100;
  background: var(--accent);
  color: inherit;
  /* Hidden by default — dialog has no [open] attribute initially */
  display: none;
  align-items: center;
  justify-content: center;
  /* Slide-up state for the close transition.
     Panel starts at 0.22s — when item 4 has just faded out and items
     3, 2, 1 are still mid-fade. They finish fading WHILE riding up
     off-screen with the panel (since they're flex children of it),
     so the close reads as one integrated motion rather than
     "items fade, then panel slides." Total close: 0.22 + 0.20 = 0.42s. */
  transform: translateY(-100%);
  transition: transform 0.20s cubic-bezier(0.65, 0, 0.35, 1) 0.22s;
}
/* When showModal() runs the browser sets [open]; we render then. */
.menu-overlay[open] {
  display: flex;
}
/* JS adds .open one frame after showModal() so the slide animates in. */
.menu-overlay[open].open {
  transform: translateY(0);
  transition: transform 0.42s cubic-bezier(0.65, 0, 0.35, 1);
}
/* Hide the default modal backdrop — the menu fills the viewport itself. */
.menu-overlay::backdrop {
  background: transparent;
}

/* Close X — sits in the same spot as the hamburger.
   The topbar centers items vertically against the tallest
   child (logo at 36px), so the hamburger's top edge lands
   at 24px + (36-22)/2 = 31px. */
.menu-close {
  position: absolute;
  top: 31px;
  right: 32px;
  width: 32px;
  height: 22px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  /* Suppress the default focus ring — showModal() auto-focuses this
     button as the dialog's first focusable element, which would otherwise
     render a blue/gray outline around the X. */
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.menu-close::before,
.menu-close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 32px;
  height: 1px;
  background: var(--fg);
  transition:
    transform 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    background 0.3s ease;
}
.menu-close::before              { transform: translate(-50%, -50%) rotate(45deg); }
.menu-close::after               { transform: translate(-50%, -50%) rotate(-45deg); }
.menu-close:hover::before        { transform: translate(-50%, -50%) rotate(135deg); }
.menu-close:hover::after         { transform: translate(-50%, -50%) rotate(45deg); }
.menu-close:hover::before,
.menu-close:hover::after         { background: var(--fg); }

/* Menu items: large centered serif labels.
   Items fade DOWN from above on open and back UP on close.
   CSS transitions (not @keyframes) so the animation plays
   naturally in reverse on close. Mirrored stagger gives
   both directions a sequential feel. */
.menu-nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 4px;
}
.menu-nav a {
  display: block;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(48px, 9vw, 112px);
  line-height: 1.05;
  letter-spacing: -0.005em;
  color: #fff;
  text-decoration: none;
  padding: 4px 0;
  position: relative;
  /* Initial offstage state */
  opacity: 0;
  transform: translateY(-30px);
  /* Default = close transition. Uses ease-IN (the time-reverse of the
     ease-OUT curve used on open), so items accelerate as they leave —
     the natural way for an exit. Reverse cubic-bezier formula:
     reverse of (x1, y1, x2, y2) is (1-x2, 1-y2, 1-x1, 1-y1).
     Durations are ~70% of the open durations: same animation shape,
     just compressed so the close feels snappier than the entrance. */
  transition:
    opacity 0.22s ease,
    transform 0.33s cubic-bezier(0.64, 0, 0.78, 0),
    color 0.27s ease;
}
.menu-nav a .label {
  display: inline-block;
  transition: color 0.4s ease;
}

/* Hover — label flips to black against the orange */
.menu-nav a:hover                { color: #000; }

/* Close stagger — same shape as open's stagger, compressed.
   Open uses an 0.08s gap (item 1 through item 4), so close uses
   ~0.06s (~75%) and reverses the ORDER: item 4 exits first, item 1 last. */
.menu-nav a:nth-child(1) { transition-delay: 0.18s; }
.menu-nav a:nth-child(2) { transition-delay: 0.12s; }
.menu-nav a:nth-child(3) { transition-delay: 0.06s; }
.menu-nav a:nth-child(4) { transition-delay: 0s; }

/* Open state — items drop into place from above, after the panel has
   begun sliding down. Uses an ease-OUT curve so items decelerate as
   they arrive — the natural way for an entrance. */
.menu-overlay.open .menu-nav a {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 0.32s ease,
    transform 0.45s cubic-bezier(0.22, 1, 0.36, 1),
    color 0.4s ease;
}
.menu-overlay.open .menu-nav a:nth-child(1) { transition-delay: 0.22s; }
.menu-overlay.open .menu-nav a:nth-child(2) { transition-delay: 0.30s; }
.menu-overlay.open .menu-nav a:nth-child(3) { transition-delay: 0.38s; }
.menu-overlay.open .menu-nav a:nth-child(4) { transition-delay: 0.46s; }




/* -------------------------------------------------------
   8. Slot machine
   Continuous-position selector. Each row is absolutely
   centered in its column; JS positions each row every
   frame by its shortest signed distance to the current
   scroll position, wrapping the list seamlessly.
   ------------------------------------------------------- */
.slot-wrap {
  position: fixed;
  inset: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.slot {
  position: relative;
  width: min(1320px, 96vw);
  height: 360px;
  pointer-events: auto;
}
/* Soft radial darken behind the slot — no hard edges, just
   an elliptical shadow pool for text legibility. */
.slot::before {
  content: '';
  position: absolute;
  inset: -200px;
  background: radial-gradient(
    ellipse 50% 45% at center,
    rgba(0, 0, 0, 0.34) 0%,
    rgba(0, 0, 0, 0.22) 30%,
    rgba(0, 0, 0, 0.10) 60%,
    rgba(0, 0, 0, 0.03) 80%,
    transparent         100%
  );
  pointer-events: none;
  z-index: 0;
}

.slot-cols {
  position: relative;
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-columns: 1.7fr 1fr;
  gap: 36px;
}

.slot-col {
  position: relative;
  height: 100%;
  overflow: hidden;
  /* Smoothly fade rows in/out at the top and bottom edges */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 24%, #000 76%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 24%, #000 76%, transparent 100%);
}

.slot-rail {
  position: absolute;
  inset: 0;
}

.slot-row {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 60px;
  margin-top: -30px;
  display: flex;
  align-items: center;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.4s ease;
  will-change: transform, opacity;
}

.col-name .slot-row {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 38px;
  letter-spacing: 0;
  text-transform: uppercase;
  justify-content: flex-end;
  color: var(--fg);
}
.col-type .slot-row {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-2);
  justify-content: flex-start;
}

/* Active row scales up. Transform is on the inner span so
   it doesn't conflict with the JS-driven translateY on the
   row itself. Origin is anchored to the column edge so the
   active text grows inward. */
.slot-row .txt {
  display: inline-block;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.col-name .slot-row .txt { transform-origin: right center; }
.col-type .slot-row .txt { transform-origin: left center; }
.slot-row.active .txt    { transform: scale(1.1); }

/* The inline type element only renders on mobile (see media queries
   below). Desktop hides it because the type-rail column shows the type
   in its own second column. */
.row-type { display: none; }

/* Slot machine — responsive */
@media (max-width: 1100px) {
  .col-name .slot-row { font-size: 28px; }
  .slot-cols          { gap: 32px; }
}
@media (max-width: 760px) {
  /* Use only the title rail. Each row shows its title with the type
     stacked underneath inline (.row-type), so every visible title
     carries its type — not just the active one. The separate type rail
     is hidden. Both rows are right-aligned and share the serif. */
  .slot-wrap { justify-content: flex-end; padding-right: 6vw; }
  .slot      { width: 88vw; height: 320px; }
  .slot-cols {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
  }
  .col-type { display: none; }

  .col-name .slot-row { justify-content: flex-end; }
  /* Stack title + type vertically inside the inline-block .txt span.
     Right-aligned so the trailing type pin to the same edge as the title. */
  .col-name .slot-row .txt {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-end;
    line-height: 1;
  }
  .col-name .row-title {
    display: block;
    font-family: var(--serif);
    font-size: 28px;
    line-height: 1.05;
    text-transform: uppercase;
    letter-spacing: 0;
  }
  .col-name .row-type {
    display: block;       /* re-show on mobile */
    margin-top: 4px;
    font-family: var(--serif);
    font-size: 11px;
    line-height: 1.2;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--fg-2);
  }
  /* Active scale grows from the right edge so titles can't overflow */
  .col-name .slot-row .txt { transform-origin: right center; }
  .slot-row.active .txt    { transform: scale(1.04); }
}
@media (max-width: 460px) {
  .col-name .row-title   { font-size: 22px; }
  .col-name .row-type    { font-size: 10px; }
  .slot-row.active .txt  { transform: scale(1.02); }
}


/* -------------------------------------------------------
   Fade-up on scroll — opt-in via class="fade-up". js/reveal.js
   adds .visible when the element crosses ~12% into the viewport.
   Borrowed verbatim from the reference site, just slightly softer
   travel (64px vs 96px) since our cells are denser than its hero.
   ------------------------------------------------------- */
.fade-up {
  opacity: 0;
  transform: translateY(64px);
  transition:
    opacity   1.3s cubic-bezier(0.33, 0,    0.2, 1),
    transform 2.1s cubic-bezier(0.2,  0.65, 0.2, 1);
}
.fade-up.visible {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .fade-up { opacity: 1; transform: none; transition: none; }
}
