/* =====================================================================
   MAN OF ALL - manofallmusic.com
   Single-screen splash. Static: no framework, no build step.

   LAYOUT CONTRACT
   ---------------
   Everything lives inside one viewport - there is no scrolling. .stage is
   100svh (small viewport height, so a phone's browser chrome can never push
   content out of view) and the painting is sized from the height budget
   rather than the width, so the block below it always has room.

   ARTWORK GEOMETRY
   ----------------
   manofall-banner-100.png is 2172 x 724 - the painting itself, at a true
   3:1, no letterboxing. (manofall-banner-youtube.png is the same artwork
   padded to 16:9 with black bars for YouTube; it is not used here.)

   --band-h sets the painting's height; its width follows as
   min(100%, --band-h * 3). When that width hits 100% the frame is narrower
   than 3:1 and .frame__img crops horizontally, so:

     visible width fraction = frameWidth / frameHeight / 3

   The baked-in "MAN OF ALL" lettering spans x 32%..68% of the artwork, so
   the frame must stay at ~1.15:1 or wider to keep it whole. Every --band-h
   below satisfies that at its breakpoint.
   ===================================================================== */

/* ---------------------------------------------------------------- tokens */
:root {
  --ink:        #05090D;
  --charcoal:   #0B1118;

  --gold:       #C99355;
  --amber:      #F0B869;
  --ivory:      #E6D4B8;
  --violet:     #6D416E;

  --text:       rgba(230, 212, 184, 0.92);
  --text-soft:  rgba(230, 212, 184, 0.62);
  --text-faint: rgba(230, 212, 184, 0.34);

  --serif-display: "Cinzel", "Trajan Pro", "Optima", "Palatino Linotype",
                   "Book Antiqua", Georgia, "Times New Roman", serif;
  --serif-body:    "Cormorant Garamond", "EB Garamond", Garamond,
                   "Palatino Linotype", Georgia, "Times New Roman", serif;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* Phones, portrait: ~27svh tall, full width -> ~1.7:1 frame, ~57% visible.
     The `100svh - Nrem` term is what actually guarantees no scrolling: the
     block beneath the painting has a floor height in rem (it stops growing
     with the viewport once the type clamps max out), so the painting must
     yield to it on short screens rather than the page overflowing. */
  --band-h: max(104px, min(clamp(118px, 27vh, 330px), calc(100vh - 26rem)));
  --band-h: max(104px, min(clamp(118px, 27svh, 330px), calc(100svh - 26rem)));

  --gutter: clamp(0.9rem, 4vw, 2.5rem);
  --stack:  clamp(0.9rem, 2.6vh, 1.9rem);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: rgba(201, 147, 85, 0.35) var(--ink);
  scrollbar-width: thin;
}

body {
  margin: 0;
  background-color: var(--ink);
  background-image:
    radial-gradient(120% 68% at 50% 0%,   rgba(14, 22, 32, 0.85) 0%, rgba(5, 9, 13, 0) 60%),
    radial-gradient(95%  60% at 50% 46%,  rgba(109, 65, 110, 0.11) 0%, rgba(5, 9, 13, 0) 70%),
    radial-gradient(120% 60% at 50% 100%, rgba(11, 17, 24, 0.9) 0%, rgba(5, 9, 13, 0) 62%);
  background-attachment: fixed;
  color: var(--text);
  font-family: var(--serif-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  overflow-x: clip;
}

img { display: block; max-width: 100%; }
h1, h2, p, ul { margin: 0; }
ul { list-style: none; padding: 0; }
a { color: inherit; text-decoration: none; }

::selection { background: rgba(201, 147, 85, 0.30); color: #FFF3E0; }

::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: var(--ink); }
::-webkit-scrollbar-thumb {
  background: linear-gradient(var(--gold), var(--violet));
  border-radius: 10px;
  border: 3px solid var(--ink);
}

:focus-visible {
  outline: 1px solid var(--amber);
  outline-offset: 5px;
  border-radius: 3px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* gradient definitions holder - zero-sized, never display:none (that can
   stop the gradients resolving in some engines) */
.defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* -------------------------------------------------------- atmosphere */
.atmos { display: contents; }

.atmos__stars,
.atmos__vignette,
.atmos__grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

.atmos__stars {
  z-index: 0;
  overflow: hidden;
  animation: star-drift 240s linear infinite alternate;
}
@keyframes star-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-1.5%, -2.2%, 0); }
}

