/* Hero: the tape.
 *
 * Not a panel — the background. The chart runs off the right edge of the page
 * and its left half passes under the headline, blurred and faint, so what sits
 * behind the words reads as the shadow of a chart rather than a chart the
 * words have to compete with. Two layers do that: `is-ghost`, blurred and
 * masked to the left, hands off to `is-sharp`, masked to the right. The
 * generator emits identical markup for both, so they play in step.
 *
 * No JavaScript. Every mark is static SVG from scripts/build-hero-chart.mjs
 * and the play is CSS keyframes staggered by a per-mark `--d`. The site has no
 * build step and serves script-src 'self', so a hero that needs a framework is
 * a hero that shows nothing while the framework loads.
 *
 * ---- On colour -----------------------------------------------------------
 *
 * This is the second attempt at the palette, and the first one is the lesson.
 * The tape was first drawn as a dark terminal panel, in a terminal's own
 * colours: pale ink on near-black, labels in low-alpha greys. Lifted out of
 * the panel and onto a white page, every one of those choices inverted from
 * "depth" to "invisible" — a #7d8899 label on #ffffff is 3.1:1, which is not
 * a label, it is a smudge.
 *
 * So nothing here is a terminal colour. Marks come from the site's own tokens
 * re-derived for a light ground, and every piece of TEXT is set at a value
 * that clears 4.5:1 on white — the same bar the rest of the page is held to,
 * because a price nobody can read is worse than no price. The candles and the
 * averages are shapes rather than text and sit a little lighter, but not much:
 * they are the subject.
 */

.hero-tape {
  position: absolute;
  /* Anchored to the right and allowed past it. `.hero` already clips, so the
     bleed costs no horizontal scroll — which is checked in check-launch and
     in the browser pass, because a hero that widens the document is the one
     failure this layout could plausibly introduce. */
  top: 50%;
  right: -4%;
  z-index: 1;
  width: min(1180px, 78%);
  transform: translateY(-50%);
  pointer-events: none;
}

.tape-chart {
  --tape-up: #0e9c78;
  --tape-down: #cf4750;
  --tape-fast: #0071e3;
  --tape-slow: #c2761d;

  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  font-family: var(--font-mono);

  /* landing.css dresses every <svg> on the site as a line icon:
     `fill: none; stroke: currentColor; stroke-width: 1.8`. Stroke is an
     inherited property, so in a chart that is mostly filled shapes and text
     that put a 1.8-unit near-black outline around every glyph, every arrow,
     every volume bar and the live dot. It is what made the labels read as
     heavy and broken — they were not bold, they were outlined in #1d1d1f.
     The tape opts out of the icon defaults once, here; every mark below then
     says what it actually wants, and anything that says nothing gets nothing. */
  fill: none;
  stroke: none;
  stroke-width: 1;
}

/* The two layers occupy the same box; the second is lifted onto the first. */
.tape-chart.is-sharp {
  position: absolute;
  inset: 0;
}

/* Under the headline: blurred, and faint enough that the words never have to
   fight it. The mask does the fading, so the blur can stay constant. */
.tape-chart.is-ghost {
  filter: blur(7px);
  opacity: 0.5;
  mask-image: linear-gradient(
    90deg,
    transparent 0%,
    rgba(0, 0, 0, 0.5) 11%,
    rgba(0, 0, 0, 0.5) 27%,
    transparent 44%
  );
}

