/* Original to MIRA: the hero stage. A phone shows MIRA doing its job live: a
   line from the book lands on a post, the hashtags land, the post is
   scheduled and the feed swipes to the next one. Three cards orbit the phone
   (the source chapter, the week's schedule and the views), with a ring light
   behind it. Tokens only; every text on it respects the 14px floor.

   Without JS (or with reduced motion) the first post renders fully composed:
   hook written, tags in, "Scheduled" toast shown, day marked, views counted. */

.hero-stage {
  --stage-ease: cubic-bezier(.22, 1, .36, 1);
  --phone-w: clamp(250px, 21vw, 312px);
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  height: min(660px, calc(100svh - 180px));
  min-height: 540px;
  perspective: 1400px;
}

/* ---------- Atmosphere: soft light, the ring light and its comet ---------- */

.hero-stage__glow {
  position: absolute;
  inset: -10% -18%;
  z-index: 0;
  background:
    radial-gradient(closest-side, rgba(var(--ui-primary-rgb), .12), transparent 72%),
    radial-gradient(40% 36% at 70% 30%, rgba(var(--ui-medium-rgb), .10), transparent 70%);
  pointer-events: none;
}

.hero-stage__rings {
  position: absolute;
  inset: 50% auto auto 50%;
  z-index: 0;
  width: min(100%, calc(var(--phone-w) * 2.1));
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  overflow: visible; /* the ring light's bloom spills past the outer ring */
  pointer-events: none;
}

/* ---------- Ring light ----------
   Where the two orbit rings were (dashed r 36, solid r 49.5), a creator's
   ring light sits behind the phone, drawn in the SVG: dark plastic housing,
   frosted diffuser, LEDs faintly through it, an overexposed core and the
   gloss of the plastic. Around it a soft bloom (.hero-stage__bloom) and a
   faint rim of light on the phone. It switches on once, with the stage's
   entrance. The light is the same in both themes; only the bloom changes
   (warmer on a light page, brighter in the dark). */
.hero-stage {
  --ringlight-bloom: rgba(255, 210, 140, .5);
  --ringlight-rim: rgba(255, 246, 228, .10);
}
:root[data-theme='dark'] .hero-stage {
  --ringlight-bloom: rgba(255, 240, 214, .7);
  --ringlight-rim: rgba(255, 244, 222, .22);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']):not([data-theme='dark']) .hero-stage {
    --ringlight-bloom: rgba(255, 240, 214, .7);
    --ringlight-rim: rgba(255, 244, 222, .22);
  }
}

.hero-ringlight circle { fill: none; }
/* Housing 35.3 → 50; diffuser 35.85 → 49.45, so a hairline lip of housing shows
   on both sides, each with a bevel catching the light. */
