/* flawdagoth — "Blacklight"
   Tokens, the hard-slab box language, the UV pointer wash, and the lander.
   Written mobile-first: 99% of traffic is a phone in an in-app browser. */

/* ------------------------------------------------------------- fonts ---- */

@font-face {
  font-family: 'Syne';
  src: url('/assets/fonts/Syne-var.woff2') format('woff2');
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Space Mono';
  src: url('/assets/fonts/SpaceMono-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Space Mono';
  src: url('/assets/fonts/SpaceMono-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ------------------------------------------------------------ tokens ---- */

:root {
  --ground: #06040d;
  --pitch: #0d0820;
  --uv: #7b3dff;
  --uv-hot: #a855ff;
  --cy: #00e5ff;
  --bone: #ece8f6;
  --muted: rgba(236, 232, 246, 0.6);
  --silver: #8d86a8;
  --hairline: rgba(168, 140, 255, 0.28);

  --f-display: 'Syne', ui-sans-serif, system-ui, sans-serif;
  --f-body: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Slab offset. One number drives the hard drop on every pressable thing. */
  --off: 7px;

  /* UV wash origin; script.js overwrites these on pointermove. */
  --mx: 50%;
  --my: 42%;

  --pad: 22px;
  --measure: 34rem;
  color-scheme: dark;
}

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

html {
  -webkit-text-size-adjust: 100%;
}

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

/* Film grain. Static, generated, and cheap — an animated canvas grain is a
   battery and paint-cost mistake on a phone. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0.055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

::selection {
  background: var(--uv);
  color: #fff;
}

a {
  color: var(--uv-hot);
}

:focus-visible {
  outline: 2px solid var(--uv-hot);
  outline-offset: 3px;
  border-radius: 1px;
}

.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;
}

/* -------------------------------------------------------------- drip ---- */
/* The signature. A hairline with a bead that swells, falls, and starts over.
   It appears under every page title and nowhere else, so it reads as a mark
   rather than as decoration. */

.drip {
  position: relative;
  width: 62px;
  height: 30px;
  margin: 10px auto 0;
  flex: none;
}
.drip::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--uv) 22%, var(--uv) 78%, transparent);
}
.drip i {
  position: absolute;
  top: 1px;
  left: 50%;
  width: 7px;
  height: 7px;
  margin-left: -3.5px;
  border-radius: 50%;
  background: var(--uv);
  transform-origin: 50% 0;
  animation: drip 7.5s cubic-bezier(0.55, 0.06, 0.68, 0.19) infinite;
}
@keyframes drip {
  0%, 48% { transform: translateY(0) scale(0.85, 0.35); opacity: 0; }
  58%     { transform: translateY(0) scale(1, 1);       opacity: 1; }
  66%     { transform: translateY(3px) scale(0.78, 1.55); opacity: 1; }
  100%    { transform: translateY(26px) scale(0.6, 1.15); opacity: 0; }
}

/* ------------------------------------------------------------ buttons ---- */
/* Two layers: the outer element paints the rim, .face paints the fill. Both
   carry the same clip path, and because .face is inset by 1px the gap between
   the two clips becomes a border on every edge — including the diagonals,
   which a plain border cannot follow. */

.btn {
  display: block;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  padding: 0;
  text-decoration: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Hard offset, no blur: a rave-flyer slab that looks printed rather than
     rendered. Hover translates the slab onto its own shadow so the press is
     a physical movement instead of a colour change. */
  box-shadow: var(--off) var(--off) 0 0 var(--cy);
  transition:
    transform 0.13s ease,
    box-shadow 0.13s ease,
    filter 0.13s ease;
}

.btn:hover {
  transform: translate(var(--off), var(--off));
  box-shadow: 0 0 0 0 var(--cy);
}

.btn:active {
  transform: translate(var(--off), var(--off));
  filter: brightness(0.94);
}

.btn .face {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  /* Taller than the sibling sites: this is the whole reason the page exists
     and the tap target should say so. */
  min-height: 86px;
  padding: 0 24px;
  font-family: var(--f-body);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-align: center;
}

/* A single scanline sweep across the fill — blacklight tube, not gloss. */
.btn .face::before {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.055) 0 1px,
    rgba(255, 255, 255, 0) 1px 4px
  );
  pointer-events: none;
}

.btn-vip {
  background: var(--uv-hot);
}

