/* ==========================================================================
   MangoPlayz
   Palette sampled directly from the main channel's avatar (@NathanMCShorts):
   a tropical sunset — gold hoodie and hair, purple-magenta sky, palm green.

   Unlike the Dewier site, this source is genuinely multi-hue: 61.5% of its
   chromatic pixels are amber/gold (H20-59), 9.3% palm green (H130-179) and
   7.4% sunset mauve (H290-339). So the ambient can hold two different hues at
   once — amber and sky-plum — and the mauve is a real spice, not a lift.
   ========================================================================== */

:root {
  /* sampled from assets/pfp.jpg — % is that bucket's share of the circle */
  /* The one colour here that is NOT straight off the avatar. This is a bright
     cartoon: its darkest well-represented bucket is #2D1B19 at L14, and using
     that as the page floor left the render at mean luma 51.8 against 33-40 for
     the sibling sites — the family's depth was gone. So the floor is that exact
     bucket taken down to L7: same hue and saturation, half the lightness. */
  --void-950:   #160D0C; /* #2D1B19 (0.52%, L14) darkened to L7 */
  --clay-950:   #2D1B19; /* 0.52% — the sampled original */
  --bark-900:   #321D1A; /* 1.61% */
  --cocoa-800:  #38231A; /* 2.49% */
  --abyss-900:  #032C2B; /* 0.31% — H179, the palm/water shadow: a cool floor */
  --amber-700:  #A6570A; /* 1.52% — S89 L35, the one saturated mid-dark */
  --amber-600:  #D57918; /* 1.33% */
  --gold-500:   #FDC735; /* 3.26% — the single most common bucket */
  --gold-400:   #FED639; /* 1.68% */
  --yellow-300: #FEFB05; /* 2.37% — the hair highlight */
  --cream-100:  #FEFCD9; /* 1.64% */
  --plum-600:   #653C68; /* 0.57% — the deep end of the sunset sky */
  --mauve-400:  #B66693; /* 0.98% — the sky itself; kept only for the avatar ring */
  --leaf-400:   #08D736; /* 0.52% — the palms */
  --white:      #FDFEF9; /* 1.55% */

  /* sampled from assets/bg.jpg, the same way — the two sources agree on hue
     (the avatar's gold is H20-59, the background's fire is H0-59), which is
     why this drops in without a repaint of the accents */
  --flame-500:  #FC7614; /* 6.17% of the background — its signature orange */
  --flame-600:  #FC6716; /* 5.57% */
  --ember-800:  #871703; /* 0.93% — L27 */
  --ember-900:  #570704; /* 1.29% — L18, the darkest well-represented */
  --sun-300:    #FDE847; /* 0.91% — the sun itself */

  --text:       var(--white);
  --text-dim:   rgba(253, 254, 249, 0.58);
  --text-faint: rgba(253, 254, 249, 0.34);

  /* 0.05 / 0.10 rather than the 0.035 / 0.08 the first two sites use: warm
     backgrounds read closer than their luminance says, so tiles need the lift
     to stay separate. Measured on the Dewier build — see that CLAUDE.md. */
  --tile-bg:        rgba(255, 255, 255, 0.05);
  --tile-bg-hover:  rgba(255, 255, 255, 0.085);
  --tile-border:    rgba(255, 255, 255, 0.10);

  /* per-tile brand accent, overridden by [data-brand] */
  --brand: var(--gold-500);

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

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

body {
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(1.5rem, 5vw, 3rem) 1.25rem;
  background: var(--void-950);
  color: var(--text);
  font-family: "Outfit", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

::selection { background: var(--gold-500); color: var(--void-950); }

:focus-visible {
  outline: 2px solid var(--gold-400);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ==========================================================================
   Background — tropical dusk, drifting aurora, VHS grain + scanlines
   ========================================================================== */

.bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--void-950);
}

/* The photograph, knocked a long way down. It is an extremely hot source —
   99.8% of its pixels are chromatic and its dominant buckets sit at L50+ —
   so at full strength nothing on top of it is readable. Brightness does the
   heavy lifting, saturation comes down so the oranges stop vibrating against
   the gold accents, and 1.5px of blur kills the JPEG noise the darkening
   would otherwise exaggerate. inset:-2% hides the blur bleeding at the edges. */
.photo {
  position: absolute;
  inset: -2%;
  background: url("assets/bg.jpg") center 42% / cover no-repeat;
  filter: saturate(0.9) brightness(0.72) blur(2px);
}

/* Darkest through the middle, where the card sits, and lighter at the edges —
   the opposite of a vignette, so the pagoda and the tree still read around the
   content instead of being flattened out of existence. */
.scrim {
  position: absolute;
  inset: 0;
  background:
    /* The scrim is vertical, not centred, because on a phone the card is 440px
       inside a 480px window — any ellipse big enough to make the tiles readable
       swallows the whole screen and the photograph may as well not be there.
       So the image lives at the TOP, behind the hero, where the type is large
       and gold and can survive it, and the page darkens into the tile stack.

       Sized in rem rather than %, so it tracks the 27.5rem card at every width
       instead of the window. */
    radial-gradient(ellipse 30rem 26rem at 50% 76%,
      rgba(10, 4, 3, 0.82) 0%,
      rgba(10, 4, 3, 0.5) 60%,
      rgba(10, 4, 3, 0) 100%),
    linear-gradient(to bottom,
      rgba(10, 4, 3, 0.36) 0%,
      rgba(10, 4, 3, 0.46) 26%,
      rgba(10, 4, 3, 0.76) 62%,
      rgba(10, 4, 3, 0.88) 100%);
}

.aurora {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  will-change: transform;
}

/* The auroras are no longer the ambient — the photograph is. They survive only
   to keep the drift alive behind a still image and to bleed the photo's own
   colours into the dark corners, so both are sampled from bg.jpg and both are
   far weaker than they were. The sky-plum one is gone: there is no purple
   anywhere in this background and it fought the fire. */
.aurora--1 {
  width: 58rem; height: 58rem;
  top: -24rem; left: -16rem;
  background: radial-gradient(circle, rgba(252, 118, 20, 0.16) 0%, transparent 68%);
  animation: drift-1 26s var(--ease-soft) infinite alternate;
}

.aurora--2 {
  width: 52rem; height: 52rem;
  bottom: -22rem; right: -14rem;
  background: radial-gradient(circle, rgba(135, 23, 3, 0.30) 0%, transparent 68%);
  animation: drift-2 32s var(--ease-soft) infinite alternate;
}

@keyframes drift-1 {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(6rem, 4rem, 0) scale(1.14); }
}
@keyframes drift-2 {
  from { transform: translate3d(0, 0, 0) scale(1.08); }
  to   { transform: translate3d(-5rem, -3.5rem, 0) scale(1); }
}
@keyframes drift-3 {
  from { transform: translate3d(0, 0, 0); opacity: 0.75; }
  to   { transform: translate3d(-4rem, 5rem, 0); opacity: 1; }
}