.hero-ringlight__housing { stroke: url(#hero-ringlight-housing); stroke-width: 14.7; }
.hero-ringlight__bevel-in,
.hero-ringlight__bevel-out { stroke: rgba(255, 255, 255, .22); stroke-width: .2; }
.hero-ringlight__diffuser { stroke: url(#hero-ringlight-diffuser); stroke-width: 13.6; }
/* the LEDs behind the frosting: a faint, even beading along the middle */
.hero-ringlight__leds { stroke: #FFF6E6; stroke-width: 7.5; stroke-dasharray: .55 1.25; opacity: .5; }
/* the hot core of the light, overexposed like on camera */
.hero-ringlight__hot { stroke: #FFFFFF; stroke-width: 5.5; opacity: .9; }
.hero-ringlight__gloss { stroke: url(#hero-ringlight-gloss); stroke-width: 13.6; mix-blend-mode: soft-light; }

/* Bloom around the tube, outside and inside, as a CSS glow on a circle the
   size of the tube's middle (r 42.75 of 50). The box is 1.4× the rings so
   the glow has room; its left side fades like the tube's (behind the text). */
.hero-stage__bloom {
  position: absolute;
  inset: 50% auto auto 50%;
  z-index: 0;
  width: calc(min(100%, calc(var(--phone-w) * 2.1)) * 1.4);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  -webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, .22) 15.7%, #000 35.7%);
  mask-image: linear-gradient(90deg, rgba(0, 0, 0, .22) 15.7%, #000 35.7%);
  pointer-events: none;
  animation: hero-ringlight-on 1.1s var(--stage-ease) .6s both;
}
.hero-stage__bloom::before {
  content: "";
  position: absolute;
  inset: 19.46%;
  border-radius: 50%;
  box-shadow:
    0 0 60px 26px var(--ringlight-bloom),
    inset 0 0 60px 26px var(--ringlight-bloom);
}

/* Switch-on: dark tube → a quick flash → steady light (after the stage enters). */
.hero-ringlight { animation: hero-ringlight-on 1.1s var(--stage-ease) .6s both; }
@keyframes hero-ringlight-on {
  0% { opacity: .25; filter: brightness(.7); }
  30% { opacity: 1; filter: brightness(1.18); }
  100% { opacity: 1; filter: none; }
}

/* The comet: one dot travelling the outer ring, the only ambient loop. */
.hero-stage__comet {
  transform-origin: 50% 50%;
  transform-box: view-box;
  animation: hero-stage-orbit 22s linear infinite;
}

/* A comet, not a plain dot: an indigo core with a light rim, a soft halo and
   a tail that fades along the ring behind it. */
.hero-stage__tail {
  fill: none;
  stroke: url(#hero-comet-tail);
  stroke-width: 3;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.hero-stage__tail-from { stop-color: var(--ui-account-1); stop-opacity: 0; }
.hero-stage__tail-to { stop-color: var(--ui-account-1); stop-opacity: 1; }

.hero-stage__halo { fill: var(--ui-account-1); opacity: .18; }

.hero-stage__core {
  fill: var(--ui-account-1);
  stroke: var(--ui-surface);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}

@keyframes hero-stage-orbit { to { transform: rotate(360deg); } }

/* hero-stage.js adds .is-paused off screen and in a hidden tab: the CSS
   loops (comet, audio disc, played bar) stop with the JS ones. */
.hero-stage.is-paused,
.hero-stage.is-paused * { animation-play-state: paused; }

/* ---------- Tilt rig (pointer parallax, set by JS) ---------- */

.hero-stage__rig {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  transform-style: preserve-3d;
  transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transition: transform .9s var(--stage-ease);
}

/* ---------- Phone ---------- */

.hero-phone {
  position: relative;
  width: var(--phone-w);
  aspect-ratio: 9 / 19.2;
  padding: 9px;
  border-radius: 46px;
  background: var(--ui-inverse);
  box-shadow:
    0 0 0 1px rgba(var(--ui-text-rgb), .08),
    0 2px 0 1px rgba(var(--ui-on-photo-rgb), .06) inset,
    0 40px 90px -20px rgba(var(--ui-shadow-rgb), .45),
    0 18px 36px -18px rgba(var(--ui-shadow-rgb), .35),
    0 0 70px 4px var(--ringlight-rim); /* the ring light rims it from behind */
  transform: translateZ(40px);
}

/* Side buttons, for the silhouette. */
.hero-phone::before,
.hero-phone::after {
  content: "";
  position: absolute;
  left: -3px;
  width: 3px;
  border-radius: 3px 0 0 3px;
  background: var(--ui-inverse);
}
.hero-phone::before { top: 22%; height: 9%; }
.hero-phone::after { top: 34%; height: 9%; }

.hero-phone__screen {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: 38px;
  background: var(--ui-inverse);
  isolation: isolate;
}

.hero-phone__island {
  position: absolute;
  top: 10px;
  left: 50%;
  z-index: 8; /* over the notification, which slides out from behind it */
  width: 30%;
  height: 24px;
  border-radius: 999px;
  background: var(--ui-inverse);
  transform: translateX(-50%);
}

/* ---------- TikTok layout (the phone screen) ----------
   Content first: the video fills the screen down to the bottom bar and the
   controls float on it, in TikTok's fixed places so the eye knows them:
   top = feed switch, right = one column of actions, bottom-left = who and
   what, a hairline of progress, then the black navigation bar. Controls are
   white over the video; the bottom bar is solid black in both themes. */

.hero-phone__screen {
  --tt-nav-h: 9.5%;
  --tt-rail-x: 10px;
}

/* Top: LIVE · Following / For You · search */
.hero-tt__top {
  position: absolute;
  top: 44px;
  right: 14px;
  left: 14px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--ui-on-photo);
  filter: drop-shadow(0 1px 3px rgba(var(--ui-shadow-rgb), .45));
}

.hero-tt__live {
  width: 30px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.hero-tt__live text {
  fill: currentColor;
  stroke: none;
  font-family: var(--font-body);
  font-size: 7px; /* glyph inside the icon */
  font-weight: 800;
  letter-spacing: .02em;
}

.hero-tt__tabs {
  display: flex;
  gap: 14px;
  font-size: var(--fs-label);
  font-weight: 600;
}

.hero-tt__tabs span { opacity: .62; }
.hero-tt__tabs strong { font-weight: 800; }

.hero-tt__top > svg:last-child {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
}

/* ---------- Posts: the video + everything floating on it ---------- */

.hero-post {
  position: absolute;
  inset: 0 0 var(--tt-nav-h);
  color: var(--ui-on-photo);
  transform: translateY(100%);
  transition:
    transform .9s var(--stage-ease),
    filter .9s var(--stage-ease),
    opacity .9s var(--stage-ease);
}

.hero-post.is-active { transform: translateY(0); }

.hero-post.is-leaving {
  z-index: 0;
  opacity: .35;
  filter: blur(6px);
  transform: translateY(-18%) scale(.92);
}

.hero-post__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}

.hero-post__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.12);
  transition: transform 7s linear;
}

.hero-post.is-active .hero-post__media img { transform: scale(1); }

/* Just enough shade for white controls: top for the tabs, bottom for the
   caption; the middle stays clear. */
.hero-post__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(var(--ui-shadow-rgb), .38) 0%,
    rgba(var(--ui-shadow-rgb), .06) 20%,
    rgba(var(--ui-shadow-rgb), .22) 42%,
    rgba(var(--ui-shadow-rgb), .1) 60%,
    rgba(var(--ui-shadow-rgb), .74) 100%
  );
}

/* The hook: text on the video itself (as BookTok posts do), words settling
   in one after another. Kept clear of the right column. */
.hero-post__hook {
  position: absolute;
  top: 34%;
  right: 70px;
  left: 16px;
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(21px, 1.8vw, 26px);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -.045em;
  text-wrap: balance;
  /* readable on light photos too: a tight shadow for the edges, a wide one
     for the body of the letters */
  text-shadow:
    0 1px 2px rgba(var(--ui-shadow-rgb), .55),
    0 4px 22px rgba(var(--ui-shadow-rgb), .6);
}

.hero-post__hook .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(.3em);
  transition: opacity .55s var(--stage-ease), transform .55s var(--stage-ease);
  transition-delay: calc(var(--i) * 60ms);
}

.hero-post.is-written .hero-post__hook .w {
  opacity: 1;
  transform: none;
}

/* Right: one column on a single axis, from ~48% of the height down. */
.hero-post__rail {
  position: absolute;
  right: var(--tt-rail-x);
  bottom: 14px;
  display: grid;
  gap: 9px;
  justify-items: center;
  width: 46px;
  font-size: var(--fs-label);
  font-weight: 700;
  filter: drop-shadow(0 1px 3px rgba(var(--ui-shadow-rgb), .5));
}

.hero-post__rail > span { display: grid; justify-items: center; gap: 1px; line-height: 1.1; }

.hero-post__rail svg { width: 30px; height: 30px; }

.hero-post__rail svg.is-solid { fill: var(--ui-on-photo); stroke: none; }

/* Counts climb while the video plays (hero-stage.js); tabular figures so
   the column never jitters as they change. */
.hero-post__count { font-variant-numeric: tabular-nums; }
.hero-post__rail svg .dot { fill: var(--brand-tiktok-black); }

/* Avatar with the follow "+" overlapping its lower edge, followers below. */
.hero-post__creator { position: relative; margin-bottom: 8px; }

.hero-post__avatar {
  display: block;
  width: 42px;
  height: 42px;
  border: 2px solid var(--ui-on-photo);
  border-radius: 50%;
  background: var(--account, var(--ui-account-1));
}

/* Profile photo; the account colour shows while it loads. */
.hero-post__avatar img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}

.hero-post__creator b {
  position: absolute;
  bottom: -9px;
  left: 50%;
  display: grid;
  width: 20px;
  height: 20px;
  place-items: center;
  border-radius: 50%;
  background: var(--brand-tiktok-pink);
  color: var(--ui-on-photo);
  font-size: 16px; /* glyph */
  font-weight: 700;
  line-height: 1;
  transform: translateX(-50%);
  transition: background-color .3s ease, scale .5s var(--stage-ease), opacity .3s ease;
}

/* ---------- Reactions: like, save, follow ----------
   A second into each post (hero-stage.js), as if someone watching reacted:
   the heart fills TikTok red and pops, the bookmark fills TikTok yellow and
   pops, and the follow "+" turns into a check on white, then tucks away,
   the way TikTok does after you follow. Reset with each new post. */
.hero-post__rail svg.hero-post__like,
.hero-post__rail svg.hero-post__save { transition: fill .25s ease; }

.hero-post.is-liked .hero-post__like { fill: var(--brand-tiktok-pink); animation: hero-tt-react .5s var(--stage-ease); }
.hero-post.is-saved .hero-post__save { fill: var(--brand-tiktok-yellow); animation: hero-tt-react .5s var(--stage-ease); }

@keyframes hero-tt-react {
  0% { scale: 1; }
  35% { scale: 1.28; }
  100% { scale: 1; }
}

.hero-post__follow > span,
.hero-post__follow svg { grid-area: 1 / 1; transition: opacity .2s ease, rotate .35s var(--stage-ease), scale .35s var(--stage-ease); }

.hero-post__rail .hero-post__follow svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: var(--brand-tiktok-pink);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  rotate: -90deg;
  scale: .4;
}

.hero-post.is-followed .hero-post__follow { background: var(--ui-on-photo); }
.hero-post.is-followed .hero-post__follow > span { opacity: 0; rotate: 90deg; scale: .4; }
.hero-post.is-followed .hero-post__follow svg { opacity: 1; rotate: 0deg; scale: 1; }
.hero-post.is-follow-done .hero-post__follow { scale: 0; opacity: 0; }

/* The sound disc: black record, white rim, a white note in the middle,
   turning slowly. */
.hero-post__disc {
  width: 38px;
  height: 38px;
  place-items: center;
  margin-top: 2px;
  border: 7px solid var(--brand-tiktok-black);
  border-radius: 50%;
  background: var(--brand-tiktok-black);
  box-shadow: 0 0 0 1.5px rgba(var(--ui-on-photo-rgb), .85);
  animation: hero-tt-disc 5s linear infinite;
}

.hero-post__disc svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: var(--ui-on-photo);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@keyframes hero-tt-disc { to { transform: rotate(360deg); } }

/* Bottom-left: @user (strongest) · caption with hashtags · sound. */
.hero-post__info {
  position: absolute;
  right: 66px;
  bottom: 16px;
  left: 16px;
  display: grid;
  gap: 5px;
  font-size: var(--fs-label);
  line-height: 1.3;
  text-shadow: 0 1px 3px rgba(var(--ui-shadow-rgb), .5);
}

.hero-post__info p { margin: 0; }

.hero-post__user { font-weight: 800; }

.hero-post__caption {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  font-weight: 500;
}

.hero-post__tags { font-weight: 700; }

.hero-post__tags span {
  display: inline-block;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .45s var(--stage-ease), transform .45s var(--stage-ease);
  transition-delay: calc(var(--i) * 90ms);
}

.hero-post.is-tagged .hero-post__tags span { opacity: 1; transform: none; }

.hero-post__sound {
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
  font-weight: 500;
  white-space: nowrap;
}

.hero-post__sound svg {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.hero-post__sound span { overflow: hidden; text-overflow: ellipsis; }

/* Hairline of video progress right on top of the bar; the dot runs once per
   post. What has played is a little thicker and brighter (4px) than what is
   left (2px), as on TikTok (2026-09-23, on request); it grows with the dot. */
.hero-tt__scrub {
  position: absolute;
  right: 0;
  bottom: var(--tt-nav-h);
  left: 0;
  z-index: 6; /* over the bar, so the dot shows whole */
  height: 2px;
  background: rgba(var(--ui-on-photo-rgb), .28);
}

.hero-tt__scrub::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 4px;
  border-radius: 0 2px 2px 0;
  background: rgba(var(--ui-on-photo-rgb), .9);
  /* Grows with scaleX from the left edge (compositor only), not width. */
  transform: translateY(-50%) scaleX(.4);
  transform-origin: left center;
}

.hero-tt__scrub.is-running::before { animation: hero-tt-played var(--post-ms, 6000ms) linear forwards; }

@keyframes hero-tt-played {
  from { transform: translateY(-50%) scaleX(.02); }
  to { transform: translateY(-50%) scaleX(.98); }
}

.hero-tt__scrub i {
  position: absolute;
  top: 50%;
  left: 40%;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ui-on-photo);
  transform: translate(-50%, -50%);
}

.hero-tt__scrub.is-running i { animation: hero-tt-scrub var(--post-ms, 6000ms) linear forwards; }

@keyframes hero-tt-scrub { from { left: 2%; } to { left: 98%; } }

/* Bottom bar: solid black, five evenly spaced icons, create in the middle. */
.hero-tt__nav {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  align-items: center;
  justify-items: center;
  height: var(--tt-nav-h);
  padding-bottom: 6px;
  background: var(--brand-tiktok-black);
  color: var(--ui-on-photo);
}

.hero-tt__nav svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .78;
}