.btn-vip .face {
  background: linear-gradient(168deg, #b06bff 0%, var(--uv) 52%, #4a17c4 100%);
  color: #fff;
  text-shadow: 0 0 18px rgba(0, 229, 255, 0.55);
}

.btn-tiktok {
  background: var(--hairline);
  box-shadow: var(--off) var(--off) 0 0 var(--uv);
}

.btn-tiktok:hover {
  box-shadow: 0 0 0 0 var(--uv);
}

.btn-tiktok .face {
  background: rgba(10, 5, 7, 0.72);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--bone);
}

/* A press state, not a hover state — there is no hover on a phone. */
.btn:active {
  transform: scale(0.985);
}
.btn-vip:active {
  filter: drop-shadow(0 4px 26px rgba(255, 45, 74, 0.7));
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover {
    transform: translateY(-2px);
  }
  .btn-vip:hover {
    filter: drop-shadow(0 12px 30px rgba(255, 45, 74, 0.6));
  }
  .btn-tiktok:hover {
    background: rgba(236, 228, 230, 0.42);
  }
}

/* ------------------------------------------------------------- lander ---- */

.stage {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
  min-height: calc(var(--vh, 1vh) * 100);
}

.video-panel {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--ground);
}
.video-panel video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Darkens the top and the bottom, leaves the middle — where her face is —
   alone. The content sits in the dark bottom band rather than over her. */
.scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(
      180deg,
      rgba(10, 5, 7, 0.86) 0%,
      rgba(10, 5, 7, 0.24) 15%,
      rgba(10, 5, 7, 0.08) 26%,
      /* The name sits around a third of the way down. Without this stop it
         lands on her face at almost no contrast and the type has to be rescued
         by a heavy shadow. Darkening from here keeps her eyes clear above and
         gives the display type a ground to sit on. */
      rgba(10, 5, 7, 0.5) 40%,
      rgba(10, 5, 7, 0.84) 56%,
      rgba(10, 5, 7, 0.96) 70%,
      var(--ground) 100%
    ),
    radial-gradient(120% 60% at 50% 100%, rgba(107, 6, 23, 0.55), transparent 70%);
}

#unmute-btn {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 14px);
  right: 14px;
  z-index: 3;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 50%;
  background: rgba(10, 5, 7, 0.55);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--bone);
  font-family: var(--f-body);
  font-size: 0.62rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#unmute-btn[data-on='true'] {
  border-color: var(--uv);
  color: var(--uv-hot);
}

.link-panel {
  position: relative;
  z-index: 2;
  min-height: 100vh;
  min-height: 100dvh;
  min-height: calc(var(--vh, 1vh) * 100);
  display: flex;
  flex-direction: column;
  /* Bottom-weighted on mobile: keeps her face visible, puts the CTAs where a
     thumb already is, and lets the scrim do the contrast work. */
  justify-content: flex-end;
  align-items: center;
  gap: 0;
  padding: 0 var(--pad) calc(env(safe-area-inset-bottom, 0px) + 30px);
  text-align: center;
}

/* `display: flex` on a class beats the UA's `[hidden] { display: none }`, so
   without this the element stays laid out when the counter suppresses itself
   and a lone pulsing dot floats at the top of the page with no number beside
   it. Every `hidden` toggle in the markup needs its display rule neutralised
   the same way. */
.live[hidden] {
  display: none;
}

.live {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 auto;
  padding-top: calc(env(safe-area-inset-top, 0px) + 18px);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.8);
}
.live i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--uv-hot);
  box-shadow: 0 0 8px var(--uv-hot);
  animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.25; }
}

.name {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: clamp(2.7rem, 14vw, 5rem);
  line-height: 0.9;
  letter-spacing: -0.025em;
  margin: 0;
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.85);
}

.tagline {
  margin: 12px 0 26px;
  font-size: 0.82rem;
  letter-spacing: 0.19em;
  text-transform: lowercase;
  color: var(--muted);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.9);
}

.links {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: 460px;
}

.sub-links {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 22px;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: lowercase;
}
.sub-links a {
  color: var(--muted);
  text-decoration: none;
  /* 14px of vertical padding on a ~20px line box clears the 48px tap-target
     floor. The underline is drawn by a pseudo-element rather than the border,
     so the padding can grow the target without pushing the rule away from the
     text. */
  padding: 14px 8px;
  position: relative;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.9);
}
.sub-links a::after {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 12px;
  height: 1px;
  background: transparent;
  transition: background 0.16s ease;
}
.sub-links a:active,
.sub-links a:hover {
  color: var(--bone);
}
.sub-links a:active::after,
.sub-links a:hover::after {
  background: var(--uv);
}
.sub-links span {
  color: var(--silver);
  opacity: 0.5;
}