.grain {
  position: absolute;
  inset: -50%;
  opacity: 0.045;
  mix-blend-mode: overlay;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-shift 0.7s steps(1) infinite;
}

@keyframes grain-shift {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-2%, 1%); }
  50%  { transform: translate(1%, -2%); }
  75%  { transform: translate(-1%, -1%); }
  100% { transform: translate(2%, 1%); }
}

.scanlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0px,
    rgba(0, 0, 0, 0) 2px,
    rgba(0, 0, 0, 0.045) 3px,
    rgba(0, 0, 0, 0.045) 4px
  );
}

.vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(125% 95% at 50% 50%, transparent 38%, rgba(9, 4, 3, 0.78) 100%);
}

/* ==========================================================================
   Card
   ========================================================================== */

.card {
  width: 100%;
  max-width: 27.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* staggered entrance */
[data-reveal] {
  opacity: 0;
  transform: translateY(14px);
  filter: blur(6px);
  animation: reveal 0.9s var(--ease) forwards;
  animation-delay: calc(var(--i) * 70ms + 120ms);
}

@keyframes reveal {
  to { opacity: 1; transform: translateY(0); filter: blur(0); }
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  /* full width, not shrink-to-fit: otherwise the hero is only as wide as the
     bio line and the stats row wraps the view pill onto its own line */
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-bottom: 2.25rem;
}

.avatar {
  position: relative;
  width: 8.25rem;
  height: 8.25rem;
  display: grid;
  place-items: center;
  margin-bottom: 1.35rem;
}

/* amber-600 rather than amber-700: this has to read as a halo against the
   ambient right behind it, so it needs the lift in lightness */
.avatar__glow {
  position: absolute;
  inset: -18%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(213, 121, 24, 0.52) 0%, transparent 68%);
  filter: blur(20px);
  animation: breathe 5.5s var(--ease-soft) infinite alternate;
}