/* Home, the active tab: solid and a size up. */
.hero-tt__nav .is-active svg { width: 28px; height: 28px; fill: currentColor; stroke: none; opacity: 1; }

/* Create: white key with TikTok's cyan and pink edges, black plus. */
.hero-tt__create i {
  display: grid;
  width: 40px;
  height: 26px;
  place-items: center;
  border-radius: 8px;
  background: var(--ui-on-photo);
  box-shadow: -3px 0 0 var(--brand-tiktok-cyan), 3px 0 0 var(--brand-tiktok-pink);
  color: var(--brand-tiktok-black);
  font-size: 20px; /* glyph */
  font-style: normal;
  font-weight: 800;
  line-height: 1;
}

/* ---------- Notification: the post just got scheduled ----------
   Built like a real phone notification (2026-09-23, on request): it slides
   down from the top of the screen, from behind the island, over everything
   (tabs and video), and goes back up the same way. Frosted material that
   follows the theme, the MIRA app icon, a bold title with "now" on the
   right, and one plain sentence. */

.hero-phone__toast {
  position: absolute;
  top: 40px;
  right: 8px;
  left: 8px;
  z-index: 7;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 20px;
  background: rgba(var(--ui-surface-rgb), .9);
  color: var(--ui-text);
  box-shadow: 0 10px 30px -8px rgba(var(--ui-shadow-rgb), .45);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  transform: translateY(calc(-100% - 48px));
  transition: transform .5s cubic-bezier(.55, 0, .75, .2); /* leaving: back up */
}