/* Tap-burst canvas. A cursor trail is dead weight on a phone, so on touch this
   is what the trail becomes — see script.js, which binds by pointer type
   rather than by screen width. */
#fx {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
}

/* --------------------------------------------------------- sticky CTA ---- */
/* On the long pages the CTA scrolls away and conversion dies with it. This
   pins after roughly a screen of scroll, inside the home-indicator inset. */

.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  padding: 10px var(--pad) calc(env(safe-area-inset-bottom, 0px) + 10px);
  background: linear-gradient(180deg, rgba(10, 5, 7, 0), rgba(10, 5, 7, 0.94) 42%);
  transform: translateY(140%);
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.sticky-cta.on {
  transform: translateY(0);
  pointer-events: auto;
}
.sticky-cta .btn .face {
  min-height: 56px;
}

/* ------------------------------------------------------------ desktop ---- */

@media (min-width: 769px) {
  .stage {
    display: grid;
    grid-template-columns: 40% 60%;
  }
  .video-panel {
    position: fixed;
    inset: 0 60% 0 0;
  }
  /* Full-height panel, so the scrim only needs to protect the unmute control
     rather than carry text contrast. */
  .scrim {
    background: linear-gradient(
      180deg,
      rgba(10, 5, 7, 0.6) 0%,
      rgba(10, 5, 7, 0) 22%,
      rgba(10, 5, 7, 0) 78%,
      rgba(10, 5, 7, 0.6) 100%
    );
  }
  #unmute-btn {
    top: auto;
    bottom: 20px;
    right: 20px;
  }
  .link-panel {
    grid-column: 2;
    justify-content: center;
    padding: 40px;
  }
  .live {
    position: absolute;
    top: 26px;
    margin: 0;
    padding-top: 0;
  }
  .name {
    text-shadow: none;
  }
  .tagline,
  .sub-links a,
  .live {
    text-shadow: none;
  }
  .sticky-cta {
    display: none; /* the CTA is always in view on a desktop layout */
  }
}

/* ---------------------------------------------------- reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .drip i {
    opacity: 1;
    transform: none;
  }
  .sticky-cta {
    transition: none;
  }
  #fx {
    display: none;
  }
}

/* === Signature: the UV wash ===
   A blacklight the visitor drags around the page. It sits above the footage
   and below the content, tinting whatever the pointer is near without ever
   touching legibility of the text on top.

   Position comes from two custom properties written by script.js on
   pointermove, so the only thing changing per frame is a variable — no layout,
   no repaint of the video layer. It starts centred so a page that never
   receives a pointer event still looks deliberate rather than broken. */
.uv-wash {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.85;
  background: radial-gradient(
    circle 38vmin at var(--mx, 50%) var(--my, 42%),
    rgba(123, 61, 255, 0.42) 0%,
    rgba(0, 229, 255, 0.16) 38%,
    rgba(6, 4, 13, 0) 72%
  );
  transition: opacity 0.3s ease;
}

/* Coarse pointers have no hover position to track, and a wash pinned under a
   thumb is just a smudge — so it stays centred and static there. */
@media (hover: none), (prefers-reduced-motion: reduce) {
  .uv-wash {
    background: radial-gradient(
      circle 46vmin at 50% 40%,
      rgba(123, 61, 255, 0.34) 0%,
      rgba(0, 229, 255, 0.12) 40%,
      rgba(6, 4, 13, 0) 74%
    );
  }
}

/* The lander carried 26 words of visible text -- a name, a tagline and two
   button labels -- on a template shared across ~20 sibling domains. Google
   ruled a sibling's homepage "Duplicate, Google chose different canonical"
   on exactly that. This surfaces the per-creator intro that previously only
   rendered on /about, which is the one piece of copy on the page that is
   genuinely this creator's. Kept visually quiet so it does not compete with
   the buttons, which are still the point of the page. */
.lander-lede {
  margin: 18px auto 0;
  max-width: 34rem;
  font-size: 0.86rem;
  line-height: 1.65;
  text-align: center;
  opacity: 0.72;
}
@media (max-width: 480px) {
  .lander-lede { font-size: 0.8rem; }
}
