/* stephandjeff.lol — the unserious one.
 *
 * Deliberately the opposite of .love: that site is warm paper and restraint,
 * this one is a dark room with the lights on. Same six-stop spectrum idea,
 * pushed to neon, and here it is allowed to touch everything.
 *
 * Display type is the Impact stack. On a site whose whole subject is jokes
 * that is the correct font and not a lazy one; where it is missing the page
 * falls back to a heavy grotesque and loses nothing structural.
 *
 * System fonts only: the CSP forbids external font hosts.
 */

:root {
  --void:   #0b0710;
  --void2:  #140d1c;
  --card:   #180f22;
  --chalk:  #f4eefb;
  --chalk2: #a99cba;
  --edge:   #2c1f3d;

  --n1: #ff2d78;  /* hot pink  */
  --n2: #ff8a1f;  /* orange    */
  --n3: #ffe03d;  /* yellow    */
  --n4: #3dffa8;  /* mint      */
  --n5: #37c2ff;  /* cyan      */
  --n6: #b07cff;  /* lilac     */

  /* Seventh stop repeats the first so a background-position loop has no seam. */
  --spectrum: linear-gradient(90deg,
    var(--n1), var(--n2), var(--n3), var(--n4), var(--n5), var(--n6), var(--n1));

  --fat: Haettenschweiler, "Arial Narrow Bold", Impact, "Franklin Gothic Heavy",
         "Helvetica Neue Condensed Black", "DejaVu Sans", "Liberation Sans", sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--void);
  color: var(--chalk);
  font-family: var(--mono);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Two fixed layers of glow. On a dark ground the colour has to come from
   light sources rather than from tinting the paper, so these are bright and
   heavily blurred rather than washed and transparent. */
body::before,
body::after {
  content: "";
  position: fixed;
  pointer-events: none;
  z-index: 0;
}
body::before {
  inset: -25%;
  background:
    radial-gradient(30rem 22rem at 10% 6%,   rgba(255, 45,120,.30), transparent 60%),
    radial-gradient(28rem 20rem at 88% 12%,  rgba( 55,194,255,.26), transparent 60%),
    radial-gradient(34rem 26rem at 78% 78%,  rgba(176,124,255,.28), transparent 62%),
    radial-gradient(26rem 20rem at 16% 88%,  rgba( 61,255,168,.20), transparent 62%),
    radial-gradient(24rem 18rem at 50% 42%,  rgba(255,138, 31,.16), transparent 64%);
  filter: blur(20px);
  animation: drift 28s ease-in-out infinite alternate;
}
/* A faint scanline grid, so the glow has something to sit on and the
   background is not just a smooth blur. */
body::after {
  inset: 0;
  opacity: .5;
  background-image:
    repeating-linear-gradient(0deg, rgba(255,255,255,.028) 0 1px, transparent 1px 3px);
}

@keyframes drift {
  from { transform: translate3d(-2%, -1%, 0) scale(1);    }
  to   { transform: translate3d( 2%,  2%, 0) scale(1.09); }
}

/* ── caution tape ──────────────────────────────────────────────────────── */

.tape {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 6px;
  z-index: 3;
  background: var(--spectrum);
  background-size: 300% 100%;
  animation: shimmer 6s linear infinite;
}

@keyframes shimmer {
  from { background-position:    0% 50%; }
  to   { background-position: -300% 50%; }
}

/* For anything painting the FULL spectrum at once: rotate the hue rather than
   slide the gradient, so every stop stays on screen while the colour moves. */
@keyframes spin-hue {
  to { filter: hue-rotate(360deg); }
}

/* ── stage ─────────────────────────────────────────────────────────────── */

.stage {
  position: relative;
  z-index: 1;
  max-width: 62rem;
  margin: 0 auto;
  padding: clamp(3rem, 8vw, 5.5rem) clamp(1.1rem, 4vw, 2.5rem) 5rem;
}

.kicker {
  margin: 0 0 1rem;
  font-size: .72rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--chalk2);
}

