/* ---------------------------------------------------------------------------
   landing.css -- FSTrading deck-style marketing page (/) + auth pages.

   Loaded AFTER /style.css, which stays the single source of truth for the
   palette. Every colour resolves through the dashboard's own :root vars, so
   the marketing site and the logged-in app can't drift apart. The only
   literals are rgba() washes/shadows and --lp-ink, the dark text used on
   accent-filled surfaces.

   LAYOUT MODEL -- see landing.js. The narrative is one pinned viewport with
   every panel absolutely stacked inside it. Panels cross-fade; nothing
   scrolls down the page. JS scrubs --d/--o/--blur per panel every frame.

   Because panels are absolutely positioned, the un-pin fallbacks at the
   bottom of this file are load-bearing: without them a reader on a phone,
   with reduced motion, or with JS disabled would see panel one and nothing
   else at all.
   --------------------------------------------------------------------------- */

:root {
  --lp-max: 1180px;
  --lp-radius: 14px;
  --lp-ink: #06121f;
  --lp-accent-soft: rgba(77, 163, 255, .12);
  --lp-accent-line: rgba(77, 163, 255, .28);
  --lp-green-soft: rgba(63, 185, 80, .13);
  --lp-red-soft: rgba(248, 81, 73, .12);
  --lp-ease: cubic-bezier(.22, .61, .36, 1);
  --lp-panel-ease: cubic-bezier(.33, 0, .2, 1);
  /* Arrival decelerates hard, like something closing distance then stopping. */
  --lp-fly: cubic-bezier(.16, .84, .28, 1);
  /* Departure accelerates away -- no ease-out, or it looks like it stalls. */
  --lp-fly-out: cubic-bezier(.6, 0, .9, .35);
}

body.lp { font-size: 16px; line-height: 1.6; -webkit-font-smoothing: antialiased; overflow-x: hidden; }

.lp-wrap { max-width: var(--lp-max); margin: 0 auto; padding: 0 28px; position: relative; }
.lp-narrow { max-width: 820px; }

/* Hide the native scrollbar while the animated deck is actually running
   (2026-08-16): every panel transition -- wheel, touch, or the
   scroll-settle snap -- drives window.scrollTo() programmatically many
   times a frame, which makes most browsers' auto-hide scrollbar flash in
   and out on every single transition. .lp-progress is this page's own
   scroll indicator already, so the native one is redundant here, not
   load-bearing. Scoped to js + no-preference only: under reduced-motion
   or no-JS the deck fully un-pins into an ordinary long page with real
   user-driven scrolling, where the native scrollbar is genuinely useful
   and there's no programmatic-scroll flashing to hide in the first place. */
@media (prefers-reduced-motion: no-preference) {
  html.js body.lp,
  html.js body.lp .panel-inner {
    scrollbar-width: none;       /* Firefox */
    -ms-overflow-style: none;    /* legacy Edge */
  }
  html.js body.lp::-webkit-scrollbar,
  html.js body.lp .panel-inner::-webkit-scrollbar {
    display: none;               /* Chrome/Safari/Edge */
  }
}

/* =========================================================================
   Header + progress
   ========================================================================= */

body.lp header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 60; padding: 14px 0;
  border-bottom: 1px solid transparent;
  transition: background .3s var(--lp-ease), border-color .3s var(--lp-ease), padding .3s var(--lp-ease);
}

body.lp header.is-stuck {
  background: rgba(11, 15, 20, .82);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom-color: var(--panel-border); padding: 9px 0;
}

/* style.css's bare `main` selector is a CSS grid with auto-fit columns --
   correct for the dashboard app (app.html's widget panels), wrong for
   these marketing pages, where every direct child is a full-width
   .lp-section meant to stack vertically. Harmless on pages with a single
   section (pricing.html, features.html -- one grid item just fills the
   row) but breaks as soon as a page has more than one, since auto-fit
   places multiple 420px+ sections side by side instead of stacking them.
   Overriding to block here, scoped to body.lp so the dashboard app is
   untouched. */
body.lp main { display: block; }

/* Full width, not the usual `max-width: var(--lp-max); margin: 0 auto`
   content-column treatment every other `.lp-wrap`/`.panel-inner` gets --
   the header logo and nav are meant to sit at the actual screen edges
   (top-left / top-right), not clustered together in the middle of a
   1180px column on wide viewports. */
body.lp .header-top { padding: 0 28px; }
body.lp header .header-logo { height: 36px; transition: height .3s var(--lp-ease); }
body.lp header.is-stuck .header-logo { height: 30px; }

.lp-progress {
  position: absolute; left: 0; bottom: -1px; height: 2px; width: 100%;
  background: var(--accent); transform: scaleX(0); transform-origin: 0 50%;
}

.lp-nav { display: flex; align-items: center; gap: 14px; }
.lp-nav a.header-link { font-size: 15px; position: relative; padding-bottom: 2px; }

.lp-nav a.header-link::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .25s var(--lp-ease);
}

.lp-nav a.header-link:hover::after { transform: scaleX(1); }

/* Slightly tighter than .lp-btn-sm's own default (8px 17px) -- makes room
   in the header row for the hamburger button below at every width, not
   just the narrow-viewport override further down this file which tightens
   it again on top of this. Scoped to .lp-nav so login.html's lone "Sign
   up" button (same class, no hamburger next to it) gets the same sizing
   for visual consistency between pages rather than by necessity. */
.lp-nav .lp-btn-sm { padding: 8px 14px; }

/* =========================================================================
   Header hamburger menu (2026-08-16) -- FAQ/Features/Pricing plus a
   duplicate Log in/Sign up, mirroring the dashboard's own bot-switcher
   hamburger (app.html + bot_nav.js + style.css's .bot-nav* rules) but
   styled with this page's own tokens rather than sharing that stylesheet.
   ========================================================================= */

.lp-menu { position: relative; }

.lp-menu-btn {
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid var(--panel-border); border-radius: 8px;
  color: var(--text); font-size: 15px; cursor: pointer; flex-shrink: 0;
  transition: border-color .2s var(--lp-ease), color .2s var(--lp-ease);
}

.lp-menu-btn:hover, .lp-menu-btn:focus, .lp-menu-btn[aria-expanded="true"] {
  border-color: var(--accent); color: var(--accent); outline: none;
}

.lp-menu-dropdown {
  display: none; position: absolute; top: calc(100% + 8px); right: 0; min-width: 180px;
  background: var(--panel); border: 1px solid var(--panel-border); border-radius: var(--lp-radius);
  box-shadow: 0 20px 44px -20px rgba(0, 0, 0, .7); padding: 6px; z-index: 1;
}

.lp-menu-dropdown.open { display: block; }

.lp-menu-dropdown a {
  display: block; padding: 9px 11px; border-radius: 7px;
  font-size: 14px; color: var(--text); text-decoration: none; white-space: nowrap;
}

.lp-menu-dropdown a:hover, .lp-menu-dropdown a:focus { background: var(--bg); color: var(--accent); }

.lp-menu-divider { height: 1px; background: var(--panel-border); margin: 6px 4px; }

/* NOTE: this class is .fs-panel, not .panel. The dashboard's own stylesheet
   (style.css, loaded first) defines .panel as its card component --
   `background: var(--panel)` plus `.panel h2 { font-size: 13px; uppercase }`.
   Reusing that name gave all 18 full-viewport panels an opaque background
   (hiding the backdrop and making panels occlude each other) and rendered
   every headline at 13px uppercase grey, because `.panel h2` at (0,1,1)
   outranks `.p-title` at (0,1,0).
   ========================================================================= */

/* =========================================================================
   THE DECK
   ========================================================================= */

/* --vh (set by landing.js) tracks the real viewport height instead of the
   unstable 100vh unit -- see landing.js's setVh() for why (iOS Safari's
   collapsing address bar). Falls back to 1vh (i.e. plain 100vh behavior)
   if JS hasn't run yet, so there's no flash of zero-height content. */
.deck { position: relative; height: calc(var(--panels, 17) * var(--vh, 1vh) * 100); }

/* The camera. Perspective lives here so every panel and every element
   inside them shares one vanishing point at the centre of the viewport.
   Depth is produced by scaling elements at different rates -- see the
   fly-through block below. */
.deck-pin {
  position: sticky; top: 0; height: calc(var(--vh, 1vh) * 100); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}

/* The panel itself carries NO transform, filter or opacity. All three would
   flatten the 3D context (or force it, per spec, in most engines) and the
   children would lose their individual depth. The panel is only a stage --
   every bit of motion happens on the elements inside it. */
.fs-panel {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 96px 28px 76px;
  pointer-events: none;
}

/* Painting order and pointer-events are set inline by landing.js from each
   panel's live distance -- whatever is nearest the viewer paints on top. The
   class here only carries the "you have arrived" state that stage internals
   (eval bars, flow rail) key their reveal off. */
.fs-panel { z-index: 1; }
.fs-panel.is-on { pointer-events: auto; }

/* No 3D anywhere: no perspective, no preserve-3d, no translateZ. */
/* max-height + overflow-y are a defensive fallback (2026-08-16, mobile
   fly-through): .deck-pin clips at the viewport edge, so a panel whose
   content is still taller than the screen despite the compact-mode rules
   below would otherwise be silently cut off at the bottom on a short
   phone. This makes it scrollable in place instead of clipped. */
.panel-inner { width: 100%; max-width: var(--lp-max); max-height: 100%; overflow-y: auto; }

.panel-split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center;
}

.panel-flip .panel-stage { order: 2; }
.panel-flip .panel-copy  { order: 1; }

