/* stephandjeff.love — warm paper with light coming through it.
   System fonts only: the CSP forbids external font hosts, and there is no
   webfont worth the round-trip.

   The rainbow rule, inherited from the proposal book: the spectrum carries
   every rule, ornament and display line, and it NEVER touches body text.
   Colour is the decoration; the reading stays ink on paper. */

:root {
  --paper:  #f7f2e9;
  --paper2: #fbf7f0;
  --ink:    #23201c;
  --ink2:   #5d564c;
  --rule:   #d9cebc;
  --gold:   #a9773d;
  --rose:   #b4566a;

  /* six stops, warm-shifted so they sit on cream rather than fizzing off it */
  --s1: #e23a6e;  /* rose      */
  --s2: #f07b2c;  /* tangerine */
  --s3: #efc22f;  /* gold      */
  --s4: #35b27c;  /* jade      */
  --s5: #2f86dd;  /* azure     */
  --s6: #8b4ed6;  /* violet    */

  /* The seventh stop repeats the first, so a background-position animation
     loops with no seam where violet would otherwise butt against rose. */
  --spectrum: linear-gradient(90deg,
    var(--s1), var(--s2), var(--s3), var(--s4), var(--s5), var(--s6), var(--s1));

  --serif: ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", Palatino, serif;
  --sans:  ui-sans-serif, "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ── the wash ───────────────────────────────────────────────────────────
   Two fixed layers. The first is the old warm wash, now in six colours; the
   second is a fine prismatic haze rotating against it. Both are enormous and
   very low-opacity, so what you see is the paper changing temperature rather
   than any edge you could point at. */

body::before,
body::after {
  content: "";
  position: fixed;
  pointer-events: none;
  z-index: 0;
}

body::before {
  inset: -30%;
  background:
    radial-gradient(38rem 26rem at 14% 8%,  rgba(226, 58,110,.13), transparent 60%),
    radial-gradient(34rem 24rem at 86% 4%,  rgba(240,123, 44,.11), transparent 60%),
    radial-gradient(40rem 30rem at 92% 62%, rgba(139, 78,214,.12), transparent 62%),
    radial-gradient(36rem 28rem at 8%  74%, rgba( 47,134,221,.11), transparent 62%),
    radial-gradient(30rem 22rem at 50% 104%, rgba( 53,178,124,.12), transparent 62%);
  animation: drift 34s ease-in-out infinite alternate;
}

body::after {
  inset: 0;
  opacity: .07;
  background: conic-gradient(from 0deg at 50% 40%,
    var(--s1), var(--s2), var(--s3), var(--s4), var(--s5), var(--s6), var(--s1));
  /* A conic gradient at full width is a colour wheel; blurring it past any
     detail leaves only the hue shift, which is the whole point of it. */
  filter: blur(90px) saturate(1.3);
  animation: wheel 70s linear infinite;
}

@keyframes drift {
  from { transform: translate3d(-2%, -1%, 0) scale(1);    }
  to   { transform: translate3d( 2%,  2%, 0) scale(1.08); }
}
@keyframes wheel {
  to { transform: rotate(360deg); }
}

.page {
  position: relative;
  z-index: 1;
  max-width: 46rem;
  margin: 0 auto;
  padding: clamp(3rem, 9vw, 7rem) clamp(1.25rem, 5vw, 2.5rem) 5rem;
}

/* ── masthead ──────────────────────────────────────────────────────────── */

.kicker {
  margin: 0 0 1.25rem;
  font-family: var(--sans);
  font-size: .75rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  /* The one place small text takes the spectrum: it is a label, not prose,
     and it sits on paper at 100% opacity where every stop clears 4.5:1. */
  background: var(--spectrum);
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: spin-hue 18s linear infinite;
}

/* background-clip: text with a transparent colour is invisible where the
   property is unsupported. Only take the colour away once it is known to work. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .kicker, .wordmark, .chapter-no, .card__eyebrow { color: var(--rose) !important; }
}

.wordmark {
  margin: 0;
  font-size: clamp(2.75rem, 10vw, 5.25rem);
  line-height: 1.02;
  letter-spacing: -.02em;
  font-weight: 400;
  font-style: italic;
  background: var(--spectrum);
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: spin-hue 14s linear infinite;
}
.amp {
  font-style: normal;
  padding: 0 .05em;
}

@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); }
}

.lede {
  max-width: 32rem;
  margin: clamp(1.75rem, 4vw, 2.5rem) 0 0;
  font-size: clamp(1.0625rem, 2.1vw, 1.25rem);
  line-height: 1.68;
  color: var(--ink2);
}

/* ── the twinkle row ────────────────────────────────────────────────────
   Six stars, one per stop, each on its own phase. Purely decorative and
   aria-hidden in the markup. */

.twinkle {
  display: flex;
  gap: .7rem;
  margin: clamp(1.75rem, 4vw, 2.5rem) 0 0;
}
.twinkle i {
  width: 16px;
  height: 16px;
  display: block;
  /* Four-point star with a pinched waist — the same silhouette the canvas
     draws, so the static ornament and the cursor sparks are one family. */
  clip-path: polygon(50% 0%, 59% 41%, 100% 50%, 59% 59%, 50% 100%, 41% 59%, 0% 50%, 41% 41%);
  transform-origin: 50% 50%;
  animation: twinkle 3.4s ease-in-out infinite;
}
.twinkle i:nth-child(1) { background: var(--s1); animation-delay: 0s;    }
.twinkle i:nth-child(2) { background: var(--s2); animation-delay: .28s;  }
.twinkle i:nth-child(3) { background: var(--s3); animation-delay: .56s;  }
.twinkle i:nth-child(4) { background: var(--s4); animation-delay: .84s;  }
.twinkle i:nth-child(5) { background: var(--s5); animation-delay: 1.12s; }
.twinkle i:nth-child(6) { background: var(--s6); animation-delay: 1.4s;  }

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

/* ── chapter ───────────────────────────────────────────────────────────── */

/* border-image paints a gradient into a border edge, which a plain
   `border-top` colour cannot do. The `1` is the required slice value. */
.chapter {
  margin-top: clamp(3.5rem, 9vw, 5.5rem);
  padding-top: clamp(2rem, 5vw, 3rem);
  border-top: 2px solid transparent;
  border-image: var(--spectrum) 1;
  border-left: 0; border-right: 0; border-bottom: 0;
}
.chapter-no {
  margin: 0 0 .4rem;
  font-family: var(--sans);
  font-size: .75rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  background: var(--spectrum);
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: spin-hue 16s linear infinite;
}
.chapter-title {
  margin: 0 0 clamp(1.5rem, 4vw, 2.25rem);
  font-size: clamp(1.6rem, 4.5vw, 2.25rem);
  font-weight: 400;
  letter-spacing: -.015em;
}

/* ── the card ──────────────────────────────────────────────────────────── */

/* Two backgrounds, two paint boxes: the flat paper is clipped to the padding
   box and the spectrum to the border box, so a transparent border shows the
   gradient and nothing else does. A gradient ring with no extra element. */
.card {
  display: grid;
  grid-template-columns: minmax(0, 11rem) 1fr;
  gap: clamp(1.25rem, 4vw, 2.25rem);
  align-items: center;
  padding: clamp(1.25rem, 3.5vw, 1.75rem);
  border: 2px solid transparent;
  border-radius: 12px;
  background:
    linear-gradient(var(--paper2), var(--paper2)) padding-box,
    var(--spectrum) border-box;
  background-size: auto, 300% 100%;
  animation: shimmer 20s linear infinite;
  text-decoration: none;
  color: inherit;
}
@media (max-width: 34rem) {
  .card { grid-template-columns: 1fr; justify-items: start; }
}

.card__cover {
  position: relative;
  display: block;
  width: 100%;
  max-width: 11rem;
  aspect-ratio: 17 / 22;
}

/* Two offset sheets behind the face, so it reads as a stack of paper. */
.cover__stack {
  position: absolute;
  inset: 0;
  transform: translate(6px, 6px) rotate(1.4deg);
  background: #fffdf8;
  border: 1px solid var(--rule);
  border-radius: 3px;
}
.cover__stack::before {
  content: "";
  position: absolute;
  inset: 0;
  transform: translate(-3px, -3px) rotate(-.9deg);
  background: #fffdf8;
  border: 1px solid var(--rule);
  border-radius: 3px;
}

/* The cover itself stays paper and ink. It is a drawing of a printed
   document; a rainbow one would be a drawing of something else. */
.cover__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: 1.1rem .95rem;
  background: #fffdf8;
  border: 1px solid #cbbfa9;
  border-radius: 3px;
  box-shadow: 0 6px 18px rgba(35,32,28,.10);
}
.cover__ref {
  font-family: var(--sans);
  font-size: .5625rem;
  letter-spacing: .16em;
  color: var(--gold);
}
.cover__rule {
  display: block;
  width: 2.4rem;
  height: 2px;
  margin: .5rem 0 .7rem;
  background: var(--spectrum);
  background-size: 200% 100%;
}
.cover__title {
  font-size: .9375rem;
  line-height: 1.25;
  letter-spacing: -.01em;
  color: var(--ink);
}
.cover__meta {
  margin-top: auto;
  font-family: var(--sans);
  font-size: .5rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #8d8375;
}