.wordmark {
  margin: 0;
  font-family: var(--fat);
  /* Impact and Haettenschweiler ship one weight; asking for 900 costs them
     nothing and lets a fallback grotesque pick its real black face. Synthesis
     is off so nothing gets faux-bolded into mush. */
  font-weight: 900;
  font-synthesis: none;
  /* "steph&jeff.lol" is set with no spaces, so it is one unbreakable word and
     will happily run off the side of the viewport. Size it to fit the widest
     plausible fallback (Impact is condensed; DejaVu Sans Bold is not), and
     allow a break as the backstop rather than an overflow. */
  font-size: clamp(2.6rem, 11vw, 6.5rem);
  overflow-wrap: anywhere;
  line-height: .86;
  letter-spacing: -.015em;
  text-transform: lowercase;
  /* A hair of skew: enough to read as leaning, not enough to look broken. */
  transform: skewX(-4deg);
  transform-origin: left bottom;
  background: var(--spectrum);
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: spin-hue 11s linear infinite;
}
/* Transparent text is invisible where background-clip:text is unsupported. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .wordmark { color: var(--n1) !important; }
}
.amp { padding: 0 .04em; }
.tld { font-size: .46em; vertical-align: .82em; letter-spacing: .01em; }

.lede {
  max-width: 34rem;
  margin: clamp(1.5rem, 4vw, 2.25rem) 0 0;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--chalk2);
}
.lede em { color: var(--n3); font-style: italic; }

.twinkle {
  display: flex;
  gap: .7rem;
  margin: 1.75rem 0 0;
}
.twinkle i {
  width: 16px;
  height: 16px;
  display: block;
  /* The same pinched four-point star the cursor sparkler draws, so the static
     ornament and the canvas particles are one family. */
  clip-path: polygon(50% 0%, 59% 41%, 100% 50%, 59% 59%, 50% 100%, 41% 59%, 0% 50%, 41% 41%);
  animation: twinkle 3.4s ease-in-out infinite;
}
.twinkle i:nth-child(1) { background: var(--n1); animation-delay: 0s;    }
.twinkle i:nth-child(2) { background: var(--n2); animation-delay: .28s;  }
.twinkle i:nth-child(3) { background: var(--n3); animation-delay: .56s;  }
.twinkle i:nth-child(4) { background: var(--n4); animation-delay: .84s;  }
.twinkle i:nth-child(5) { background: var(--n5); animation-delay: 1.12s; }
.twinkle i:nth-child(6) { background: var(--n6); animation-delay: 1.4s;  }

@keyframes twinkle {
  0%, 100% { transform: scale(.74) rotate(0deg);   opacity: .62; }
  50%      { transform: scale(1.18) rotate(45deg); opacity: 1;  }
}

/* ── the board ─────────────────────────────────────────────────────────── */

.board-section {
  margin-top: clamp(3rem, 8vw, 4.5rem);
  padding-top: clamp(1.75rem, 4vw, 2.5rem);
  border-top: 2px solid transparent;
  border-image: var(--spectrum) 1;
}

.board-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: clamp(1.5rem, 4vw, 2rem);
}
.board-title {
  margin: 0;
  font-family: var(--fat);
  font-weight: 900;
  font-synthesis: none;
  font-size: clamp(1.75rem, 5vw, 2.75rem);
  letter-spacing: .01em;
  text-transform: lowercase;
  color: var(--chalk);
}
.board-count {
  margin: 0;
  font-size: .78rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--chalk2);
}
#count { color: var(--n3); }

/* Columns rather than grid: the cards are different heights on purpose, and
   a masonry flow keeps the board looking pinned up rather than laid out. */
.board {
  margin: 0;
  padding: 0;
  list-style: none;
  columns: 3 17rem;
  column-gap: 1.1rem;
}
@media (max-width: 40rem) { .board { columns: 1; } }