.panel-stage { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.panel-center { text-align: center; max-width: 900px; margin: 0 auto; }

/* Deck progress rail -- when the page holds still, the reader needs to see
   that scrolling is doing something and how far through they are. */
.deck-rail {
  position: fixed; right: 26px; top: 50%; transform: translateY(-50%);
  z-index: 50; display: flex; flex-direction: column; gap: 8px;
}

.deck-rail i {
  width: 22px; height: 3px; border-radius: 999px; display: block;
  background: var(--panel-border); cursor: pointer; border: 0; padding: 0;
  transition: background .35s var(--lp-panel-ease), width .35s var(--lp-panel-ease);
}

.deck-rail i:hover { background: var(--muted); }
.deck-rail i.on { background: var(--accent); width: 34px; }

@media (max-width: 1100px) { .deck-rail { display: none; } }

/* =========================================================================
   Panel typography
   ========================================================================= */

.p-num {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 12px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 16px;
}

.p-num::before { content: ""; width: 20px; height: 1px; background: var(--accent); }
.panel-center .p-num { justify-content: center; }

.p-title {
  font-size: clamp(24px, 3.4vw, 40px); line-height: 1.14; font-weight: 700;
  letter-spacing: -.026em; margin: 0 0 16px; color: var(--text);
}

.p-body { margin: 0; color: var(--muted); font-size: clamp(15px, 1.5vw, 18px); max-width: 50ch; }
.panel-center .p-body { margin-left: auto; margin-right: auto; }
.p-body + .p-body { margin-top: 13px; }
.p-body strong { color: var(--text); font-weight: 600; }

.p-terms { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.panel-center .p-terms { justify-content: center; }

.p-terms li {
  font-size: 14px; color: var(--text); background: var(--bg);
  border: 1px solid var(--panel-border); border-radius: 7px; padding: 6px 13px;
}

.p-statement {
  font-size: clamp(26px, 4.4vw, 52px); line-height: 1.12; font-weight: 700;
  letter-spacing: -.03em; color: var(--text); margin: 0 auto; max-width: 900px;
}

.p-statement em { font-style: normal; color: var(--muted); }
.p-statement-caps { text-transform: uppercase; }
.p-accent { color: var(--accent); }

.p-stack { display: flex; flex-direction: column; gap: 4px; align-items: center; margin: 32px 0; }
.p-stack span {
  font-size: clamp(16px, 2.1vw, 22px); color: var(--muted); font-weight: 600;
  padding: 4px 12px; border-radius: 8px;
  box-shadow: 0 0 0 1px transparent; background: transparent;
}
.p-stack span.on { color: var(--text); }

/* The punchline line gets a continuous blue flash + glow while held, plus
   the same highlight container as the spotlight lines below. Color/
   text-shadow/box-shadow/background only -- no transform -- since .p-stack
   span is one of the elements the scroll fly-through itself scales (see
   the FLY-THROUGH block further down); a second transform source here
   would fight that scroll-driven one and visibly break the arrival/
   departure animation. Its own keyframe, not the shared lp-glow-pulse --
   that one's also used by .risk-line.is-key b, which must stay a static
   accent blue and only pulse its text-shadow, not flip color. */
.fs-panel.is-on .p-stack span.on {
  animation: lp-stack-on-flash 2.2s var(--lp-panel-ease) infinite;
}
@keyframes lp-stack-on-flash {
  0%, 100% {
    color: var(--text); text-shadow: 0 0 0 transparent;
    box-shadow: 0 0 0 1px transparent; background: transparent;
  }
  50% {
    color: var(--accent); text-shadow: 0 0 10px var(--lp-accent-line);
    box-shadow: 0 0 0 1px var(--accent); background: var(--lp-accent-soft);
  }
}

/* The list lines above the punchline take turns being highlighted -- a
   container (box-shadow + background, not border, so it doesn't add box
   size beyond the padding already on .p-stack span -- same reasoning as
   .p-card's glow above) fades in around whichever line is currently
   spotlighted. One shared keyframe, staggered per line via nth-child so
   exactly one is highlighted at a time as the delays cycle through. */
@keyframes lp-stack-spotlight {
  0%   { color: var(--muted); opacity: .55; box-shadow: 0 0 0 1px transparent; background: transparent; }
  4%   { color: var(--text);  opacity: 1;   box-shadow: 0 0 0 1px var(--accent); background: var(--lp-accent-soft); }
  16%  { color: var(--text);  opacity: 1;   box-shadow: 0 0 0 1px var(--accent); background: var(--lp-accent-soft); }
  22%  { color: var(--muted); opacity: .55; box-shadow: 0 0 0 1px transparent; background: transparent; }
  100% { color: var(--muted); opacity: .55; box-shadow: 0 0 0 1px transparent; background: transparent; }
}

.fs-panel.is-on .p-stack span:not(.on) {
  animation: lp-stack-spotlight 3.5s var(--lp-panel-ease) infinite;
}
.fs-panel.is-on .p-stack span:nth-child(1):not(.on) { animation-delay: 0ms; }
.fs-panel.is-on .p-stack span:nth-child(2):not(.on) { animation-delay: 700ms; }
.fs-panel.is-on .p-stack span:nth-child(3):not(.on) { animation-delay: 1400ms; }
.fs-panel.is-on .p-stack span:nth-child(4):not(.on) { animation-delay: 2100ms; }
.fs-panel.is-on .p-stack span:nth-child(5):not(.on) { animation-delay: 2800ms; }

.p-cta { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-top: 32px; }

/* =========================================================================
   Hero candlestick strip (panel 01) -- purely decorative, sits between the
   header and the headline. Grouped into the fly-through's --sa "stage
   cards" depth band alongside .panel-stage (see the FLY-THROUGH block
   below) so it arrives/departs with the same scroll-scrubbed scale+fade+
   blur as the rest of panel 1's content, instead of staying static while
   everything around it flies. Safe to add there because .hero-chart
   itself carries no local transform-based animation of its own -- only
   its children (.hc::after's scaleY tick, .hero-chart-trend's
   background-position drift) animate, and neither touches .hero-chart's
   own transform, so the scroll-driven scale(var(--sa)) on the container
   never gets clobbered by anything inside it. Colors are all existing
   tokens (--green/--red/--panel-border/--accent) so it reads as part of
   the same system as every other widget in the deck, not a new palette. */
.hero-chart {
  width: min(560px, 100%); height: clamp(54px, 9vw, 82px);
  margin: 0 auto 20px; position: relative;
}

.hero-chart-trend {
  position: absolute; left: 6%; right: 6%; top: 38%; height: 2px;
  background: repeating-linear-gradient(90deg, var(--lp-accent-line) 0 6px, transparent 6px 12px);
  background-size: 16px 100%; transform: rotate(-3deg); opacity: .8;
}

.fs-panel.is-on .hero-chart-trend { animation: lp-flow-dash-x .6s linear infinite; }
@keyframes lp-flow-dash-x { to { background-position: -16px 0; } }

.hero-chart-candles { display: flex; align-items: center; gap: clamp(4px, 1vw, 7px); height: 100%; position: relative; }

.hc { position: relative; flex: 1; height: 100%; }
.hc::before {
  content: ""; position: absolute; left: 50%; width: 1px;
  transform: translateX(-50%); background: var(--muted); opacity: .55;
}
.hc::after {
  content: ""; position: absolute; left: 50%; width: 34%; min-width: 3px; border-radius: 1px;
  transform: translateX(-50%); transform-origin: center;
}
.hc.up::after { background: var(--green); }
.hc.down::after { background: var(--red); }

/* Bodies are derived from a single zigzag price path (p0..p14, percent
   top-edge coordinates, smaller = higher price) rather than picked
   independently per candle: candle i's body is top=min(p[i-1],p[i]),
   height=abs(p[i]-p[i-1]). That makes real candlestick continuity
   automatic -- an up candle's close is its body TOP and a down
   candle's close is its body BOTTOM, so because each candle's open
   equals the previous candle's close (no gap, same convention the
   reference chart image uses), consecutive bodies always share an
   exact touching edge: two ups stack bottom-on-top (climbing
   staircase), two downs stack top-on-bottom (falling staircase), and
   an up/down pivot touches at the shared high or low between them.
   Path: 64,76,62,50,58,66,32,54,14,32,52,98,70,2,24 -- four
   successive swing highs (candle 3's close at 50, candle 6's at 32,
   candle 8's at 14, candle 13's own close at 2), each one a clear step
   higher than the last and evenly spread across the whole 0-100%
   range rather than bunched near the top, so the "the process can be
   automated" rally reads as a real ascending trend, not just noise --
   reinforced by the taller .hero-chart box (44-68px -> 54-82px) giving
   that separation more literal pixels to work with. Candle 13's own
   height is exactly 2x its prior value (34 -> 68) -- reaching that
   meant deepening candle 11's trough (p11: 72 -> 98) so candle 12 has
   still has real room of its own (its height shrank 34 -> 28 as a
   result, an unavoidable side effect of the fixed 0-100% range, not
   an arbitrary choice) before candle 13's rally to the new high.
   Colors in the markup already match this path's up/down direction
   candle-to-candle.
   Each wick (::before) is still sized independently per candle (only
   this candle's own high/low excursion -- wicks never need to touch
   neighbors, only bodies do) so a trail visibly pokes out both ends
   of every body. .hc::after's "top"/"height" are true edge-based
   coordinates (only translateX in its transform, no vertical shift),
   so these values place the body exactly where they say. */
.hc:nth-child(1)::after  { top: 64%; height: 12%; }
.hc:nth-child(1)::before { top: 50%; bottom: 8%; }
.hc:nth-child(2)::after  { top: 62%; height: 14%; }
.hc:nth-child(2)::before { top: 48%; bottom: 10%; }
.hc:nth-child(3)::after  { top: 50%; height: 12%; }
.hc:nth-child(3)::before { top: 36%; bottom: 20%; }
.hc:nth-child(4)::after  { top: 50%; height: 8%; }
.hc:nth-child(4)::before { top: 36%; bottom: 22%; }
.hc:nth-child(5)::after  { top: 58%; height: 8%; }
.hc:nth-child(5)::before { top: 44%; bottom: 18%; }
.hc:nth-child(6)::after  { top: 32%; height: 34%; }
.hc:nth-child(6)::before { top: 16%; bottom: 18%; }
.hc:nth-child(7)::after  { top: 32%; height: 22%; }
.hc:nth-child(7)::before { top: 16%; bottom: 24%; }
.hc:nth-child(8)::after  { top: 14%; height: 40%; }
.hc:nth-child(8)::before { top: 2%;  bottom: 24%; }
.hc:nth-child(9)::after  { top: 14%; height: 18%; }
.hc:nth-child(9)::before { top: 2%;  bottom: 44%; }
.hc:nth-child(10)::after { top: 32%; height: 20%; }
.hc:nth-child(10)::before { top: 16%; bottom: 28%; }
.hc:nth-child(11)::after { top: 52%; height: 46%; }
.hc:nth-child(11)::before { top: 36%; bottom: 0%; }
.hc:nth-child(12)::after { top: 70%; height: 28%; }
.hc:nth-child(12)::before { top: 54%; bottom: 0%; }
.hc:nth-child(13)::after { top: 2%;  height: 68%; }
.hc:nth-child(13)::before { top: 0%;  bottom: 10%; }
.hc:nth-child(14)::after { top: 2%;  height: 22%; }
.hc:nth-child(14)::before { top: 0%;  bottom: 52%; }

/* A dedicated keyframe rather than reusing .mod-bars' shared lp-bar-tick:
   a CSS animation's transform keyframes replace the element's whole
   transform property while running, so animating bare "scaleY(...)"
   here would wipe out the translateX(-50%) that horizontally centers
   each body over its own wick -- the body would drift sideways mid-tick
   since the wick (::before, unanimated) stays put. Baking the translate
   into every keyframe keeps the body centered on its wick the whole
   time; transform-origin: center (set on .hc::after above) keeps the
   scaleY itself growing/shrinking around the body's own vertical
   center rather than its top edge. Staggered per candle so it reads as
   a left-to-right ripple ticking through live prices rather than
   everything pulsing in lockstep. */
@keyframes lp-candle-tick {
  0%, 100% { transform: translateX(-50%) scaleY(1); }
  50%      { transform: translateX(-50%) scaleY(.8); }
}

.fs-panel.is-on .hc::after { animation: lp-candle-tick 1.2s var(--lp-panel-ease) infinite; }
.fs-panel.is-on .hc:nth-child(1)::after  { animation-delay: 0ms; }
.fs-panel.is-on .hc:nth-child(2)::after  { animation-delay: 70ms; }
.fs-panel.is-on .hc:nth-child(3)::after  { animation-delay: 140ms; }
.fs-panel.is-on .hc:nth-child(4)::after  { animation-delay: 210ms; }
.fs-panel.is-on .hc:nth-child(5)::after  { animation-delay: 280ms; }
.fs-panel.is-on .hc:nth-child(6)::after  { animation-delay: 350ms; }
.fs-panel.is-on .hc:nth-child(7)::after  { animation-delay: 420ms; }
.fs-panel.is-on .hc:nth-child(8)::after  { animation-delay: 490ms; }
.fs-panel.is-on .hc:nth-child(9)::after  { animation-delay: 560ms; }
.fs-panel.is-on .hc:nth-child(10)::after { animation-delay: 630ms; }
.fs-panel.is-on .hc:nth-child(11)::after { animation-delay: 700ms; }
.fs-panel.is-on .hc:nth-child(12)::after { animation-delay: 770ms; }
.fs-panel.is-on .hc:nth-child(13)::after { animation-delay: 840ms; }
.fs-panel.is-on .hc:nth-child(14)::after { animation-delay: 910ms; }

/* =========================================================================
   THE FLY-THROUGH -- scroll-scrubbed, 2D scale only
   -------------------------------------------------------------------------
   No perspective, no translateZ, no preserve-3d, no backface-visibility.

   Perspective was only ever producing a scale: an element at translateZ(-300)
   under perspective(1100) renders at 1100/(1100+300) = 0.79. So the scale IS
   the effect, and computing it directly removes every 3D code path -- along
   with the entire class of bug where a browser reports a correct transform
   and correct opacity and then paints nothing, which is what the on-screen
   diagnostic caught.

   landing.js computes the perspective division itself and publishes one
   scale per depth band:

     --sa  stage cards   (deepest -- travels furthest)
     --sb  eyebrow
     --sc  headline
     --sd  body copy
     --se  chips, lists, cards
     --sf  buttons, scroll hint  (nearest -- travels least)

   plus --o (opacity) and --blur. Different bands scale at different rates,
   which is what reads as depth.

   Gated to where the deck is actually pinned. Below 901px, under reduced
   motion, or with JS off, none of this applies, so there is no hidden state
   left to undo. html.js prefixes are uniform on purpose -- mixing prefixed
   and unprefixed selectors here inverts specificity and hides the site.
   ========================================================================= */

/* 2026-08-16: was min-width:901px + no-preference -- the width gate is
   gone, so the fly-through now also runs on phones/tablets (see
   landing.js's setVh() for the --vh fix that makes pinning safe there).
   prefers-reduced-motion is the only remaining exclusion; it must stay,
   since motion-sensitive users need the un-pinned fallback regardless of
   device. */
@media (prefers-reduced-motion: no-preference) {

  html.js .fs-panel { visibility: hidden; }

  html.js .fs-panel .p-num,
  html.js .fs-panel .p-title,
  html.js .fs-panel .p-statement,
  html.js .fs-panel .p-body,
  html.js .fs-panel .p-terms,
  html.js .fs-panel .p-cta,
  html.js .fs-panel .p-list,
  html.js .fs-panel .p-cards,
  html.js .fs-panel .p-chips,
  html.js .fs-panel .p-stack span,
  html.js .fs-panel .p-hint,
  html.js .fs-panel .panel-stage,
  html.js .fs-panel .hero-chart {
    opacity: var(--o, 0);
  }

  html.js .fs-panel .panel-stage,
  html.js .fs-panel .hero-chart   { transform: scale(var(--sa, .5)); }
  html.js .fs-panel .p-num        { transform: scale(var(--sb, .5)); }
  html.js .fs-panel .p-title,
  html.js .fs-panel .p-statement  { transform: scale(var(--sc, .5)); }
  html.js .fs-panel .p-body,
  html.js .fs-panel .p-stack span { transform: scale(var(--sd, .5)); }
  html.js .fs-panel .p-terms,
  html.js .fs-panel .p-chips,
  html.js .fs-panel .p-list,
  html.js .fs-panel .p-cards      { transform: scale(var(--se, .5)); }
  html.js .fs-panel .p-cta        { transform: scale(var(--sf, .5)); }

  /* .p-hint is absolutely placed and already carries a centring translateX,
     which has to survive or it jumps to the left edge. */
  html.js .fs-panel .p-hint { transform: translateX(-50%) scale(var(--sf, .5)); }

  /* Blur only while a panel is leaving. Declared globally it runs a filter
     pass on every element every frame even at blur(0px). */
  html.js .fs-panel[data-leaving="1"] .p-num,
  html.js .fs-panel[data-leaving="1"] .p-title,
  html.js .fs-panel[data-leaving="1"] .p-statement,
  html.js .fs-panel[data-leaving="1"] .p-body,
  html.js .fs-panel[data-leaving="1"] .p-terms,
  html.js .fs-panel[data-leaving="1"] .p-cta,
  html.js .fs-panel[data-leaving="1"] .p-list,
  html.js .fs-panel[data-leaving="1"] .p-cards,
  html.js .fs-panel[data-leaving="1"] .p-chips,
  html.js .fs-panel[data-leaving="1"] .p-stack span,
  html.js .fs-panel[data-leaving="1"] .panel-stage,
  html.js .fs-panel[data-leaving="1"] .hero-chart {
    filter: blur(calc(var(--blur, 0) * 1px));
  }
}

/* =========================================================================
   Persistent site backdrop
   -------------------------------------------------------------------------
   One fixed layer behind everything, rather than a copy inside the hero.
   Panels fly over it, so it reads as the space they're moving through and
   the page never loses its floor between sections. z-index:-1 keeps it above
   the body's background colour but under all content.
   ========================================================================= */

.site-bg {
  position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden;
}

/* Content sits explicitly above the backdrop. Relying on z-index:-1 to slip
   the backdrop under everything is fragile -- it depends on background
   propagation to the canvas and on no ancestor forming a stacking context.
   An explicit layer above it is predictable instead. */
body.lp > header,
body.lp > .deck,
body.lp > .lp-section,
body.lp > footer { position: relative; z-index: 1; }

/* This selector ties in specificity with "body.lp header" above and wins by
   source order, so it's the one that actually controls header stacking --
   give it an explicit z-index well above the deck panels' inline JS-driven
   range (~95-105 while a panel is held/clickable) so the header nav never
   gets covered by an active panel. */
body.lp > header { position: fixed; z-index: 200; }

/* Very slight drift over the length of the page -- enough that the backdrop
   isn't dead still behind moving content, small enough not to read as a
   second scrolling layer competing with the panels. */
.site-bg .p-grid {
  background-position: 0 calc(var(--scroll, 0) * -60px);
}

.site-bg .p-glow {
  top: 46%;
  transform: translate(-50%, -50%) translateY(calc(var(--scroll, 0) * -90px))
             scale(calc(1 + var(--scroll, 0) * .25));
  opacity: calc(1 - var(--scroll, 0) * .35);
}

/* Kept for the auth pages, which use the same two layers locally. */
.p-bg { position: absolute; inset: 0; pointer-events: none; z-index: -1; }

.p-grid {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(var(--panel-border) 1px, transparent 1px),
    linear-gradient(90deg, var(--panel-border) 1px, transparent 1px);
  background-size: 64px 64px; opacity: .55;
  mask-image: radial-gradient(ellipse 85% 75% at 50% 45%, #000 35%, transparent 88%);
  -webkit-mask-image: radial-gradient(ellipse 85% 75% at 50% 45%, #000 35%, transparent 88%);
}

.p-glow {
  position: absolute; left: 50%; top: 46%; width: 820px; height: 440px;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at center, var(--lp-accent-soft) 0%, transparent 68%);
}

.p-hint {
  position: absolute; left: 50%; bottom: 34px; transform: translateX(-50%);
  display: inline-flex; flex-direction: column; align-items: center; gap: 8px;
  color: var(--muted); font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
}

/* On desktop widths the panel's centered content block (headline, body,
   CTA row) can run tall enough on a short-but-wide browser window to
   nudge into the hint's fixed 34px-from-bottom position -- the narrow-
   viewport compacting rules below don't apply here since they're keyed
   off width, not height. Sitting a little closer to the bottom edge
   gives the CTA row more clearance without touching mobile at all. */
@media (min-width: 901px) {
  .p-hint { bottom: 14px; }
}

.p-hint span {
  width: 1px; height: 38px; background: linear-gradient(var(--accent), transparent);
  animation: lp-drop 1.9s var(--lp-ease) infinite;
}

@keyframes lp-drop {
  0%   { transform: scaleY(0); transform-origin: 0 0;    opacity: 0; }
  40%  { transform: scaleY(1); transform-origin: 0 0;    opacity: 1; }
  60%  { transform: scaleY(1); transform-origin: 0 100%; opacity: 1; }
  100% { transform: scaleY(0); transform-origin: 0 100%; opacity: 0; }
}

/* =========================================================================
   Buttons
   ========================================================================= */

.lp-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: 9px; border: 1px solid transparent; padding: 13px 26px;
  font-size: 16px; font-weight: 600; font-family: inherit; text-decoration: none; cursor: pointer;
  transition: background .2s var(--lp-ease), border-color .2s var(--lp-ease),
              transform .12s var(--lp-ease), box-shadow .25s var(--lp-ease);
}

.lp-btn-primary { background: var(--accent); color: var(--lp-ink); }
.lp-btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 30px -8px var(--lp-accent-line); }
.lp-btn-secondary { background: transparent; color: var(--text); border-color: var(--panel-border); }
.lp-btn-secondary:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-2px); }
.lp-btn:active { transform: translateY(0); }
.lp-btn-sm { padding: 8px 17px; font-size: 14px; }