@keyframes breathe {
  from { opacity: 0.55; transform: scale(0.94); }
  to   { opacity: 1;    transform: scale(1.06); }
}

/* thin conic ring, masked hollow — gold into the hair yellow into the sky */
.avatar__ring {
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  padding: 2px;
  background: conic-gradient(
    from 0deg,
    transparent 0deg,
    var(--gold-500) 70deg,
    var(--yellow-300) 120deg,
    var(--mauve-400) 190deg,
    transparent 250deg,
    transparent 360deg
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  opacity: 0.9;
  animation: spin 9s linear infinite;
}

@keyframes spin { to { transform: rotate(1turn); } }

.avatar__img {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(45, 27, 25, 0.9);
  box-shadow: 0 12px 40px -10px rgba(0, 0, 0, 0.85);
  transition: transform 0.5s var(--ease);
  user-select: none;
}

.avatar:hover .avatar__img { transform: scale(1.04); }

.name {
  font-size: clamp(2.1rem, 8vw, 2.6rem);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1.05;
  background: linear-gradient(175deg, var(--white) 30%, var(--gold-500) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}

.kana {
  font-family: "Noto Sans JP", "Outfit", sans-serif;
  font-size: 0.8rem;
  font-weight: 400;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  margin-top: 0.5rem;
  color: var(--gold-500);
  opacity: 0.85;
}

.bio {
  margin-top: 0.85rem;
  font-size: 0.95rem;
  font-weight: 300;
  color: var(--text-dim);
}

/* --- live subscriber pill (the main channel, @NathanMCShorts) --- */

/* the sub pill and the view count sit as a matched pair.
   width:100% is load-bearing — shrink-to-fit sized this box to 242px while its
   contents needed 243, so the view pill wrapped to a second line over a single
   pixel of rounding. */
.stats {
  width: 100%;
  margin-top: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Deliberately quieter than the sub pill beside it: no border, no dot, no
   backdrop blur. Two pills of equal weight compete, and the subscriber count is
   the one that matters — this is a footnote that happens to live up here. */
.views {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.85rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--text-dim);
  transition: background 0.4s var(--ease), color 0.4s var(--ease);
}

.views:hover {
  background: rgba(253, 199, 53, 0.14);
  color: var(--gold-400);
}

.views__icon {
  width: 0.95rem;
  height: 0.95rem;
  flex: none;
  display: block;
}

.views__n {
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  transition: opacity 0.4s var(--ease);
}

.views[data-state="loading"] .views__n { opacity: 0.4; }

/* both counter services down — take the whole pill away rather than leaving an
   eye staring at an em dash */
.views[data-state="dead"] { display: none; }

.subs {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem 1.05rem 0.5rem 0.9rem;
  border-radius: 999px;
  text-decoration: none;
  color: var(--text);
  background: rgba(253, 199, 53, 0.1);
  border: 1px solid rgba(253, 199, 53, 0.26);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: background 0.4s var(--ease), border-color 0.4s var(--ease),
              transform 0.4s var(--ease), box-shadow 0.4s var(--ease);
}

.subs:hover {
  background: rgba(253, 199, 53, 0.18);
  border-color: rgba(254, 214, 57, 0.5);
  transform: translateY(-2px);
  box-shadow: 0 10px 30px -12px rgba(253, 199, 53, 0.7);
}

.subs__dot {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--gold-400);
  box-shadow: 0 0 0 0 rgba(254, 214, 57, 0.7);
  animation: pulse 2.4s var(--ease-soft) infinite;
  flex: none;
}

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(254, 214, 57, 0.55); }
  70%  { box-shadow: 0 0 0 0.5rem rgba(254, 214, 57, 0); }
  100% { box-shadow: 0 0 0 0 rgba(254, 214, 57, 0); }
}

.subs__count {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--gold-400);
  transition: opacity 0.3s var(--ease);
  /* the count-up passes through wider strings than it lands on
     ("1.23K" -> "101K"); reserve the widest so the pill can't resize */
  min-width: 5ch;
  text-align: center;
}

.subs__count[data-state="loading"] { opacity: 0.4; }

/* brief flash when the live count ticks */
.subs__count.is-bump { animation: bump 0.65s var(--ease); }

@keyframes bump {
  0%   { transform: translateY(0); text-shadow: none; }
  35%  { transform: translateY(-3px); text-shadow: 0 0 14px rgba(254, 214, 57, 0.85); }
  100% { transform: translateY(0); text-shadow: none; }
}