.card {
  --hue: 0;
  break-inside: avoid;
  position: relative;
  margin: 0 0 1.1rem;
  padding: 1.1rem 1.15rem 1rem;
  background: var(--card);
  border: 1px solid var(--edge);
  border-left: 3px solid hsl(var(--hue) 100% 68%);
  border-radius: 10px;
  transform: rotate(var(--tilt, 0deg));
  transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
.card:hover {
  transform: rotate(0deg) translateY(-3px);
  box-shadow: 0 12px 34px rgba(0,0,0,.55),
              0 0 0 1px hsl(var(--hue) 100% 68% / .5),
              0 0 26px hsl(var(--hue) 100% 60% / .22);
  border-color: hsl(var(--hue) 100% 68% / .55);
}

.card__no {
  display: block;
  margin-bottom: .5rem;
  font-size: .68rem;
  letter-spacing: .16em;
  color: hsl(var(--hue) 100% 72%);
}

.card__text {
  margin: 0;
  font-family: var(--fat);
  font-weight: 900;
  font-synthesis: none;
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  line-height: 1.14;
  letter-spacing: .005em;
  color: var(--chalk);
}

.card__origin {
  margin: .7rem 0 0;
  font-size: .8125rem;
  line-height: 1.6;
  color: var(--chalk2);
}

.card__meta {
  margin: .85rem 0 0;
  padding-top: .6rem;
  border-top: 1px dashed var(--edge);
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #9a8cad;
}

/* Who said it, as a dot in the corner. Two people, two colours, and no
   legend anywhere — they both know which is which. */
.card--steph::after,
.card--jeff::after {
  content: "";
  position: absolute;
  top: .85rem;
  right: .9rem;
  width: 8px;
  height: 8px;
  border-radius: 50%;
}
.card--steph::after { background: var(--n1); box-shadow: 0 0 10px var(--n1); }
.card--jeff::after  { background: var(--n5); box-shadow: 0 0 10px var(--n5); }

.card--empty { border-left-color: var(--edge); }

.howto {
  margin: 2rem 0 0;
  font-size: .8125rem;
  line-height: 1.75;
  color: #9a8cad;
}
code {
  padding: .1rem .35rem;
  font-size: .95em;
  color: var(--n4);
  background: rgba(61,255,168,.08);
  border-radius: 4px;
}

/* ── footer ────────────────────────────────────────────────────────────── */

.foot {
  margin-top: clamp(3rem, 7vw, 4.5rem);
  padding-top: 1.4rem;
  border-top: 1px solid var(--edge);
  font-size: .8125rem;
  color: #9a8cad;
}
.foot p { margin: 0; }

a {
  color: var(--n5);
  text-decoration: none;
  background-image: var(--spectrum);
  background-size: 200% 2px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  padding-bottom: 2px;
  transition: background-size .25s ease, color .25s ease;
}
a:hover { background-size: 200% 3px; color: var(--n3); }
a:focus-visible { outline: 2px solid var(--n3); outline-offset: 3px; }

/* ── the star field ─────────────────────────────────────────────────────
   Eight stars scattered behind everything, each on its own phase and drift.
   This is what makes the page read as sparkly before the cursor has moved —
   the canvas sparkler only exists once someone brings a mouse to it.

   On this dark ground they can run brighter than on .love's paper. */

.stars {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.stars i {
  position: absolute;
  display: block;
  clip-path: polygon(50% 0%, 59% 41%, 100% 50%, 59% 59%, 50% 100%, 41% 59%, 0% 50%, 41% 41%);
  opacity: 0;
  animation: float-twinkle 9s ease-in-out infinite;
}
.stars i:nth-child(1) { top:  12%; left:  8%; width: 14px; height: 14px; background: var(--n1); animation-delay: 0s;   animation-duration: 8s;  }
.stars i:nth-child(2) { top:  22%; left: 84%; width: 10px; height: 10px; background: var(--n2); animation-delay: 1.1s; animation-duration: 11s; }
.stars i:nth-child(3) { top:  46%; left: 92%; width: 16px; height: 16px; background: var(--n3); animation-delay: 2.4s; animation-duration: 9s;  }
.stars i:nth-child(4) { top:  68%; left:  5%; width: 12px; height: 12px; background: var(--n4); animation-delay: 3.2s; animation-duration: 10s; }
.stars i:nth-child(5) { top:  84%; left: 78%; width: 9px;  height: 9px;  background: var(--n5); animation-delay: 4.6s; animation-duration: 8s;  }
.stars i:nth-child(6) { top:  34%; left: 66%; width: 8px;  height: 8px;  background: var(--n6); animation-delay: 5.4s; animation-duration: 12s; }
.stars i:nth-child(7) { top:  58%; left: 27%; width: 11px; height: 11px; background: var(--n1); animation-delay: 6.3s; animation-duration: 9s;  }
.stars i:nth-child(8) { top:   6%; left: 46%; width: 13px; height: 13px; background: var(--n5); animation-delay: 7.1s; animation-duration: 11s; }

@keyframes float-twinkle {
  0%, 100% { opacity: 0;  transform: translateY(6px)  scale(.5)  rotate(0deg);  }
  50%      { opacity: .8; transform: translateY(-6px) scale(1.1) rotate(45deg); }
}

/* ── the sparkler ──────────────────────────────────────────────────────── */

#sparkle-canvas {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
}

/* ── motion ────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  body::before, .tape, .wordmark, .twinkle i, .stars i { animation: none !important; }
  .stars { display: none; }
  .card { transform: none !important; transition: none !important; }
}