/* =========================================================================
   Stage shell
   ========================================================================= */

.stage {
  width: 100%; max-width: 500px; background: var(--panel);
  border: 1px solid var(--panel-border); border-radius: var(--lp-radius); padding: 22px;
  box-shadow: 0 30px 70px -34px rgba(0, 0, 0, .8);
}

.stage-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 16px; }
.stage-title { font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }

.stage-pill {
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent); background: var(--lp-accent-soft); border-radius: 999px; padding: 4px 11px;
}

/* Same pill, muted -- for a section that isn't live yet (e.g. the
   Crypto/Futures/Forex headers on /strategies), so "Coming Soon" doesn't
   read with the same active-and-connected color as "Live"/"Connected". */
.stage-pill.is-muted { color: var(--muted); background: rgba(139, 152, 165, .12); }

.stage-note { font-size: 12px; color: var(--muted); text-align: center; max-width: 460px; margin: 0; }

/* =========================================================================
   STAGE VISUALS
   -------------------------------------------------------------------------
   Each stage is self-contained and driven by a data-phase attribute set in
   the markup, not by deck state -- so a panel's visual is correct the moment
   that panel becomes active, and stays correct in the un-pinned fallback
   where every panel renders at once.
   ========================================================================= */

/* Shared "live" keyframe library (2026-08-16) -- reused across most of the
   stage widgets below for their continuous, .is-on-gated looping motion.
   Kept to a handful of shapes (breathing opacity, an expanding status-dot
   ring, a text glow) so the whole deck reads as one consistent visual
   language rather than ten unrelated effects. */
@keyframes lp-live-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .4; }
}

@keyframes lp-ring-pulse {
  0%   { box-shadow: 0 0 0 4px var(--lp-green-soft); }
  60%  { box-shadow: 0 0 0 9px transparent; }
  100% { box-shadow: 0 0 0 4px var(--lp-green-soft); }
}

@keyframes lp-ring-pulse-sm {
  0%   { box-shadow: 0 0 0 0 var(--lp-green-soft); }
  70%  { box-shadow: 0 0 0 5px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

@keyframes lp-glow-pulse {
  0%, 100% { text-shadow: 0 0 0 transparent; }
  50%      { text-shadow: 0 0 10px var(--lp-accent-line); }
}

/* ---- webull integration ---- */

.wb-link { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }

/* Both sides are just their own logo image -- /logo.png already bakes
   in the FSTrading wordmark (same as the header), and /webull-logo.png
   is Webull's own crescent+wordmark lockup (the exact asset, not a
   redrawn approximation -- has a transparent background and a white
   wordmark, so it reads correctly against this page's dark theme).
   height only, width:auto, so each scales uniformly without distorting
   its own aspect ratio. */
.wb-node-logo { height: 26px; width: auto; flex-shrink: 0; }

/* Same "current flowing" technique as .exec-link (a dashed line whose
   background-position loops), just horizontal instead of vertical --
   the literal illustration of orders moving from FSTrading into the
   user's own Webull account. */
.wb-rail { position: relative; flex: 1; height: 2px; }
.wb-rail::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg, var(--accent) 0 4px, transparent 4px 8px);
  background-size: 16px 100%;
}
.fs-panel.is-on .wb-rail::before { animation: lp-wb-flow-dash .5s linear infinite; }
@keyframes lp-wb-flow-dash { to { background-position: -16px 0; } }