.hero-phone.is-scheduled .hero-phone__toast {
  transform: none;
  transition: transform .65s var(--stage-ease); /* arriving: fast, soft landing */
}

/* The app icon: MIRA's "M" in white on the brand's dark square, the same
   in both themes, as an app icon is. */
.hero-phone__toast-app {
  display: grid;
  flex-shrink: 0;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--ui-inverse);
  color: var(--ui-inverse-text);
}

.hero-phone__toast-app .mira-mark {
  width: 22px;
  height: 14px;
  aspect-ratio: auto;
  -webkit-mask-size: auto 100%;
  mask-size: auto 100%;
  -webkit-mask-position: left center;
  mask-position: left center;
}

.hero-phone__toast-text {
  display: grid;
  flex: 1;
  min-width: 0;
  gap: 1px;
  font-size: var(--fs-label);
  line-height: 1.3;
}

.hero-phone__toast-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.hero-phone__toast-head strong { font-weight: 700; }
.hero-phone__toast-head span { color: var(--ui-muted); font-weight: 500; }

/* ---------- Orbit cards ---------- */

.hero-card {
  position: absolute;
  z-index: 2;
  padding: 14px 16px;
  border: 1px solid var(--ui-soft);
  border-radius: 18px;
  background: rgba(var(--ui-surface-rgb), .9);
  color: var(--ui-text);
  box-shadow: 0 24px 50px -22px rgba(var(--ui-shadow-rgb), .35);
  backdrop-filter: blur(14px) saturate(140%);
  font-size: var(--fs-label);
  text-align: left;
  transform: translateZ(90px);
}

