/*
 * getfeder.com teaser page: its visuals and their motion.
 *
 * One motion language (easing tokens in site.css): lines draw left to right
 * or top to bottom, text and panels rise into place, a status resolves with a
 * short crossfade. Each visual plays once, the first time it scrolls into
 * view (site.js adds .is-in to [data-reveal]), then rests in its final state.
 *
 * The hero panel tells the product's story in about four seconds: fourteen
 * days of readings arrive, the daily check runs, three events resolve, then
 * the last reading on example.com falls to zero and the check reports it.
 * Nothing loops, pulses or bounces, nothing scans, and nothing runs off screen
 * (brand deck section 8); every sequence ends inside five seconds (WCAG 2.2.2).
 * Transform, opacity and a clip for lines (a wipe stays exact when a
 * sparkline is stretched; a stroke-dash draw does not).
 *
 * Every "before" state is scoped to html.motion, which the page head adds
 * only when the visitor allows motion. Without it (no JavaScript, reduced
 * motion, or site.js failing to start) every visual is drawn in its final
 * state, so nobody misses the point of a section.
 *
 * Colour: emerald appears only in the mark (the #notify plate). Data lines
 * are neutral; amber appears only with the warning status it belongs to.
 */

html.motion { scroll-behavior: smooth; }

@keyframes wipe { to { clip-path: inset(0 0 0 0); } }
@keyframes appear { from { opacity: 0; } }
@keyframes fade-in { to { opacity: 1; } }
@keyframes fade-out { to { opacity: 0; } }
@keyframes settle { to { opacity: 1; transform: none; } }
@keyframes sweep { to { transform: rotate(1turn); } }
@keyframes draw-x { from { transform: scaleX(0); } to { transform: none; } }
@keyframes ping {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--fd-warning) 35%, transparent); }
  to { box-shadow: 0 0 0 10px color-mix(in srgb, var(--fd-warning) 0%, transparent); }
}
@keyframes arrive {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--fd-text) 40%, transparent); }
  to { box-shadow: 0 0 0 12px color-mix(in srgb, var(--fd-text) 0%, transparent); }
}

/* ---------- Shared: text that rises into place, staggered by --i ---------- */
.motion .rise > * { opacity: 0; transform: translateY(14px); }
.motion .rise.is-in > * {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease-settle), transform .9s var(--ease-settle);
  transition-delay: calc(var(--i, 0) * 80ms);
}
/* A keyboard visitor who tabs into a block before it reveals sees it at once. */
.motion .rise:focus-within > * { opacity: 1; transform: none; }

/* A status that starts as "Checking status" and resolves in place. The
   pending label is aria-hidden; the final one is what assistive tech reads. */
.resolve { display: inline-grid; justify-items: start; }
.resolve > * { grid-area: 1 / 1; }
.resolve > .pending { opacity: 0; color: var(--fd-inactive); }
.motion .resolve > .pending { opacity: 1; }
.motion .resolve > .status:not(.pending) { opacity: 0; }

/* ---------- Hero ---------- */

/* Depth: the panel sits on the brand's construction grid, in the empty space
   around it (brand deck section 2: a faint grid in empty space, never a net
   behind the data), and gives off a little cool light. The grid lines up with
   the panel's left and top edges: the panel starts 12px past the middle of
   the hero and 136px down (96px hero padding plus its 40px margin), so the
   grid starts exactly two 48px cells before both. Two-column hero only. */