.subs__label {
  font-size: 0.85rem;
  font-weight: 300;
  color: var(--text-dim);
}

/* ==========================================================================
   Link tiles
   ========================================================================== */

.links {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

/* Three of these five tiles are YouTube channels, so brand-red on all three
   would say nothing about which is which. The main channel keeps the red; the
   other two take palette colours (the fruit gold, the palm green) and the
   glyph carries the platform. */
.tile[data-brand="youtube"]  { --brand: #FF0033; }
.tile[data-brand="yt-mango"] { --brand: var(--gold-500); }
.tile[data-brand="yt-mods"]  { --brand: var(--leaf-400); }
.tile[data-brand="discord"]  { --brand: #5865F2; }
.tile[data-brand="x"]        { --brand: #FFFFFF; }

.tile {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 0.95rem;
  padding: 0.95rem 1.1rem;
  border-radius: 1rem;
  text-align: left;
  text-decoration: none;
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
  background: var(--tile-bg);
  border: 1px solid var(--tile-border);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transition:
    transform 0.45s var(--ease),
    background 0.45s var(--ease),
    border-color 0.45s var(--ease),
    box-shadow 0.45s var(--ease);
}

.tile:hover {
  transform: translateY(-3px);
  background: var(--tile-bg-hover);
  border-color: color-mix(in srgb, var(--brand) 45%, transparent);
  box-shadow:
    0 14px 34px -14px color-mix(in srgb, var(--brand) 60%, transparent),
    0 0 0 1px color-mix(in srgb, var(--brand) 14%, transparent) inset;
}

.tile:active { transform: translateY(-1px) scale(0.99); }

/* diagonal sheen sweeping across on hover */
.tile__sheen {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    105deg,
    transparent 30%,
    color-mix(in srgb, var(--brand) 16%, transparent) 48%,
    transparent 66%
  );
  transform: translateX(-120%);
  transition: opacity 0.3s var(--ease);
}

.tile:hover .tile__sheen {
  opacity: 1;
  animation: sweep 0.95s var(--ease) forwards;
}

@keyframes sweep {
  from { transform: translateX(-120%); }
  to   { transform: translateX(120%); }
}

.tile__icon {
  flex: none;
  width: 2.4rem;
  height: 2.4rem;
  display: grid;
  place-items: center;
  border-radius: 0.7rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.06);
  transition: background 0.45s var(--ease), border-color 0.45s var(--ease),
              transform 0.45s var(--ease);
}

.tile__icon svg {
  width: 1.15rem;
  height: 1.15rem;
  fill: var(--text-dim);
  transition: fill 0.45s var(--ease), filter 0.45s var(--ease);
}

.tile:hover .tile__icon {
  background: color-mix(in srgb, var(--brand) 16%, transparent);
  border-color: color-mix(in srgb, var(--brand) 34%, transparent);
  transform: scale(1.06);
}

.tile:hover .tile__icon svg {
  fill: var(--brand);
  filter: drop-shadow(0 0 8px color-mix(in srgb, var(--brand) 55%, transparent));
}

.tile__text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  flex: 1;
}

.tile__title {
  font-size: 0.98rem;
  font-weight: 500;
  letter-spacing: -0.012em;
}

.tile__sub {
  font-size: 0.78rem;
  font-weight: 300;
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.45s var(--ease);
}

.tile:hover .tile__sub { color: var(--text-dim); }

/* marks the main channel — three YouTube tiles otherwise look interchangeable */
.tile__badge {
  flex: none;
  margin-right: 0.15rem;
  padding: 0.2rem 0.5rem;
  border-radius: 0.45rem;
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-400);
  background: rgba(253, 199, 53, 0.12);
  border: 1px solid rgba(253, 199, 53, 0.28);
  transition: background 0.45s var(--ease), border-color 0.45s var(--ease),
              color 0.45s var(--ease);
}

.tile:hover .tile__badge {
  background: rgba(253, 199, 53, 0.2);
  border-color: rgba(254, 214, 57, 0.5);
  color: var(--cream-100);
}

/* live subscriber / member / follower count, right-aligned before the arrow */
.tile__count {
  flex: none;
  margin-right: 0.15rem;
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  transition: color 0.45s var(--ease), opacity 0.4s var(--ease);
}

.tile:hover .tile__count { color: var(--gold-400); }