.hero-card__label {
  display: block;
  margin-bottom: 8px;
  color: var(--ui-muted);
  font-weight: 700;
}

/* Source: which book and chapter the line came from. */
/* Ends at the phone's edge: it used to reach 9px into the screen and sit on
   the "Scheduled" toast. */
.hero-card--source {
  top: 8%;
  right: calc(50% + var(--phone-w) * .5 - 4px);
  display: flex;
  align-items: center;
  gap: 12px;
  width: max-content;
  max-width: 250px;
}

/* A real book cover (2026-09-23, on request; it used to be a hardback
   drawn in CSS): the post's cover image, with the page block showing on the
   fore-edge and a shadow under it. hero-stage.js swaps it per post with a
   cross-fade. The title on it is part of the image. */
.hero-card__cover {
  position: relative;
  flex-shrink: 0;
  width: 44px;
  height: 66px;
  border-radius: 2px 4px 4px 2px;
  background: var(--ui-gray-100);
  box-shadow:
    2px 1px 0 -1px var(--ui-surface),
    3px 2px 0 -1px var(--ui-soft),
    0 10px 18px -8px rgba(var(--ui-shadow-rgb), .55);
}

.hero-card__cover img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
  transition: opacity .45s ease;
}

.hero-card__cover img.is-out { opacity: 0; }