.hero { position: relative; isolation: isolate; }
@media (min-width: 1200px) {
  .hero::before {
    content: ""; position: absolute; z-index: -1; pointer-events: none;
    top: 40px; bottom: 0; left: calc(50% + 12px - 96px); right: 0;
    background-image:
      linear-gradient(var(--fd-rule) 1px, transparent 1px),
      linear-gradient(90deg, var(--fd-rule) 1px, transparent 1px);
    background-size: 48px 48px;
    opacity: .45;
    -webkit-mask-image: radial-gradient(ellipse 62% 58% at 58% 44%, #000 30%, transparent 100%);
    mask-image: radial-gradient(ellipse 62% 58% at 58% 44%, #000 30%, transparent 100%);
  }
  .motion .hero::before { animation: appear 1.8s var(--ease-settle) .5s both; }
}
.hero-panel {
  box-shadow: inset 0 1px 0 rgba(248, 250, 252, .05), 0 40px 100px -36px rgba(148, 163, 184, .2);
}
.hero-panel .panel-head h2 { font: 600 16px/24px var(--feder-font-sans); margin: 0; }
.panel-foot {
  padding: 12px 24px; border-top: 1px solid var(--fd-rule);
  font: var(--feder-meta); color: var(--fd-text-secondary);
}
.watch .trend svg { overflow: visible; }
.spark-line { fill: none; stroke: var(--fd-text-secondary); stroke-width: 1.5; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.spark-line.paused { stroke: var(--fd-rule); stroke-dasharray: 3 3; }
.spark-drop { fill: none; stroke: var(--fd-warning); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
/* The latest daily reading: a zero-length path with a round cap is a dot that
   stays round however far the sparkline stretches. */
.spark-end { fill: none; stroke: var(--fd-text); stroke-width: 4; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.spark-end.warn { stroke: var(--fd-warning); }

/* Hero copy leads; the panel follows 300ms behind it. The hero-note rule
   draws in as the note arrives. */
.motion .rise .hero-note::before { transform: scaleX(0); }
.motion .rise.is-in .hero-note::before { transform: none; transition: transform 1.1s var(--ease-draw) .55s; }

.motion .live { opacity: 0; transform: translateY(16px); }
.motion .live.is-in { animation: settle .9s var(--ease-settle) .3s forwards; }

/* The story, timed from the moment the panel is revealed:
   .3s   the panel settles; its rows arrive one by one
   .7s   each row's last 14 days draw in (--read), clocks turn on "Checking status"
   1.95s three rows resolve: Healthy, Healthy, Inactive (--check)
   2.55s example.com's last reading falls to zero
   3.05s the check reports it: "Tracking issue detected", one soft ring
   3.5s  the footer states when the check ran */
.live tbody tr { --read: calc(.7s + var(--i) * .12s); --check: calc(1.8s + var(--i) * .15s); }
.live tbody tr:first-child { --check: 3.05s; }

.motion .live .spark-line:not(.paused), .motion .live .spark-drop { clip-path: inset(0 100% 0 0); }
.motion .live .spark-line.paused, .motion .live .spark-end { opacity: 0; }
.motion .live .panel-foot { opacity: 0; }

.motion .live.is-in tbody tr { animation: appear .6s var(--ease-settle) calc(.4s + var(--i) * .08s) both; }
.motion .live.is-in .spark-line:not(.paused) { animation: wipe 1s var(--ease-draw) var(--read) forwards; }
.motion .live.is-in .spark-line.paused { animation: fade-in .6s var(--ease-settle) var(--read) forwards; }
.motion .live.is-in .spark-end { animation: fade-in .25s var(--ease-settle) calc(var(--read) + .9s) forwards; }
.motion .live.is-in .spark-drop { animation: wipe .4s var(--ease-fall) 2.55s forwards; }
.motion .live.is-in .spark-end.warn { animation-delay: 2.92s; }

/* "Checking status": the clock hand turns until the check resolves. */
.live .pending path { transform-box: view-box; transform-origin: center; }
.motion .live.is-in .pending path { animation: sweep 1.2s linear var(--read) 2; }
.motion .live.is-in .resolve > .pending { animation: fade-out .2s var(--ease-settle) var(--check) forwards; }
.motion .live.is-in .resolve > .status:not(.pending) { animation: fade-in .2s var(--ease-settle) var(--check) forwards; }
.motion .live.is-in .resolve > .status.warning:not(.pending) {
  animation: fade-in .2s var(--ease-settle) var(--check) forwards, ping 1.2s var(--ease-settle) calc(var(--check) + .05s) backwards;
}
.motion .live.is-in .panel-foot { animation: fade-in .6s var(--ease-settle) 3.5s forwards; }

/* The two-column hero needs the full 1200 px grid; below that it stacks
   (tablet is 768 to 1199 px in the brand grid). */
@media (min-width: 768px) and (max-width: 1199px) {
  .hero { grid-template-columns: 1fr; }
  .hero-copy, .hero-panel { grid-column: 1 / -1; }
  .hero-panel { margin-top: 64px; max-width: 640px; }
}

/* ---------- Get notified: the mark assembles on its construction grid ---------- */
.notify { position: relative; border-top: 0; }
.notify:focus { outline: none; }
/* The band's top rule is its own box so it can draw in (a border, not a
   background, so it survives forced-colors mode). */
.notify::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 0;
  border-top: 1px solid var(--fd-rule); transform-origin: left;
}
.notify-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 48px; align-items: center; }
.plate { margin: 0; display: flex; justify-content: center; }
.plate svg { display: block; width: 192px; height: auto; overflow: visible; }
.plate-grid path { fill: none; stroke: var(--fd-rule); stroke-width: 1; vector-effect: non-scaling-stroke; }
.plate-mark path { fill: var(--fd-mark); }
.notify-fine { margin-top: 24px; font-size: 14px; line-height: 20px; color: var(--fd-text-secondary); }

.notify-form { max-width: 420px; }
.notify-field { display: flex; flex-wrap: wrap; gap: 12px; }
.notify-field input[type="email"] {
  flex: 1 1 220px; min-width: 0; min-height: var(--feder-target-min);
  padding: 10px 16px; border-radius: var(--feder-radius-control);
  border: 1px solid var(--fd-outline); background: var(--fd-surface); color: var(--fd-text);
  font: var(--feder-control);
}
.notify-field input[type="email"]::placeholder { color: var(--fd-text-secondary); }
.notify-field input[type="email"]:focus { outline: 2px solid var(--fd-focus); outline-offset: 2px; }
.notify-field .btn:disabled { opacity: .6; cursor: not-allowed; }
.notify-status { margin-top: 12px; font-size: 14px; line-height: 20px; min-height: 20px; }
.notify-status[data-state="error"] { color: var(--fd-critical); }
.notify-status[data-state="success"] { color: var(--fd-healthy); }

.motion .notify::before { transform: scaleX(0); }
.motion .notify.is-in::before { transform: none; animation: draw-x 1.4s var(--ease-draw) both; }

/* The grid is drawn first, verticals down and horizontals across, then the
   spine grows and the barbs settle onto it in pairs. */
.motion .plate-grid path:first-child { clip-path: inset(0 0 100% 0); }
.motion .plate-grid path:last-child { clip-path: inset(0 100% 0 0); }
.motion .plate .spine { transform: scaleY(0); transform-box: fill-box; transform-origin: center; }
.motion .plate .barb { opacity: 0; }
.motion .plate .barb.l { transform: translate(5px, 3.5px); }
.motion .plate .barb.r { transform: translate(-5px, 3.5px); }
.motion .plate.is-in .plate-grid path { animation: wipe .9s var(--ease-draw) forwards; }
.motion .plate.is-in .plate-grid path:last-child { animation-delay: .15s; }
.motion .plate.is-in .spine { transition: transform .7s var(--ease-settle) .5s; transform: none; }
.motion .plate.is-in .barb {
  opacity: 1; transform: none;
  transition: opacity .5s var(--ease-settle), transform .6s var(--ease-settle);
  transition-delay: calc(.95s + var(--i) * 90ms);
}

/* Arriving from a "Get notified" link: once the band has risen into place,
   one soft ring marks the action the visitor came for. */
.motion .notify:target .btn { animation: arrive 1.2s var(--ease-settle) 1.1s backwards; }

/* ---------- Tablet and mobile ---------- */
@media (max-width: 1099px) {
  .notify-grid { grid-template-columns: 1fr; gap: 32px; }
  .plate { justify-content: flex-start; }
}

@media (max-width: 767px) {
  .panel-foot { padding: 12px 16px; }
  .plate svg { width: 144px; }
}
