/* =============================================================
   Password gate — overlay
   Reference: Figma node 125:1907 (Pfolio)
   -------------------------------------------------------------
   Full-screen lock that appears when the user scrolls past the
   first content section of a gated project. Owns its own z-index
   stack above the page-wipe and cursor so nothing on the page
   ever pokes through.

   Lives in its own stylesheet (rather than appended to main.css)
   so the gate can ship independently without touching shared
   tokens. All color/font references use the same CSS variables
   defined in :root in main.css (--color-black-dark, --color-cream,
   --color-black-light, --font-display, --font-body).
   ============================================================= */

/* Body lock — paired with Lenis.stop() in JS. Belt and suspenders:
   even if Lenis is unavailable we kill native scroll too. */
body.password-gate-locked {
  overflow: hidden;
}

/* While the protected container is still encrypted (empty), give it
   one full viewport of breathing room so the user can scroll past
   the first content section. That scroll is exactly what trips the
   IntersectionObserver and shows the gate. Once decrypted content
   is injected the container has real height and this rule no-ops. */
.project-view[data-gated="true"] [data-protected-container]:empty {
  display: block;
  min-height: 100vh;
}

/* The sentinel itself is 1px tall — just enough for the
   IntersectionObserver to track without affecting layout. */
[data-gate-sentinel] {
  display: block;
  height: 1px;
  width: 100%;
  pointer-events: none;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.password-gate {
  position: fixed;
  inset: 0;
  /* Above the page-wipe (99999) and the custom cursor (99998) so
     nothing ever sneaks past while the gate is up. The cursor is
     selectively bumped above this z-index when the gate is locked
     (see body.password-gate-locked .custom-cursor below) so the
     user can see where they're pointing while interacting. */
  z-index: 100000;
  pointer-events: none;
  /* Resting / hidden state — parked one full viewport BELOW the
     screen with opacity 0, so the entry animation reads as a slide
     UP from the bottom. The exit (.is-exiting below) slides off to
     the LEFT instead, so this rest state is only used for the
     INITIAL show and after a full enter→exit→reset cycle. JS snaps
     the panel back here without a transition (via .is-resetting)
     once the exit completes, so the next show always starts from
     below.

     ENTRY motion uses a long, smooth ease-in-out curve so the
     panel feels like it's RISING from below in response to the
     user's scroll input rather than popping in.
       • transform: 1100ms cubic-bezier(0.45, 0, 0.55, 1) — slow
         acceleration off rest, slow deceleration into place. The
         in-out curve (vs. easeOut alone) keeps the early frames
         visibly moving so the user reads the animation as
         continuous with their scroll gesture.
       • opacity: 700ms cubic-bezier(0.4, 0, 0.6, 1) — pace-matched
         to the slide so the panel materializes WHILE rising,
         instead of being fully opaque before it's halfway up.

     EXIT motion is overridden by .is-exiting / .is-slow-exit
     below — those use shorter, easeOutExpo curves for a snappier
     dismissal.

     `will-change` pre-promotes the overlay to its own compositor
     layer so the slide doesn't paint-thrash on first show. */
  opacity: 0;
  transform: translate3d(0, 100%, 0);
  transition:
    opacity 700ms cubic-bezier(0.4, 0, 0.6, 1),
    transform 1100ms cubic-bezier(0.45, 0, 0.55, 1);
  will-change: transform, opacity;
}

.password-gate.is-visible {
  pointer-events: auto;
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Exit state — applied by JS hideOverlay() right after .is-visible
   is removed. The panel slides OFF the left edge (translateX(-100%))
   while fading out. Different direction from the entrance so the
   user gets a clear "this is dismissing" cue rather than seeing the
   same animation play in reverse. After the 600ms exit completes
   (1200ms with .is-slow-exit), JS removes .is-exiting and applies
   .is-resetting for one frame to snap the panel back to its
   below-screen rest position without the user seeing a diagonal
   travel. */
.password-gate.is-exiting {
  pointer-events: none;
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
  /* Override the entry's long ease-in-out with a snappier
     ease-out-expo for the dismissal — the exit doesn't need to
     feel scroll-connected, it just needs to clear cleanly. */
  transition:
    opacity 240ms cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* Slow-exit variant — applied alongside .is-exiting on the
   SUCCESSFUL-unlock dismissal so the panel takes ~2× as long to
   slide off. Reads as a deliberate, victorious exit, distinct
   from the snappier X-button dismiss. The fade gets a longer
   duration too (480ms) so the longer slide doesn't end on a panel
   that's already long-since invisible — they bottom out together. */
.password-gate.is-slow-exit {
  /* Fade tracks the slide: a longer, gentler ease-in-out opacity ramp
     (1100ms) so the panel dissolves gradually across nearly the whole
     1200ms slide instead of vanishing in the first ~480ms. */
  transition:
    opacity 1100ms cubic-bezier(0.4, 0, 0.2, 1),
    transform 1200ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* Reset frame — disables transitions for one paint so the snap
   from "off the left edge" back to "below the screen" is invisible
   to the user. Same pattern the page-wipe (.page-wipe.is-resetting)
   uses in main.css. */
.password-gate.is-resetting {
  transition: none;
}

/* Bring the custom cursor above the gate while it's locked, so the
   user can see where they're pointing while typing / hovering the
   submit button. Default z-index keeps the cursor BELOW the page-
   wipe (99999) so the wipe still cleanly covers it during the
   return-to-home transition; this rule only overrides for the
   gate's specific lifetime. */
body.password-gate-locked .custom-cursor {
  z-index: 100001;
}

/* Panel — the dark inner card. Figma 125:1907: bg #151314, padding
   80px, rounded 20px, drop shadow 8/8/24 rgba(0,0,0,0.2). On smaller
   viewports we shrink the radius + padding so the panel still has
   breathing room.

   Positioned `absolute; inset: 24px;` instead of flex-stretched —
   guarantees the panel fills the viewport minus a 24px margin on
   every side regardless of UA flex quirks. */
.password-gate__panel {
  position: absolute;
  inset: 24px;
  padding: 80px;
  background-color: var(--color-black-dark);
  border-radius: 20px;
  box-shadow: 8px 8px 24px rgba(0, 0, 0, 0.2);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 40px;
  /* Defensive: if something downstream removes pointer-events from
     the parent, the panel still owns the click surface so users
     can't click through to the encrypted (hidden) content. */
  pointer-events: auto;
}

@media (max-width: 767px) {
  .password-gate__panel {
    inset: 12px;
    padding: 32px 24px;
    border-radius: 12px;
    gap: 24px;
  }
}

/* Close X — top-right, cream by default. On hover/focus: spins a
   full 360° and turns gold (Figma node 132:118). Reduced-motion
   users still get the color shift but no rotation. */
.password-gate__close {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--color-cream-dark);
  cursor: inherit;
  transform: rotate(0deg);
  transition: transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1),
    color 200ms cubic-bezier(0.2, 0.8, 0.2, 1),
    opacity 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
  opacity: 0.85;
}
.password-gate__close:hover,
.password-gate__close:focus-visible {
  opacity: 1;
  color: var(--color-gold);
  transform: rotate(360deg);
  outline: none;
}
.password-gate__close svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Form area — flex column, centered both axes, gap 40px (Figma). */
.password-gate__form {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 40px;
  margin: 0;
  width: 100%;
}

/* Small "Password required." eyebrow (or the wrong-password
   message on error). Figma: Gibson 16/24, color #F1E3C8. */
.password-gate__label {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.5;
  text-align: center;
  color: var(--color-cream);
  transition: color 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.password-gate__input-wrap.has-error ~ .password-gate__label,
.password-gate__form:has(.password-gate__input-wrap.has-error)
  .password-gate__label {
  color: var(--color-red-light);
}

/* Input wrapper — provides the underline (752px cream line in
   Figma), capped to the available width. Centers the input.
   We treat the input visually as the headline: huge Obviously
   Demo Narrow Black caps, dim-by-default, brighter when typed. */
.password-gate__input-wrap {
  position: relative;
  width: 100%;
  max-width: 752px;
  padding-bottom: 16px;
  border-bottom: 2px solid var(--color-cream);
  transition: border-color 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.password-gate__input-wrap.has-error {
  border-bottom-color: var(--color-red-light);
}

.password-gate__input {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  outline: none;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 900;
  /* Matches the project-view headline scaling so the gate's typed
     text feels native to the page (Headline 1 from main.css's type
     scale). */
  font-size: clamp(36px, 5.625vw, 72px);
  line-height: 1.0278;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-cream);
  /* Smooth color reset back to cream after the shake animation
     clears the .is-shaking class — keeps the recovery from feeling
     hard-cut. */
  transition: color 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
  /* iOS Safari adds inner shadows on inputs — kill it. */
  -webkit-appearance: none;
  appearance: none;
  /* Hide the native caret; the dim placeholder + huge letterforms
     are the visual focus state. */
  caret-color: var(--color-cream);
}

/* Wrong-password feedback — applied via the JS form.onsubmit catch:
   the typed (incorrect) text turns light red and shakes for ~520ms,
   then JS clears the field + removes the class and the color
   transition above carries it smoothly back to cream. The shake
   keyframes use a damped horizontal oscillation that's clearly
   "wrong" without being seizure-inducing. `transform-origin: 50% 50%`
   keeps the centered text pivoting around its own midline.

   The animation's `forwards` fill is intentional — we want the input
   to hold the final keyframe (translateX(0)) until JS removes the
   class on animationend, so there's no late-frame jump back to
   center. */
@keyframes password-gate__shake {
  0% {
    transform: translateX(0);
  }
  10% {
    transform: translateX(-10px);
  }
  20% {
    transform: translateX(10px);
  }
  30% {
    transform: translateX(-8px);
  }
  40% {
    transform: translateX(8px);
  }
  55% {
    transform: translateX(-5px);
  }
  70% {
    transform: translateX(5px);
  }
  85% {
    transform: translateX(-2px);
  }
  100% {
    transform: translateX(0);
  }
}

.password-gate__input.is-shaking {
  color: var(--color-red-light);
  /* Color flip is INSTANT (override the .input transition above) so
     the red lands on the same frame as the shake starts. */
  transition: none;
  animation: password-gate__shake 520ms
    cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
  transform-origin: 50% 50%;
}

@media (prefers-reduced-motion: reduce) {
  .password-gate__input.is-shaking {
    animation: none;
    /* Still flash red briefly so reduced-motion users get a visible
       error state — JS removes the class on the same timeout, so the
       red flashes for ~520ms then fades back via the input's color
       transition. */
  }
}

/* Dim placeholder ("Awwww shucks...") — Figma 125:1907 uses
   #33322E (--color-black-light) which is intentionally low-contrast
   on the dark panel, like an "in the dark, can't read it" tease.
   The placeholder stays visible while the field is empty (whether
   focused or not), per the default-state mockup — it only
   disappears once the user begins typing, at which point the
   browser's native placeholder-hiding-on-input takes over.

   `font-size` scales by the input's `--placeholder-scale` custom
   property so longer escalation messages (e.g. "You're doing it
   on purpose now…") shrink to fit the 752px wrap width without
   getting clipped. JS sets the variable when assigning the
   placeholder text, defaulting to 1 (= full 72px) for short
   strings. Only the placeholder shrinks — typed text stays at the
   full headline size. */
.password-gate__input::placeholder {
  color: var(--color-black-light);
  opacity: 1;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: calc(
    clamp(36px, 5.625vw, 72px) * var(--placeholder-scale, 1)
  );
  transition: color 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Submit button — pill that swaps between disabled (Figma 136:374)
   and enabled (Figma 136:344) states based on whether the user has
   typed something. The button is ALWAYS visible — only its colors,
   cursor, and interactivity change. The smooth bg/color transitions
   make the disabled→enabled handoff read as a state change rather
   than a sudden appearance.

   Default state — DISABLED (Figma 136:374):
     - bg: --color-black-light  (#33322e)
     - text + icon: --color-black-lighter  (#787770, "Black/Lighter
       Black" per Figma's color set)
     - cursor: not-allowed (form.onsubmit also bails on empty input,
       so the cursor is purely advisory)

   Enabled state — applied via .is-ready when the input has any
   non-empty value (see Figma 136:344):
     - bg: --color-cream
     - text + icon: --color-ink
     - cursor: inherit (the page's custom finger cursor)            */
.password-gate__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 8px 16px;
  background-color: var(--color-black-light);
  color: var(--color-black-lighter);
  border: 0;
  border-radius: 1000px;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  letter-spacing: 0;
  text-transform: none;
  cursor: not-allowed;
  transition:
    background-color 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
    color 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.password-gate__submit.is-ready {
  background-color: var(--color-cream);
  color: var(--color-ink);
  cursor: inherit;
}

/* Hover / focus / active state (only when enabled) — Figma 136:370.
   Bg shifts to Dark Gold (--color-gold-dark = #c98317) while text
   + icon stay --color-ink for contrast. Small upward lift adds a
   tactile bounce on top of the color shift; both transition over
   the 180ms cubic curve declared on the base rule.

   `:active` is included alongside `:hover` and `:focus-visible` so
   the same dark-gold state shows while the user is mid-press —
   either holding the mouse button down OR holding Enter / Space
   while keyboard-focused (`:active` matches both). This makes the
   tap state and the hover state visually identical, per the
   Figma spec. */
.password-gate__submit.is-ready:hover,
.password-gate__submit.is-ready:focus-visible,
.password-gate__submit.is-ready:active {
  background-color: var(--color-gold-dark);
  transform: translateY(-2px);
  outline: none;
}

.password-gate__submit.is-checking {
  opacity: 0.6;
  pointer-events: none;
}

.password-gate__submit-icon {
  display: inline-flex;
  width: 12px;
  height: 12px;
  color: currentColor;
}
.password-gate__submit-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

@media (max-width: 767px) {
  .password-gate__submit {
    padding: 8px 16px;
    font-size: 16px;
  }
  .password-gate__submit-icon {
    width: 12px;
    height: 12px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .password-gate,
  .password-gate__input-wrap,
  .password-gate__submit {
    transition: none;
  }
  /* Reduced-motion: keep the color swap on hover but kill the spin. */
  .password-gate__close {
    transition: color 200ms linear, opacity 200ms linear;
  }
  .password-gate__close:hover,
  .password-gate__close:focus-visible {
    transform: none;
  }
}