/* One line at a time (hero-stage.js): the old one leaves up, then the new
   one enters from below. Never both visible, so they can't overlap. */
.hero-card__swap { display: grid; }
.hero-card__swap > * { grid-area: 1 / 1; transition: opacity .22s ease, transform .3s var(--stage-ease); }
.hero-card__swap > .is-out { opacity: 0; transform: translateY(-6px); }
.hero-card__swap > .is-in { opacity: 0; transform: translateY(6px); }

.hero-card--source strong { display: block; font-size: var(--fs-small); line-height: 1.2; }
/* Title and chapter always on one line: a wrapped line pushed the card taller
   mid-swap and made the lines collide. */
.hero-card--source strong,
.hero-card--source .hero-card__swap span { white-space: nowrap; }
.hero-card--source span { color: var(--ui-muted); }

/* Week and views sit OUTSIDE the phone, on its right, overlapping only the
   bezel: the TikTok screen keeps its right column (avatar, like, comments,
   save, share, disc) and its bottom bar fully visible. Hence compact cards. */
.hero-card--week,
.hero-card--views { box-sizing: border-box; }

/* -3px: resting on the 9px bezel, never onto the screen. */
.hero-card--week {
  top: 28%;
  left: calc(50% + var(--phone-w) * .5 - 3px);
  width: 184px;
  /* shallow depth: a deep translateZ enlarged it past the viewport edge */
  transform: translateZ(10px);
}

.hero-card--week .hero-card__time { flex-direction: column; gap: 2px; }

.hero-week {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  text-align: center;
}

.hero-week span {
  display: grid;
  justify-items: center;
  gap: 6px;
  padding: 6px 0 8px;
  border-radius: 10px;
  color: var(--ui-muted);
  font-weight: 700;
  transition: background-color .4s ease, color .4s ease;
}

.hero-week span::after {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ui-soft);
  transition: background-color .4s ease, transform .5s var(--stage-ease);
}

.hero-week span.is-set::after { background: var(--account, var(--ui-success)); }
.hero-week span.is-now { background: var(--ui-gray-50); color: var(--ui-text); }
.hero-week span.is-now::after { transform: scale(1.5); }

.hero-card__time {
  display: flex;
  gap: 12px;
  white-space: nowrap;
  justify-content: space-between;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid rgba(var(--ui-text-rgb), .08);
  color: var(--ui-muted);
}
.hero-card__time strong { color: var(--ui-text); }

/* Views: counting up as posts go out. */
.hero-card--views {
  bottom: 8%;
  left: calc(50% + var(--phone-w) * .5 - 3px);
  width: 184px;
  transform: translateZ(10px);
}

.hero-card--views .hero-card__number { flex-wrap: wrap; row-gap: 4px; font-size: 28px; }

.hero-card__number {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
}

.hero-card__delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(var(--ui-success-rgb), .1);
  color: var(--ui-success);
  font-family: var(--font-body);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0;
}

.hero-card__delta svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- Entrance (once, after load) ---------- */

.hero-stage.is-armed .hero-phone,
.hero-stage.is-armed .hero-card {
  opacity: 0;
  filter: blur(10px);
}

.hero-stage.is-armed .hero-phone { transform: translateZ(40px) translateY(60px) rotateX(18deg); }
.hero-stage.is-armed .hero-card { transform: translateZ(90px) translateY(30px) scale(.94); }