/* Clear of the text, the chart is itself. */
.tape-chart.is-sharp {
  mask-image: linear-gradient(90deg, transparent 20%, #000 40%, #000 100%);
}

/* ---- Header and readout ------------------------------------------------ */

/* One grey, one weight, everywhere on the tape.
 *
 * The chart used to set its labels at 600 in a monospace face, colour-coded
 * per level. On a white hero that reads as heavy and broken — mono at 600 has
 * no thin strokes left to give it a shape, and three label colours turn a
 * background into something demanding to be read. It is a background: the
 * headline is what the eye is for.
 *
 * So everything on it is #6e6e73, the site's own --faint, at the normal
 * weight. That token is 5.07:1 on white, which clears the 4.5:1 the rest of
 * the page holds its small text to — subtle is allowed, unreadable is not.
 * Colour survives only on the marks: the candles, the two averages, the
 * arrows, and a callout's border. */
.tape-chart text {
  fill: var(--faint);
  font-weight: 400;
}

.tape-head text,
.tape-legend text {
  font-size: 15px;
  letter-spacing: 0.06em;
}

.tape-pulse {
  fill: var(--tape-up);
  animation: tapeLive 2.4s ease-in-out infinite;
}

.tape-legend line {
  stroke-width: 3;
  stroke-linecap: round;
}

.tape-legend .is-fast {
  stroke: var(--tape-fast);
}

.tape-legend .is-slow {
  stroke: var(--tape-slow);
}

.tape-readout text {
  font-size: 14px;
  letter-spacing: 0.08em;
}

/* Said once, quietly, and never taken off: the tape is drawn from a sample
   session, not from a track record, and the page must not be readable as
   claiming otherwise. */
/* #83898f measured 3.56:1 on white, under the 4.5 this page holds small text
   to — and of everything on the tape this is the line that must be readable:
   it is the disclaimer. It gets the same grey as the rest. */
.tape-readout .is-note {
  fill: var(--faint);
}

/* ---- Chart ------------------------------------------------------------- */

.tape-grid line {
  stroke: rgba(29, 29, 31, 0.055);
  stroke-width: 1;
}

.tape-level line {
  stroke-width: 1.4;
  stroke-dasharray: 6 6;
}

.tape-level text {
  font-size: 15px;
  letter-spacing: 0.04em;
}


.tape-level.is-entry line {
  stroke: rgba(0, 113, 227, 0.55);
  stroke-dasharray: none;
}

.tape-level.is-target line {
  stroke: rgba(14, 156, 120, 0.5);
}

.tape-level.is-stop line {
  stroke: rgba(207, 71, 80, 0.5);
}

.tape-ema {
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tape-ema.is-fast {
  stroke: var(--tape-fast);
}

.tape-ema.is-slow {
  stroke: var(--tape-slow);
  opacity: 0.85;
}

.tape-bar.is-up .tape-wick,
.tape-bar.is-up .tape-body {
  stroke: var(--tape-up);
  fill: var(--tape-up);
}

.tape-bar.is-down .tape-wick,
.tape-bar.is-down .tape-body {
  stroke: var(--tape-down);
  fill: var(--tape-down);
}

.tape-wick {
  stroke-width: 1.6;
}

.tape-body {
  stroke-width: 0.6;
}

.tape-bar.is-up .tape-vol {
  fill: rgba(14, 156, 120, 0.3);
}

.tape-bar.is-down .tape-vol {
  fill: rgba(207, 71, 80, 0.3);
}

.tape-mark.is-buy .tape-arrow {
  fill: var(--tape-up);
}

.tape-mark.is-sell .tape-arrow {
  fill: var(--tape-down);
}

.tape-ping {
  fill: none;
  stroke-width: 1.6;
}

.tape-mark.is-buy .tape-ping {
  stroke: var(--tape-up);
}

.tape-mark.is-sell .tape-ping {
  stroke: var(--tape-down);
}

/* A card is exactly what the hero is not, so the callouts are the one thing
   allowed a surface: they carry text over the chart, and text over candles is
   unreadable without one. */
.tape-callout rect {
  fill: rgba(255, 255, 255, 0.94);
  stroke-width: 1;
}

.tape-callout-title {
  font-size: 15px;
  letter-spacing: 0.03em;
}

.tape-callout-body {
  font-size: 13.5px;
  letter-spacing: 0.02em;
}

.tape-callout.is-buy rect {
  stroke: rgba(14, 156, 120, 0.45);
}

.tape-callout.is-sell rect {
  stroke: rgba(207, 71, 80, 0.45);
}

/* ---- Play --------------------------------------------------------------
   Price sweeps left to right first, and nothing else may appear until it has,
   because every mark after it is a conclusion drawn from it. The generator
   owns the timing: it writes the block below, one `--d` per mark. Everything
   is `both`, so a mark's pre-animation state is its keyframe rather than its
   drawn one and nothing flashes into place before its turn.

   These beats are here, and not on the marks as `style="--d:…"`, because the
   site serves `style-src 'self'`. That blocks a style attribute the same way
   it blocks an inline script, so the inline version was being dropped by every
   browser and the whole tape played at delay 0. Do not hand-edit: run
   `node scripts/build-hero-chart.mjs`. check-launch.js fails the build if this
   block and index.html drift apart. */
/* hero-chart:start (generated by scripts/build-hero-chart.mjs) */
.tape-level.is-target {
  --d: 3.4s;
}
.tape-level.is-entry {
  --d: 3.1s;
}
.tape-level.is-stop {
  --d: 3.3s;
}
.tape-mark.is-buy {
  --d: 2.9s;
}
.tape-mark.is-sell {
  --d: 4s;
}
.tape-callout.is-buy {
  --d: 3.7s;
}
.tape-callout.is-sell {
  --d: 4.3s;
}
.tape-readout {
  --d: 4.5s;
}
/* hero-chart:end */

/* One wipe, not forty-six pops.
 *
 * Candles used to fade in where they stand while only the averages moved left
 * to right, so the chart assembled itself in place beside a line that was
 * drawing. Now a single clip rect widens from nothing across the whole plot,
 * and candles, volume and both averages come out from under the headline
 * together — which is what a chart printing actually looks like, and what the
 * tape running out of the text is supposed to read as.
 *
 * The rect carries width="1240" as an attribute, so if these keyframes never
 * run — an old engine, or prefers-reduced-motion below — the clip is the whole
 * plot and the tape is simply there. */
.tape-sweep-mask {
  animation: tapeSweep var(--sweep, 2.8s) cubic-bezier(0.35, 0, 0.5, 1) both;
}

.tape-level {
  animation: tapeSlide 520ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
  animation-delay: var(--d);
}

.tape-mark {
  animation: tapePop 460ms cubic-bezier(0.2, 1.4, 0.4, 1) both;
  animation-delay: var(--d);
  transform-box: fill-box;
  transform-origin: 50% 50%;
}

.tape-ping {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  /* Begins once its arrow has landed, then keeps going — the only thing still
     moving after the tape has settled. */
  animation: tapePing 2s ease-out infinite;
  animation-delay: calc(var(--d) + 460ms);
}

.tape-callout {
  animation: tapeRise 460ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
  animation-delay: var(--d);
  transform-box: fill-box;
  transform-origin: 50% 100%;
}

.tape-readout {
  animation: tapeFade 600ms ease both;
  animation-delay: var(--d);
}

.tape-head {
  animation: tapeFade 600ms ease both;
}

@keyframes tapeFade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes tapeSweep {
  from { width: 0%; }
  to { width: 100%; }
}

@keyframes tapeSlide {
  from { opacity: 0; transform: translateX(-14px); }
  to { opacity: 1; transform: none; }
}

@keyframes tapeRise {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

@keyframes tapePop {
  from { opacity: 0; transform: scale(0.25); }
  to { opacity: 1; transform: none; }
}

@keyframes tapePing {
  0% { opacity: 0.6; transform: scale(0.5); }
  70%, 100% { opacity: 0; transform: scale(2); }
}

@keyframes tapeLive {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

/* Someone who asked not to be moved gets the settled tape, immediately and in
   full. The chart is the content; the printing of it is the decoration. */
@media (prefers-reduced-motion: reduce) {
  .hero-tape *,
  .hero-tape {
    animation: none !important;
  }

  .tape-ping {
    opacity: 0.28;
  }
}

/* ---- Narrow ------------------------------------------------------------
   The hero stacks, and the tape goes behind the whole of it rather than
   beside it. Below the width where the type would stop being readable, the
   text goes and the shapes stay: candles, averages, levels and markers scale
   without becoming illegible, which is what a background wants anyway. */

/* Stacked layouts: the copy goes full width, so there is no longer a right
   half for the tape to be sharp in — every part of it is behind something.
   It becomes texture: the shapes stay, every piece of type goes, and the whole
   layer drops to an opacity low enough that the body copy over it still clears
   its contrast bar. Nothing readable is shown, so nothing unreadable is
   either, and with the prices gone there is no figure left to disclaim. */

@media (max-width: 980px) {
  .hero-tape {
    top: 50%;
    right: -18%;
    width: 128%;
    /* Measured, not guessed: at 0.22 the darkest ground under .hero-meta came
       out at 4.33:1 against #6e6e73, under the 4.5 the rest of the page holds
       its small text to. This is the value that clears it. */
    opacity: 0.15;
    transform: translateY(-50%);
  }

  .tape-chart.is-ghost {
    mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 0.55) 16%, rgba(0, 0, 0, 0.55) 52%, transparent 74%);
  }

  .tape-chart.is-sharp {
    mask-image: linear-gradient(90deg, transparent 58%, #000 82%, #000 100%);
  }

  .tape-head,
  .tape-legend,
  .tape-level text,
  .tape-callout,
  .tape-readout {
    display: none;
  }
}

@media (max-width: 700px) {
  .hero-tape {
    right: -32%;
    width: 178%;
    opacity: 0.14;
  }
}