/* A small dot riding the rail, reading as a single order in transit. */
.wb-rail-pulse {
  position: absolute; top: 50%; left: 0; width: 8px; height: 8px; margin-top: -4px;
  border-radius: 50%; background: var(--accent); box-shadow: 0 0 8px 1px var(--lp-accent-line);
  opacity: 0;
}
.fs-panel.is-on .wb-rail-pulse { animation: lp-wb-rail-pulse 2.2s var(--lp-panel-ease) infinite; }
@keyframes lp-wb-rail-pulse {
  0%   { left: 0; opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}

/* Same fact-row pattern as .risk-out/.risk-line (panel 07), kept as its
   own class rather than reused directly so this widget stays
   self-contained. */
.wb-meta { border: 1px solid var(--panel-border); border-radius: 10px; background: var(--bg); padding: 13px 15px; }
.wb-meta-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13px; padding: 5px 0; }
.wb-meta-row span { color: var(--muted); }
.wb-meta-row b { color: var(--text); font-weight: 600; }
.wb-meta-row.is-key { border-top: 1px solid var(--panel-border); margin-top: 6px; padding-top: 10px; }
.wb-meta-row.is-key b { color: var(--accent); }
.fs-panel.is-on .wb-meta-row.is-key b { animation: lp-glow-pulse 2.1s var(--lp-panel-ease) infinite; }

/* ---- manual steps ---- */

.man-list { display: flex; flex-direction: column; gap: 7px; }

.man-item {
  display: flex; align-items: center; gap: 12px;
  background: var(--bg); border: 1px solid var(--panel-border);
  border-radius: 8px; padding: 10px 13px; font-size: 14px; color: var(--muted);
}

.man-item i {
  font-style: normal; width: 20px; height: 20px; flex-shrink: 0; border-radius: 50%;
  border: 1px solid var(--panel-border); display: inline-flex;
  align-items: center; justify-content: center; font-size: 10px; color: var(--muted);
}

.man-list[data-phase="scattered"] { --man-bounce: 6px; }
.man-list[data-phase="scattered"] .man-item:nth-child(odd)  { transform: translateX(-12px); }
.man-list[data-phase="scattered"] .man-item:nth-child(even) { transform: translateX(14px); }
.man-list[data-phase="merged"] .man-item { opacity: .4; transform: scale(.98); }

/* Manual steps bounce left-to-right while held -- reinforces "scattered,
   done by hand" against panel 02b's calm merged list right after it.
   Keyframes oscillate around the same -12px/+14px centers the resting
   rules above already use, so the "scattered" look is unchanged at rest
   (no-JS, reduced-motion) and the animation just adds motion on top --
   necessary since a running CSS animation's transform keyframes replace
   the whole property, so baking the existing offset in is the only way
   to animate .man-item without the static scatter vanishing while held.
   Odd/even start in opposite phase so items read as independently
   drifting rather than one synced wave.
   --man-bounce is redeclared smaller at each compact breakpoint below
   (matching .stage's own shrinking padding: 22px -> 18px -> 14px ->
   10px) so it's still visibly bouncing everywhere without the swing
   growing with it -- even's resting center (+14px) already touches or
   exceeds the two tightest paddings on its own (the pre-existing
   static design already tolerated that with no visible breakage), so
   the two smallest tiers keep a modest few-px reach past the edge
   rather than disabling the animation outright. */
@keyframes lp-man-bounce-odd {
  0%, 100% { transform: translateX(calc(-12px - var(--man-bounce))); }
  50%      { transform: translateX(calc(-12px + var(--man-bounce))); }
}
@keyframes lp-man-bounce-even {
  0%, 100% { transform: translateX(calc(14px + var(--man-bounce))); }
  50%      { transform: translateX(calc(14px - var(--man-bounce))); }
}

.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(odd) {
  animation: lp-man-bounce-odd 2.6s ease-in-out infinite;
}
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(even) {
  animation: lp-man-bounce-even 2.6s ease-in-out infinite;
}
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(1) { animation-delay: 0ms; }
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(2) { animation-delay: 130ms; }
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(3) { animation-delay: 260ms; }
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(4) { animation-delay: 390ms; }
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(5) { animation-delay: 520ms; }
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(6) { animation-delay: 650ms; }
.fs-panel.is-on .man-list[data-phase="scattered"] .man-item:nth-child(7) { animation-delay: 780ms; }

.man-merged {
  margin-top: 14px; border: 1px solid var(--accent); border-radius: 10px;
  background: var(--lp-accent-soft); padding: 13px 15px;
  display: flex; align-items: center; gap: 12px;
}

.man-merged strong { color: var(--text); font-size: 14px; font-weight: 600; }
.man-merged span { color: var(--muted); font-size: 13px; }

.man-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--green);
  flex-shrink: 0; box-shadow: 0 0 0 4px var(--lp-green-soft);
}

/* Continuous "live" motion while the panel is held (2026-08-16) -- unlike
   eval-bar/flow/scan-grid's one-shot arrival animation, these loop for as
   long as .is-on is present and stop the instant the panel departs (the
   rule simply no longer matches). Targets are all several levels inside
   .panel-stage, never the elements the scroll fly-through itself
   transforms (.panel-stage, .p-num, .p-title, .p-body, .p-terms, .p-cta,
   .p-list, .p-cards, .p-chips, .p-stack span, .p-hint -- see the FLY-
   THROUGH block above), so nothing here fights that scroll-driven
   transform: scale(...). */

.fs-panel.is-on .man-item i {
  animation: lp-live-pulse 1.7s var(--lp-panel-ease) infinite;
}

.fs-panel.is-on .man-item:nth-child(1) i { animation-delay: 0ms; }
.fs-panel.is-on .man-item:nth-child(2) i { animation-delay: 90ms; }
.fs-panel.is-on .man-item:nth-child(3) i { animation-delay: 180ms; }
.fs-panel.is-on .man-item:nth-child(4) i { animation-delay: 270ms; }
.fs-panel.is-on .man-item:nth-child(5) i { animation-delay: 360ms; }
.fs-panel.is-on .man-item:nth-child(6) i { animation-delay: 450ms; }
.fs-panel.is-on .man-item:nth-child(7) i { animation-delay: 540ms; }

/* The "Running" status dot (02b only) gets its own expanding-ring pulse --
   a plain live-indicator, matching the label right next to it. */
.fs-panel.is-on .man-merged .man-dot {
  animation: lp-ring-pulse 1.8s var(--lp-panel-ease) infinite;
}

/* ---- market scan grid ----
   Represents the universe being scanned: each cell is a real (illustrative)
   ticker symbol, not a blank swatch -- 2026-08-16, previous version was
   colored squares with no label, which read as an abstract heatmap rather
   than "tickers being scanned." Fewer, wider cells than before so the
   symbol text has room; most are unmatched (visible but dim, so the grid
   still reads as a grid and every symbol stays legible), a handful are
   flagged matches (accent), and phase 3's single top-tier cell is the
   confirmed candidate (green). Base state already shows the final look
   with no JS/animation required -- .is-on below only adds a one-time
   pulse-in + scan sweep on top, the same "base = resting look, is-on =
   one-shot flourish" split .eval-bar already uses. */

.scan-grid { position: relative; overflow: hidden; display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; margin-bottom: 16px; }

.scan-cell {
  aspect-ratio: 1.7 / 1; border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); border: 1px solid var(--panel-border); opacity: .55;
  font-size: 11px; font-weight: 600; letter-spacing: .01em; color: var(--muted);
}

.scan-cell[data-tier] { opacity: 1; border-color: var(--accent); background: var(--lp-accent-soft); color: var(--text); }

.scan-grid[data-phase="3"] .scan-cell[data-tier="3"] {
  border-color: var(--green); background: var(--lp-green-soft); transform: scale(1.1);
}

/* One sweep band, not per-cell -- stays cheap regardless of grid size. */
.scan-grid::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--lp-accent-soft) 45%, transparent 88%);
  transform: translateX(-120%);
}

.fs-panel.is-on .scan-grid::after {
  animation: lp-scan-sweep 1.6s var(--lp-panel-ease) .1s 1 both;
}

@keyframes lp-scan-sweep {
  0%   { transform: translateX(-120%); opacity: .9; }
  80%  { opacity: .9; }
  100% { transform: translateX(120%); opacity: 0; }
}

/* Matches pulse in, staggered via each cell's own --seq (set inline in
   the markup, same convention as .eval-bar's --w fill width). */
.fs-panel.is-on .scan-cell[data-tier] {
  animation: lp-cell-in .45s var(--lp-panel-ease) both;
  animation-delay: calc(var(--seq, 0) * 55ms);
}

@keyframes lp-cell-in {
  from { opacity: 0; transform: scale(.5); }
  to   { opacity: 1; transform: scale(1); }
}

.fs-panel.is-on .scan-grid[data-phase="3"] .scan-cell[data-tier="3"] {
  animation-name: lp-cell-in-confirmed;
}

@keyframes lp-cell-in-confirmed {
  from { opacity: 0; transform: scale(.5); }
  to   { opacity: 1; transform: scale(1.15); }
}

.scan-legend { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 12px; color: var(--muted); }
.scan-stat { display: flex; align-items: baseline; gap: 7px; }
.scan-stat b { color: var(--text); font-size: 18px; font-weight: 700; letter-spacing: -.02em; }

.eval-rows { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.eval-row { display: grid; grid-template-columns: 86px 1fr; align-items: center; gap: 12px; font-size: 12px; color: var(--muted); }
.eval-bar { height: 5px; border-radius: 999px; background: var(--bg); border: 1px solid var(--panel-border); overflow: hidden; }

.eval-bar span { display: block; height: 100%; background: var(--accent); width: var(--w, 50%); transform-origin: 0 50%; }
.fs-panel.is-on .eval-bar span { animation: lp-fill .9s var(--lp-panel-ease) both; animation-delay: .3s; }

@keyframes lp-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ---- rules + risk ---- */

.rule-block { display: flex; flex-direction: column; gap: 6px; }

.rule-row {
  display: flex; align-items: center; gap: 11px; border: 1px solid var(--accent);
  border-radius: 8px; background: var(--bg); padding: 10px 13px; font-size: 13px; color: var(--muted);
}

.rule-row b { color: var(--text); font-weight: 600; }

.rule-flag {
  margin-left: auto; font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--accent);
}

/* Each "Defined" flag pulses -- reads as continuously enforced, not just
   a static label. */
.fs-panel.is-on .rule-flag { animation: lp-live-pulse 1.9s var(--lp-panel-ease) infinite; }
.fs-panel.is-on .rule-row:nth-child(1) .rule-flag { animation-delay: 0ms; }
.fs-panel.is-on .rule-row:nth-child(2) .rule-flag { animation-delay: 110ms; }
.fs-panel.is-on .rule-row:nth-child(3) .rule-flag { animation-delay: 220ms; }
.fs-panel.is-on .rule-row:nth-child(4) .rule-flag { animation-delay: 330ms; }
.fs-panel.is-on .rule-row:nth-child(5) .rule-flag { animation-delay: 440ms; }
.fs-panel.is-on .rule-row:nth-child(6) .rule-flag { animation-delay: 550ms; }