/* One element per layer; the stars themselves are its box-shadows, so the
   whole sky is three animated nodes rather than eighty. */
.star-layer {
  position: absolute;
  top: 0; left: 0;
  width: 1px; height: 1px;
  border-radius: 50%;
  opacity: var(--o-min, 0.2);
  animation: twinkle var(--dur, 8s) ease-in-out var(--delay, 0s) infinite alternate;
}
@keyframes twinkle {
  from { opacity: var(--o-min, 0.12); }
  to   { opacity: var(--o-max, 0.55); }
}

.atmos__vignette {
  z-index: 60;
  background: radial-gradient(ellipse 95% 82% at 50% 46%,
              rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, 0.28) 76%, rgba(0, 0, 0, 0.6) 100%);
}

.atmos__grain {
  z-index: 61;
  opacity: 0.055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/></filter><rect width='220' height='220' filter='url(%23n)'/></svg>");
  background-size: 220px 220px;
}

/* ============================== STAGE ============================== */
.stage {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--stack);
  padding: clamp(0.85rem, 2.4vh, 1.9rem) var(--gutter);
  text-align: center;
}

/* --- entrance: the scene arrives, it does not pop ------------------- */
.js .rise {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity 1.1s var(--ease), transform 1.1s var(--ease);
}
.js .is-ready .rise { opacity: 1; transform: translate3d(0, 0, 0); }

.js .rise--frame   { transition-delay: 0s; }
.js .rise--tagline { transition-delay: 0.30s; }
.js .rise--release { transition-delay: 0.50s; }
.js .rise--social  { transition-delay: 0.70s; }
.js .rise--legal   { transition-delay: 0.88s; }

/* --- the painting -------------------------------------------------- */
.frame {
  position: relative;
  height: var(--band-h);
  width: min(100%, calc(var(--band-h) * 3));
  margin-inline: auto;
  overflow: hidden;
  isolation: isolate;
  flex: none;
  /* stands in for the sky until the artwork decodes - no layout shift */
  background:
    radial-gradient(48% 42% at 50% 24%, rgba(240, 184, 105, 0.16) 0%, rgba(109, 65, 110, 0.10) 42%, rgba(5, 9, 13, 0) 72%),
    linear-gradient(180deg, #0A121B 0%, #070D14 55%, #05090D 100%);
}

/* The 1.05 overscan is load-bearing, not decoration: at wide sizes the
   painting covers the frame exactly, so the pointer drift in script.js has
   nowhere to move without pulling an empty edge into view. 5% buys at least
   ~10px of slack in every direction at every supported size, and the script
   measures that slack and clamps itself to it. OVERSCAN in script.js mirrors
   this value - change one and you must change the other. */
.frame__shift {
  position: absolute;
  inset: 0;
  transform: translate3d(0, 0, 0) scale(1.05);
  will-change: transform;
}

.frame__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
}
/* fade-in only when scripting is live, so the artwork can never be left hidden */
.js .frame__img { opacity: 0; transition: opacity 1.5s var(--ease); }
.js .frame__img.is-loaded { opacity: 1; }

/* light breathing around the eclipse (22.5% down, dead centre) */
.frame__halo {
  position: absolute;
  left: 50%;
  top: 22.5%;
  width: 46%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  mix-blend-mode: screen;
  background: radial-gradient(circle,
              rgba(240, 184, 105, 0.20) 0%,
              rgba(201, 147, 85, 0.11) 28%,
              rgba(130, 85, 138, 0.10) 48%,
              rgba(0, 0, 0, 0) 70%);
  animation: breathe 13s ease-in-out infinite alternate;
}
@keyframes breathe {
  from { opacity: 0.45; transform: translate(-50%, -50%) scale(0.94); }
  to   { opacity: 1;    transform: translate(-50%, -50%) scale(1.07); }
}

/* Dissolves the painting's edges into the page - a vignette, not a crop.
   The eclipse's corona reaches to within ~2% of the top edge, so the top
   fade is kept deliberately shallow: any deeper and it dims the crown. */