/* placeholder before the first fetch lands — dim, so it never looks broken */
.tile__count[data-state="loading"] { opacity: 0.35; }

/* a source that dies just leaves the tile clean rather than showing a lie */
.tile__count[data-state="dead"] { display: none; }

.tile__count.is-bump { animation: count-bump 0.6s var(--ease); }

@keyframes count-bump {
  0%   { transform: translateY(0); text-shadow: none; }
  35%  { transform: translateY(-2px); text-shadow: 0 0 12px rgba(254, 214, 57, 0.8); }
  100% { transform: translateY(0); text-shadow: none; }
}

.tile__arrow {
  flex: none;
  display: grid;
  place-items: center;
  color: var(--text-faint);
  transition: color 0.45s var(--ease), transform 0.45s var(--ease);
}

.tile__arrow svg { width: 1rem; height: 1rem; display: block; }

.tile:hover .tile__arrow {
  color: var(--brand);
  transform: translateX(3px);
}

/* ==========================================================================
   Now playing — drives a hidden YouTube IFrame player
   ========================================================================== */

/* the real player: offscreen at full size rather than display:none or 0x0,
   which some browsers treat as a reason not to start playback */
.yt-host {
  position: fixed;
  left: -10000px;
  top: 0;
  width: 320px;
  height: 180px;
  opacity: 0;
  pointer-events: none;
}

.player {
  width: 100%;
  margin-top: 0.9rem;
  padding: 0.85rem 1rem 0.75rem;
  border-radius: 1rem;
  background: var(--tile-bg);
  border: 1px solid var(--tile-border);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transition: border-color 0.45s var(--ease), background 0.45s var(--ease);
}

.player:hover { border-color: rgba(253, 199, 53, 0.28); }

/* set by player.js when the API is blocked or the video isn't embeddable */
.player[hidden] { display: none; }

.player__top {
  display: flex;
  align-items: center;
  gap: 0.8rem;
}

.player__art {
  flex: none;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 0.7rem;
  overflow: hidden;
  position: relative;
  border: 1px solid rgba(255, 255, 255, 0.09);
  box-shadow: 0 6px 18px -8px rgba(0, 0, 0, 0.9);
}

.player__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  user-select: none;
  transition: transform 0.6s var(--ease), filter 0.6s var(--ease);
  filter: saturate(0.9) brightness(0.92);
}

/* the art only comes fully alive while it's actually playing */
.player[data-playing="true"] .player__art img {
  transform: scale(1.06);
  filter: saturate(1) brightness(1);
}

.player__meta {
  display: flex;
  flex-direction: column;
  gap: 0.14rem;
  min-width: 0;
  flex: 1;
}

.player__label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.player__title {
  font-size: 0.92rem;
  font-weight: 500;
  letter-spacing: -0.012em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.4s var(--ease);
}

.player__title:hover { color: var(--gold-400); }

/* three bars that only move while the track is running */
.player__eq {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 0.55rem;
}

.player__eq i {
  width: 2px;
  height: 30%;
  border-radius: 1px;
  background: var(--gold-400);
  animation: eq 0.9s var(--ease-soft) infinite alternate;
  opacity: 0.35;
}

.player__eq i:nth-child(2) { animation-delay: 0.28s; }
.player__eq i:nth-child(3) { animation-delay: 0.55s; }

.player[data-playing="true"] .player__eq i { opacity: 1; }
.player[data-playing="false"] .player__eq i { animation-play-state: paused; }


@keyframes eq {
  from { height: 25%; }
  to   { height: 100%; }
}

.player__btn {
  flex: none;
  width: 2.3rem;
  height: 2.3rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  cursor: pointer;
  color: var(--gold-400);
  background: rgba(253, 199, 53, 0.13);
  border: 1px solid rgba(253, 199, 53, 0.3);
  transition: background 0.4s var(--ease), border-color 0.4s var(--ease),
              transform 0.4s var(--ease), box-shadow 0.4s var(--ease);
}

.player__btn:hover {
  background: rgba(253, 199, 53, 0.22);
  border-color: rgba(254, 214, 57, 0.55);
  transform: scale(1.06);
  box-shadow: 0 8px 22px -10px rgba(253, 199, 53, 0.8);
}

.player__btn:active { transform: scale(0.97); }

.player__btn svg { width: 0.95rem; height: 0.95rem; fill: currentColor; display: block; }

.player[data-playing="false"] .ico-pause,
.player[data-playing="true"]  .ico-play { display: none; }

/* --- seek --- */