.risk-out { border: 1px solid var(--panel-border); border-radius: 10px; background: var(--bg); padding: 13px 15px; }
.risk-line { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13px; padding: 5px 0; }
.risk-line span { color: var(--muted); }
.risk-line b { color: var(--text); font-weight: 600; }
.risk-line.is-key { border-top: 1px solid var(--panel-border); margin-top: 6px; padding-top: 10px; }
.risk-line.is-key b { color: var(--accent); }

/* The closing line ("Trade structure -- Determined automatically") gets a
   continuous glow, marking it as the live conclusion the rest builds to. */
.fs-panel.is-on .risk-line.is-key b {
  animation: lp-glow-pulse 2.1s var(--lp-panel-ease) infinite;
}

/* ---- execution ---- */

.exec-chain { display: flex; flex-direction: column; }

.exec-node {
  display: flex; align-items: center; gap: 13px; border: 1px solid var(--accent);
  border-radius: 10px; background: var(--bg); padding: 12px 15px;
}

.exec-node strong { color: var(--text); font-size: 14px; font-weight: 600; }
.exec-node em { font-style: normal; color: var(--muted); font-size: 12px; margin-left: auto; }

/* A dashed line rather than a solid one so a looping background-position
   shift reads as current continuously flowing down the chain -- the most
   literal match for "Signal, risk, order -- automatically" this deck has. */
.exec-link {
  width: 2px; height: 16px; margin: 0 auto;
  background: repeating-linear-gradient(180deg, var(--accent) 0 4px, transparent 4px 8px);
  background-size: 100% 16px;
}

.fs-panel.is-on .exec-link { animation: lp-flow-dash .5s linear infinite; }

@keyframes lp-flow-dash { to { background-position: 0 16px; } }

.fs-panel.is-on .exec-node { animation: lp-live-pulse 2s var(--lp-panel-ease) infinite; }
.fs-panel.is-on .exec-node:nth-child(1) { animation-delay: 0ms; }
.fs-panel.is-on .exec-node:nth-child(3) { animation-delay: 220ms; }
.fs-panel.is-on .exec-node:nth-child(5) { animation-delay: 440ms; }

.manage-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px; }

.manage-chip {
  border: 1px solid var(--panel-border); border-radius: 8px; background: var(--bg);
  padding: 9px 12px; font-size: 13px; color: var(--text);
  display: flex; align-items: center; gap: 9px;
}

.manage-chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--green); flex-shrink: 0; }

/* Each safeguard's status dot pulses -- a bank of live monitors, not a
   static checklist. */
.fs-panel.is-on .manage-chip::before { animation: lp-ring-pulse-sm 1.6s var(--lp-panel-ease) infinite; }
.fs-panel.is-on .manage-chip:nth-child(1)::before { animation-delay: 0ms; }
.fs-panel.is-on .manage-chip:nth-child(2)::before { animation-delay: 80ms; }
.fs-panel.is-on .manage-chip:nth-child(3)::before { animation-delay: 160ms; }
.fs-panel.is-on .manage-chip:nth-child(4)::before { animation-delay: 240ms; }
.fs-panel.is-on .manage-chip:nth-child(5)::before { animation-delay: 320ms; }
.fs-panel.is-on .manage-chip:nth-child(6)::before { animation-delay: 400ms; }
.fs-panel.is-on .manage-chip:nth-child(7)::before { animation-delay: 480ms; }
.fs-panel.is-on .manage-chip:nth-child(8)::before { animation-delay: 560ms; }

/* ---- strategy modules ---- */

.mods { display: grid; grid-template-columns: repeat(2, 1fr); gap: 9px; }

.mod { border: 1px solid var(--panel-border); border-radius: 10px; background: var(--bg); padding: 13px; opacity: .35; }
.mod-name { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 9px; display: flex; align-items: center; gap: 8px; }
.mod-name::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--panel-border); flex-shrink: 0; }
.mod-bars { display: flex; gap: 3px; align-items: flex-end; height: 24px; }
.mod-bars i { flex: 1; background: var(--panel-border); border-radius: 2px; height: 30%; transform-origin: bottom; }

/* Live-ticking sparkline while the panel is held -- oscillates around
   each bar's own height (set below per mod/bar), never replacing it, so
   this works regardless of which bars a given mod has tall vs short.
   Only the markup's active phase (data-phase="3", every mod running in
   parallel) is ever actually rendered on this page. */
@keyframes lp-bar-tick {
  0%, 100% { transform: scaleY(1); }
  50%      { transform: scaleY(.8); }
}

.fs-panel.is-on .mods[data-phase="3"] .mod-bars i { animation: lp-bar-tick 1.3s var(--lp-panel-ease) infinite; }
.fs-panel.is-on .mods[data-phase="3"] .mod-bars i:nth-child(2) { animation-delay: 90ms; }
.fs-panel.is-on .mods[data-phase="3"] .mod-bars i:nth-child(3) { animation-delay: 180ms; }
.fs-panel.is-on .mods[data-phase="3"] .mod-bars i:nth-child(4) { animation-delay: 270ms; }
.fs-panel.is-on .mods[data-phase="3"] .mod-bars i:nth-child(5) { animation-delay: 360ms; }
.fs-panel.is-on .mods[data-phase="3"] .mod-bars i:nth-child(6) { animation-delay: 450ms; }
.fs-panel.is-on .mods[data-phase="3"] .mod-bars i:nth-child(7) { animation-delay: 540ms; }
.fs-panel.is-on .mods[data-phase="3"] .mod-bars i:nth-child(8) { animation-delay: 630ms; }

.mods[data-phase="1"] .mod:nth-child(1),
.mods[data-phase="2"] .mod:nth-child(-n+3),
.mods[data-phase="3"] .mod { opacity: 1; border-color: var(--accent); }

.mods[data-phase="1"] .mod:nth-child(1) .mod-name::before,
.mods[data-phase="2"] .mod:nth-child(-n+3) .mod-name::before,
.mods[data-phase="3"] .mod .mod-name::before { background: var(--green); box-shadow: 0 0 0 4px var(--lp-green-soft); }

.mods[data-phase="1"] .mod:nth-child(1) .mod-bars i,
.mods[data-phase="2"] .mod:nth-child(-n+3) .mod-bars i,
.mods[data-phase="3"] .mod .mod-bars i { background: var(--accent); }

.mods[data-phase="3"] .mod:nth-child(1) .mod-bars i:nth-child(2n)   { height: 78%; }
.mods[data-phase="3"] .mod:nth-child(2) .mod-bars i:nth-child(3n)   { height: 92%; }
.mods[data-phase="3"] .mod:nth-child(3) .mod-bars i:nth-child(2n+1) { height: 64%; }
.mods[data-phase="3"] .mod:nth-child(4) .mod-bars i:nth-child(3n+1) { height: 85%; }

/* ---- lifecycle chain ---- */

.flow { position: relative; width: 100%; max-width: 320px; }
.flow-rail { position: absolute; left: 15px; top: 10px; bottom: 10px; width: 2px; background: var(--panel-border); overflow: hidden; }

.flow-rail::after {
  content: ""; display: block; width: 100%; height: 0; background: var(--accent);
  transition: height 1.4s var(--lp-panel-ease) .2s;
}

.fs-panel.is-on .flow-rail::after { height: 100%; }

.flow-node { display: flex; align-items: center; gap: 15px; padding: 5px 0; position: relative; }

.flow-dot {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  background: var(--bg); border: 1px solid var(--panel-border);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700; color: var(--muted);
  transition: border-color .4s var(--lp-panel-ease), color .4s var(--lp-panel-ease), background .4s var(--lp-panel-ease);
}

.flow-label { font-size: 12px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); transition: color .4s var(--lp-panel-ease); }

.fs-panel.is-on .flow-dot { border-color: var(--accent); color: var(--accent); background: var(--lp-accent-soft); }
.fs-panel.is-on .flow-label { color: var(--text); }

.fs-panel.is-on .flow-node:nth-child(2)  .flow-dot { transition-delay: .25s; }
.fs-panel.is-on .flow-node:nth-child(3)  .flow-dot { transition-delay: .38s; }
.fs-panel.is-on .flow-node:nth-child(4)  .flow-dot { transition-delay: .51s; }
.fs-panel.is-on .flow-node:nth-child(5)  .flow-dot { transition-delay: .64s; }
.fs-panel.is-on .flow-node:nth-child(6)  .flow-dot { transition-delay: .77s; }
.fs-panel.is-on .flow-node:nth-child(7)  .flow-dot { transition-delay: .90s; }
.fs-panel.is-on .flow-node:nth-child(8)  .flow-dot { transition-delay: 1.03s; }
.fs-panel.is-on .flow-node:nth-child(9)  .flow-dot { transition-delay: 1.16s; }
.fs-panel.is-on .flow-node:nth-child(10) .flow-dot { transition-delay: 1.29s; }

/* ---- cards used inside panels ---- */

.p-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 8px; }

.p-card {
  background: var(--panel); border: 1px solid var(--panel-border);
  border-radius: var(--lp-radius); padding: 22px; text-align: left;
}

.p-card h3 { font-size: 16px; font-weight: 650; margin: 0 0 8px; color: var(--text); }
.p-card p { margin: 0; color: var(--muted); font-size: 14px; }

/* Individual .p-card (not the .p-cards container, which the fly-through
   already scales) gets a continuous border-glow -- "always watching."
   box-shadow only, so it composes cleanly under the container's own
   scroll-driven scale rather than fighting it. */
@keyframes lp-card-glow {
  0%, 100% { box-shadow: 0 0 0 0 transparent; border-color: var(--panel-border); }
  50%      { box-shadow: 0 0 18px -4px var(--lp-accent-line); border-color: var(--accent); }
}
.fs-panel.is-on .p-card { animation: lp-card-glow 2.6s var(--lp-panel-ease) infinite; }
.fs-panel.is-on .p-card:nth-child(2) { animation-delay: 300ms; }
.fs-panel.is-on .p-card:nth-child(3) { animation-delay: 600ms; }

.p-list { list-style: none; margin: 0; padding: 0; text-align: left; }
.p-list li { position: relative; padding: 0 0 15px 28px; color: var(--muted); font-size: 16px; }
.p-list li::before { content: ""; position: absolute; left: 4px; top: 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--green); }
.p-list li strong { color: var(--text); font-weight: 600; }

/* Bullet dots pulse -- only the bullets, not .p-list itself (a fly-through
   target). */
.fs-panel.is-on .p-list li::before { animation: lp-ring-pulse-sm 1.8s var(--lp-panel-ease) infinite; }
.fs-panel.is-on .p-list li:nth-child(1)::before { animation-delay: 0ms; }
.fs-panel.is-on .p-list li:nth-child(2)::before { animation-delay: 120ms; }
.fs-panel.is-on .p-list li:nth-child(3)::before { animation-delay: 240ms; }
.fs-panel.is-on .p-list li:nth-child(4)::before { animation-delay: 360ms; }

.p-chips { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }

.p-chip {
  border: 1px solid var(--panel-border); background: var(--panel); border-radius: 999px;
  padding: 9px 18px; font-size: 14px; color: var(--text);
  position: relative; overflow: hidden;
}

