/*
  home.css — the homepage only: hero overlay, the cast, the vote, product cards,
  the signup box and the tape deck.

  Loads AFTER components.css and only adds. Nothing here redefines an existing
  component, so a change on this page cannot leak into /books/ or /free-page/.
  Every colour is a token — see tokens.css. No new colours exist.
*/

/* ---- the night behind everything ---------------------------------------- */
/* Fixed, so it does not grow with the page — the parallax comes from redrawing
   wrapped star positions, not from moving a 6000px-tall layer around. */
/* The canvas now paints the page GROUND as well as the stars — a gradient from night
   blue at the top to attic brown further down — so it must sit behind content but in
   front of the body colour, which stays as the no-JS fallback. */
#sky {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;   /* it must never eat a click meant for the page */
  display: block;
}
/* Content sits above the sky. Without this the canvas paints over the page. */
.site-head, main, .site-foot, .tape { position: relative; z-index: 1; }

/* ---- motion: reveal on scroll ------------------------------------------- */
.rev { opacity: 0; transform: translateY(14px); transition: opacity .7s ease, transform .7s ease; }
.rev.in { opacity: 1; transform: none; }
.rev.d1 { transition-delay: .09s; }
.rev.d2 { transition-delay: .18s; }

/* ---- hero: full-bleed cover ---------------------------------------------- */
.hero-cover {
  position: relative;
  /* Tall enough to be a scene, capped so it never eats a whole 4K screen. min() with
     vh rather than a fixed height so a short laptop window still shows the words. */
  height: min(86vh, 880px);
  min-height: 460px;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  /* Negative margin so the next section begins UNDER the fade rather than after it —
     the dissolve has nothing to dissolve into if the layout puts a gap there. */
  margin-bottom: calc(var(--sp-5) * -1);
  padding-bottom: var(--sp-5);
}
.hero-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* The image DISSOLVES at the bottom rather than stopping.
     A scrim cannot solve this any more: its bottom stop is one fixed colour, and the
     page ground is now a gradient that changes as you scroll, so the two only match at
     one scroll position and show a hard line everywhere else. A mask makes the image
     itself go transparent, so whatever the canvas is painting shows through and there is
     no colour to match. */
  -webkit-mask-image: linear-gradient(to bottom,
    #000 0%, #000 62%, rgba(0,0,0,.72) 82%, rgba(0,0,0,0) 100%);
  mask-image: linear-gradient(to bottom,
    #000 0%, #000 62%, rgba(0,0,0,.72) 82%, rgba(0,0,0,0) 100%);
  /* 42%, not centre: the cast stands in the lower half of the artwork and a centred
     crop cuts their feet off before it cuts the empty ceiling. */
  object-position: center 42%;
  animation: settle 1.8s cubic-bezier(.2,.7,.3,1) both;
}
/* The words are on a painting, so they need their own ground. A scrim rather than a
   text-shadow: a shadow on a serif this large reads as a print fault. */
.hero-scrim {
  position: absolute; inset: 0;
  /* The bottom stop is EXACTLY the page ground (--attic #211712). Anything darker and
     the hero ends in a visible band where the scrim meets the page -- the same hard-edge
     defect the letterboxed banner had, just rotated. */
  /* No longer lands on a solid colour at the bottom — the mask on the image does that
     job now. This only has to make the words legible over the artwork. */
  background: linear-gradient(to top,
    rgba(28,20,17,.90) 0%, rgba(28,20,17,.78) 16%,
    rgba(28,20,17,.44) 38%, rgba(28,20,17,.12) 58%, rgba(28,20,17,0) 76%);
}
.hero-words { position: relative; padding-bottom: var(--sp-5); }
.hero-words h1 {
  font-family: var(--font-display);
  font-variation-settings: var(--fraunces-axes);
  font-size: clamp(32px, 5.6vw, 62px);
  line-height: 1.02;
  margin: 0 0 var(--sp-2);
  max-width: 18ch;
}
.hero-words .lede { max-width: 46ch; margin: 0; }
@media (max-width: 700px) {
  .hero-cover { height: min(80vh, 620px); }
  .hero-words { padding-bottom: var(--sp-4); }
  /* The words block is proportionally much taller on a phone — the lede wraps to five
     lines — so it reaches up into the part of the hero the scrim had almost stopped
     covering. MEASURED: the eyebrow sat 324px above the hero bottom, where the scrim was
     at 0.18 alpha, and sage-on-artwork at 13px was unreadable. Carry the scrim higher
     rather than shrink the type. */
  .hero-scrim { background: linear-gradient(to top,
    rgba(28,20,17,.94) 0%, rgba(28,20,17,.90) 24%,
    rgba(28,20,17,.78) 48%, rgba(28,20,17,.42) 70%, rgba(28,20,17,0) 94%); }
}
@keyframes settle { from { transform: scale(1.045); } to { transform: scale(1); } }

/* ---- the cast ----------------------------------------------------------- */
.band { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end;
  justify-content: space-between; margin-bottom: var(--sp-2); }
.band-note { margin: 0; max-width: 46ch; font-size: 15px; }
.band-note strong { color: var(--candle); }
.vote-intro { margin: 0 0 var(--sp-3); font-size: 13px; color: var(--sage); }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: var(--sp-3);
}