/* padded wrapper so the 4px bar has a comfortable grab area */
.player__seekwrap {
  margin-top: 0.7rem;
  padding: 0.45rem 0;
  cursor: pointer;
  touch-action: none;
}

.player__seekwrap:focus-visible { outline-offset: 6px; }

.player__track {
  position: relative;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.13);
  overflow: visible;
}

.player__fill {
  width: 0%;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--amber-600), var(--gold-400));
  box-shadow: 0 0 12px -2px rgba(253, 199, 53, 0.65);
}

.player__knob {
  position: absolute;
  top: 50%;
  left: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--gold-400);
  box-shadow: 0 0 10px rgba(253, 199, 53, 0.75);
  transform: translate(-50%, -50%);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}

.player:hover .player__knob,
.player__seekwrap:focus-visible .player__knob { opacity: 1; }

/* --- foot: time + volume --- */

.player__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
}

.player__time {
  font-size: 0.7rem;
  font-weight: 300;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}

.player__sep { margin: 0 0.3rem; opacity: 0.55; }

.player__vol {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.player__mute {
  display: grid;
  place-items: center;
  padding: 0.15rem;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--text-faint);
  transition: color 0.4s var(--ease);
}

.player__mute:hover { color: var(--gold-400); }
.player__mute svg { width: 0.95rem; height: 0.95rem; display: block; }

.player[data-muted="false"] .ico-muted,
.player[data-muted="true"]  .ico-vol { display: none; }
.player[data-muted="true"] .player__mute { color: var(--mauve-400); }

.player__slider {
  -webkit-appearance: none;
  appearance: none;
  width: 5.5rem;
  height: 3px;
  border-radius: 999px;
  cursor: pointer;
  /* --vol is kept in sync by player.js so the filled part tracks the thumb */
  background: linear-gradient(
    to right,
    var(--gold-500) calc(var(--vol, 28) * 1%),
    rgba(255, 255, 255, 0.14) calc(var(--vol, 28) * 1%)
  );
}

.player__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--gold-400);
  box-shadow: 0 0 0 3px rgba(253, 199, 53, 0.16);
  transition: box-shadow 0.3s var(--ease);
}

.player__slider:hover::-webkit-slider-thumb { box-shadow: 0 0 0 5px rgba(253, 199, 53, 0.22); }

.player__slider::-moz-range-thumb {
  width: 10px;
  height: 10px;
  border: 0;
  border-radius: 50%;
  background: var(--gold-400);
  box-shadow: 0 0 0 3px rgba(253, 199, 53, 0.16);
}

/* On anything wide enough, the player leaves the column and parks in the
   top-left corner of the viewport. Two reasons: it stops pushing the tiles down
   past the fold, and a persistent transport control belongs at a screen edge
   rather than buried under the links. Below 64rem it drops back into the flow,
   because a fixed panel would cover the hero on a phone.

   This block has to sit AFTER the base `.player` rule, not before it: same
   specificity means source order decides, and in front of it the base
   `width: 100%` won and the panel spanned the whole viewport. */
@media (min-width: 64rem) {
  .player {
    position: fixed;
    top: 1.15rem;
    left: 1.15rem;
    z-index: 20;
    width: 19.5rem;
    margin-top: 0;
  }
}

/* ==========================================================================
   Intro gate — the one click browsers insist on, turned into the entrance.

   Every visitor gets it, because that click is the only thing that guarantees
   audible playback: after a real gesture no browser can refuse. It also holds
   the staggered reveal below, so the page plays its entrance AS the overlay
   dissolves rather than having quietly finished behind it.
   ========================================================================== */

/* the reveal is paused, not delayed — pausing stops the delay clock too, so
   removing .gated replays the whole stagger from the top */
html.gated [data-reveal] { animation-play-state: paused; }
html.gated body { overflow: hidden; }

.gate {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  cursor: pointer;
  background:
    radial-gradient(70% 55% at 50% 45%, rgba(166, 87, 10, 0.22) 0%, transparent 70%),
    rgba(11, 6, 5, 0.82);
  backdrop-filter: blur(14px) saturate(0.9);
  -webkit-backdrop-filter: blur(14px) saturate(0.9);
  animation: gate-in 0.55s var(--ease) both;
}

.gate[hidden] { display: none; }

/* set by player.js just before removal, so it dissolves instead of vanishing */
.gate.is-leaving {
  animation: gate-out 0.6s var(--ease) forwards;
  pointer-events: none;
}