/* A soft light shimmer loops inside each chip -- these represent the
   always-running infrastructure pieces underneath a strategy, so a
   continuous sweep (not a one-shot reveal) is the point. Scoped to each
   .p-chip (clipped to its own rounded-pill shape via overflow:hidden)
   rather than one band behind the whole row, so it only shows inside a
   chip's own container, not passing over the gaps between them. A
   pseudo-element is a separate box, so this doesn't touch .p-chips'/
   .p-chip's own scroll-driven transform. */
.p-chip::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--lp-accent-soft) 45%, transparent 88%);
  transform: translateX(-120%); opacity: 0;
}
.fs-panel.is-on .p-chip::after {
  animation: lp-chip-shimmer 2.2s var(--lp-panel-ease) infinite;
}
@keyframes lp-chip-shimmer {
  0%   { transform: translateX(-120%); opacity: 0; }
  12%  { opacity: .8; }
  50%  { opacity: .8; }
  62%  { transform: translateX(120%); opacity: 0; }
  100% { transform: translateX(120%); opacity: 0; }
}
.fs-panel.is-on .p-chip:nth-child(1)::after { animation-delay: 0ms; }
.fs-panel.is-on .p-chip:nth-child(2)::after { animation-delay: 130ms; }
.fs-panel.is-on .p-chip:nth-child(3)::after { animation-delay: 260ms; }
.fs-panel.is-on .p-chip:nth-child(4)::after { animation-delay: 390ms; }
.fs-panel.is-on .p-chip:nth-child(5)::after { animation-delay: 520ms; }
.fs-panel.is-on .p-chip:nth-child(6)::after { animation-delay: 650ms; }
.fs-panel.is-on .p-chip:nth-child(7)::after { animation-delay: 780ms; }

/* Short viewports: pull everything in so no panel can overflow its 100vh.
   920px (was 760px, 2026-08-16) covers common phone portrait heights too
   (roughly 640-930px across current devices), not just a short desktop
   window -- the reasoning is the same either way: less vertical room
   means content needs to be more compact to fit one pinned screen. */
@media (max-height: 920px) {
  .fs-panel { padding: 84px 28px 60px; }
  .p-title { font-size: clamp(21px, 2.8vw, 30px); }
  .p-body { font-size: 15px; }
  .stage { padding: 18px; max-width: 440px; }
  .p-cards { gap: 12px; }
  .p-card { padding: 16px; }
  .p-stack { margin: 22px 0; }
  .p-stack span { padding: 3px 10px; }
  .scan-grid { gap: 4px; }

  /* Width-agnostic, so this also covers a short-but-wide desktop
     window -- unlike the two max-width:900px tiers below (which only
     ever fire for narrow phones), this is the only compacting tier a
     short desktop window can hit. It previously left .man-list/
     .man-item/.man-merged untouched, so panel 02b's 7 items + margin
     + "Running" bar didn't fit the shortened .fs-panel padding above
     and got clipped on wide-but-short windows even though phones were
     fine. Reusing the exact values already proven at the narrower
     tier just below, rather than inventing new ones. */
  .man-list { gap: 4px; }
  .man-list[data-phase="scattered"] { --man-bounce: 6px; }
  .man-item { padding: 7px 10px; gap: 8px; }
  .man-item i { width: 16px; height: 16px; font-size: 9px; }
  .man-merged { margin-top: 8px; padding: 9px 11px; gap: 8px; }

  /* Same gap as above, this time in panel 11's 9-node trading-lifecycle
     list (.flow): .stage-head/.flow-node/.flow-dot were only compacted at
     the narrower max-width:900px tiers below, so a short-but-wide desktop
     window got .stage's reduced padding without the matching content
     compaction here, risking the same clipping panel 02b had. Reusing the
     exact values already proven at the narrower tier just below. */
  .stage-head { margin-bottom: 10px; }
  .flow-node { padding: 3px 0; gap: 11px; }
  .flow-dot { width: 24px; height: 24px; }

  /* .wb-link (panel 01b, "Webull integration") is new alongside this
     fix pass -- adding its compaction here up front rather than waiting
     for the same short-but-wide-desktop clipping bug to resurface a
     third time. Sized in between the two narrower tiers below. */
  .wb-link { gap: 11px; margin-bottom: 13px; }
  .wb-node-logo { height: 24px; }
  .wb-meta-row { padding: 4px 0; }
}

/* =========================================================================
   Conventional tail below the deck (FAQ + footer)
   ========================================================================= */

.lp-section { padding: 104px 0; position: relative; }
.lp-section h2 { font-size: clamp(26px, 3.6vw, 38px); line-height: 1.18; font-weight: 700; letter-spacing: -.024em; margin: 0 0 18px; color: var(--text); text-transform: none; }

.lp-eyebrow {
  display: inline-flex; align-items: center; gap: 9px; font-size: 13px; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase; color: var(--accent); margin: 0 0 16px;
}

.lp-eyebrow::before { content: ""; width: 22px; height: 1px; background: var(--accent); }

[data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .7s var(--lp-ease), transform .7s var(--lp-ease); }
[data-reveal].is-in { opacity: 1; transform: none; }
html:not(.js) [data-reveal] { opacity: 1; transform: none; }

.lp-shot { margin: 0 auto; max-width: 980px; border: 1px solid var(--panel-border); border-radius: var(--lp-radius); background: var(--panel); overflow: hidden; box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .75); }
.lp-shot img { display: block; width: 100%; height: auto; }
.lp-shot-chrome { display: flex; align-items: center; gap: 7px; padding: 12px 15px; border-bottom: 1px solid var(--panel-border); background: rgba(0, 0, 0, .25); }
.lp-dot { width: 11px; height: 11px; border-radius: 50%; background: var(--panel-border); }
.lp-dot:first-child { background: var(--red); opacity: .55; }
.lp-dot:nth-child(2) { background: var(--amber); opacity: .55; }
.lp-dot:nth-child(3) { background: var(--green); opacity: .55; }

.lp-shot-placeholder {
  aspect-ratio: 16/9; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 9px; color: var(--muted); font-size: 15px; text-align: center; padding: 24px;
  background: repeating-linear-gradient(45deg, transparent, transparent 12px, rgba(139,152,165,.05) 12px, rgba(139,152,165,.05) 24px);
}

.lp-shot-placeholder strong { color: var(--text); }
.lp-shot-placeholder code { font-size: 13px; color: var(--accent); }
.lp-shot-caption { text-align: center; font-size: 14px; color: var(--muted); margin: 22px 0 0; }

.lp-faq { max-width: 800px; }
.lp-faq details { border-bottom: 1px solid var(--panel-border); padding: 22px 0; }

.lp-faq summary {
  cursor: pointer; font-size: 17px; font-weight: 600; color: var(--text); list-style: none;
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  transition: color .2s var(--lp-ease);
}

.lp-faq summary:hover { color: var(--accent); }
.lp-faq summary::-webkit-details-marker { display: none; }
.lp-faq summary::after { content: "+"; color: var(--muted); font-size: 23px; line-height: 1; flex-shrink: 0; }
.lp-faq details[open] summary::after { content: "\2212"; color: var(--accent); }
.lp-faq details p { margin: 15px 0 0; color: var(--muted); font-size: 16px; max-width: 68ch; }

body.lp footer { border-top: 1px solid var(--panel-border); padding: 46px 28px 64px; color: var(--muted); font-size: 13px; line-height: 1.65; text-align: center; }
.lp-footer-inner { max-width: 800px; margin: 0 auto; }
.lp-footer-brand { color: var(--text); font-weight: 700; font-size: 16px; letter-spacing: -.01em; margin-bottom: 4px; }
.lp-footer-tag { color: var(--muted); margin-bottom: 18px; }
.lp-footer-by { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--panel-border); }

/* =========================================================================
   NARROW-VIEWPORT LAYOUT (2026-08-16: no longer an un-pin fallback -- the
   fly-through now runs at every width, see the media query above). This
   block only adjusts internal panel layout for narrow screens: side-by-side
   splits and card grids don't fit two/three across on a phone, and the
   flipped stage/copy order stops mattering once there's only one column.
   ========================================================================= */

@media (max-width: 900px) {
  .fs-panel { padding-left: 20px; padding-right: 20px; }
  .panel-split { grid-template-columns: 1fr; gap: 32px; }
  .panel-flip .panel-stage, .panel-flip .panel-copy { order: 0; }
  .p-cards { grid-template-columns: 1fr; }
  .section4-split { grid-template-columns: 1fr !important; gap: 32px !important; }
}

/* =========================================================================
   COMPACT VISUALS -- narrow AND short together, i.e. an actual phone in
   portrait (2026-08-16). The max-height:920px block above already shrinks
   text sizes; this block shrinks everything that ISN'T text -- icons, card
   padding, list/grid gaps, the copy-to-stage gap -- so even the busiest
   stage (the 7-item manual-steps list) fits one screen on its own, rather
   than relying on .panel-inner's overflow-y:auto fallback to make the tail
   of the content reachable. That fallback stays in place regardless, for
   any device/zoom level this still isn't enough for.
   ========================================================================= */
@media (max-width: 900px) and (max-height: 920px) {
  .fs-panel { padding-top: 60px; padding-bottom: 40px; }
  .panel-split { gap: 16px; }
  .panel-stage { gap: 10px; }
  .p-num { margin-bottom: 10px; }
  .p-title { margin-bottom: 10px; }
  .p-body + .p-body { margin-top: 8px; }

  .stage { padding: 14px; }
  .stage-head { margin-bottom: 10px; }

  .man-list { gap: 4px; }
  .man-list[data-phase="scattered"] { --man-bounce: 5px; }
  .man-item { padding: 7px 10px; gap: 8px; }
  .man-item i { width: 16px; height: 16px; font-size: 9px; }
  .man-merged { margin-top: 8px; padding: 9px 11px; gap: 8px; }

  .scan-grid { gap: 3px; margin-bottom: 10px; }
  .eval-rows { gap: 5px; margin-bottom: 10px; }

  .rule-block { gap: 4px; }
  .rule-row { padding: 7px 10px; gap: 7px; }
  .risk-out { padding: 9px 11px; }
  .risk-line { padding: 3px 0; }

  .exec-node { padding: 8px 11px; gap: 9px; }
  .exec-link { height: 10px; }
  .manage-grid { gap: 5px; }
  .manage-chip { padding: 7px 9px; }

  .mods { gap: 6px; }
  .mod { padding: 9px; }
  .mod-bars { height: 18px; }

  .flow-node { padding: 3px 0; gap: 11px; }
  .flow-dot { width: 24px; height: 24px; }

  .wb-link { gap: 9px; margin-bottom: 11px; }
  .wb-node-logo { height: 21px; }
  .wb-meta-row { padding: 3px 0; }

  .p-cards { gap: 8px; }
  .p-card { padding: 12px; }
}

/* One tier further for genuinely short screens (2026-08-16) -- an older or
   smaller phone with the browser's address bar and nav buttons both
   visible commonly lands in the 600-700px range. The block above already
   covers the common case; this one exists for stages that are still tight
   at that range, particularly the manual-steps list once its "merged"
   summary box is added (panel 02b). Same rule as always: spacing and
   sizing only, never font-size. */