.pf {
  background: var(--paper);
  border-radius: 2px;
  box-shadow: var(--paper-shadow);
  padding: 10px 10px 12px;
  color: var(--attic);
  display: flex;
  flex-direction: column;
  /* A card tilts a degree on hover. Any more reads as a web effect rather than a
     photograph somebody put down at an angle. */
  transition: transform .35s cubic-bezier(.2,.7,.3,1), box-shadow .35s;
}
.pf:hover, .pf:focus-within {
  transform: translateY(-4px) rotate(-.5deg);
  box-shadow: 0 26px 50px -14px rgba(0,0,0,.72), 0 2px 0 rgba(0,0,0,.25);
}

.stage { position: relative; aspect-ratio: 1/1; overflow: hidden; background: var(--attic); }
.stage img, .stage video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.stage video { opacity: 0; transition: opacity .32s ease; }
.pf.on .stage video { opacity: 1; }
.pf.on .stage img { opacity: 0; transition: opacity .32s ease; }

.cue {
  position: absolute; left: 8px; bottom: 8px;
  font-family: var(--font-typed); font-size: 10px; letter-spacing: .14em;
  color: var(--paper); background: rgba(33,23,18,.72);
  padding: 3px 7px; border-radius: 2px; opacity: .9; transition: opacity .3s;
}
.pf.on .cue { opacity: 0; }

.meta { border-top: 1px dashed var(--thread); padding-top: 11px; margin-top: 11px; flex: 1; }
.meta .name {
  font-family: var(--font-typed); font-size: 13px; font-weight: 700;
  letter-spacing: .06em; margin: 0 0 4px;
}
.meta p { margin: 0; font-size: 13.5px; line-height: 1.5; }

/* ---- the vote ----------------------------------------------------------- */
.vote {
  margin-top: 11px; width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  background: none; border: 1px solid var(--thread); border-radius: 2px;
  padding: 7px 10px; cursor: pointer;
  font-family: var(--font-typed); font-size: 11.5px; letter-spacing: .05em;
  color: var(--attic);
  transition: background .25s, border-color .25s, color .25s;
}
.vote:hover, .vote:focus-visible { background: var(--attic); color: var(--paper); outline: none; }
.vote[aria-pressed='true'] { background: var(--attic); color: var(--paper); border-color: var(--attic); }
.vote[aria-pressed='true'] .vote-label::before { content: '\2713\00a0'; }
.vote[disabled] { cursor: default; opacity: .82; }
.vote[disabled]:hover { background: none; color: var(--attic); }
.vote[disabled][aria-pressed='true']:hover { background: var(--attic); color: var(--paper); }
.vote-count { font-variant-numeric: tabular-nums; opacity: .7; }