.frame__edges {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(5, 9, 13, 0.85) 0%, rgba(5, 9, 13, 0) 3.5%),
    linear-gradient(to top,    rgba(5, 9, 13, 0.95) 0%, rgba(5, 9, 13, 0) 5%),
    linear-gradient(to right,  rgba(5, 9, 13, 0.80) 0%, rgba(5, 9, 13, 0) 5%),
    linear-gradient(to left,   rgba(5, 9, 13, 0.80) 0%, rgba(5, 9, 13, 0) 5%);
}

/* --- tagline --------------------------------------------------------- */
.tagline {
  font-style: italic;
  font-weight: 300;
  font-size: clamp(1rem, 2.1vw + 0.4rem, 1.6rem);
  letter-spacing: 0.07em;
  color: var(--text-soft);
  text-wrap: balance;
  flex: none;
}

/* --- release --------------------------------------------------------- */
.release {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.4rem, 1.2vh, 0.85rem);
  flex: none;
}

.release__ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(240px, 42vw, 540px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  z-index: -1;
  /* Atmosphere, not geometry. An actual drawn ring reads as a stray circle
     once it crosses the icon row on a single screen - and the real eclipse
     is already in the painting directly above. */
  background:
    radial-gradient(circle,
      rgba(240, 184, 105, 0.07) 0%,
      rgba(130, 85, 138, 0.09) 38%,
      rgba(109, 65, 110, 0.04) 58%,
      rgba(5, 9, 13, 0) 72%);
  animation: breathe-soft 17s ease-in-out infinite alternate;
}
@keyframes breathe-soft {
  from { opacity: 0.55; transform: translate(-50%, -50%) scale(0.95); }
  to   { opacity: 1;    transform: translate(-50%, -50%) scale(1.05); }
}

.eyebrow {
  font-family: var(--serif-display);
  font-size: clamp(0.56rem, 1.1vw + 0.25rem, 0.72rem);
  font-weight: 500;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  text-transform: uppercase;
  color: var(--gold);
}

.release__title {
  font-family: var(--serif-display);
  font-weight: 400;
  font-size: clamp(1.45rem, 4.6vw + 0.35rem, 3.6rem);
  line-height: 1.14;
  letter-spacing: 0.09em;
  text-indent: 0.09em;
  text-transform: uppercase;
  color: var(--ivory);
  text-wrap: balance;
  text-shadow: 0 0 42px rgba(201, 147, 85, 0.18);
}

.rule {
  display: block;
  width: min(260px, 48vw);
  height: 1px;
  background: linear-gradient(90deg,
              rgba(201, 147, 85, 0) 0%,
              rgba(201, 147, 85, 0.55) 22%,
              rgba(240, 184, 105, 0.85) 50%,
              rgba(201, 147, 85, 0.55) 78%,
              rgba(201, 147, 85, 0) 100%);
}

.release__status {
  font-family: var(--serif-display);
  font-size: clamp(0.6rem, 1.1vw + 0.28rem, 0.82rem);
  letter-spacing: 0.38em;
  text-indent: 0.38em;
  text-transform: uppercase;
  color: var(--amber);
  opacity: 0.85;
}

/* --- follow ---------------------------------------------------------- */
.social {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.5rem, 1.4vh, 0.9rem);
  width: 100%;
  flex: none;
}

.social__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(0.35rem, 1.4vh, 0.9rem) clamp(0.2rem, 1.5vw, 0.9rem);
  width: 100%;
  max-width: 34rem;
}

.social__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(0.3rem, 0.9vh, 0.5rem);
  min-height: 52px;
  padding: 0.35rem 0.15rem;
  border-radius: 8px;
  transition: transform 0.45s var(--ease), background-color 0.45s var(--ease);
}

.social__icon {
  width: clamp(24px, 2.2vw + 1rem, 36px);
  height: clamp(24px, 2.2vw + 1rem, 36px);
  display: block;
  transition: filter 0.45s var(--ease);
}
.social__icon svg { width: 100%; height: 100%; display: block; }

.social__name {
  font-family: var(--serif-display);
  font-size: clamp(0.46rem, 0.7vw + 0.24rem, 0.63rem);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(230, 212, 184, 0.52);
  transition: color 0.45s var(--ease);
  white-space: nowrap;
}

a.social__link:hover,
a.social__link:focus-visible { transform: translateY(-2px); }

a.social__link:hover .social__name,
a.social__link:focus-visible .social__name { color: var(--ivory); }