.card__body { display: block; }
.card__eyebrow {
  display: block;
  font-family: var(--sans);
  font-size: .8125rem;
  font-weight: 600;
  letter-spacing: .01em;
  background: var(--spectrum);
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: spin-hue 20s linear infinite;
}
.card__desc {
  display: block;
  margin-top: .6rem;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink2);
}
.card__lock {
  display: inline-block;
  margin-top: 1rem;
  padding: .28rem .6rem;
  font-family: var(--sans);
  font-size: .6875rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #7a7165;
  background: rgba(35,32,28,.05);
  border-radius: 999px;
}

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

.foot {
  margin-top: clamp(3.5rem, 8vw, 5rem);
  padding-top: 1.5rem;
  border-top: 2px solid transparent;
  border-image: var(--spectrum) 1;
  font-family: var(--sans);
  font-size: .875rem;
  color: #8d8375;
}
.foot p { margin: 0; }

/* A gradient underline, drawn as a background rather than a border so it can
   thicken on hover without moving the text. */
a:not(.card) {
  color: var(--rose);
  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:not(.card):hover { background-size: 200% 3px; color: var(--s6); }

/* ── the star field ─────────────────────────────────────────────────────
   Eight stars scattered behind the masthead, 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.

   Fixed, aria-hidden, pointer-events:none, and behind .page in the stacking
   order, so it can never take a click or land in the accessibility tree. */

.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(--s1); animation-delay: 0s;    animation-duration: 8s;  }
.stars i:nth-child(2) { top:  22%; left: 84%; width: 10px; height: 10px; background: var(--s2); animation-delay: 1.1s;  animation-duration: 11s; }
.stars i:nth-child(3) { top:  46%; left: 92%; width: 16px; height: 16px; background: var(--s3); animation-delay: 2.4s;  animation-duration: 9s;  }
.stars i:nth-child(4) { top:  68%; left:  5%; width: 12px; height: 12px; background: var(--s4); animation-delay: 3.2s;  animation-duration: 10s; }
.stars i:nth-child(5) { top:  84%; left: 78%; width: 9px;  height: 9px;  background: var(--s5); animation-delay: 4.6s;  animation-duration: 8s;  }
.stars i:nth-child(6) { top:  34%; left: 66%; width: 8px;  height: 8px;  background: var(--s6); animation-delay: 5.4s;  animation-duration: 12s; }
.stars i:nth-child(7) { top:  58%; left: 27%; width: 11px; height: 11px; background: var(--s1); animation-delay: 6.3s;  animation-duration: 9s;  }
.stars i:nth-child(8) { top:   6%; left: 46%; width: 13px; height: 13px; background: var(--s5); animation-delay: 7.1s;  animation-duration: 11s; }

@keyframes float-twinkle {
  0%, 100% { opacity: 0;   transform: translateY(6px)  scale(.5)  rotate(0deg);   }
  50%      { opacity: .55; 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: no-preference) {
  .card, .cover__face, .cover__stack { transition: transform .3s ease, box-shadow .3s ease; }
  .card:hover .cover__face  { transform: translateY(-5px) rotate(-1deg); box-shadow: 0 14px 30px rgba(35,32,28,.16); }
  .card:hover .cover__stack { transform: translate(9px, 8px) rotate(2.4deg); }
}

.card:focus-visible {
  outline: 2px solid var(--s6);
  outline-offset: 3px;
}

/* Stop every ambient loop for a visitor who asked for less motion. The
   sparkler does not start at all — see the top of sparkle.js. */
@media (prefers-reduced-motion: reduce) {
  body::before, body::after,
  .kicker, .wordmark, .chapter-no, .card, .card__eyebrow, .twinkle i, .stars i {
    animation: none !important;
  }
  /* The star field's resting state is opacity:0 — with the animation stopped
     it is eight invisible boxes, so take it out of the page entirely. */
  .stars { display: none; }
}