.vote-result { margin-top: var(--sp-4); }
.tally { list-style: none; margin: var(--sp-2) 0 var(--sp-2); padding: 0;
  max-width: 460px; display: grid; gap: 7px; }
.tally li { display: grid; grid-template-columns: 13ch 1fr 4ch; gap: 10px; align-items: center;
  font-family: var(--font-typed); font-size: 12px; }
.tally .bar { height: 8px; background: var(--thread); border-radius: 2px; }
.tally .bar span { display: block; height: 100%; background: var(--candle); border-radius: 2px;
  transition: width .8s cubic-bezier(.2,.7,.3,1); }
.tally .n { text-align: right; font-variant-numeric: tabular-nums; color: var(--sage); }
.tally li.lead .bar span { background: var(--rose); }

/* ---- product cards ------------------------------------------------------ */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--sp-3); margin-top: var(--sp-3); }
.card { border: 1px solid var(--thread); border-radius: 2px; padding: var(--sp-3); }
.card-kind { font-family: var(--font-typed); font-size: 11px; letter-spacing: .12em;
  text-transform: lowercase; color: var(--sage); margin: 0 0 var(--sp-1); }
.card h3 { font-family: var(--font-display); font-variation-settings: var(--fraunces-axes);
  font-size: 22px; margin: 0 0 var(--sp-1); }
.card p { margin: 0 0 var(--sp-1); font-size: 15px; }
.card-state { font-family: var(--font-typed); font-size: 11.5px; color: var(--candle);
  margin: var(--sp-2) 0 0; }

/* ---- signup ------------------------------------------------------------- */
.signup-box { border: 1px solid var(--thread); border-radius: 2px; padding: var(--sp-4) var(--sp-3); }
.signup-box h2 { margin-top: 0; }
.field-row { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-3); }
.field-row input {
  flex: 1 1 240px; min-width: 0;
  background: var(--attic); color: var(--paper);
  border: 1px solid var(--thread); border-radius: 2px;
  padding: 12px 14px; font-family: var(--font-body); font-size: 16px;
}
.field-row input::placeholder { color: var(--sage); opacity: .7; }
.field-row input:focus { outline: none; border-color: var(--candle); }
.field-row input[aria-invalid='true'] { border-color: var(--rose); }
.field-row .btn { flex: 0 0 auto; }
/* Off-screen rather than display:none — a bot reading the DOM sees a normal field. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.fineprint { margin: var(--sp-2) 0 0; font-size: 12.5px; color: var(--sage); }
.form-msg { margin: var(--sp-1) 0 0; font-size: 14px; min-height: 1.4em; }
.form-msg.ok { color: var(--candle); }
.form-msg.bad { color: var(--rose); }

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

/* ---- the deck: audio opt-in, framed as Boombeau's mixtape --------------- */
.tape { position: fixed; right: 22px; bottom: 22px; z-index: 20;
  font-family: var(--font-typed); font-size: 12px; letter-spacing: .06em; }
.deck { display: flex; align-items: center; gap: 10px;
  background: #191110; border: 1px solid var(--thread); border-radius: 2px;
  padding: 10px 14px; color: var(--paper); cursor: pointer; transition: border-color .3s; }
.deck:hover, .deck:focus-visible { border-color: var(--candle); outline: none; }
.cass { width: 46px; height: 30px; flex-shrink: 0; color: var(--thread); transition: color .3s; }
.tape.on .cass { color: var(--candle); }
@keyframes reel { to { transform: rotate(360deg); } }
.tape.on .cass .hub { animation: reel 2.6s linear infinite; }
.tape .said { color: var(--sage); }

/* The tracklist is a J-card, because that is the object this brand already owns.
   TWO things here, and both were bugs:

   1. ABSOLUTELY POSITIONED. As a normal-flow child it kept its ~400px of layout even at
      opacity 0, so .tape's hover box reached most of the way up the viewport and the
      list sprang open when the pointer was nowhere near the cassette.
   2. IT ONLY EXISTS WHEN THE TAPE IS ON. A tracklist for music that is not playing is a
      menu with nothing to apply itself to. Turn the tape on and it appears; that is also
      the reveal that tells you there are fourteen of them. */