@keyframes gate-in {
  from { opacity: 0; backdrop-filter: blur(0); }
  to   { opacity: 1; }
}

@keyframes gate-out {
  to { opacity: 0; transform: scale(1.02); }
}

.gate__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 1.5rem;
  animation: gate-rise 0.7s var(--ease) 0.08s both;
}

@keyframes gate-rise {
  from { opacity: 0; transform: translateY(12px); filter: blur(5px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

.gate__art {
  position: relative;
  width: 5.4rem;
  height: 5.4rem;
  display: grid;
  place-items: center;
}

.gate__art img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(22, 13, 12, 0.9);
  box-shadow: 0 14px 40px -12px rgba(0, 0, 0, 0.9);
  user-select: none;
}

/* one slow pulse ring — reads as "waiting for you" without being a button */
.gate__ring {
  position: absolute;
  inset: -6%;
  border-radius: 50%;
  border: 1px solid rgba(253, 199, 53, 0.5);
  animation: gate-pulse 2.6s var(--ease-soft) infinite;
}

@keyframes gate-pulse {
  0%   { transform: scale(0.96); opacity: 0.9; }
  70%  { transform: scale(1.22); opacity: 0; }
  100% { transform: scale(1.22); opacity: 0; }
}

.gate__eq {
  display: inline-flex;
  align-items: flex-end;
  gap: 3px;
  height: 0.85rem;
  margin-top: 1.15rem;
}

.gate__eq i {
  width: 2px;
  height: 30%;
  border-radius: 1px;
  background: var(--gold-400);
  animation: eq 0.85s var(--ease-soft) infinite alternate;
}

.gate__eq i:nth-child(2) { animation-delay: 0.16s; }
.gate__eq i:nth-child(3) { animation-delay: 0.33s; }
.gate__eq i:nth-child(4) { animation-delay: 0.5s; }
.gate__eq i:nth-child(5) { animation-delay: 0.67s; }

.gate__title {
  margin-top: 0.8rem;
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -0.03em;
  background: linear-gradient(175deg, var(--white) 30%, var(--gold-500) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}

.gate__hint {
  margin-top: 0.4rem;
  font-size: 0.78rem;
  font-weight: 300;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-faint);
  animation: gate-breathe 2.8s var(--ease-soft) infinite alternate;
}

@keyframes gate-breathe {
  from { opacity: 0.45; }
  to   { opacity: 0.95; }
}

/* ==========================================================================
   Leaf gust — plays once as the intro gate clears. See leaves.js.

   Above the gate (z 70) rather than under it, so the leaves stay crisp while
   the overlay is still fading instead of being dimmed by it.
   ========================================================================== */

.leaf {
  position: fixed;
  top: var(--top);
  left: -14vw;
  z-index: 70;
  pointer-events: none;
  will-change: transform, opacity;
  animation: leaf-cross var(--dur) linear var(--delay) both;
}

.leaf i {
  display: block;
  will-change: transform;
  animation: leaf-sway var(--swayDur) ease-in-out var(--delay) infinite alternate;
}

.leaf svg {
  display: block;
  width: var(--size);
  height: var(--size);
  color: var(--tint);
  filter: blur(var(--blur)) drop-shadow(0 3px 6px rgba(0, 0, 0, 0.45));
  animation: leaf-spin var(--spinDur) linear infinite var(--spinDir);
}

/* linear on purpose — an eased sweep reads as a swipe, wind does not slow down
   in the middle of the screen */
@keyframes leaf-cross {
  from { transform: translate3d(0, 0, 0); opacity: 0; }
  9%   { opacity: var(--op); }
  82%  { opacity: var(--op); }
  to   { transform: translate3d(128vw, var(--drift), 0); opacity: 0; }
}

@keyframes leaf-sway {
  from { transform: translateY(calc(var(--sway) * -1)); }
  to   { transform: translateY(var(--sway)); }
}

@keyframes leaf-spin {
  to { transform: rotate(1turn); }
}

/* --- the ambient drift that follows the gust ---
   z-index 1 against the card's 2, so nothing ever crosses the text and the
   tiles' backdrop-filter blurs the leaves as they pass behind the glass. */
.leaf-fall {
  position: fixed;
  top: -16vh;
  left: var(--left);
  z-index: 1;
  pointer-events: none;
  will-change: transform, opacity;
  animation: leaf-fall var(--dur) linear var(--delay) both;
}

.leaf-fall i {
  display: block;
  will-change: transform;
  animation: leaf-sway var(--swayDur) ease-in-out infinite alternate;
}

.leaf-fall svg {
  display: block;
  width: var(--size);
  height: var(--size);
  color: var(--tint);
  /* the drop-shadow is what actually separates them from the photograph — the
     background is the same warm family, so without it they read as texture */
  filter: blur(var(--blur)) drop-shadow(0 2px 5px rgba(0, 0, 0, 0.55));
  animation: leaf-spin var(--spinDur) linear infinite;
}

/* --y0 lets a leaf begin part-way down the screen while still playing from 0%,
   so it fades in wherever it starts. See leaves.js for why a negative delay
   was the wrong tool for that. The ramp is slow at both ends deliberately —
   these should drift into existence, not switch on. */
@keyframes leaf-fall {
  from { transform: translate3d(0, var(--y0, 0vh), 0); opacity: 0; }
  14%  { opacity: var(--op); }
  86%  { opacity: var(--op); }
  to   { transform: translate3d(var(--drift), 132vh, 0); opacity: 0; }
}

/* the card has to own a stacking position for the drift to sit behind it */
.card {
  position: relative;
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .leaf,
  .leaf-fall { display: none; }
}

/* ==========================================================================
   Cursor trail — a lagging, speed-stretched glow behind the real pointer.
   The native cursor stays visible on purpose; see cursor.js.
   ========================================================================== */

.cursor-glow {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  pointer-events: none;   /* must never eat a click */
  opacity: 0;
  will-change: transform, opacity;
  background: radial-gradient(
    circle,
    rgba(254, 214, 57, 0.5) 0%,
    rgba(252, 118, 20, 0.26) 45%,
    rgba(252, 118, 20, 0) 72%
  );
  filter: blur(6px);
  mix-blend-mode: screen; /* lifts off the dark page instead of greying it */
}

@media (pointer: coarse), (prefers-reduced-motion: reduce) {
  .cursor-glow { display: none; }
}

/* ==========================================================================
   Fitting on one screen — the page is meant to be read without scrolling.
   Everything here is spacing only; nothing changes size or colour.
   ========================================================================== */

@media (min-width: 64rem) and (max-height: 60rem) {
  body { padding: 1.25rem; }
  .hero { margin-bottom: 1.5rem; }
  .avatar { width: 7rem; height: 7rem; margin-bottom: 1rem; }
  .name { font-size: clamp(1.9rem, 6vw, 2.25rem); }
  .bio { margin-top: 0.6rem; }
  /* .stats, not .subs — the pill lives inside the stats row now, and setting a
     margin on it just knocks it out of alignment with the view count */
  .stats { margin-top: 1.1rem; }
  .links { gap: 0.55rem; }
  .tile { padding: 0.8rem 1.05rem; }
  .foot { margin-top: 1.2rem; }
}

/* very short windows — laptop screens with the browser chrome eating 150px */
@media (min-width: 64rem) and (max-height: 46rem) {
  body { padding: 0.9rem; }
  .hero { margin-bottom: 1.1rem; }
  .avatar { width: 5.6rem; height: 5.6rem; margin-bottom: 0.7rem; }
  .kana { margin-top: 0.35rem; }
  .bio { margin-top: 0.45rem; font-size: 0.88rem; }
  .stats { margin-top: 0.8rem; }
  .subs { padding: 0.4rem 0.9rem 0.4rem 0.75rem; }
  .links { gap: 0.45rem; }
  .tile { padding: 0.65rem 1rem; border-radius: 0.85rem; }
  .tile__icon { width: 2.1rem; height: 2.1rem; }
  .foot { margin-top: 0.9rem; }
}

/* ==========================================================================
   Footer
   ========================================================================== */

.foot {
  margin-top: 2rem;
  font-size: 0.72rem;
  font-weight: 300;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ==========================================================================
   Small screens
   ========================================================================== */

@media (max-width: 26rem) {
  .avatar { width: 7.25rem; height: 7.25rem; }
  .tile { padding: 0.85rem 0.95rem; gap: 0.8rem; }
  .tile__sub { font-size: 0.74rem; }
  .tile__badge { font-size: 0.55rem; padding: 0.18rem 0.4rem; letter-spacing: 0.1em; }
}

/* ==========================================================================
   Reduced motion — keep the design, drop the movement
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; filter: none; }
  .grain { animation: none; }
  .avatar__ring { animation: none; }
}