@media (max-width: 900px) and (max-height: 820px) {
  /* padding-top must clear the fixed header, not just look tight: header
     is position:fixed and overlays panel content, so panel-inner's own
     top padding is the ONLY thing keeping content out from under it.
     header.is-stuck (true for any panel past the first, since scrolling
     into the deck immediately passes the 24px is-stuck threshold) is
     9px padding + 30px logo + 9px = 48px tall -- padding-top must stay
     above that with a small margin, or the header clips the panel's
     first line exactly as happened when this was tightened to 34px. */
  .fs-panel { padding-top: 54px; padding-bottom: 18px; }
  .panel-split { gap: 8px; }
  .p-num { margin-bottom: 6px; }
  .p-title { margin-bottom: 6px; }
  .p-body + .p-body { margin-top: 6px; }

  .stage { padding: 10px; }
  .stage-head { margin-bottom: 6px; }

  .man-list { gap: 3px; }
  .man-list[data-phase="scattered"] { --man-bounce: 4px; }
  .man-item { padding: 5px 8px; gap: 7px; }
  .man-item i { width: 14px; height: 14px; }
  .man-merged { margin-top: 4px; padding: 5px 8px; gap: 6px; }

  .panel-stage { gap: 6px; }
  .scan-grid { gap: 2px; margin-bottom: 6px; }
  .eval-rows { gap: 3px; margin-bottom: 6px; }
  .stage-note { margin-top: 2px; }
  .p-terms { margin-top: 10px; gap: 6px; }
  .p-terms li { padding: 4px 10px; }

  .rule-block { gap: 3px; }
  .rule-row { padding: 6px 9px; gap: 6px; }
  .risk-out { padding: 8px 10px; }
  .risk-line { padding: 2px 0; }

  .exec-node { padding: 7px 10px; gap: 8px; }
  .exec-link { height: 8px; }
  .manage-grid { gap: 4px; }
  .manage-chip { padding: 6px 8px; }

  .mods { gap: 5px; }
  .mod { padding: 8px; }
  .mod-bars { height: 16px; }

  .flow-node { padding: 2px 0; gap: 9px; }
  .flow-dot { width: 22px; height: 22px; }

  .wb-link { gap: 7px; margin-bottom: 8px; }
  .wb-node-logo { height: 18px; }
  .wb-meta-row { padding: 2px 0; }

  .p-cards { gap: 6px; }
  .p-card { padding: 10px; }
}

/* =========================================================================
   UN-PIN FALLBACKS -- load-bearing. Without these, a reader with reduced
   motion or with JS off sees panel one and nothing else. (A phone with JS
   and no reduced-motion preference now gets the full fly-through above,
   same as desktop -- these two are the only remaining cases that need it.)
   ========================================================================= */

html:not(.js) .deck { height: auto; }
html:not(.js) .deck-pin { position: relative; height: auto; overflow: visible; display: block; }
html:not(.js) .deck-rail { display: none; }
html:not(.js) .fs-panel {
  position: relative; inset: auto; opacity: 1; transform: none; filter: none;
  pointer-events: auto; padding: 64px 24px; border-bottom: 1px solid var(--panel-border);
}
html:not(.js) .fs-panel .p-num, html:not(.js) .fs-panel .p-title, html:not(.js) .fs-panel .p-body,
html:not(.js) .fs-panel .p-terms, html:not(.js) .fs-panel .p-statement,
html:not(.js) .fs-panel .p-stack span, html:not(.js) .fs-panel .p-cta,
html:not(.js) .fs-panel .panel-stage, html:not(.js) .fs-panel .hero-chart { opacity: 1; transform: none; }
html:not(.js) .p-hint { display: none; }
html:not(.js) .fs-panel .flow-rail::after { height: 100%; }

@media (prefers-reduced-motion: reduce) {
  .deck { height: auto !important; }
  .deck-pin { position: relative !important; height: auto !important; overflow: visible !important; display: block !important; }
  .deck-rail { display: none !important; }
  .fs-panel {
    position: relative !important; inset: auto !important; opacity: 1 !important;
    transform: none !important; filter: none !important; pointer-events: auto !important;
    padding: 64px 24px !important; border-bottom: 1px solid var(--panel-border);
  }
  .fs-panel * { transition: none !important; animation: none !important; }
  .fs-panel .p-num, .fs-panel .p-title, .fs-panel .p-body, .fs-panel .p-terms,
  .fs-panel .p-statement, .fs-panel .p-stack span, .fs-panel .p-cta,
  .fs-panel .panel-stage, .fs-panel .hero-chart { opacity: 1 !important; transform: none !important; }
  [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
  .p-hint { display: none !important; }
  .fs-panel .flow-rail::after { height: 100% !important; }
  * { scroll-behavior: auto !important; }
}

@media (max-width: 480px) {
  .lp-wrap { padding: 0 20px; }
  .p-cta { flex-direction: column; }
  .lp-btn { width: 100%; }
  /* NOT reducing scan-grid to 8 columns here (2026-08-16): the grids in
     the deck always hold a multiple of 10 cells (6 or 3 rows), so fewer
     columns means MORE rows -- taller, not more compact -- which is the
     opposite of what this breakpoint is for. 10 columns at this width
     already renders comfortably sized cells (see .man-list's neighbors,
     the "COMPACT VISUALS" block, for the rest of the short-viewport
     tuning). */
  .mods, .manage-grid { grid-template-columns: 1fr; }

  /* "Log in"/"Sign up" as bordered/filled buttons (2026-08-16) are wider
     than the plain text links they replaced -- at this width the pair no
     longer fits the logo + nav row on one line, so each button's text
     wraps onto two lines. That's not just visually broken: it roughly
     doubles the fixed header's real height, which every fs-panel's
     top padding is sized to clear (see the COMPACT VISUALS blocks
     above) -- an unaccounted-for taller header there means the header
     overlaps the first line of panel content instead of sitting above
     it. Tightening spacing here (never font-size) keeps both buttons on
     one line, which keeps the header's height fixed and predictable. */
  .lp-nav { gap: 6px; }
  /* white-space:nowrap alone is the fix. flex-shrink:0 looks like it
     should belong here too ("don't let this shrink below content size")
     but Chromium resolves the shrink-0 flex-basis for these items to
     something far wider than their actual content (~155px measured for
     "Log in" vs. the ~68px it renders at with shrink left at its default
     of 1) -- adding it re-breaks exactly what this rule fixes, just via
     overflow past the viewport edge instead of wrapping. Confirmed via
     direct measurement; leave shrink at its default. */
  .lp-nav a { white-space: nowrap; }
  .lp-nav .lp-btn-sm { padding: 6px 9px; }

  /* Room for the hamburger button (2026-08-16): two buttons plus the icon
     button don't fit next to the logo at its full size below ~480px, even
     at the tightened padding/gap above. Shrinking the logo and the header
     row's own padding/gap is the same "resize non-text elements" approach
     applied one level up, rather than hiding either button. */
  body.lp .header-top { padding: 0 16px; gap: 8px; }
  body.lp header .header-logo,
  body.lp header.is-stuck .header-logo { height: 26px; }
}

/* =========================================================================
   Auth pages (/signup, /login)

   NOTE: auth.js resets the result element with `resultEl.className =
   "status-bar"` on every submit, destroying any extra class on that node.
   Error styling is therefore scoped through .lp-auth-card ancestors plus
   the .auth-error class auth.js itself adds -- never a bespoke class on the
   element, which would style one failure and then silently stop working.
   ========================================================================= */

body.lp-auth header { position: sticky; }

.lp-auth-main {
  position: relative; min-height: calc(100vh - 200px);
  display: flex; align-items: center; justify-content: center;
  padding: 110px 28px 88px; overflow: hidden;
}

.lp-auth-bg { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.lp-auth-bg .p-grid { opacity: .2; }
.lp-auth-bg .p-glow { top: 45%; width: 620px; height: 380px; opacity: .75; }

.lp-auth-card {
  position: relative; z-index: 1; width: 100%; max-width: 420px;
  background: var(--panel); border: 1px solid var(--panel-border);
  border-radius: var(--lp-radius); padding: 38px 36px 32px;
  box-shadow: 0 30px 70px -28px rgba(0, 0, 0, .7);
}

.lp-auth-card h1 { font-size: 26px; font-weight: 700; letter-spacing: -.02em; margin: 0 0 8px; color: var(--text); }
.lp-auth-sub { font-size: 15px; color: var(--muted); margin: 0 0 28px; }

.lp-auth-card .settings-field { margin-bottom: 18px; font-size: 14px; }
.lp-auth-card .settings-field > span { color: var(--text); font-weight: 500; margin-bottom: 2px; }
.lp-auth-card .settings-field .muted { color: var(--muted); font-weight: 400; }

.lp-auth-card .settings-field input {
  padding: 12px 14px; font-size: 15px; border-radius: 8px;
  transition: border-color .2s var(--lp-ease), box-shadow .2s var(--lp-ease);
}

.lp-auth-card .settings-field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--lp-accent-soft); }
.lp-auth-card .lp-btn { width: 100%; margin-top: 4px; }

.lp-auth-card .status-bar { display: block; font-size: 14px; color: var(--muted); margin: 0; }
.lp-auth-card .status-bar:not(:empty) { margin: 0 0 16px; }

.lp-auth-card .status-bar.auth-error {
  color: var(--red); background: var(--lp-red-soft);
  border: 1px solid rgba(248, 81, 73, .25); border-radius: 8px; padding: 11px 14px;
}

.lp-auth-switch {
  margin: 22px 0 0; padding-top: 20px; border-top: 1px solid var(--panel-border);
  font-size: 14px; color: var(--muted); text-align: center;
}

.lp-auth-switch a { color: var(--accent); text-decoration: none; font-weight: 600; }
.lp-auth-switch a:hover { text-decoration: underline; }

.lp-auth-col { display: flex; flex-direction: column; align-items: center; width: 100%; }

.lp-auth-trust {
  position: relative; z-index: 1; margin: 26px auto 0; max-width: 420px;
  display: flex; flex-direction: column; gap: 10px;
}

.lp-auth-trust li { position: relative; list-style: none; padding-left: 26px; font-size: 14px; color: var(--muted); line-height: 1.5; }
.lp-auth-trust li::before { content: ""; position: absolute; left: 4px; top: 7px; width: 7px; height: 7px; border-radius: 50%; background: var(--green); }

@media (max-width: 480px) {
  .lp-auth-card { padding: 30px 22px 26px; }
  .lp-auth-main { padding: 92px 20px 64px; min-height: 0; }
}

/* =========================================================================
   HOME PAGE (2026-08-17) -- everything below is specific to the new
   landing.html content: the Markets card icons, the hero's Strategy/
   Signal/Execution relay, the How It Works pipeline timeline, and a
   couple of scoped overrides for widgets/components reused from
   elsewhere on the site that need different behavior here.
   ========================================================================= */

/* The home page reuses .wb-link/.wb-rail/.wb-meta from the Webull
   integration panel on /how-it-works verbatim, but that panel's rail
   flow / pulse / "Live" glow are gated behind .fs-panel.is-on (only
   ever true inside the scroll deck). The home page has no deck, so
   scope the same animations to fire unconditionally when this widget
   is used inside a plain .lp-section instead. */
.lp-section .wb-rail::before { animation: lp-wb-flow-dash .5s linear infinite; }
.lp-section .wb-rail-pulse { animation: lp-wb-rail-pulse 2.2s var(--lp-panel-ease) infinite; }
.lp-section .wb-meta-row.is-key b { animation: lp-glow-pulse 2.1s var(--lp-panel-ease) infinite; }

/* .lp-auth-trust's existing rule (above) caps it at 420px and
   auto-centers it, correct for the standalone auth-card usage on
   login/signup. On the home page it sits under a full-width,
   left-aligned paragraph inside a split column, so let it fill that
   column instead of floating centered at a fixed width. */
.section4-split .lp-auth-trust { max-width: none; margin: 26px 0 0; }