/* ---- the tape player -------------------------------------------------------
   Open/closed is its own state, driven by [hidden] on #player rather than by
   .tape.on. `.tape.on` now means ONLY "audio is running", which is what makes the
   spinning reels honest and lets the list be dismissed while the music continues. */
.player { position: absolute; bottom: 100%; right: 0; margin: 0 0 8px;
  width: 268px; background: var(--paper); color: var(--ink);
  border: 1px solid var(--thread); border-radius: 4px; padding: 10px 10px 8px;
  box-shadow: 0 18px 40px rgba(0,0,0,.45); }
.player[hidden] { display: none; }

.ph { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin: 0 0 6px; }
.ph-side { font-family: var(--mono); font-size: 9.5px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--thread); }
.ph-close { background: none; border: 0; color: var(--thread); cursor: pointer;
  font-size: 18px; line-height: 1; padding: 0 2px; }
.ph-close:hover, .ph-close:focus-visible { color: var(--ink); outline: none; }

/* The list scrolls rather than growing without limit: nineteen tracks would otherwise
   run off the top of a laptop viewport, and the transport would go with it. */
.jcard { list-style: none; margin: 0; padding: 0; max-height: 208px; overflow-y: auto;
  overscroll-behavior: contain; }
.jcard button { display: flex; width: 100%; gap: 8px; align-items: baseline;
  justify-content: space-between; text-align: left; background: none; border: 0;
  font: inherit; font-family: var(--mono); font-size: 12px; color: inherit;
  padding: 3px 2px; opacity: .72; cursor: pointer; }
.jcard button:hover, .jcard button:focus-visible { opacity: 1; outline: none;
  text-decoration: underline; }
.tk-name::before { content: '· '; color: var(--thread); }
.tk-time { font-size: 11px; color: var(--thread); flex: none; }
.jcard button[aria-current='true'] { opacity: 1; font-weight: 700; }
.jcard button[aria-current='true'] .tk-name::before { content: '▸ '; color: var(--candle); }

/* ---- transport ---- */
.pt { display: flex; align-items: center; gap: 6px; margin-top: 8px;
  padding-top: 8px; border-top: 1px dashed var(--thread); }
.tr { background: none; border: 0; color: var(--ink); cursor: pointer; padding: 4px;
  line-height: 0; opacity: .75; border-radius: 3px; }
.tr svg { width: 14px; height: 14px; }
.tr:hover, .tr:focus-visible { opacity: 1; outline: none; }
.tr-main { background: var(--candle); color: var(--paper); opacity: 1;
  border-radius: 50%; width: 30px; height: 30px; display: grid; place-items: center; }
.tr-main svg { width: 12px; height: 12px; }
/* One button, two glyphs: whichever the state is not, is hidden. */
.tr-main .i-pause, .tr-main.is-playing .i-play { display: none; }
.tr-main.is-playing .i-pause { display: block; }
.tr-loop[aria-pressed='true'] { color: var(--candle); opacity: 1; }

.vol { margin-left: auto; display: flex; align-items: center; }
.vol input[type='range'] { width: 64px; accent-color: var(--candle); cursor: pointer; }
.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; }

/* The deck bar's two lines: a small kicker over the track name, so "now playing"
   reads as a label and the title reads as the title. */
.deck-lines { display: grid; line-height: 1.25; }
.eyebrow-sm { font-family: var(--mono); font-size: 9px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--thread); }

@media (max-width: 560px) {
  .grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-2); }
  .tape { right: 12px; bottom: 12px; }
  .jcard { width: 168px; }
}

/* ---- reduced motion: everything above becomes still --------------------- */
@media (prefers-reduced-motion: reduce) {
  .rev, .rev.in { opacity: 1; transform: none; transition: none; }
  .pf, .pf:hover, .pf:focus-within { transform: none; transition: none; }
  .hero-figure img { animation: none; }
  .stage video { transition: none; }
  .tape.on .cass .hub { animation: none; }
  .tally .bar span { transition: none; }
}
