/* yeaboi.ai landing page — extracted from index.html so it can be
   universally loaded (needed for client-side nav to/from the landing page). */
  /* Landing-page layout. Colors route through site.css tokens. */

  /* ---------- hero — the headline owns the first viewport; product below the fold ---------- */
  /* overflow:clip, NOT hidden — hidden would make .hero the scrollport that
     position:sticky resolves against (it never scrolls, so the pinned
     terminal in .scrolly-sticky would never stick); clip just clips. */
  .hero{padding:0 0 110px; position:relative; overflow:clip}
  .hero > .wrap{
    width:100%; max-width:1120px;
    /* body is inset by the fixed rail, so "centered" lands right of the true
       viewport centre — pull back by up to rail/2, clamped so the content
       never slides under the rail on narrower windows */
    transform:translateX(calc(-1 * min(var(--rail) / 2, max(0px, (100vw - var(--rail) - 1120px) / 2))));
  }
  .hero-copy{
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    text-align:center; min-height:100vh; position:relative; padding:40px 0;
  }
  .scroll-hint{
    /* centered via inset+margin, not transform — the entrance animation's
       end state (transform:none) would cancel a translateX(-50%) */
    position:absolute; bottom:30px; left:0; right:0; margin:0 auto; width:max-content;
    font-family:var(--mono); font-size:.74rem; letter-spacing:.14em; text-transform:uppercase;
    color:var(--text-dim); display:flex; flex-direction:column; align-items:center; gap:6px;
  }
  .scroll-hint::after{content:"↓"; font-size:1rem; animation:hint-bob 2s ease-in-out infinite}
  @keyframes hint-bob{50%{transform:translateY(5px)}}
  @media (prefers-reduced-motion:reduce){.scroll-hint::after{animation:none}}
  .tagline{
    margin:26px 0 0; font-family:var(--display); font-weight:640;
    font-size:clamp(2.7rem,5.8vw,4.4rem); letter-spacing:-.032em; line-height:1.04;
  }
  .tagline em{font-style:normal; color:var(--accent-bright); font-weight:640}
  .lede{margin:24px 0 36px; max-width:56ch; color:var(--text-muted); font-size:1.14rem; line-height:1.7}
  .lede strong{color:var(--text); font-weight:600}
  .hero-cta{display:flex; align-items:center; gap:14px; flex-wrap:wrap; justify-content:center}
  .hero-cta .codeblock{min-width:360px}
  .subnote{margin:16px 0 0; font-family:var(--mono); font-size:.8rem; color:var(--text-dim)}
  .subnote a{color:var(--text-muted)} .subnote a:hover{color:var(--accent)}

  /* staggered hero entrance */
  @keyframes rise{from{opacity:0; transform:translateY(22px)} to{opacity:1; transform:none}}
  .hero-copy > *{animation:rise .8s cubic-bezier(.2,.6,.2,1) both}
  .hero-copy > *:nth-child(1){animation-delay:.05s}
  .hero-copy > *:nth-child(2){animation-delay:.14s}
  .hero-copy > *:nth-child(3){animation-delay:.23s}
  .hero-copy > *:nth-child(4){animation-delay:.32s}
  .hero-copy > *:nth-child(5){animation-delay:.4s}
  .hero-demo{animation:rise 1s cubic-bezier(.2,.6,.2,1) .35s both}
  @media (prefers-reduced-motion:reduce){.hero-copy > *,.hero-demo{animation:none}}

  /* the product, front and centre, floating on a glow — below the fold. */
  .hero-demo{position:relative; margin-top:8vh; padding-bottom:60px}

  /* ---- scrollytelling: pinned terminal + a step rail at the side.
     The terminal column is sticky (centred in the viewport) while the steps
     scroll past; site.js watches each .scrolly-step cross the viewport's
     middle and drives the TUI's real selection — the same show() the
     arrow-key auto-cycle used. Pure CSS sticky + IntersectionObserver,
     no scroll library. ---- */
  .scrolly{
    display:grid; grid-template-columns:minmax(0,1fr) 320px;
    gap:56px; align-items:start;
  }
  .scrolly-sticky{
    position:sticky; top:max(28px, calc(50vh - 340px));
    z-index:1;
  }
  .scrolly-sticky::before{
    /* the frame's glow, moved here so it travels with the pinned terminal */
    content:""; position:absolute; inset:-12% -10%; z-index:0; pointer-events:none;
    background:
      radial-gradient(50% 60% at 50% 22%, var(--accent-glow), transparent 72%),
      radial-gradient(38% 42% at 22% 70%, rgba(80,150,210,.16), transparent 74%);
    filter:blur(36px); opacity:.62;
  }
  .scrolly-steps{display:flex; flex-direction:column; padding:4vh 0 8vh}
  .scrolly-step{
    min-height:62vh; display:flex; flex-direction:column; justify-content:center;
    gap:14px; opacity:.25; transition:opacity .45s ease;
  }
  .scrolly-step.is-active{opacity:1}
  .step-k{
    margin:0; font-family:var(--mono); font-size:.78rem; font-weight:600;
    letter-spacing:.14em; text-transform:uppercase; color:var(--sh);
  }
  .scrolly-step h3{
    margin:0; font-family:var(--display); font-weight:620; font-size:1.35rem;
    letter-spacing:-.02em; line-height:1.25; color:var(--text);
  }
  .scrolly-step > p:not(.step-k){
    margin:0; color:var(--text-muted); font-size:.98rem; line-height:1.65;
  }
  @media (max-width:900px){
    /* mobile: the step rail goes away entirely — as a flat list it just
       duplicated the modes grid below and doubled the page length. The
       terminal stands alone and auto-cycles through the modes itself
       (site.js re-enables the auto-advance when the rail is hidden). */
    .scrolly{grid-template-columns:1fr}
    .scrolly-sticky{position:static}
    .scrolly-steps{display:none}
  }
  .app-frame{
    /* A real terminal WINDOW, not a floating panel: chrome bar, solid deep
       ground, heavy soft shadow. The one place on the page allowed to be
       fully opaque — it's the product. Sized for the app's real 80-column
       frame at a 14px cell. */
    position:relative; z-index:1; width:100%; max-width:780px; margin:0 auto;
    background:#0a0b0d; border:1px solid var(--border-strong);
    border-radius:var(--radius-lg); overflow:hidden;
    box-shadow:var(--shadow-term), 0 0 0 1px rgba(0,0,0,.4);
  }
  .tui-bar{
    display:flex; align-items:center; gap:7px; padding:12px 16px;
    background:linear-gradient(180deg,#15171b,#0f1114);
    border-bottom:1px solid var(--border);
  }
  .tui-bar .dot{width:11px; height:11px; border-radius:50%}
  .tui-bar .dot:nth-child(1){background:#ec6a5e}
  .tui-bar .dot:nth-child(2){background:#f4bf4f}
  .tui-bar .dot:nth-child(3){background:#61c554}
  .tui-bar-title{
    margin:0 auto; transform:translateX(-20px);
    font-family:var(--mono); font-size:.74rem; color:var(--text-dim); white-space:nowrap;
  }

  /* HTML recreation of the real app STARTUP — src/yeaboi/ui/splash.py's
     wordmark fade/shine/fade, then the actual mode-select screen
     (src/yeaboi/ui/mode_select/screens/_screens.py:_build_mode_screen).
     One terminal cell size everywhere (a real terminal has exactly one
     grid); the app's own rounded white Panel border drawn inside. */
  .tui{
    --t-text:#e4e6e8;          /* rich "white" */
    --t-muted:#787890;         /* theme.muted rgb(120,120,140) */
    --t-dim:#62676f;           /* theme.dim */
    color:var(--t-text); font-family:var(--mono);
    font-size:13.5px; line-height:1.5; padding:12px 14px 16px;
    /* faint phosphor scanlines — the texture that says "terminal", too
       subtle to read as an effect, just enough to not read as a flat div */
    background-image:repeating-linear-gradient(0deg,
      rgba(255,255,255,.012) 0px, rgba(255,255,255,.012) 1px,
      transparent 1px, transparent 3px);
  }
  /* the app's Panel(box=ROUNDED, border_style="white", padding=(1,2)) */
  .tui-panel{
    position:relative; border:1px solid rgba(228,230,232,.45);
    border-radius:10px; padding:20px 24px 14px;
  }

  .tui-splash{
    /* min-height = the drawn menu's exact height (590px panel − 34px panel
       padding − 2px border) so the Panel doesn't resize by a single pixel
       when the splash hands over to the mode screen */
    display:flex; align-items:center; justify-content:center; min-height:554px;
    animation:tui-splash-fade 2.4s ease forwards;
  }
  .tui-splash-word{
    margin:0; font-size:13px; line-height:1.2; letter-spacing:0; text-align:center;
    color:#4664b4; text-shadow:0 0 16px var(--accent-glow);
  }
  @keyframes tui-splash-fade{
    0%{opacity:0} 18%{opacity:1} 72%{opacity:1} 100%{opacity:0}
  }
  @media (prefers-reduced-motion:reduce){
    .tui-splash{animation:none; opacity:0}
  }

  .tui-menu{display:flex; flex-direction:column; gap:20px; animation:tui-fade .5s ease both}
  /* the hidden attribute must actually hide: this class's display:flex is an
     author rule, so it silently overrides the UA's [hidden]{display:none} —
     without this, the "hidden" menu renders UNDER the splash during startup,
     stacking the two and inflating the terminal until the splash is removed */
  .tui-menu[hidden]{display:none}
  @keyframes tui-fade{from{opacity:0; transform:translateY(4px)} to{opacity:1; transform:none}}
  @media (prefers-reduced-motion:reduce){.tui-menu{animation:none}}

  .tui-mode{
    -webkit-appearance:none; appearance:none; background:none; border:none; padding:0;
    display:block; width:100%; text-align:left; cursor:pointer;
    padding-left:4ch; /* the app's PAD = "    " before every title/desc row */
    font-size:inherit; font-family:inherit;
  }
  .tui-mode-title{
    /* zero letter-spacing, tight line-height — these ▀▄█░ block glyphs are
       designed to tile edge-to-edge with no gap, exactly like a real
       terminal's character grid; any spacing breaks them into a dotted
       mess instead of solid letterforms. Same cell size as every other row —
       a terminal has exactly one grid. */
    margin:0; font-size:inherit; line-height:1.1; letter-spacing:0;
    /* unselected: the app's exact dim — each channel halved, floored at 40
       (see _build_mode_row's _unsel_style), baked into --mcd per mode */
    color:rgb(var(--mcd)); transition:color .35s;
  }
  .tui-mode.is-active .tui-mode-title{
    /* selected: full accent with the app's traveling white shimmer hotspot
       (shimmer_style: 0.6 sweeps/sec) — a narrow white band crossing the
       block letters, painted via background-clip:text */
    color:transparent;
    background-image:linear-gradient(90deg,
      rgb(var(--mc)) 0%, rgb(var(--mc)) 42%,
      rgb(255,255,255) 50%,
      rgb(var(--mc)) 58%, rgb(var(--mc)) 100%);
    background-size:300% 100%;
    -webkit-background-clip:text; background-clip:text;
    animation:tui-shimmer 1.67s linear infinite;
  }
  @keyframes tui-shimmer{from{background-position:100% 0} to{background-position:0% 0}}
  @media (prefers-reduced-motion:reduce){
    .tui-mode.is-active .tui-mode-title{
      color:rgb(var(--mc)); background-image:none; animation:none;
      -webkit-background-clip:border-box; background-clip:border-box;
    }
  }
  .tui-mode-desc{
    /* only the selected item's description takes up space, same as the
       real arrow-key list — one blank row then the typewritten desc in
       white (matches _build_mode_row's style="white"). The open slot is a
       FIXED two-row height (the real builder reserves desc space in body_h
       and the Panel is terminal-fixed) so the frame never wobbles as
       different-length descriptions cycle. */
    margin:0; height:0; overflow:hidden; line-height:1.5; color:var(--t-text);
    opacity:0; transition:opacity .2s, height .25s ease, margin .25s ease;
  }
  .tui-mode.is-active .tui-mode-desc{opacity:1; height:42px; margin:14px 0 0}
  @media (hover:hover){
    .tui-mode:focus-visible{outline:1px solid var(--accent-dim); outline-offset:4px; border-radius:2px}
  }

  /* bottom furniture — the app's tip block + version row (_build_tip_rows /
     _build_version_row), pinned under the centred mode list */
  .tui-foot{margin-top:26px; display:flex; flex-direction:column; gap:4px}
  .tui-tip{
    margin:0; text-align:center; color:rgb(198,198,208);
    transition:opacity .9s ease; /* cross-fade stand-in for tip_brightness lerp */
  }
  .tui-tip.fading{opacity:0}
  .tui-tip-dots{margin:0; text-align:center; color:rgb(70,70,82)}
  .tui-tip-dots .on{color:rgb(226,186,96)}
  .tui-tip-hint{margin-left:2.5em}
  .tui-tip-hint b{font-weight:700; color:rgb(210,210,220)}
  .tui-version{margin:6px 0 0; color:rgb(70,70,82)}
  .tui-version .v{color:rgb(120,120,140)}
  .tui-version b{font-weight:700; color:rgb(210,210,220)}
  .tui-version .sep,.tui-version .lbl{color:rgb(70,70,82)}
  /* music status stamped on the Panel's bottom BORDER, right-aligned
     (MusicLive sets Panel.subtitle) — masks the border behind it */
  .tui-music{
    position:absolute; right:22px; bottom:0; transform:translateY(50%);
    background:#0a0b0d; padding:0 8px; white-space:pre;
    font-size:.92em; color:var(--t-muted);
  }
  .tui-music .hint{color:var(--t-dim)}

  /* ---------- shared section furniture ---------- */
  section{padding:84px 0; position:relative}
  .eyebrow{
    font-family:var(--mono); font-size:.76rem; color:var(--accent); margin:0 0 14px;
    letter-spacing:.16em; text-transform:uppercase; font-weight:500;
  }
  h2{
    margin:0 0 12px; font-family:var(--display); font-weight:620;
    font-size:clamp(1.7rem,3.4vw,2.35rem); letter-spacing:-.025em; line-height:1.15;
  }
  h2 em{font-style:normal; color:var(--accent-bright); font-weight:620}
  .section-lede{margin:0 0 44px; color:var(--text-muted); max-width:58ch; font-size:1.05rem}
  .rule{border:none; border-top:1px solid var(--border); margin:0}

  /* ---------- stack / integrations (plain left-aligned text, on-theme) —
     two labelled groups: board/data sources and LLM providers ---------- */
  .stack{padding:40px 0}
  .stack .eyebrow{margin-bottom:22px}
  .stack-group{
    display:grid; grid-template-columns:170px 1fr; align-items:baseline; gap:8px 28px;
    max-width:760px;
  }
  .stack-group + .stack-group{margin-top:18px; padding-top:18px; border-top:1px solid var(--border)}
  .stack-cat{
    font-family:var(--mono); font-size:.74rem; letter-spacing:.14em; text-transform:uppercase;
    color:var(--text-dim); font-weight:500;
  }
  .stack-list{
    display:flex; flex-wrap:wrap; gap:12px 26px;
    font-family:var(--mono); font-size:.92rem; font-weight:500;
  }
  .stack-list span{color:var(--text-muted); transition:color .2s}
  .stack-list span:hover{color:var(--text)}
  @media (max-width:600px){
    .stack-group{grid-template-columns:1fr; gap:10px}
  }

  /* ---------- modes — one connected panel, hairline-divided, each cell
     carrying its own TUI accent (--mh, set inline in the HTML from the
     per-mode tokens). Grid rows share track height so cells always align;
     copy is never clamped — the cell grows to fit and the mono tag line
     pins to the bottom so there's no dead space, only structure. ---------- */
  .modes{
    display:grid; grid-template-columns:repeat(3,1fr); gap:1px; background:var(--border);
    border:1px solid var(--border); border-radius:var(--radius-lg); overflow:hidden;
    /* Scopes each card's backdrop-filter to blur only what's inside .modes —
       without this, the cards' glassy blur reaches straight through to the
       page's fixed, scroll-linked .scroll-aura glow behind everything and
       shows it as a smeared colored haze once you're scrolled far enough
       down for its opacity to have built up. */
    isolation:isolate;
  }
  .mode-card{
    position:relative; min-height:225px;
    background:rgba(255,255,255,.022); isolation:isolate;
    -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
    border:none; border-radius:0;
    padding:28px 26px 24px; display:flex; flex-direction:column; gap:12px;
    transition:background .25s;
  }
  /* mode-colored hairline along the top edge, lit on hover — the cell's
     accent identity without painting the whole card */
  .mode-card::before{
    content:""; position:absolute; top:0; left:0; right:0; height:2px;
    background:linear-gradient(90deg, var(--mh), transparent 75%);
    opacity:.28; transition:opacity .25s;
  }
  .mode-card:hover::before{opacity:.9}
  .mode-card:hover{background:color-mix(in srgb, var(--mh) 4.5%, rgba(255,255,255,.015))}
  .mode-head{display:flex; align-items:baseline; gap:12px}
  .mode-head .num{font-family:var(--mono); font-size:.76rem; color:var(--mh); font-weight:600; letter-spacing:.04em}
  .mode-head h3{margin:0; font-size:1.08rem; color:var(--text); font-weight:600; letter-spacing:-.01em}
  .mode-card > p{margin:0; color:var(--text-muted); font-size:.93rem; line-height:1.62; max-width:44ch; flex:1}
  .mode-tag{
    font-family:var(--mono); font-size:.72rem; letter-spacing:.08em; text-transform:uppercase;
    color:var(--text-dim);
  }
  .mode-tag::before{content:"→ "; color:var(--mh)}

  /* ---------- pipeline — a real flow diagram: numbered rings sitting on a
     connecting line, each stage a step off it. Mirrors the hero demo above,
     which cycles through these exact same five stages. ---------- */
  .pipeline{
    display:grid; grid-template-columns:repeat(5,1fr); gap:0; counter-reset:stage;
    border:1px solid var(--border); border-radius:var(--radius-lg); overflow:hidden;
    background:linear-gradient(180deg, var(--bg-raised-2), var(--bg-raised)); box-shadow:var(--shadow-card);
    position:relative;
  }
  .pipeline::before{
    content:""; position:absolute; left:44px; right:44px; top:47px; height:1px;
    background:linear-gradient(90deg, transparent, var(--border-strong) 6%, var(--border-strong) 94%, transparent);
    z-index:0;
  }
  .stage{
    padding:26px 22px 24px; border-left:1px solid var(--border); position:relative;
    transition:background .2s;
  }
  .stage:first-child{border-left:none}
  .stage:hover{background:rgba(255,255,255,.02)}
  .stage::before{
    counter-increment:stage; content:counter(stage,decimal-leading-zero);
    position:relative; z-index:1; display:flex; align-items:center; justify-content:center;
    width:30px; height:30px; border-radius:50%;
    border:1px solid var(--border-strong); background:var(--bg-raised);
    font-family:var(--mono); font-size:.7rem; color:var(--accent); letter-spacing:.02em;
    transition:border-color .2s, color .2s, box-shadow .2s;
  }
  .stage:hover::before{border-color:var(--accent); color:var(--accent-bright); box-shadow:0 0 0 3px var(--accent-dim)}
  .stage h3{margin:14px 0 8px; font-size:1rem; font-weight:600; letter-spacing:-.01em}
  .stage p{margin:0; color:var(--text-muted); font-size:.85rem; line-height:1.6}
  .pipe-note{
    margin:26px 0 0; display:flex; gap:14px; align-items:baseline; max-width:64ch;
    color:var(--text-muted); font-size:.95rem;
  }
  .pipe-dots{display:none}  /* phone-only carousel indicator (see 600px block) */
  .pipe-note .k{font-family:var(--mono); font-size:.8rem; color:var(--accent); white-space:nowrap}
  .pipe-note code{color:var(--text); font-size:.85em; background:var(--bg-raised);
    border:1px solid var(--border); border-radius:5px; padding:1px 6px}

  /* ---------- get started ---------- */
  .start-grid{display:grid; grid-template-columns:1fr 1fr; gap:64px; align-items:start}
  /* one compact ROW per step — label · command chip · note — so the cards
     stay short and fill their width instead of stranding empty space. Each
     part owns its own grid column, so nothing wraps mid-line. Collapses to a
     clean vertical stack on narrow phones (see the 600px block). */
  .steps{display:grid; gap:10px}
  .step{
    display:grid; grid-template-columns:108px auto minmax(0,1fr);
    align-items:center; gap:16px; padding:12px 20px; transition:border-color .2s;
  }
  .step:hover{border-color:var(--border-strong)}
  .step-k{
    margin:0; font-family:var(--mono); font-size:.74rem; letter-spacing:.14em;
    text-transform:uppercase; color:var(--text-dim); font-weight:500; white-space:nowrap;
  }
  .step-k .n{color:var(--accent); font-weight:700; margin-right:8px}
  .step-cmd{
    font-family:var(--mono); font-size:.92rem; color:var(--text);
    background:var(--bg-sunken); border:1px solid var(--border); border-radius:6px;
    padding:6px 12px; max-width:100%; overflow-x:auto; white-space:nowrap; justify-self:start;
  }
  .step-cmd::before{content:"$ "; color:var(--accent)}
  .step-note{margin:0; color:var(--text-muted); font-size:.86rem}
  .step-note code{color:var(--text); background:var(--bg-sunken); border:1px solid var(--border);
    border-radius:5px; padding:2px 7px; font-size:.88em}
  /* step 1's alternative is wide (a whole command) — drop it to a second row
     under the command chip instead of cramming the note column */
  .step-2row .step-note{grid-column:2 / -1; grid-row:2}
  .dim{color:var(--text-dim)}
  .note{margin-top:18px; color:var(--text-muted); font-size:.92rem}
  .note code{color:var(--text); font-size:.86em}

  .cta-panel h2{margin-bottom:16px}
  .cta-panel .lede2{color:var(--text-muted); margin:0 0 30px; max-width:40ch; font-size:1.05rem}
  .cta-panel .codeblock{max-width:430px}
  .cta-links{margin-top:22px; display:flex; gap:12px; flex-wrap:wrap}

  /* ---------- footer (left-aligned, on-theme) ---------- */
  /* divider fades in from the left (a border-image gradient rather than a flat
     hairline) so it reads as emerging into the footer rather than a hard rule */
  footer{
    border-top:1px solid transparent;
    border-image:linear-gradient(90deg, transparent 0%, var(--border) 22%, var(--border) 100%) 1;
    padding:48px 0 80px; position:relative; z-index:1;
  }
  .foot{display:flex; flex-direction:column; align-items:flex-start; gap:24px}

  /* the mascot: walks the bottom edge of the viewport, x driven from scroll
     progress (site.js). Transform layering, outermost → innermost:
       .duck-walker  — JS translateX + direction flip
       .duck-rig     — waddle while scrolling
       .d-wing       — continuous gentle flap (own timeline)
       .d-glasses    — slow sunglasses bob (own timeline)
     Separate elements per transform so none of them ever fight. */
  .duck-walker{
    position:fixed; top:0; left:0; z-index:40; pointer-events:none;
    will-change:transform;
    transition:opacity .15s ease;
  }
  /* the sprite itself IS tappable (the wrapper stays click-through so it never
     blocks links behind it) — a tap makes it jump, same startle as the footer
     cursor-dodge. */
  .duck-rig{pointer-events:auto; cursor:pointer; touch-action:manipulation}
  /* between areas the duck TELEPORTS — fades out here, pops in there
     (site.js swaps the position while this class holds opacity at 0) */
  .duck-walker.teleporting{opacity:0}
  /* page entrance: hidden until the hero's staggered rise finishes, then a
     gentle fade-in (backwards fill only — a forwards fill would pin opacity
     and block the teleport transitions afterwards) */
  .duck-walker.unloaded{opacity:0}
  .duck-walker.hatch{animation:duck-hatch .9s ease backwards}
  @keyframes duck-hatch{from{opacity:0} to{opacity:1}}
  .duck-rig{position:relative; width:64px}
  .duck-rig img{
    display:block; width:100%; height:auto;
    image-rendering:pixelated; user-select:none; -webkit-user-drag:none;
  }
  .duck-rig .d-base{filter:drop-shadow(0 5px 4px rgba(0,0,0,.45))}
  .duck-rig .d-wing,.duck-rig .d-glasses,.duck-rig .d-glasses2{position:absolute; top:0; left:0}
  /* the SECOND pair of shades — hidden until a startle jump reveals it at
     eye level while the first pair is up the forehead (DOM-ordered under
     .d-glasses so the returning pair lands on top of it) */
  .duck-rig .d-glasses2{opacity:0}
  .duck-walker.startled .d-glasses2{animation:glasses2-reveal 1.35s linear both}
  @keyframes glasses2-reveal{
    0%,6%{opacity:0}
    12%{opacity:1}
    56%{opacity:1}
    63%,100%{opacity:0}
  }
  .d-wing{transform-origin:42% 55%; animation:wing-flap 1.4s ease-in-out infinite}
  @keyframes wing-flap{
    0%,100%{transform:translateY(0) rotate(0deg)}
    50%{transform:translateY(-2.2%) rotate(-2.5deg)}
  }
  .d-glasses{animation:glasses-bob 2.6s ease-in-out infinite}
  @keyframes glasses-bob{
    0%,100%{transform:translateY(0)}
    50%{transform:translateY(1.5%)}
  }
  /* startled (cursor touch → jump): the shades fly high up the forehead,
     hang there through the whole flight, and drop back with an overshoot */
  .duck-walker.startled .d-glasses{animation:glasses-pop 1.35s cubic-bezier(.3,1.3,.4,1) both}
  @keyframes glasses-pop{
    0%{transform:translateY(0) rotate(0deg)}
    10%{transform:translateY(-10%) rotate(-7deg)}
    52%{transform:translateY(-8.5%) rotate(-5deg)}
    64%{transform:translateY(1.2%) rotate(0deg)}
    72%{transform:translateY(-1%) rotate(0deg)}
    80%,100%{transform:translateY(0) rotate(0deg)}
  }
  /* airborne: the wings work hard — much faster, deeper flap while the
     physics floats it back down to the ground */
  .duck-walker.airborne .d-wing{animation:wing-flap-hard .3s ease-in-out infinite}
  @keyframes wing-flap-hard{
    0%,100%{transform:translateY(0) rotate(0deg)}
    50%{transform:translateY(-4.5%) rotate(-6deg)}
  }
  .duck-walker.walking .duck-rig{animation:duck-waddle .38s ease-in-out infinite}
  @keyframes duck-waddle{
    0%,100%{transform:rotate(0deg) translateY(0)}
    25%{transform:rotate(-4deg) translateY(-2px)}
    75%{transform:rotate(4deg) translateY(-2px)}
  }
  /* tap-jump: a self-contained hop played when the mascot is clicked/tapped
     away from the footer (the footer perch uses the physics leap instead). The
     .hop class wins over .walking since it's applied last / one-shot. */
  .duck-walker .duck-rig.hop{animation:duck-hop .62s cubic-bezier(.3,1.1,.4,1) 1}
  @keyframes duck-hop{
    0%{transform:translateY(0) rotate(0deg)}
    28%{transform:translateY(-34px) rotate(-4deg)}
    46%{transform:translateY(-38px) rotate(0deg)}
    70%{transform:translateY(0) rotate(0deg)}
    82%{transform:translateY(-5px) rotate(0deg)}
    100%{transform:translateY(0) rotate(0deg)}
  }
  /* its whole purpose is motion — hide under reduced motion */
  @media (prefers-reduced-motion:reduce){.duck-walker{display:none}}

  /* the duck's rotating "catch me" speech bubble — fades in once the page is
     scrolled to the very bottom, sits IN LINE beside the duck (level with its
     head), tail pointing sideways back at it. The line cycles on a timer and a
     jump interrupts it with a reaction (landing only). */
  .duck-bubble{
    position:fixed; top:0; left:0; z-index:41; pointer-events:none;
    transform:translateX(-6px); transform-origin:left center;
    background:var(--bg-raised); color:var(--text);
    border:1px solid var(--border-strong); border-radius:12px;
    padding:8px 13px; font-family:var(--mono); font-size:.82rem; white-space:nowrap;
    box-shadow:0 8px 22px rgba(0,0,0,.5);
    opacity:0; visibility:hidden;
    transition:opacity .28s ease, transform .32s cubic-bezier(.2,1.3,.4,1);
  }
  .duck-bubble.show{opacity:1; visibility:visible; transform:translateX(0)}
  /* the cycling text swaps with a quick pop (retriggered from JS on each line) */
  .duck-bubble.say{animation:duck-say .34s ease}
  @keyframes duck-say{0%{opacity:.25; transform:translateX(0) scale(.94)} 100%{opacity:1; transform:translateX(0) scale(1)}}
  /* tail on the LEFT side, pointing back toward the duck (bubble is to its right) */
  .duck-bubble::after{
    content:""; position:absolute; top:50%; left:-6px; margin-top:-5px;
    width:11px; height:11px; background:var(--bg-raised);
    border-left:1px solid var(--border-strong); border-bottom:1px solid var(--border-strong);
    transform:rotate(45deg);
  }
  /* flipped: bubble sits to the LEFT of the duck → tail on the right */
  .duck-bubble.flip{transform-origin:right center; transform:translateX(6px)}
  .duck-bubble.flip.show{transform:translateX(0)}
  .duck-bubble.flip::after{
    left:auto; right:-6px;
    border-left:none; border-bottom:none;
    border-right:1px solid var(--border-strong); border-top:1px solid var(--border-strong);
  }
  .foot .brandline{font-family:var(--mono); font-weight:800; font-size:1.3rem;
    letter-spacing:.08em; text-transform:uppercase; color:var(--text)}
  .foot .brandline .sub{display:block; margin-top:8px; font-weight:400; font-size:.8rem;
    letter-spacing:0; text-transform:none; color:var(--text-dim)}
  .foot .links{display:flex; flex-wrap:wrap; gap:10px 28px; font-family:var(--mono); font-size:.88rem}
  .foot .links a{color:var(--text-muted); transition:color .2s} .foot .links a:hover{color:var(--accent)}
  .foot .meta{font-family:var(--mono); font-size:.78rem; color:var(--text-dim); line-height:1.9; text-align:left}
  .foot .meta code{color:var(--text-muted)}

  @media (max-width:980px){
    .modes{grid-template-columns:1fr 1fr}
    .pipeline{grid-template-columns:1fr 1fr}
    .pipeline::before{display:none}
    .stage{border-top:1px solid var(--border)}
    .stage:nth-child(-n+2){border-top:none}
    .stage:nth-child(odd){border-left:none}
    .start-grid{grid-template-columns:1fr; gap:48px}
    /* drop the full-height hero centering as soon as we're off the wide
       layout — the 100vh min + justify-center strands the copy mid-screen with
       a big dead band above it once the demo stacks below rather than beside.
       Top-align it and sit it just under the (sticky) nav. */
    .hero-copy{min-height:0; justify-content:flex-start; padding:20px 0 8px}
    .scroll-hint{display:none}
  }
  @media (max-width:860px){
    .hero{padding:0 0 72px}
    .hero-copy{min-height:0; justify-content:flex-start; padding:16px 0 8px}
    /* the "AI agent…" pill wraps to two lines and crowds the hero on phones */
    .hero-copy .badge{display:none}
    /* the hint exists to point below the fold on the full-height desktop
       hero; on mobile the terminal is directly beneath the CTA */
    .scroll-hint{display:none}
    .hero-cta .codeblock{min-width:0; width:100%}
    .tui{font-size:9.5px; padding:8px 8px 12px}
    .tui-panel{padding:14px 14px 10px}
    .tui-splash{min-height:380px}
    .tui-splash-word{font-size:8px}
  }
  @media (max-width:600px){
    section{padding:60px 0}
    /* steps go back to a clean left-aligned stack on phones, where a row
       would wrap */
    .step{grid-template-columns:1fr; justify-items:start; gap:9px; padding:14px 18px}
    /* phone: the full 8-row menu is a 7px unreadable wall — show a
       single-mode showcase instead: only the active mode renders, at a
       legible size, cycling with its typed description. Tip block, version
       row, and music bar are noise at this scale and are dropped. */
    .tui{font-size:11px}
    .tui-panel{padding:16px 16px 14px}
    .tui-splash{min-height:170px}
    .tui-splash-word{font-size:7px}
    .tui-menu{gap:0}
    .tui-mode{display:none; padding-left:0}
    /* centred composition: big title filling the width (site.js scales each
       title from its column count), description centred beneath */
    .tui-mode.is-active{
      display:flex; flex-direction:column; align-items:center; justify-content:center;
      min-height:170px; text-align:center;
    }
    .tui-mode-desc{max-width:36ch; text-align:left}
    .tui-mode.is-active .tui-mode-desc{height:54px; margin:14px 0 0}
    .tui-foot,.tui-music{display:none}
    /* the compact frame doesn't need the desktop demo's breathing room */
    .hero{padding-bottom:36px}
    .hero-demo{margin-top:20px; padding-bottom:0}
    .modes{grid-template-columns:1fr}
    .mode-card{min-height:0; padding:22px 20px 20px}
    /* pipeline: a horizontal snap carousel instead of five stacked cards —
       one swipe-height instead of ~5 screens; the 76% column leaves the next
       stage peeking in as the scroll affordance */
    .pipeline{
      grid-template-columns:none; grid-auto-flow:column; grid-auto-columns:76%;
      overflow-x:auto; scroll-snap-type:x mandatory; -webkit-overflow-scrolling:touch;
      scrollbar-width:none;
    }
    .pipeline::-webkit-scrollbar{display:none}
    .pipeline::before{display:none}
    .stage{scroll-snap-align:start; border-top:none; border-left:1px solid var(--border)}
    /* re-assert over the 980px two-column rules' higher-specificity selectors */
    .stage:nth-child(-n+2){border-top:none}
    .stage:nth-child(odd){border-left:1px solid var(--border)}
    .stage:first-child{border-left:none}
    /* label stacks above the text — the two-column note is too cramped here */
    .pipe-note{flex-direction:column; gap:8px}
    /* carousel affordances: content fades out at the right edge while there's
       more to scroll (site.js drops .at-end's mask when you reach the end),
       plus position dots + a swipe hint in the TUI's ● ○ language */
    .pipeline{
      -webkit-mask-image:linear-gradient(90deg, #000 calc(100% - 56px), rgba(0,0,0,.15));
              mask-image:linear-gradient(90deg, #000 calc(100% - 56px), rgba(0,0,0,.15));
    }
    .pipeline.at-end{-webkit-mask-image:none; mask-image:none}
    .pipe-dots{
      position:relative; display:flex; align-items:center; justify-content:center; gap:9px;
      margin-top:16px; font-family:var(--mono); font-size:.72rem; color:rgb(70,70,82);
    }
    .pipe-dots .on{color:var(--accent)}
    /* absolute so the hint never shifts the dot cluster off true centre —
       as a flex sibling it kept occupying space even at opacity 0 */
    .pipe-dots .pipe-hint{
      position:absolute; left:50%; margin-left:56px; top:50%; transform:translateY(-50%);
      font-size:.66rem; letter-spacing:.14em; text-transform:uppercase;
      color:var(--text-dim); transition:opacity .5s;
    }
    .pipe-dots.swiped .pipe-hint{opacity:0}
  }