/* Market visual components -- one small animated icon per Markets
   card, sitting above its stage-head. Always-on (not gated behind
   .fs-panel.is-on, since these live on a static page, not the deck) --
   Live gets a vivid, energetic motion; Coming Soon gets the same idea
   rendered muted and slower, so status reads through the motion
   itself, not just the pill.

   Each shape is specific to its own market, researched rather than one
   icon recolored five times: stocks get an actual price chart, crypto
   a spinning coin, futures an order book, forex an actual traded pair,
   options a real payoff diagram.

   Do not redeclare height on any .mv-* class below -- .mv already sets
   a fixed height, and a second same-specificity height rule on the
   same element would silently cancel it (whichever rule is declared
   later in the cascade wins), letting percentage-height children
   resolve against nothing definite and blow the widget up past its
   card's border. */
.mv { height: 64px; margin-bottom: 20px; }

/* Stocks -- ticking candlesticks with a trend line overlay, the same
   "current price chart" language the hero already uses. Neutral
   palette (var(--muted)/var(--panel-border), no green/red) so the
   widget reads as one consistent visual system across all five markets
   -- status lives in the pill, not in the shape's own color. Up/down
   still shows through as alternating shade, not hue; tempo (fastest of
   the five) is what marks this one as actually trading in real time. */
.mv-candles { position: relative; display: flex; align-items: flex-end; gap: 4px; padding: 0 2px; }
.mv-candles-trend {
  position: absolute; left: 0; right: 0; top: 22%; height: 2px;
  background: repeating-linear-gradient(90deg, var(--muted) 0 6px, transparent 6px 12px);
  background-size: 16px 100%; opacity: .55; animation: mv-trend-flow .7s linear infinite;
}
@keyframes mv-trend-flow { to { background-position: -16px 0; } }
.mv-candles i { flex: 1; display: block; border-radius: 2px; transform-origin: bottom; animation: mv-candle-tick 1.3s var(--lp-panel-ease) infinite; }
.mv-candles i.up { background: var(--muted); }
.mv-candles i.down { background: var(--panel-border); border: 1px solid var(--muted); }
.mv-candles i:nth-child(2) { height: 42%; animation-delay: 0ms; }
.mv-candles i:nth-child(3) { height: 58%; animation-delay: 90ms; }
.mv-candles i:nth-child(4) { height: 34%; animation-delay: 180ms; }
.mv-candles i:nth-child(5) { height: 70%; animation-delay: 270ms; }
.mv-candles i:nth-child(6) { height: 50%; animation-delay: 360ms; }
.mv-candles i:nth-child(7) { height: 88%; animation-delay: 450ms; }
.mv-candles i:nth-child(8) { height: 66%; animation-delay: 540ms; }
@keyframes mv-candle-tick { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(.82); } }

/* Crypto -- a coin flipping in place, a token itself rather than the
   ledger behind it. backface-visibility hides each face while it's
   edge-on, so the swap between the two reads as one continuous spin
   rather than a flat card flattening (Coming Soon: muted, unhurried). */
.mv-coin { display: flex; align-items: center; justify-content: center; perspective: 320px; }
.mv-coin-disc { position: relative; width: 40px; height: 40px; transform-style: preserve-3d; animation: mv-coin-spin 3.2s linear infinite; }
.mv-coin-face {
  position: absolute; inset: 0; border-radius: 50%; backface-visibility: hidden;
  background: var(--panel-border); border: 2px solid var(--muted);
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 700; color: var(--muted);
}
.mv-coin-face-b { transform: rotateY(180deg); }
@keyframes mv-coin-spin { from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } }

/* Futures -- an order book: bid depth (left) and ask depth (right)
   stacked as rows around a central spread, exactly the ladder a
   futures trader actually watches -- not a chart of price over time
   (that's what Stocks already is) but a snapshot of resting size at
   each level right now. Each bar's width pulses independently and out
   of phase, so the book reads as continuously being added to and
   pulled, the way a live book never sits still (Coming Soon: muted,
   unhurried). No height rule on .mv-book itself -- see the note above
   .mv. */
.mv-book { display: flex; flex-direction: column; justify-content: center; gap: 5px; padding: 0 2px; }
.mv-book-row { position: relative; display: flex; align-items: center; height: 6px; }
.mv-book-row::after {
  content: ""; position: absolute; left: 50%; top: -3px; bottom: -3px; width: 1px;
  background: var(--panel-border);
}
.mv-book-side { width: 50%; height: 100%; display: flex; }
.mv-book-bids { justify-content: flex-end; }
.mv-book-bar {
  height: 100%; width: var(--w, 40%); border-radius: 2px;
  animation: mv-book-depth 2.8s var(--lp-panel-ease) infinite; animation-delay: var(--d, 0ms);
}
.mv-book-bids .mv-book-bar { background: var(--muted); transform-origin: 100% 50%; }
.mv-book-asks .mv-book-bar { background: var(--panel-border); border: 1px solid var(--muted); transform-origin: 0% 50%; }
@keyframes mv-book-depth { 0%, 100% { transform: scaleX(1); } 50% { transform: scaleX(.62); } }

/* Forex -- an actual traded pair (EUR/USD, the single most-traded pair
   in the world) rather than an abstract "FX" label, quoted rate
   swapping direction between them along a dashed rail (Coming Soon:
   muted). */
.mv-pair { display: flex; align-items: center; justify-content: space-between; padding: 0 2px; }
.mv-pair-node {
  width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0;
  background: var(--bg); border: 1px solid var(--panel-border);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; color: var(--muted);
}
.mv-pair-rail { position: relative; flex: 1; height: 2px; margin: 0 8px; }
.mv-pair-rail::before {
  content: ""; position: absolute; inset: 0; opacity: .5;
  background: repeating-linear-gradient(90deg, var(--muted) 0 4px, transparent 4px 8px);
}
.mv-pair-dot {
  position: absolute; top: 50%; left: 0; width: 6px; height: 6px; margin-top: -3px;
  border-radius: 50%; background: var(--muted); opacity: .8;
  animation: mv-pair-swap 3s ease-in-out infinite;
}
@keyframes mv-pair-swap { 0%, 100% { left: 0; } 50% { left: calc(100% - 6px); } }

/* Options -- an actual payoff diagram (flat, then a kinked rise past
   the strike), the chart every options course opens with, drawing
   itself in like a live quote rather than a static print (Coming Soon:
   muted). */
.mv-payoff { display: flex; align-items: center; }
.mv-payoff svg { width: 100%; height: 100%; overflow: visible; }
.mv-payoff-base { stroke: var(--panel-border); stroke-width: 1; }
.mv-payoff-strike { stroke: var(--panel-border); stroke-width: 1; stroke-dasharray: 2 2; }
.mv-payoff-line {
  fill: none; stroke: var(--muted); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 106; stroke-dashoffset: 106;
  animation: mv-payoff-draw 3.4s ease-in-out infinite;
}
@keyframes mv-payoff-draw {
  0%   { stroke-dashoffset: 106; opacity: .35; }
  14%  { opacity: 1; }
  55%  { stroke-dashoffset: 0; opacity: 1; }
  86%  { stroke-dashoffset: 0; opacity: 1; }
  100% { stroke-dashoffset: 106; opacity: .35; }
}

/* Hero pipeline -- Strategy / Signal / Execution, the three stages
   named in the closing CTA's tagline ("Define the strategy. Automate
   the process. Let the system execute."), illustrated as a relay: a
   pulse travels rail-to-rail while each badge lights up in turn
   behind it. */
.hero-flow { max-width: 460px; margin: 0 auto 36px; }
.hero-flow-row {
  display: grid; grid-template-columns: 48px 1fr 48px 1fr 48px; align-items: center;
}
.hero-node-badge {
  width: 48px; height: 48px; border-radius: 50%; background: var(--panel);
  border: 1px solid var(--panel-border); display: flex; align-items: center; justify-content: center;
  animation: hero-badge-glow 3.6s var(--lp-panel-ease) infinite;
}
.hero-node-badge svg { width: 20px; height: 20px; color: var(--muted); }
.hero-node-badge.n2 { animation-delay: 1.2s; }
.hero-node-badge.n3 { animation-delay: 2.4s; }
@keyframes hero-badge-glow {
  0%, 100% { box-shadow: 0 0 0 0 transparent; border-color: var(--panel-border); }
  50% { box-shadow: 0 0 14px 2px var(--lp-accent-line); border-color: var(--accent); }
}
.hero-rail { position: relative; height: 2px; }
.hero-rail::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg, var(--panel-border) 0 6px, transparent 6px 12px);
}
.hero-rail-pulse {
  position: absolute; top: 50%; left: 0; width: 8px; height: 8px; margin-top: -4px;
  border-radius: 50%; background: var(--accent); box-shadow: 0 0 8px 2px var(--lp-accent-line);
  opacity: 0; animation: hero-rail-travel 3.6s var(--lp-panel-ease) infinite;
}
.hero-rail.r2 .hero-rail-pulse { animation-delay: 1.2s; }
@keyframes hero-rail-travel {
  0%   { left: 0; opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { left: calc(100% - 8px); opacity: 0; }
}
.hero-flow-labels { display: flex; margin-top: 10px; }
.hero-flow-labels span {
  flex: 1; font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); text-align: center; white-space: nowrap;
}

/* Pipeline flow -- the 6 how-it-works stages connected by a single
   timeline: a dashed rail runs between each pair of cards (in reading
   order, including the row-wrap from card 3 to card 4) with a
   traveling pulse relaying through all five segments, plus a numbered
   step badge on each card so the sequence reads clearly even with
   motion off. */
.p-cards.pipeline-flow { row-gap: 16px; }
@media (min-width: 901px) {
  .p-cards.pipeline-flow { grid-template-columns: 1fr 34px 1fr 34px 1fr; align-items: stretch; }
}
@media (max-width: 900px) {
  .p-cards.pipeline-flow { grid-template-columns: 1fr; }
}
.pf-rail { position: relative; }
.pf-rail.pf-rail-row { grid-column: 1 / -1; }
@media (min-width: 901px) {
  .pf-rail { height: 2px; align-self: center; }
  .pf-rail.pf-rail-row { margin: 2px 0; }
}
@media (max-width: 900px) {
  .pf-rail { height: 26px; width: 2px; margin: 0 auto; }
}
.pf-rail::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg, var(--panel-border) 0 5px, transparent 5px 10px);
}
@media (max-width: 900px) {
  .pf-rail::before { background: repeating-linear-gradient(180deg, var(--panel-border) 0 5px, transparent 5px 10px); }
}
.pf-rail-pulse {
  position: absolute; top: 50%; left: 0; width: 6px; height: 6px; margin-top: -3px;
  border-radius: 50%; background: var(--accent); box-shadow: 0 0 6px 1px var(--lp-accent-line);
  opacity: 0; animation: pf-rail-travel-h 4.5s linear infinite;
}
@keyframes pf-rail-travel-h { 0% { left: 0; opacity: 0; } 8% { opacity: 1; } 92% { opacity: 1; } 100% { left: calc(100% - 6px); opacity: 0; } }
@media (max-width: 900px) {
  .pf-rail-pulse { top: 0; left: 50%; margin-top: 0; margin-left: -3px; animation-name: pf-rail-travel-v; }
  @keyframes pf-rail-travel-v { 0% { top: 0; opacity: 0; } 8% { opacity: 1; } 92% { opacity: 1; } 100% { top: calc(100% - 6px); opacity: 0; } }
}
.pf-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%; background: var(--bg);
  border: 1px solid var(--panel-border); color: var(--muted);
  font-size: 12px; font-weight: 700; margin-bottom: 10px;
}