a.social__link:hover .social__icon,
a.social__link:focus-visible .social__icon {
  filter: drop-shadow(0 0 9px rgba(240, 184, 105, 0.45));
}

/* Spotify - present in the ecosystem, not yet a destination */
.social__item--soon .social__link { cursor: default; }
.social__item--soon .social__icon { opacity: 0.55; }
.social__item--soon .social__name { opacity: 0.6; }

.social__handle {
  font-size: clamp(0.85rem, 1.3vw + 0.35rem, 1.08rem);
  letter-spacing: 0.06em;
  color: var(--text-soft);
}

.social__note {
  font-family: var(--serif-display);
  font-size: clamp(0.48rem, 0.7vw + 0.24rem, 0.62rem);
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-transform: uppercase;
  color: rgba(230, 212, 184, 0.28);
}

/* --- legal ----------------------------------------------------------- */
.legal {
  margin-top: clamp(0.15rem, 1.2vh, 0.9rem);
  font-family: var(--serif-display);
  font-size: clamp(0.5rem, 0.7vw + 0.26rem, 0.64rem);
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-transform: uppercase;
  color: rgba(230, 212, 184, 0.22);
  flex: none;
}

/* ============================ RESPONSIVE ============================ */

/* roomier phones / small tablets */
@media (min-width: 560px) {
  :root {
    --band-h: max(104px, min(clamp(150px, 30vh, 380px), calc(100vh - 27rem)));
    --band-h: max(104px, min(clamp(150px, 30svh, 380px), calc(100svh - 27rem)));
  }
}

/* one row of eight once there is width for it */
@media (min-width: 700px) {
  .social__grid {
    grid-template-columns: repeat(8, minmax(0, 1fr));
    max-width: 46rem;
  }
}

/* Landscape desktop: the painting is capped at 46svh so the block beneath it
   always has room, and at 33.333vw so it is never cropped vertically. Below
   that width it sits inset, framed by the dark - the whole painting, always. */
@media (min-width: 820px) and (min-aspect-ratio: 1/1) {
  :root {
    --band-h: max(104px, min(33.333vw, 46vh, calc(100vh - 30rem)));
    --band-h: max(104px, min(33.333vw, 46svh, calc(100svh - 30rem)));
  }
  .social__grid { max-width: 52rem; }
}

/* Short viewports - landscape phones especially. Everything tightens so the
   single screen still holds. */
@media (max-height: 560px) {
  :root {
    --stack: clamp(0.4rem, 1.4vh, 0.8rem);
    --band-h: max(92px, min(33.333vw, 38vh, calc(100vh - 20rem)));
    --band-h: max(92px, min(33.333vw, 38svh, calc(100svh - 20rem)));
  }
  .stage { padding-block: 0.5rem; }
  .release { gap: 0.25rem; }
  .social__link { min-height: 44px; gap: 0.25rem; }
  .social__note { display: none; }
}

/* Short and wide: height is the scarce axis, so the icons go to a single row
   well before the 700px the tall layout waits for - two rows of icons is
   what pushes a landscape phone over the edge. */
@media (max-height: 560px) and (min-width: 560px) {
  .social__grid {
    grid-template-columns: repeat(8, minmax(0, 1fr));
    max-width: 44rem;
  }
}

/* Below this the tagline and rule step aside, which frees ~3.5rem - so the
   painting's reserve shrinks to match and it keeps what height it can. */
@media (max-height: 420px) {
  :root {
    --band-h: max(92px, min(33.333vw, 38vh, calc(100vh - 16.5rem)));
    --band-h: max(92px, min(33.333vw, 38svh, calc(100svh - 16.5rem)));
  }
  .tagline { display: none; }
  .rule { display: none; }
}

/* ========================= REDUCED MOTION ========================= */
@media (prefers-reduced-motion: reduce) {
  .js .rise {
    opacity: 1;
    transform: none;
    transition: none;
  }
  /* matches the `.js .frame__img` specificity above, so it actually wins */
  .js .frame__img { opacity: 1; transition: none; }
  .frame__shift { transform: none !important; }

  .star-layer,
  .atmos__stars,
  .frame__halo,
  .release__ring {
    animation: none !important;
  }
  .star-layer { opacity: var(--o-min, 0.3); }
  .frame__halo { opacity: 0.7; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