.hero-stage.is-in .hero-phone,
.hero-stage.is-in .hero-card {
  opacity: 1;
  filter: none;
  transition:
    opacity 1s var(--stage-ease),
    filter 1s var(--stage-ease),
    transform 1.3s var(--stage-ease);
}

.hero-stage.is-in .hero-card { transition-delay: calc(.35s + var(--i, 0) * .14s); }

/* ---------- Responsive ---------- */

/* Below 1280px there is no room beside the phone: the week card goes (as on
   phones) and the views card drops under the phone, over the bezel only. */
@media (max-width: 1279.98px) {
  /* shorter right column so it starts below the "Scheduled" toast */
  .hero-post__rail { gap: 6px; }
  .hero-post__rail svg { width: 26px; height: 26px; }
  /* narrower phone: smaller LIVE and search so "Following" keeps its air */
  .hero-tt__tabs { gap: 10px; }
  .hero-tt__live { width: 24px; height: 20px; }
  .hero-tt__top > svg:last-child { width: 20px; height: 20px; }
  .hero-post__avatar { width: 36px; height: 36px; }
  .hero-post__disc { width: 34px; height: 34px; border-width: 6px; }
  .hero-post__hook { top: 36%; }

  .hero-card--week { display: none; }
  .hero-card--source { max-width: 260px; }
  .hero-card--views {
    /* the phone sits 40px forward in 3D, so it reads ~3% taller: +10px clears it */
    top: calc(50% + var(--phone-w) * 19.2 / 18 + 10px);
    bottom: auto;
    left: calc(50% + var(--phone-w) * .5 - 170px);
    width: 196px;
  }
}

@media (max-width: 1023.98px) {
  .hero-stage {
    --phone-w: 270px;
    height: 600px;
    min-height: 0;
    margin-top: 56px;
  }

  /* Stacked, the copy is above the phone, not beside it: the ring light's
     left-side fade (tube mask and bloom mask, there to keep it behind the
     headline) would only leave its left half grey. Full ring, both sides. */
  .hero-ringlight { mask: none; }
  .hero-stage__bloom {
    -webkit-mask-image: none;
    mask-image: none;
  }
}

@media (max-width: 767.98px) {
  /* The phone fills almost the width here, so nothing may sit on it: the
     source card goes above the phone on one line (it used to wrap and land on
     the "Scheduled" toast). */
  .hero-stage {
    --phone-w: 228px;
    height: 520px;
    margin: 48px -24px 0; /* was 128px: room for the source card, now hidden */
  }

  .hero-card { padding: 12px 14px; border-radius: 16px; }
  .hero-card--source { top: -88px; right: auto; left: 20px; max-width: calc(100% - 40px); }
  /* Phone: only the phone and its rings (2026-09-23, on request). The orbit
     cards were cut off or sat on the screen, and the notification on the
     phone already tells the story. */
  .hero-card--source,
  .hero-card--views { display: none; }
  .hero-card--source strong,
  .hero-card--source span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .hero-card__cover { width: 32px; height: 48px; }
  .hero-card--week { display: none; }
  .hero-card--views { left: auto; right: 16px; width: 184px; }
  .hero-card__number { font-size: 24px; }
  .hero-post__hook { top: 43%; right: 56px; font-size: 18px; }
  .hero-post__rail { gap: 5px; width: 40px; }
  .hero-post__rail svg { width: 23px; height: 23px; }
  .hero-post__avatar { width: 32px; height: 32px; }
  .hero-post__creator { margin-bottom: 6px; }
  .hero-post__creator b { width: 17px; height: 17px; bottom: -7px; font-size: 14px; }
  .hero-post__disc { width: 30px; height: 30px; border-width: 5px; }
  .hero-post__info { right: 56px; }
  .hero-tt__tabs { gap: 10px; }
  .hero-tt__top { right: 12px; left: 12px; }
  .hero-tt__live { width: 24px; height: 20px; }
  .hero-tt__top > svg:last-child { width: 20px; height: 20px; }
}

/* ---------- Reduced motion: the composed first post, still ---------- */

@media (prefers-reduced-motion: reduce) {
  .hero-stage__comet { animation: none; }
  .hero-post__disc { animation: none; }
  .hero-tt__scrub.is-running i,
  .hero-tt__scrub.is-running::before { animation: none; }
  .hero-stage__rig { transition: none; }
  .hero-post__media img { transition: none; transform: none; }
}
