    *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
    :root{
      --bg:#000;--text:#fff;--muted:rgba(255,255,255,0.5);--dim:rgba(255,255,255,0.28);
      --border:rgba(255,255,255,0.09);--card:rgba(255,255,255,0.04);
      --blue:#5088FD;--green:#48CA7C;--dark-blue:#184383;--red:#FF6B6B;
      --grad:linear-gradient(90deg,#5088FD 0%,#48CA7C 55%,#8ef5c4 100%);
      --grad-diag:linear-gradient(135deg,#5088FD 0%,#48CA7C 100%);
      --font:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;
      --max:1170px;--ease:cubic-bezier(0.22,1,0.36,1);
    }
    html{scroll-behavior:smooth}
    body{background:var(--bg);color:var(--text);font-family:var(--font);-webkit-font-smoothing:antialiased;overflow-x:hidden}
    a{color:inherit;text-decoration:none}
    ::-webkit-scrollbar{width:5px}
    ::-webkit-scrollbar-track{background:#0a0a0a}
    ::-webkit-scrollbar-thumb{background:rgba(255,255,255,.15);border-radius:3px}
    .wrap{max-width:var(--max);margin:0 auto;padding:0 28px;position:relative}
    /* The gradient pans left to right, forever, without a seam.

       Two things make the loop invisible. The gradient is a palindrome that
       ENDS on the colour it starts with, and the pan is exactly one full image
       width (background-size:200% travelled to background-position:200%). Since
       the background repeats, that lands on a pixel-identical state, so there is
       no jump at the wrap. Change one of those numbers and the seam appears. */
    .grad-text{
      background:linear-gradient(90deg,#5088FD 0%,#48CA7C 27%,#8ef5c4 50%,#48CA7C 73%,#5088FD 100%);
      background-size:200% auto;
      -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
      animation:gradShift 9s linear infinite;
    }
    @keyframes gradShift{to{background-position:200% center}}
    @media(prefers-reduced-motion:reduce){.grad-text{animation:none;background:var(--grad);background-size:auto}}
    .r{opacity:0;transform:translateY(28px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
    .r.in{opacity:1;transform:none}
    .r.d1{transition-delay:.08s}.r.d2{transition-delay:.16s}.r.d3{transition-delay:.24s}
    .r.d4{transition-delay:.32s}.r.d5{transition-delay:.40s}.r.d6{transition-delay:.48s}

    /* Progress */
    #prog{position:fixed;top:0;left:0;height:2px;background:var(--grad);z-index:300;width:0;transition:width .08s linear;pointer-events:none}

    /* NAV */
    nav{position:fixed;top:0;left:0;right:0;z-index:200;padding:18px 0;transition:background .35s,border-color .35s,backdrop-filter .35s}
    nav.scrolled{background:rgba(0,0,0,0.75);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);border-bottom:1px solid var(--border)}
    .nav-inner{display:flex;align-items:center;justify-content:space-between;max-width:var(--max);margin:0 auto;padding:0 28px}
    .nav-logo svg{height:28px;width:auto}
    .nav-links{display:flex;align-items:center;gap:28px;list-style:none}
    .nav-links a{font-size:16px;color:#fff;transition:opacity .2s}
    .nav-links a:hover{opacity:.72}
    .nav-cta{display:inline-flex;align-items:center;gap:8px;background:white;color:#000;padding:9px 18px;border-radius:999px;font-size:16px;font-weight:600;transition:opacity .2s,transform .2s;white-space:nowrap}
    .nav-cta:hover{opacity:.88;transform:scale(.97)}
    .nav-cta svg{width:16px;height:16px}

    /* HERO */
    .hero{min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:116px 28px 64px;text-align:center;position:relative;overflow:hidden}
    .hero-badge,.hero h1,.hero-sub,.hero-actions,.hero-rating{position:relative;z-index:1}
    /* Structure without lighting: a dot grid, masked to an ellipse so it never
       ends on a hard edge. Texture rather than glow — the blobs read as a light
       source, this reads as ground. */
    /* Dot field, fixed in place. ::before is the static grid; ::after is a
       vignette painted over it and over the sparks, so everything fades out at
       the edges without needing a mask that would have to move with them. */
    .hero::before{
      content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
      background-image:radial-gradient(rgba(255,255,255,.17) 1.3px,transparent 1.3px);
      background-size:28px 28px;
      /* Anchored to the centre, not the top-left. A dot therefore always
         sits exactly at 50%/50%, so the sparks below can land on real grid
         positions with calc(50% + 28n) at ANY viewport width. */
      background-position:50% 50%;
    }
    .hero::after{
      content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
      background:radial-gradient(ellipse 68% 60% at 50% 46%,transparent 18%,#000 82%);
    }

    /* Existing grid dots catching colour. These are NOT extra dots: each is
       positioned at calc(50% + 28n), which is exactly where a grid dot already
       sits, and is sized to sit on top of it. Nothing new appears between the
       grid — the grid itself lights up.

       Durations are all coprime-ish so the set never re-synchronises into a
       visible loop, and short enough that several are always lit at once. */
    /* preserveAspectRatio="none" on the svg is load-bearing: the artwork keeps
       height="690", so with the default "meet" it scaled to fit the HEIGHT and
       sat letterboxed in a wider box — widening the element changed nothing at
       all. "none" lets it stretch horizontally, which is invisible on a shape
       this blurred and is what actually widens the wash. */
    .hero-glow{
      /* Pulled up so the bright core of the wash sits behind the nav rather
         than through the headline. In px, not %, because a percentage resolves
         against the HERO's height and would drift whenever the hero resizes. */
      position:absolute;top:-330px;left:50%;
      transform:translateX(-50%) scaleY(-1);
      width:200%;max-width:2600px;pointer-events:none;opacity:1;z-index:1;
    }
    .hero-spark{
      position:absolute;width:3px;height:3px;border-radius:50%;
      transform:translate(-50%,-50%) scale(.4);
      pointer-events:none;z-index:0;opacity:0;
      animation:spark var(--t) ease-in-out infinite;animation-delay:var(--d);
      will-change:opacity,transform;
    }
    @keyframes spark{
      0%,100%{opacity:0;transform:translate(-50%,-50%) scale(.6)}
      25%    {opacity:.7;transform:translate(-50%,-50%) scale(1)}
      55%    {opacity:.7;transform:translate(-50%,-50%) scale(1)}
      80%    {opacity:0;transform:translate(-50%,-50%) scale(.6)}
    }
    @media(prefers-reduced-motion:reduce){.hero-spark{animation:none;opacity:.6;transform:translate(-50%,-50%)}}
    .hero-rating{
      display:inline-flex;align-items:center;gap:9px;margin-top:26px;
      font-size:13.5px;color:var(--muted);transition:color .2s;
    }
    .hero-rating:hover{color:rgba(255,255,255,.8)}
    .hero-rating strong{color:#f1f5f9;font-weight:600}
    .hero-stars{color:#FBBC04;font-size:14px;letter-spacing:1px}
    @keyframes rayPulse{0%,100%{opacity:.45}50%{opacity:.75}}
    @keyframes rayShift{0%{transform:translateX(-50%) scaleX(1)}50%{transform:translateX(-50%) scaleX(1.04)}100%{transform:translateX(-50%) scaleX(1)}}
    .hero-badge{display:inline-flex;align-items:center;gap:8px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);border-radius:999px;padding:6px 14px;font-size:16px;color:var(--muted);margin-bottom:28px;animation:fadeDown .8s var(--ease) both}
    .badge-dot{width:6px;height:6px;border-radius:50%;background:var(--grad-diag);flex-shrink:0}
    @keyframes fadeDown{from{opacity:0;transform:translateY(-14px)}to{opacity:1;transform:translateY(0)}}
    .hero h1{font-size:clamp(55px,8.75vw,105px);font-weight:300;line-height:1.0;letter-spacing:-0.035em;max-width:940px;animation:fadeUp .9s var(--ease) .08s both}
    .hero h1 strong{font-weight:600}
    .hero-sub{font-size:18px;color:var(--muted);max-width:660px;line-height:1.6;margin-top:22px;animation:fadeUp .9s var(--ease) .18s both}
    @keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
    .hero-actions{margin-top:36px;animation:fadeUp .9s var(--ease) .28s both;display:flex;gap:12px;flex-wrap:wrap;justify-content:center}
    .btn-chrome{display:inline-flex;align-items:center;gap:10px;padding:14px 26px;border-radius:999px;font-size:16px;font-weight:600;background:white;color:#000;position:relative;transition:transform .2s,opacity .2s;cursor:pointer;border:none;z-index:0}
    .btn-chrome::before{content:'';position:absolute;inset:-2px;border-radius:999px;background:var(--grad);z-index:-1;padding:2px}
    .btn-chrome::after{content:'';position:absolute;inset:0;border-radius:999px;background:white;z-index:-1}
    .btn-chrome:hover{transform:scale(.97)}
    .btn-chrome{background:white;color:#000}
    .btn-chrome svg{width:18px;height:18px;flex-shrink:0}
    .btn-ghost{display:inline-flex;align-items:center;gap:8px;padding:14px 22px;border-radius:999px;font-size:16px;color:var(--muted);border:1px solid var(--border);transition:color .2s,border-color .2s,transform .2s}
    .btn-ghost:hover{color:var(--text);border-color:rgba(255,255,255,.22);transform:scale(.98)}

    /* Hero plugin image */

    /* Tool tabs on hero */

    /* ── Hero story diagram ───────────────────────────────────────────────
       Wires draw themselves, then each box fades in as its wire arrives.
       pathLength="1" lets one dash rule cover paths of very different real
       lengths. Timing is one calc() driven by --i on each element. */
    .fx{position:relative;width:100%;max-width:1120px;margin:44px auto 0;z-index:1}
    .fx-svg{display:block;width:100%;height:auto;overflow:visible}
    .fx-stack{display:none}

    .fx-label{fill:rgba(255,255,255,.62);font-size:11.5px;font-weight:500}
    .fx-hub{fill:#f1f5f9;font-size:19px;font-weight:600;letter-spacing:-.01em}
    .fx-chip{fill:rgba(255,255,255,.06);stroke:rgba(255,255,255,.10)}
    .fx-chiptext{fill:rgba(255,255,255,.6);font-size:9.5px;font-weight:700;letter-spacing:.09em}

    .fx-wire{stroke-dasharray:1;stroke-dashoffset:1}
    .fx.is-live .fx-wire{animation:fxDraw .5s ease-out forwards;animation-delay:calc(var(--i) * .16s + .12s)}
    @keyframes fxDraw{to{stroke-dashoffset:0}}
    /* Arrowheads belong to the wire, so they must not appear before it lands. */
    .fx-wire[marker-end]{opacity:0}
    .fx.is-live .fx-wire[marker-end]{animation:fxDraw .5s ease-out forwards,fxShow .01s linear forwards;animation-delay:calc(var(--i) * .16s + .12s)}
    @keyframes fxShow{to{opacity:1}}

    /* ── The loop ─────────────────────────────────────────────────────────
       One short dash per wire, running the whole length on repeat. The delay
       is derived from --i so the pulse arrives at each segment roughly when
       the previous one finishes, reading as a single wave crossing the
       diagram rather than six unrelated blips.

       The 1.5s head start lets the entrance draw finish first, so the loop
       begins on a diagram that is already fully assembled. */
    .fx-pulse{stroke-dasharray:.1 .9;stroke-dashoffset:1;opacity:0;filter:drop-shadow(0 0 5px rgba(142,245,196,.55))}
    .fx.is-live .fx-pulse{animation:fxPulse 3.4s linear infinite;animation-delay:calc(1.5s + var(--i) * .13s)}
    @keyframes fxPulse{
      0%{stroke-dashoffset:1;opacity:0}
      6%{opacity:.95}
      55%{opacity:.95}
      70%{stroke-dashoffset:0;opacity:0}
      100%{stroke-dashoffset:0;opacity:0}
    }

    .fx-tile{opacity:0;transform:translateY(6px)}
    .fx.is-live .fx-tile{animation:fxIn .45s var(--ease) forwards;animation-delay:calc(var(--i) * .16s)}
    @keyframes fxIn{to{opacity:1;transform:none}}

    /* A 760-unit-wide diagram is illegible on a phone: swap it for the list. */
    @media(max-width:760px){
      .fx{margin-top:42px;max-width:420px}
      .fx-svg{display:none}
      .fx-stack{display:flex;flex-direction:column;gap:14px;list-style:none;margin:0;padding:0}
      .fx-stack li{
        position:relative;padding:13px 16px 13px 44px;border-radius:13px;
        background:#0d1117;border:1px solid rgba(255,255,255,.09);
        font-size:13.5px;line-height:1.5;color:var(--muted);
      }
      .fx-stack li b{color:#f1f5f9;font-weight:600}
      .fx-stack li::before{
        content:'';position:absolute;left:18px;top:19px;
        width:9px;height:9px;border-radius:50%;background:var(--grad-diag);
      }
    }

    @media(prefers-reduced-motion:reduce){
      .fx-wire,.fx-wire[marker-end]{stroke-dashoffset:0;opacity:1}
      .fx-pulse,.fx.is-live .fx-pulse{animation:none;opacity:0}
      .fx-tile{opacity:1;transform:none}
      .fx.is-live .fx-wire,.fx.is-live .fx-wire[marker-end],.fx.is-live .fx-tile{animation:none}
    }

    /* MARQUEE */
    .marquee-wrap{overflow:hidden;padding:22px 0;border-top:1px solid var(--border);border-bottom:1px solid var(--border);position:relative;background:rgba(255,255,255,.015)}
    .marquee-wrap::before,.marquee-wrap::after{content:'';position:absolute;top:0;bottom:0;width:160px;z-index:2;pointer-events:none}
    .marquee-wrap::before{left:0;background:linear-gradient(90deg,var(--bg) 0%,transparent 100%)}
    .marquee-wrap::after{right:0;background:linear-gradient(-90deg,var(--bg) 0%,transparent 100%)}
    .marquee-track{display:flex;gap:0;width:max-content;animation:marquee 54s linear infinite}
    .marquee-track:hover{animation-play-state:paused}
    @keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
    .mi{display:inline-flex;align-items:center;gap:0;font-size:15px;font-weight:400;color:rgba(255,255,255,.72);white-space:nowrap;flex-shrink:0;letter-spacing:.01em;padding:0 36px;}
    .mi-sep{width:1px;height:14px;background:rgba(255,255,255,.15);flex-shrink:0;margin:0;}
    .mi-dot{display:none;}

    /* SECTIONS */
    section{padding:96px 0;position:relative}
    .sec-eyebrow{display:inline-flex;align-items:center;gap:8px;font-size:16px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--green);margin-bottom:18px}
    .sec-eyebrow::before{display:none}
    /* Green is the resolve colour everywhere else on the page, so it reads as
       the wrong signal above "The problem". Red is the same #FF6B6B already
       used for high-severity visuals — a modifier, never the base rule, since
       every other eyebrow on the page stays green. */
    .sec-eyebrow.neg{color:var(--red)}
    h2.sec-title{font-size:clamp(30px,4vw,50px);font-weight:300;letter-spacing:-0.03em;line-height:1.1;margin-bottom:14px;text-align:center}
    h2.sec-title strong{font-weight:600}
    .sec-sub{font-size:16px;color:var(--muted);max-width:500px;line-height:1.65;text-align:center;margin:0 auto}

    /* FEATURES */
    .features-intro{margin-bottom:52px;text-align:center}
    .features-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;}
    /* Icon beside the text, not stacked above it. Each card is one line now,
       and the stacked layout left most of the card empty. A 2-row grid with the
       icon spanning both keeps the icon optically aligned to the heading while
       the text sets its own rhythm — no wrapper element needed in the markup. */
    .feat-card{
      background:#0d1117;border:1px solid var(--border);border-radius:16px;
      padding:22px 24px;
      display:grid;grid-template-columns:44px 1fr;column-gap:16px;row-gap:5px;
      align-content:start;
      transition:border-color .25s,transform .25s;
    }
    .feat-card-icon{grid-column:1;grid-row:1 / span 2;align-self:start;}
    .feat-card h3{grid-column:2;grid-row:1;align-self:center;}
    .feat-card p{grid-column:2;grid-row:2;}
    .feat-card:hover{border-color:rgba(80,136,253,.4);transform:translateY(-2px);}
    .feat-card-icon{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
    .feat-card h3{font-size:16px;font-weight:600;color:#f1f5f9;margin:0;}
    .feat-card p{font-size:14px;color:var(--muted);line-height:1.6;margin:0;}
    .feat-card .feat-tag{font-size:11px;padding:2px 8px;border-radius:20px;font-weight:600;vertical-align:middle;margin-left:8px;}
    .feat-card ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:7px;}
    .feat-card ul li{font-size:13px;color:var(--muted);line-height:1.5;padding-left:14px;position:relative;}
    .feat-card ul li::before{content:'–';position:absolute;left:0;color:var(--muted);}
    @media(max-width:640px){.features-grid{grid-template-columns:1fr;}}
    .feat:hover{background:rgba(255,255,255,.03)}
    .feat-icon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
    .feat-icon svg{width:22px;height:22px}
    .feat-icon.blue{background:rgba(80,136,253,.14)}
    .feat-icon.green{background:rgba(72,202,124,.14)}
    .feat-icon.cyan{background:rgba(173,239,253,.12)}
    .feat-icon.yellow{background:rgba(254,188,46,.12)}
    .feat-icon.purple{background:rgba(180,100,255,.12)}
    .feat-icon.red{background:rgba(255,107,107,.12)}
    .feat h3{font-size:16px;font-weight:600;letter-spacing:-.01em}
    .feat-tag{display:inline-flex;align-items:center;gap:5px;font-size:16px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--blue);background:rgba(80,136,253,.12);border-radius:4px;padding:2px 7px;width:fit-content}
    .feat ul{list-style:none;display:flex;flex-direction:column;gap:7px}
    .feat li{font-size:16px;color:var(--muted);line-height:1.55;padding-left:14px;position:relative}
    .feat li::before{content:'–';position:absolute;left:0;color:var(--dim)}
    .feat-sub{font-size:16px;color:var(--dim);font-weight:600;text-transform:uppercase;letter-spacing:.05em}

    /* ── Who it's for ──────────────────────────────────────────────────────
       Four columns, each led by its accent. The accent does three jobs rather
       than one: a thick glowing bar on top, a wash fading down behind the
       column, and the hover lift — so the block reads as lit from its own
       colour instead of being a caption under a hairline. --acc is set per
       item in the markup and every rule below derives from it. */
    #segments{background:rgba(255,255,255,.012)}
    .seg-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-top:52px}
    .seg{
      position:relative;padding:24px 18px 22px;border-radius:0 0 14px 14px;
      transition:transform .25s var(--ease);
    }
    /* the wash, fading out before it reaches the copy */
    .seg::before{
      content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
      background:linear-gradient(180deg,var(--acc) 0%,transparent 58%);
      opacity:.13;
    }
    /* the bar, with its own glow */
    .seg::after{
      content:'';position:absolute;left:0;right:0;top:0;height:3px;border-radius:3px;
      background:var(--acc);box-shadow:0 0 16px var(--acc),0 0 3px var(--acc);
    }
    .seg:hover{transform:translateY(-3px)}
    .seg-role{position:relative;font-size:17px;font-weight:600;color:#fff;letter-spacing:-.015em;margin-bottom:9px}
    .seg p{position:relative;font-size:13.5px;color:rgba(255,255,255,.62);line-height:1.6}

    @media(max-width:860px){.seg-grid{grid-template-columns:repeat(2,1fr);gap:18px}}
    @media(max-width:520px){.seg-grid{grid-template-columns:1fr;gap:14px}}

    /* HOW IT WORKS */
    #how{background:rgba(255,255,255,.012)}
    .how-grid{margin-top:60px;display:grid;grid-template-columns:repeat(3,1fr);gap:24px;}
    .how-card{background:#0d1117;border:1px solid var(--border);border-radius:16px;overflow:hidden;display:flex;flex-direction:column;}
    .how-card-img{width:100%;display:block;border-bottom:1px solid var(--border);}
    .how-card-body{padding:24px;display:flex;flex-direction:column;gap:10px;}
    .how-card-num{font-size:13px;font-weight:700;color:transparent;background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;letter-spacing:.06em;}
    .how-card-body h3{font-size:17px;font-weight:600;letter-spacing:-.02em;color:#f1f5f9;margin:0;}
    .how-card-body p{font-size:14px;color:var(--muted);line-height:1.65;margin:0;}
    @media(max-width:820px){.how-grid{grid-template-columns:1fr;}}

    /* Step visuals */
    .sv-install{width:100%;display:flex;flex-direction:column;gap:10px}
    .sv-install-bar{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.08);border-radius:10px;padding:10px 14px}
    .sv-install-bar svg{width:22px;height:22px}
    .sv-install-text{flex:1}
    .sv-install-name{font-size:16px;font-weight:600}
    .sv-install-sub{font-size:16px;color:var(--dim)}
    .sv-install-btn{background:var(--blue);color:#fff;font-size:16px;font-weight:600;padding:5px 12px;border-radius:6px;white-space:nowrap}
    .sv-install-ok{font-size:16px;color:var(--green);text-align:center}

    .sv-report{width:100%}
    .sv-field{display:flex;align-items:center;gap:8px;padding:5px 0;border-bottom:1px solid rgba(255,255,255,.04);font-size:16px}
    .sv-field:last-child{border-bottom:none}
    .sv-label{color:var(--dim);width:64px;flex-shrink:0}
    .sv-val{color:var(--muted)}
    .sv-badge{padding:2px 7px;border-radius:4px;font-size:16px;font-weight:600}
    .sv-badge.crit{background:rgba(255,107,107,.15);color:#FF6B6B}

    .sv-notifs{width:100%;display:flex;flex-direction:column;gap:7px}
    .sv-notif{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);border-radius:8px;padding:9px 12px;font-size:16px}
    .sv-notif-icon{width:26px;height:26px;border-radius:7px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:16px}
    .sv-notif-body{flex:1}
    .sv-notif-title{font-weight:600;font-size:16px;margin-bottom:1px}
    .sv-notif-sub{color:var(--dim);font-size:16px}
    .sv-notif-time{color:var(--dim);font-size:16px}

    /* STATS */

    /* TESTIMONIALS */
    #testimonials{background:rgba(255,255,255,.012)}
    /* Edge fades, same treatment as the feature ticker, so cards dissolve rather
       than being cut off at the container edge. */
    .test-scroller{position:relative;margin-top:52px;overflow:hidden}
    .test-scroller::before,.test-scroller::after{content:'';position:absolute;top:0;bottom:0;width:120px;z-index:2;pointer-events:none}
    .test-scroller::before{left:0;background:linear-gradient(90deg,var(--bg) 0%,transparent 100%)}
    .test-scroller::after{right:0;background:linear-gradient(-90deg,var(--bg) 0%,transparent 100%)}
    .test-track{display:flex;width:max-content;animation:testScroll 46s linear infinite}
    .test-track:hover{animation-play-state:paused}
    @keyframes testScroll{to{transform:translateX(-50%)}}
    /* Spacing as margin-right on every card, NOT flex gap. With gap there is no
       gap after the last card, so half the track width lands 8px short of where
       the fifth card starts and the wrap visibly jumps. */
    .test-track .test-card{flex:0 0 400px;width:400px;margin-right:16px}
    @media(prefers-reduced-motion:reduce){.test-track{animation:none}}
    @media(max-width:640px){.test-track .test-card{flex-basis:300px;width:300px}}
    .test-card{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:24px;display:flex;flex-direction:column;gap:14px;transition:border-color .2s}
    .test-card:hover{border-color:rgba(255,255,255,.18)}
    .test-stars{display:flex;gap:3px}
    .test-star{color:#FBBC04;font-size:16px}
    .test-star.empty{color:var(--dim)}
    .test-text{font-size:16px;color:var(--muted);line-height:1.7;flex:1}
    .test-author{display:flex;align-items:center;gap:10px;margin-top:4px}
    .test-avatar{width:36px;height:36px;border-radius:50%;background:var(--grad-diag);display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:600;color:#fff;flex-shrink:0;overflow:hidden}.test-avatar img{width:100%;height:100%;object-fit:cover;border-radius:50%}
    .test-name{font-size:16px;font-weight:600}
    .test-date{font-size:16px;color:var(--dim)}

    /* PRICING */
    #pricing{background:rgba(255,255,255,.012)}
    .pricing-grid{display:grid;grid-template-columns:1fr 1.6fr;gap:16px;margin-top:56px;max-width:780px;margin-left:auto;margin-right:auto;align-items:stretch}
    .plan{background:var(--card);border:1px solid var(--border);border-radius:18px;padding:28px;display:flex;flex-direction:column;gap:20px;transition:transform .3s,border-color .3s}
    .plan:hover{transform:translateY(-5px)}
    .plan.pro{background:linear-gradient(145deg,rgba(80,136,253,.12) 0%,rgba(10,10,18,.95) 50%,rgba(72,202,124,.08) 100%);border-color:transparent;position:relative;}
    .plan.pro::before{content:'';position:absolute;inset:0;border-radius:18px;padding:1px;background:linear-gradient(145deg,rgba(80,136,253,.6),rgba(72,202,124,.4));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none}
    .plan.pro::after{content:'';position:absolute;inset:0;border-radius:18px;background:radial-gradient(ellipse at 30% 0%,rgba(80,136,253,.15) 0%,transparent 60%),radial-gradient(ellipse at 80% 100%,rgba(72,202,124,.1) 0%,transparent 55%);pointer-events:none}
    .plan.pro *{color:#fff!important}
    .plan.pro .plan-feat{border-color:rgba(255,255,255,.07)!important}
    .plan.pro .plan-check{color:var(--green)!important}
    .plan.pro .plan-btn{background:linear-gradient(90deg,var(--blue),var(--green));color:#fff!important;border-color:transparent}
    .plan.pro .plan-btn:hover{opacity:.88}
    .plan.pro .plan-tag{background:rgba(80,136,253,.15);color:var(--blue)!important}
    .plan.pro .plan-name{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent!important;-webkit-text-fill-color:transparent!important;font-weight:700;letter-spacing:.1em}
    .plan-name{font-size:16px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--dim);margin-bottom:8px}
    /* Billing period switch. A segmented pill rather than a checkbox toggle so
       both prices are legible at once — the yearly saving is the argument, and
       a switch that hides one number hides half of it. */
    .plan-billing{display:inline-flex;gap:2px;padding:3px;margin-bottom:14px;background:rgba(255,255,255,.06);border:1px solid var(--border);border-radius:999px}
    .pb-opt{background:none;border:none;cursor:pointer;font-family:var(--font);font-size:12.5px;font-weight:600;color:rgba(255,255,255,.55)!important;padding:6px 14px;border-radius:999px;transition:background .2s,color .2s;white-space:nowrap}
    .pb-opt:hover{color:#fff!important}
    .pb-opt.active{background:rgba(255,255,255,.14);color:#fff!important}
    .plan-price{display:flex;align-items:flex-end;gap:4px}
    .plan-curr{font-size:20px;font-weight:400;padding-bottom:5px}
    .plan-amt{font-size:48px;font-weight:300;letter-spacing:-0.04em;line-height:1}
    .plan-per{font-size:16px;color:var(--muted);padding-bottom:6px}
    .plan-tag{display:inline-flex;align-items:center;gap:5px;margin-top:10px;font-size:16px;font-weight:600;background:rgba(80,136,253,.12);color:var(--blue);border-radius:5px;padding:3px 9px;width:fit-content}
    .plan-desc{font-size:16px;color:var(--muted);line-height:1.6}
    .plan-feats{display:flex;flex-direction:column;gap:0;flex:1}
    .plan-feat{padding:8px 0;border-bottom:1px solid var(--border);font-size:16px;color:var(--muted);display:flex;align-items:flex-start;gap:8px}
    .plan-feat:last-child{border-bottom:none}
    .plan-check{color:var(--green);font-size:16px;flex-shrink:0;margin-top:1px}
    .plan-btn{display:block;text-align:center;padding:12px;border-radius:10px;font-size:16px;font-weight:600;border:1px solid var(--border);color:var(--text);transition:background .2s,transform .2s;cursor:pointer;background:none;width:100%;font-family:var(--font)}
    .plan-btn:hover{background:rgba(255,255,255,.08);transform:scale(.98)}

    /* ── Pricing cards ─────────────────────────────────────────────────────
       Price is the hero; the CTA is pinned to the bottom of the card with
       margin-top:auto so both buttons align on the same line however the two
       feature lists differ in length.

       Pro's accent is GOLD, not the brand blue/green: it is the same signal the
       app puts on a paid profile, so the badge someone sees after buying is the
       colour they were sold on. */
    .pr-cards{display:grid;grid-template-columns:1fr 1fr;gap:18px;max-width:860px;margin:56px auto 0;align-items:stretch}
    .pr-card{position:relative;display:flex;flex-direction:column;padding:30px 28px 30px;border-radius:20px;background:#0b0d13;border:1px solid var(--border);transition:transform .3s var(--ease),border-color .3s}
    .pr-card:hover{transform:translateY(-4px)}
    .pr-card--pro{background:radial-gradient(ellipse at 25% -10%,rgba(245,196,81,.13) 0%,transparent 62%),radial-gradient(ellipse at 90% 112%,rgba(224,164,25,.09) 0%,transparent 55%),#0f0d09;border-color:transparent}
    /* Gradient ring drawn with a mask so the card keeps its dark fill. */
    .pr-card--pro::before{content:'';position:absolute;inset:0;border-radius:20px;padding:1px;background:linear-gradient(150deg,rgba(245,196,81,.9),rgba(224,164,25,.55) 55%,rgba(245,196,81,.22));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none}

    .pr-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:18px}
    .pr-name{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.12em;color:var(--dim)}
    .pr-name--pro{background:linear-gradient(135deg,#f5c451,#e0a419);-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
    /* Green, not gold: the saving is a separate message from the plan itself,
       and in gold it read as more Pro branding rather than a discount. */
    .pr-badge{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#48CA7C;background:rgba(72,202,124,.12);border:1px solid rgba(72,202,124,.32);border-radius:999px;padding:3px 10px;white-space:nowrap}

    /* The hero. */
    .pr-price{display:flex;align-items:baseline;gap:2px;font-size:54px;font-weight:300;letter-spacing:-.045em;line-height:1;color:var(--text)}
    .pr-cur{font-size:24px;font-weight:400;align-self:flex-start;margin-top:6px;color:var(--muted);letter-spacing:normal}
    /* letter-spacing:normal — the price's tight -.045em is inherited by these
       small spans and crushes them into 'peryear'. */
    .pr-per{font-size:14px;color:var(--muted);font-weight:400;margin-left:9px;align-self:flex-end;padding-bottom:5px;letter-spacing:normal}
    .pr-sub{font-size:13.5px;color:var(--muted);margin-top:12px;line-height:1.5}

    .pr-rule{height:1px;background:var(--border);margin:22px 0 20px}
    .pr-list{list-style:none;display:flex;flex-direction:column;gap:11px;margin:0 0 26px;padding:0}
    .pr-list li{position:relative;padding-left:24px;font-size:13.5px;line-height:1.5;color:var(--muted)}
    .pr-list li strong{color:var(--text);font-weight:600}
    .pr-list li::before{content:'✓';position:absolute;left:0;top:-1px;color:var(--green);font-size:13px}
    .pr-card--pro .pr-list li::before{color:#f5c451}

    /* margin-top:auto is what pins this to the bottom of the card. */
    .pr-cta{display:block;margin-top:auto;text-align:center;padding:13px 16px;border-radius:11px;font-size:14px;font-weight:600;border:1px solid var(--border);color:var(--text);transition:background .2s,opacity .2s,transform .2s}
    .pr-cta:hover{background:rgba(255,255,255,.08);transform:scale(.98)}
    .pr-cta--pro{background:linear-gradient(135deg,#f5c451,#e0a419);border-color:transparent;color:#2a1e00;font-weight:800}
    .pr-cta--pro:hover{opacity:.9;background:linear-gradient(135deg,#f5c451,#e0a419)}

    /* align-self, because a flex column stretches its children by default —
       the toggle would otherwise span the whole card instead of hugging it. */
    .pr-cards .plan-billing{align-self:flex-start;margin-bottom:16px}

    @media(max-width:760px){
      .pr-cards{grid-template-columns:1fr;max-width:440px;gap:16px}
      .pr-card{padding:26px 22px}
      .pr-price{font-size:44px}
    }

    /* FAQ */
    #faq{background:rgba(255,255,255,.012)}
    .faq-list{margin-top:52px;display:flex;flex-direction:column;gap:0;max-width:700px;margin-left:auto;margin-right:auto}
    .faq-item{border-bottom:1px solid var(--border)}
    .faq-q{width:100%;text-align:left;background:none;border:none;color:var(--text);font-family:var(--font);font-size:16px;font-weight:500;padding:22px 0;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:16px;transition:color .2s}
    .faq-q:hover{color:rgba(255,255,255,.85)}
    .faq-arrow{flex-shrink:0;width:20px;height:20px;border-radius:50%;background:var(--card);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;font-size:16px;transition:transform .3s var(--ease),background .2s}
    .faq-item.open .faq-arrow{transform:rotate(45deg);background:rgba(80,136,253,.15);border-color:rgba(80,136,253,.3)}
    .faq-a{font-size:16px;color:var(--muted);line-height:1.7;max-height:0;overflow:hidden;transition:max-height .4s var(--ease),padding .3s}
    /* Headroom for the longest answer at mobile width, where a ~250-character
       reply wraps to about 7 lines and clipped against the old 200px ceiling. */
    .faq-item.open .faq-a{max-height:400px;padding-bottom:20px}

    /* CTA */
    .cta-section{padding:120px 28px;text-align:center;position:relative;overflow:hidden}
    .cta-blob{display:none}
    .cta-inner{position:relative;z-index:1;max-width:720px;margin:0 auto}
    .cta-inner h2{font-size:clamp(43px,6.25vw,73px);font-weight:300;letter-spacing:-0.03em;margin-bottom:12px}
    .cta-inner h2 strong{font-weight:600}
    .cta-tagline{font-size:16px;color:rgba(255,255,255,.8);margin-bottom:36px;line-height:1.65;max-width:440px;margin-left:auto;margin-right:auto}
    .cta-checks{display:flex;align-items:center;justify-content:center;gap:24px;margin-top:24px;flex-wrap:wrap}
    .cta-check{font-size:16px;color:rgba(255,255,255,.85);display:flex;align-items:center;gap:6px}
    .cta-check-icon{color:#fff}

    /* FOOTER */
    .footer-wrap{position:relative;overflow:hidden}
    .footer-glow-img{position:absolute;bottom:0;left:50%;transform:translateX(-50%);width:130%;max-width:1600px;pointer-events:none;opacity:.85;z-index:0}
    footer{border-top:1px solid var(--border);padding:44px 0 32px;position:relative;z-index:1}
    .footer-inner{display:flex;align-items:flex-start;justify-content:space-between;gap:40px;flex-wrap:wrap;max-width:var(--max);margin:0 auto;padding:0 28px}
    .footer-brand{display:flex;flex-direction:column;gap:10px;max-width:220px}
    .footer-logo svg{height:26px;width:auto}
    .footer-brand p{font-size:16px;color:rgba(255,255,255,.75);line-height:1.6}
    .footer-links{display:flex;gap:36px;flex-wrap:wrap}
    .footer-link{font-size:16px;color:rgba(255,255,255,.85);transition:color .2s}
    .footer-link:hover{color:#fff}
    .footer-bottom{max-width:var(--max);margin:28px auto 0;padding:20px 28px 0;border-top:1px solid rgba(255,255,255,.2);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px}
    .footer-bottom p{font-size:16px;color:rgba(255,255,255,.7)}
    .footer-bottom a{color:rgba(255,255,255,.8);transition:color .2s}
    .footer-bottom a:hover{color:#fff}

    /* NOTIFY MODAL */
    .modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.75);backdrop-filter:blur(12px);z-index:500;display:flex;align-items:center;justify-content:center;padding:20px;opacity:0;pointer-events:none;transition:opacity .3s}
    .modal-overlay.open{opacity:1;pointer-events:all}
    .modal{background:#111;border:1px solid rgba(255,255,255,.12);border-radius:20px;padding:36px;max-width:440px;width:100%;position:relative;transform:translateY(20px);transition:transform .3s var(--ease)}
    .modal-overlay.open .modal{transform:translateY(0)}
    .modal-close{position:absolute;top:16px;right:16px;background:rgba(255,255,255,.06);border:none;color:var(--muted);width:32px;height:32px;border-radius:50%;cursor:pointer;font-size:16px;display:flex;align-items:center;justify-content:center;transition:background .2s}
    .modal-close:hover{background:rgba(255,255,255,.12)}
    .modal h3{font-size:22px;font-weight:600;margin-bottom:8px}
    .modal p{font-size:16px;color:var(--muted);line-height:1.6;margin-bottom:24px}
    .modal-form{display:flex;flex-direction:column;gap:12px}
    .modal-input{background:rgba(255,255,255,.06);border:1px solid var(--border);border-radius:10px;padding:12px 16px;font-size:16px;color:var(--text);font-family:var(--font);width:100%;outline:none;transition:border-color .2s}
    .modal-input:focus{border-color:rgba(80,136,253,.5)}
    .modal-input::placeholder{color:var(--dim)}
    .modal-submit{background:var(--blue);color:#fff;border:none;border-radius:10px;padding:13px;font-size:16px;font-weight:600;cursor:pointer;font-family:var(--font);transition:opacity .2s,transform .2s}
    .modal-submit:hover{opacity:.88;transform:scale(.98)}
    .modal-submit:disabled{opacity:.5;cursor:not-allowed;transform:none}
    .modal-success{display:none;text-align:center;padding:20px 0}
    .modal-success .check{font-size:48px;margin-bottom:12px}
    .modal-success h4{font-size:18px;font-weight:600;margin-bottom:8px}
    .modal-success p{font-size:16px;color:var(--muted)}
    .modal.success-state .modal-form{display:none}
    .modal.success-state .modal-success{display:block}

    /* RESPONSIVE */
    @media(max-width:820px){
      .features-grid{grid-template-columns:1fr}
      .feat:last-child{grid-column:auto!important}
      .step{grid-template-columns:48px 1fr}
      .step-vis{display:none}
      .pricing-grid{grid-template-columns:1fr;max-width:420px}
      .nav-links{display:none}
      .footer-inner{flex-direction:column;gap:28px}
    }
    @media(max-width:600px){
      .hero h1{font-size:47px}
      .cta-inner h2{font-size:40px}
      .step-num{font-size:28px}
        }

/* ══ Typeface ══════════════════════════════════════════════════════════════
   BDO Grotesk, self-hosted. One variable file covers 300-900, which is the
   whole range the site uses, so it replaces what were five separate Inter
   weights fetched from Google — fewer bytes and no third-party request on
   first paint.

   Subset to Latin plus the punctuation actually rendered here; 260KB of TTF
   becomes 58KB of WOFF2. Licensed under the SIL OFL, which permits embedding
   provided the notice ships alongside: see assets/fonts/OFL.txt.

   font-display:swap deliberately. The face is preloaded, so the swap window
   is short, and text that is readable immediately beats text that is absent. */
@font-face{
  font-family:"BDO Grotesk";
  src:url("fonts/bdo-grotesk-var.woff2") format("woff2-variations"),
      url("fonts/bdo-grotesk-var.woff2") format("woff2");
  font-weight:300 900;
  font-style:normal;
  font-display:swap}

:root{--font:"BDO Grotesk",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}
body{font-family:var(--font)}

/* BDO Grotesk has no star glyph, so the rating stars are given an explicit
   stack rather than left to whatever the browser reaches for next. */
.test-star,.hero-stars{font-family:-apple-system,"Segoe UI Symbol","Apple Color Emoji",sans-serif}

/* ══ Multi-page additions ═══════════════════════════════════════════════════
   base.css is the homepage's original stylesheet, kept intact so the hero,
   marquee, testimonials and pricing keep the treatment already signed off.
   Everything below is new: the navigation that now has to reach fourteen
   pages, the section components those pages are built from, and the hero
   story sequence. New class names are deliberately unprefixed (.card, .chk,
   .stp) because base.css namespaces all of its own (.feat-card, .pr-card),
   so the two sets cannot collide. */

/* ── Navigation ───────────────────────────────────────────────────────────
   Two dropdown groups, a text log-in and one primary action. The menus open
   on hover for pointers and on click for keyboards and touch, which is why
   the open state is driven by a class rather than :hover alone. */
.nav-inner{display:flex;align-items:center;gap:22px;position:relative}
.nav-links{display:flex;align-items:center;gap:4px;list-style:none;margin:0 auto 0 14px}
.nav-links > li > a,.nav-grp-btn{
  display:inline-flex;align-items:center;gap:5px;padding:8px 12px;border-radius:9px;
  font-size:14px;font-weight:500;color:rgba(255,255,255,.72);background:none;border:0;
  font-family:inherit;cursor:pointer;transition:color .18s,background .18s}
.nav-links > li > a:hover,.nav-grp-btn:hover,.nav-group.open .nav-grp-btn{color:#fff;background:rgba(255,255,255,.06)}
.nav-grp-btn svg{transition:transform .22s var(--ease);opacity:.6}
.nav-group.open .nav-grp-btn svg{transform:rotate(180deg)}
.nav-group{position:static}
/* ── Mega menu ─────────────────────────────────────────────────────────────
   A wide panel anchored to the header rather than to the button that opened
   it, so both menus share one edge and the eye does not have to re-find the
   panel when switching between them. Width follows the column count, so the
   four-item Solutions menu does not sit in a half-empty product-sized box. */
.nav-menu{
  position:absolute;top:calc(100% - 8px);left:50%;transform:translateX(-50%) translateY(-6px);
  padding:12px;border-radius:12px;
  /* Frosted glass: a translucent fill plus a blur of whatever is behind it.
     The fill has to stay semi-transparent — at full opacity the backdrop
     filter has nothing to show and it reads as a flat panel again. saturate
     keeps colour passing through from tipping grey. */
  /* Opaque enough to read against on its own. backdrop-filter is a
     progressive enhancement — Firefox users with it disabled, and any browser
     without GPU compositing, get no blur at all, and at .72 the hero headline
     was legible straight through the panel. */
  background:rgba(12,12,17,.985);
  -webkit-backdrop-filter:blur(48px) saturate(185%);
  backdrop-filter:blur(48px) saturate(185%);
  border:1px solid rgba(255,255,255,.12);
  box-shadow:
    0 30px 80px rgba(0,0,0,.6),
    inset 0 1px 0 rgba(255,255,255,.07);
  opacity:0;visibility:hidden;z-index:60;
  /* visibility is stepped, not eased: delayed by the fade so the panel stays
     present while it fades out, and appears instantly on the way in. */
  transition:opacity .18s,transform .18s,visibility 0s linear .18s}
/* Keyed on the panel, not on .nav-group: site.js re-parents the panel onto
   <nav>, so it is no longer a descendant of the group. The group selector is
   kept as well so the menu still opens if JS has not run yet. */
.nav-menu.open,
.nav-group.open .nav-menu{opacity:1;visibility:visible;transform:translateX(-50%);transition-delay:0s}
/* Where the blur genuinely renders, the fill can drop back and let it show. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  /* Still high, deliberately. A browser can report support for backdrop-filter
     and then not composite it (headless, GPU blocklists, low-power modes), so
     the fill has to carry legibility on its own; the blur is what makes it
     look like glass, not what makes it readable. */
  /* The blur reaches the page now that the panel is not inside a backdrop
     root, so the fill can come back down and let it show. */
  .nav-menu{background:rgba(12,12,17,.88)}
  .nav-drawer{background:rgba(12,12,17,.94)}
}
.nav-menu-grid{
  display:grid;grid-template-columns:repeat(var(--cols,3),minmax(268px,1fr));
  gap:4px 10px}
/* Hairline rules between columns, drawn with a gradient so they fade at the
   ends instead of butting into the panel padding. */
.nav-menu-grid > .nav-item:not(:nth-child(var(--cols,3)n+1))::before{
  content:"";position:absolute;left:-5px;top:8px;bottom:8px;width:1px;
  background:linear-gradient(transparent,rgba(255,255,255,.09) 22%,rgba(255,255,255,.09) 78%,transparent)}
.nav-item{
  position:relative;display:flex;align-items:flex-start;gap:12px;
  padding:12px 13px;border-radius:8px;transition:background .16s}
.nav-item:hover{background:rgba(255,255,255,.055)}
.nav-item[aria-current="page"]{background:rgba(80,136,253,.14)}
.nav-item-ic{
  flex:none;display:grid;place-items:center;width:30px;height:30px;margin-top:1px;
  border-radius:8px;color:#8fb4ff;background:rgba(80,136,253,.12);
  border:1px solid rgba(255,255,255,.07);transition:color .18s}
.nav-item:hover .nav-item-ic{color:#cfe0ff}
.nav-item-ic .ic{width:17px;height:17px}
.nav-item-tx{display:block;min-width:0}
.nav-menu-t{display:block;font-size:13.5px;font-weight:600;color:#fff;letter-spacing:-.005em}
.nav-menu-d{display:block;font-size:12px;color:var(--muted);margin-top:3px;line-height:1.45}
.nav-right{display:flex;align-items:center;gap:8px;margin-left:auto}
.nav-login{padding:8px 12px;border-radius:9px;font-size:14px;font-weight:500;color:rgba(255,255,255,.72);transition:color .18s,background .18s}
.nav-login:hover{color:#fff;background:rgba(255,255,255,.06)}
.nav-burger{display:none;flex-direction:column;gap:4px;padding:9px;background:none;border:0;cursor:pointer}
.nav-burger span{display:block;width:18px;height:2px;border-radius:2px;background:#fff;transition:transform .22s,opacity .18s}
.nav-burger[aria-expanded="true"] span:nth-child(1){transform:translateY(6px) rotate(45deg)}
.nav-burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.nav-burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}
/* Built by site.js and appended to <nav> on every page, so it must be hidden
   by default at every width — not only inside the mobile media query, where a
   missing rule left it painting as raw links over the hero. */
.nav-drawer{display:none}

/* ── Hero additions ───────────────────────────────────────────────────── */
.hero-eyebrow{
  position:relative;z-index:1;display:inline-block;margin-bottom:20px;padding:6px 13px;
  border:1px solid var(--border);border-radius:999px;background:rgba(255,255,255,.03);
  font-size:11.5px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:rgba(255,255,255,.66);
  animation:fadeUp .9s var(--ease) .05s both}
.hero-note{position:relative;z-index:1;margin-top:20px;font-size:13.5px;color:rgba(255,255,255,.45);animation:fadeUp .9s var(--ease) .3s both}
/* Landing and persona pages open on a headline, not on a full-screen stage.
   base.css pins .hero to min-height:100vh, which is right for the homepage
   where the story sequence needs the room and wrong everywhere else — an
   inner page that costs a full viewport before its first section is a page
   people bounce off. */
.hero-compact{min-height:0;padding:150px 28px 72px}
/* 40% up from 58px. max-width is lifted with it: base.css caps .hero h1 at
   940px, which at this size would wrap the longer inner headlines into a
   third line and undo the effect. */
.hero-compact h1{font-size:81px;max-width:1120px;line-height:1.05}
.hero-compact .hero-sub{font-size:17.5px;max-width:620px}

/* ── Section furniture ────────────────────────────────────────────────── */
.sec-head{max-width:720px;margin:0 auto 54px;text-align:center}
.sec-head h2{font-size:40px;font-weight:600;letter-spacing:-.03em;line-height:1.14;text-wrap:balance}
.sec-head p{margin-top:14px;font-size:17px;line-height:1.6;color:var(--muted)}
.sec-cta{margin-top:44px;text-align:center}

/* ── Problem ──────────────────────────────────────────────────────────── */
.prob{padding:110px 0;border-top:1px solid var(--border)}
.prob-inner{max-width:760px;text-align:center}
.prob-inner h2{font-size:42px;font-weight:600;letter-spacing:-.03em;line-height:1.12;text-wrap:balance}
.prob-body{margin-top:26px;display:grid;gap:16px}
.prob-body p{font-size:17.5px;line-height:1.68;color:var(--muted)}
.prob-close{
  margin-top:34px;padding-top:30px;border-top:1px solid var(--border);
  font-size:19px;line-height:1.55;font-weight:500;color:#fff;text-wrap:balance}

/* ── Steps ────────────────────────────────────────────────────────────── */
/* flex, not grid: an item count that doesn't divide evenly by the column
   count (3, or 5, or any count not divisible by 3 or by 2 at the tablet
   breakpoint) leaves a partial last row. flex + justify-content:center lets
   that row centre itself instead of hugging the left edge; flex-basis
   reproduces the same per-breakpoint widths a grid would give a full row. */
.steps{list-style:none;display:flex;flex-wrap:wrap;justify-content:center;gap:22px;counter-reset:s}
.stp{
  flex:0 0 calc((100% - 44px) / 3);
  position:relative;padding:32px 28px 30px;border:1px solid var(--border);border-radius:16px;
  background:var(--card);overflow:hidden}
.stp::before{
  content:"";position:absolute;inset:0 0 auto 0;height:1px;
  background:var(--grad);opacity:0;transition:opacity .3s}
.stp:hover::before{opacity:.7}
.stp h3{font-size:19px;font-weight:600;letter-spacing:-.01em;margin-bottom:10px}
.stp p{font-size:14.5px;line-height:1.66;color:var(--muted)}

/* ── Card grid ────────────────────────────────────────────────────────── */
.cards{display:flex;flex-wrap:wrap;justify-content:center;gap:20px}
.card{
  flex:0 0 calc((100% - 40px) / 3);
  padding:28px 26px;border:1px solid var(--border);border-radius:16px;background:var(--card);
  transition:border-color .25s,transform .25s var(--ease)}
.card:hover{border-color:rgba(255,255,255,.2);transform:translateY(-3px)}
.card h3{font-size:17.5px;font-weight:600;letter-spacing:-.01em;margin-bottom:9px}
.card p{font-size:14.5px;line-height:1.66;color:var(--muted)}

/* ── Checklist split ──────────────────────────────────────────────────── */
.chk-inner{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:64px;align-items:start}
.chk-copy h2{font-size:40px;font-weight:600;letter-spacing:-.03em;line-height:1.14;text-wrap:balance}
.chk-copy p{margin-top:18px;font-size:17px;line-height:1.66;color:var(--muted)}
.chk{list-style:none;display:grid;gap:2px;align-self:center}
.chk li{
  display:flex;align-items:flex-start;gap:13px;padding:15px 4px;
  border-bottom:1px solid var(--border);font-size:15.5px;line-height:1.5;color:rgba(255,255,255,.86)}
.chk li:last-child{border-bottom:0}
.chk svg{flex:none;margin-top:3px;color:var(--green)}

/* ── Persona links ────────────────────────────────────────────────────── */
/* Three across. With five personas that leaves a two-card final row,
   which sits better than the single orphan a four-column grid produced. */
/* A row list rather than another illustrated-card grid — problem, features
   and steps already use that shape, and repeating it here just reads as more
   of the same. */
/* Bento grid: two wide cards, then three narrower ones underneath — six
   tracks so "span 3" and "span 2" divide evenly into the two row shapes.
   Text sits at the top of each card, an icon in a lit, textured strip sits
   at the bottom — the reverse of the row list, and distinct from the flat
   illustration panels problem/features/steps use. */
/* One mixed grid — a text/brand cell alongside five persona cards, sized
   unevenly (a wide intro, one card running the full right-hand height, four
   even ones underneath) rather than a uniform repeating grid, so it reads as
   composed the way a mixed photo/copy/CTA bento does. Every cell keeps the
   same card language though (dark surface, brand-gradient icon badge) since
   all five items are the same kind of content, just arranged unevenly. */
/* A numbered accordion instead of a card grid — native <details>, the same
   pattern the FAQ already uses, so it expands with no JS. Only one persona
   open at a time isn't enforced (each <details> is independent), which is
   fine: nothing stops two being open together, it just means both stay
   readable rather than one forcibly collapsing the other. */
/* flex, not grid: with 5 cards the last row only has 2, and flex + justify-
   content:center lets that partial row centre itself instead of hugging the
   left edge the way an unfilled grid row would. flex-basis reproduces the
   same 3-per-row (then 2, then 1) widths a grid would have given at each
   breakpoint, so full rows still look identical to a grid layout. */
.who{display:flex;flex-wrap:wrap;justify-content:center;gap:20px}
.who-i{
  display:flex;flex-direction:column;flex:0 0 calc((100% - 40px) / 3);
  padding:14px 14px 26px;
  border:1px solid var(--border);border-radius:16px;background:var(--card);
  transition:border-color .25s,transform .25s var(--ease)}
.who-i:hover{border-color:rgba(255,255,255,.2);transform:translateY(-3px)}
.who-i .card-vis{margin-bottom:20px}
.who-i h3{padding:0 12px;font-size:17.5px;font-weight:600;letter-spacing:-.01em;margin-bottom:9px}
.who-i p{padding:0 12px;font-size:14.5px;line-height:1.66;color:var(--muted)}

/* ── FAQ (native disclosure, so it works without JS) ──────────────────── */
.faq-wrap{max-width:800px}
.faq-i{border-bottom:1px solid var(--border)}
.faq-i summary{
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  padding:24px 2px;font-size:16.5px;font-weight:500;cursor:pointer;list-style:none}
.faq-i summary::-webkit-details-marker{display:none}
.faq-i summary svg{flex:none;color:var(--muted);transition:transform .25s var(--ease)}
.faq-i[open] summary svg{transform:rotate(45deg)}
.faq-i p{padding:0 2px 24px;font-size:15px;line-height:1.7;color:var(--muted);max-width:66ch}

/* ── Footer ───────────────────────────────────────────────────────────── */
footer{border-top:1px solid var(--border);padding:72px 0 36px;margin-top:0}
.foot-top{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,2fr);gap:56px}
.foot-brand p{margin-top:18px;font-size:14px;line-height:1.66;color:var(--muted);max-width:34ch}
.foot-logo svg{height:26px;width:auto}
.foot-cols{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}
.foot-col h4{font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);margin-bottom:16px}
.foot-col a{display:block;padding:5px 0;font-size:14px;color:var(--muted);transition:color .18s}
.foot-col a:hover{color:#fff}
.foot-bottom{
  display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;
  margin-top:56px;padding-top:26px;border-top:1px solid var(--border);font-size:13px;color:var(--dim)}
.foot-bottom a{color:var(--muted)}
.foot-bottom a:hover{color:#fff}
.foot-legal{display:flex;gap:20px}

/* ══ Hero story sequence ═══════════════════════════════════════════════════
   Five beats on one rail. Every beat carries `--n`; its delay is derived from
   that and `--beat`, so retiming the whole sequence is one number on the
   container. `--loop` is beat × 5 and must stay in step with the beat count. */
.st{position:relative;z-index:1;width:100%;max-width:960px;margin:60px auto 0;--loop:calc(var(--beat) * 5);animation:fadeUp 1s var(--ease) .42s both}
.st-svg{width:100%;height:auto;display:block;overflow:visible}

.st-rail{stroke:rgba(255,255,255,.1);stroke-width:2}
.st-fill{stroke:url(#stg);stroke-width:2;stroke-linecap:round;stroke-dasharray:1;stroke-dashoffset:1;
  animation:stFill var(--loop) linear infinite}
@keyframes stFill{0%{stroke-dashoffset:1}92%,100%{stroke-dashoffset:0}}

/* A beat is dim until its turn, then holds bright for one beat and settles
   back to a readable mid state — fully dimming it again would make the
   finished steps look undone. */
.st-b{--d:calc(var(--n) * var(--beat) - var(--loop));opacity:.3;
  animation:stOn var(--loop) linear infinite;animation-delay:var(--d)}
@keyframes stOn{0%{opacity:.3}6%{opacity:1}20%{opacity:1}30%,100%{opacity:.62}}

.st-card{fill:rgba(255,255,255,.04);stroke:var(--border);stroke-width:1}
.st-bar{fill:rgba(255,255,255,.05)}
.st-dot{fill:rgba(255,255,255,.22)}
.st-ln{fill:rgba(255,255,255,.14)}
.st-accent{fill:url(#stg);opacity:.75}
.st-node{fill:#0b0b0d;stroke:rgba(255,255,255,.22);stroke-width:2}
.st-b .st-node{animation:stNode var(--loop) linear infinite;animation-delay:var(--d)}
@keyframes stNode{0%{stroke:rgba(255,255,255,.22)}6%,100%{stroke:#48CA7C}}
.st-node-ok{fill:#48CA7C}
.st-lb{fill:rgba(255,255,255,.6);font:600 13px var(--font);text-anchor:middle;letter-spacing:.01em}
.st-mini-t{fill:rgba(255,255,255,.55);font:600 11px var(--font)}

/* beat 2 — the selection ring and the pin that drops onto it */
.st-sel{fill:none;stroke:#5088FD;stroke-width:1.5;stroke-dasharray:4 3;opacity:0;
  animation:stSel var(--loop) linear infinite;animation-delay:calc(1 * var(--beat) - var(--loop))}
@keyframes stSel{0%,4%{opacity:0}10%,30%{opacity:.9}42%,100%{opacity:.35}}
.st-pin{fill:#5088FD;opacity:0;transform-origin:331px 100px;
  animation:stPin var(--loop) var(--ease) infinite;animation-delay:calc(1 * var(--beat) - var(--loop))}
.st-pin-i{fill:#fff}
@keyframes stPin{0%,6%{opacity:0;transform:translateY(-14px) scale(.7)}14%{opacity:1;transform:none}100%{opacity:1;transform:none}}
.st-bub{fill:rgba(80,136,253,.16);stroke:rgba(80,136,253,.4);stroke-width:1;opacity:0;
  animation:stBub var(--loop) var(--ease) infinite;animation-delay:calc(1 * var(--beat) - var(--loop))}
.st-bub-ln{fill:rgba(255,255,255,.4);opacity:0;
  animation:stBub var(--loop) var(--ease) infinite;animation-delay:calc(1 * var(--beat) - var(--loop))}
@keyframes stBub{0%,12%{opacity:0;transform:translateY(5px)}20%{opacity:1;transform:none}100%{opacity:1;transform:none}}

/* beat 3 — rows filling in, the new comment landing last */
.st-st{fill:rgba(255,255,255,.2)}
.st-row:nth-child(6) .st-st{fill:#5088FD;animation:stLand var(--loop) var(--ease) infinite;animation-delay:calc(2 * var(--beat) - var(--loop))}
@keyframes stLand{0%,8%{opacity:0}16%{opacity:1}100%{opacity:1}}

/* beat 4 — progress bar running */
.st-prog-t{fill:rgba(255,255,255,.09)}
.st-prog{fill:url(#stg);transform-origin:612px 0;
  animation:stProg var(--loop) linear infinite;animation-delay:calc(3 * var(--beat) - var(--loop))}
@keyframes stProg{0%{transform:scaleX(.05)}22%{transform:scaleX(.72)}100%{transform:scaleX(.72)}}

/* beat 5 — the check drawing itself */
.st-ok-r{fill:rgba(72,202,124,.12);stroke:rgba(72,202,124,.45);stroke-width:1.5}
.st-ok{fill:none;stroke:#48CA7C;stroke-width:3.4;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:1;stroke-dashoffset:1;
  animation:stOk var(--loop) var(--ease) infinite;animation-delay:calc(4 * var(--beat) - var(--loop))}
@keyframes stOk{0%,4%{stroke-dashoffset:1}18%,100%{stroke-dashoffset:0}}

/* the comment travelling the rail, hidden while it sits on a beat */
.st-trav{fill:#8ef5c4;animation:stTrav var(--loop) linear infinite}
@keyframes stTrav{
  0%{transform:translateX(96px);opacity:0}
  16%{transform:translateX(96px);opacity:0}
  20%{opacity:1}
  36%{transform:translateX(288px);opacity:1}
  40%{transform:translateX(288px);opacity:0}
  56%{transform:translateX(480px);opacity:0}
  60%{opacity:1}
  76%{transform:translateX(672px);opacity:1}
  80%{transform:translateX(864px);opacity:0}
  100%{transform:translateX(864px);opacity:0}}

.st-mini{display:none;list-style:none;max-width:340px;margin:0 auto;text-align:left}
.st-mini li{display:flex;align-items:center;gap:13px;padding:13px 0;border-bottom:1px solid var(--border);font-size:14.5px;color:rgba(255,255,255,.8)}
.st-mini li:last-child{border-bottom:0}
.st-mini span{
  flex:none;width:23px;height:23px;border-radius:50%;display:grid;place-items:center;
  font-size:11px;font-weight:700;color:#04120a;background:var(--grad)}

/* ── Responsive ───────────────────────────────────────────────────────── */
@media(max-width:1000px){
  .stp{flex-basis:calc((100% - 22px) / 2)}
  .card{flex-basis:calc((100% - 20px) / 2)}
  .who-i{flex-basis:calc((100% - 20px) / 2)}
  .chk-inner{grid-template-columns:1fr;gap:36px}
  .foot-top{grid-template-columns:1fr;gap:44px}
}
@media(max-width:860px){
  .nav-links,.nav-right{display:none}
  .nav-burger{display:flex}
  .nav-drawer.open{display:block}
  .nav-drawer{
    position:absolute;top:100%;left:0;right:0;padding:16px 22px 24px;
    background:#0b0b0d;border-top:1px solid var(--border);border-bottom:1px solid var(--border);
    max-height:76vh;overflow-y:auto}
  .nav-drawer h4{font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);margin:18px 0 8px}
  .nav-drawer a{display:block;padding:10px 0;font-size:15px;color:rgba(255,255,255,.82);border-bottom:1px solid var(--border)}
  .nav-drawer .nav-drawer-cta{
    display:block;margin-top:20px;padding:13px;border-radius:11px;text-align:center;
    background:var(--grad-diag);color:#04120a;font-weight:700;border:0}
}
@media(max-width:760px){
  .st-svg{display:none}
  .st-mini{display:grid}
  .st{margin-top:44px}
  .sec-head h2,.prob-inner h2,.chk-copy h2{font-size:32px}
  .prob{padding:80px 0}
  .hero-compact h1{font-size:59px;max-width:none}
}
@media(max-width:620px){
  .stp,.card{flex-basis:100%}
  .who-i{flex-basis:100%}
  .foot-cols{grid-template-columns:1fr;gap:26px}
  .foot-bottom{flex-direction:column;align-items:flex-start;gap:14px}
}

/* Motion is decoration here; the sequence still reads as five labelled beats
   without it, so honour the preference completely rather than slowing it. */
@media(prefers-reduced-motion:reduce){
  .st-fill,.st-b,.st-sel,.st-pin,.st-bub,.st-bub-ln,.st-prog,.st-ok,.st-trav,
  .st-b .st-node,.st-row:nth-child(6) .st-st{animation:none!important}
  .st-b{opacity:1}
  .st-fill{stroke-dashoffset:0}
  .st-ok{stroke-dashoffset:0}
  .st-trav{opacity:0}
  .st-prog{transform:scaleX(.72)}
  .st-pin,.st-bub,.st-bub-ln,.st-sel{opacity:1}
}

/* ══ Media slots ═══════════════════════════════════════════════════════════
   Where real screenshots and video go. Until a `src` is supplied the block
   renders as an obvious placeholder carrying its own brief: what to shoot and
   at what size. It is styled to read as scaffolding, never as artwork, so a
   forgotten slot is impossible to miss on a review pass. */
.mwrap{padding:24px 0 88px}
.mwrap-wide .mfig{max-width:1170px}
.mfig{margin:0 auto;max-width:980px}
.mbox{
  display:block;width:100%;border-radius:16px;border:1px solid var(--border);
  background:var(--card);object-fit:cover;
  box-shadow:0 30px 80px rgba(0,0,0,.5)}
/* The hero recording carries a strip of black across the top of every frame.
   The box is sized to the content below that strip, so cover has to crop the
   difference — anchoring to the bottom spends that crop entirely on the strip
   instead of splitting it top and bottom and eating real content. */
video.mbox{object-position:50% 100%}
.mbox-ph{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:9px;
  padding:34px;text-align:center;box-shadow:none;
  border:1px dashed rgba(255,255,255,.22);
  background:
    repeating-linear-gradient(45deg,rgba(255,255,255,.022) 0 12px,transparent 12px 24px),
    radial-gradient(120% 90% at 50% 0%,rgba(80,136,253,.10),transparent 62%),
    var(--card)}
.mph-kind{
  font-size:10.5px;font-weight:700;letter-spacing:.16em;padding:4px 10px;border-radius:999px;
  background:var(--grad-diag);color:#04120a}
.mph-label{font-size:18px;font-weight:600;letter-spacing:-.01em;color:#fff;max-width:30ch;text-wrap:balance}
.mph-shot{font-size:13.5px;line-height:1.6;color:var(--muted);max-width:62ch}
.mph-spec{
  margin-top:4px;font:500 11.5px ui-monospace,"SF Mono",monospace;letter-spacing:.02em;
  color:var(--dim);padding:5px 11px;border:1px solid var(--border);border-radius:7px}
.mfig-cap{margin-top:16px;text-align:center;font-size:13.5px;color:var(--muted)}
/* 1170px matches --max and .mwrap-wide, the widest measure the site already
   uses, so the hero video reads as large without inventing a width no other
   section has. .hero's own 28px side padding still keeps it off the edge. */
.hero-media{position:relative;z-index:1;width:100%;max-width:1170px;margin:56px auto 0;animation:fadeUp 1s var(--ease) .42s both}
.hero-media .mfig{max-width:none}
@media(max-width:760px){
  .mwrap{padding:12px 0 56px}
  .mbox-ph{padding:24px 18px;gap:7px}
  .mph-label{font-size:15.5px}
  .mph-shot{font-size:12.5px}
  .hero-media{margin-top:36px}
}

/* ── Section headings, 25% larger ─────────────────────────────────────── */
.sec-head h2{font-size:50px}
.prob-inner h2{font-size:52px}
.chk-copy h2{font-size:50px}
@media(max-width:760px){
  .sec-head h2,.prob-inner h2,.chk-copy h2{font-size:40px}
}

/* ══ Icons ═════════════════════════════════════════════════════════════════
   One tile treatment everywhere so a card, a persona and a step read as the
   same family. The glyph inherits currentColor and the tile carries the tint,
   which keeps the accent gradient reserved for the few places that earn it. */
.ic{width:20px;height:20px;display:block;flex:none}
.ic-tile{
  display:inline-grid;place-items:center;width:42px;height:42px;margin-bottom:18px;
  border-radius:10px;color:#cfe0ff;
  background:linear-gradient(150deg,rgba(80,136,253,.16),rgba(72,202,124,.12));
  border:1px solid rgba(255,255,255,.09);
  transition:color .25s,border-color .25s,transform .25s var(--ease)}
.card:hover .ic-tile,.who-i:hover .ic-tile{color:#fff;border-color:rgba(255,255,255,.22);transform:translateY(-1px)}

/* Steps put the number and the glyph on one line: the number carries sequence,
   the icon carries meaning, and neither has to do both jobs. */

.mph-glyph{color:rgba(255,255,255,.34)}
.mph-glyph .ic{width:26px;height:26px}

@media(max-width:620px){
  .ic-tile{width:38px;height:38px;margin-bottom:14px;border-radius:11px}
  .ic{width:18px;height:18px}
}

/* ══ Ribbon ════════════════════════════════════════════════════════════════
   Sits above the fixed nav, so the nav is offset by its height rather than
   the two overlapping. --ribbon-h is the single source of that offset: set it
   to 0 and every dependent rule collapses correctly, which is what dismissing
   the ribbon does. The class is applied by an inline script in <head> so a
   dismissed ribbon never flashes in and drags the page down as it is removed. */
:root{--ribbon-h:42px}
html.no-ribbon{--ribbon-h:0px}
.ribbon{
  position:fixed;top:0;left:0;right:0;height:42px;z-index:210;
  display:flex;align-items:center;justify-content:center;
  /* A softer, less uniform wash than the flat diagonal gradient: the accent
     is strongest at the two ends and eases through the middle, so the copy
     sits on the calmest part of the band rather than fighting full
     saturation. */
  background:
    radial-gradient(120% 300% at 0% 50%,rgba(80,136,253,.95),transparent 58%),
    radial-gradient(120% 300% at 100% 50%,rgba(72,202,124,.95),transparent 58%),
    linear-gradient(90deg,#4a80ea,#3fb488);
  color:#04120a}
html.no-ribbon .ribbon{display:none}
/* A hairline of light along the bottom edge separates the ribbon from the
   page without drawing an actual border line. */
.ribbon::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:rgba(0,0,0,.18)}
.ribbon-in{
  display:flex;align-items:center;gap:14px;padding:0 14px;
  font-size:13.5px;font-weight:500;color:#04120a;line-height:1}
.ribbon-txt{letter-spacing:-.005em}
.ribbon-in strong{font-weight:700}
/* The supporting half recedes rather than competing with the claim. */
.ribbon-sub{color:rgba(4,18,10,.62)}

/* The button reads as a real button on the band: solid, not a tint of it. */
.ribbon-go{
  display:inline-flex;align-items:center;gap:7px;font-weight:600;font-size:12.5px;
  padding:6px 12px;border-radius:7px;background:#fff;color:#0d1117;
  box-shadow:0 1px 2px rgba(0,0,0,.18),0 4px 12px rgba(0,0,0,.12);
  transition:transform .18s var(--ease),box-shadow .18s}
.ribbon-go svg:first-child{width:14px;height:14px;flex:none}
.ribbon-arrow{opacity:.55;transition:transform .18s var(--ease)}
.ribbon-in:hover .ribbon-go{
  transform:translateY(-1px);
  box-shadow:0 2px 4px rgba(0,0,0,.2),0 8px 20px rgba(0,0,0,.18)}
.ribbon-in:hover .ribbon-arrow{transform:translateX(2px)}

.ribbon-x{
  position:absolute;right:10px;top:50%;transform:translateY(-50%);
  display:grid;place-items:center;width:26px;height:26px;border:0;border-radius:7px;
  background:transparent;color:rgba(0,0,0,.45);cursor:pointer;transition:background .16s,color .16s}
.ribbon-x:hover{background:rgba(0,0,0,.14);color:#04120a}

/* Everything pinned to the viewport top shifts by the ribbon height. */
nav{top:var(--ribbon-h)}
.hero{padding-top:calc(116px + var(--ribbon-h))}
.hero-compact{padding-top:calc(150px + var(--ribbon-h))}

@media(max-width:860px){
  :root{--ribbon-h:38px}
  .ribbon{height:38px}
  .ribbon-in{font-size:12.5px;gap:8px;padding-right:34px}
  .ribbon-in .ic{display:none}
}
@media(max-width:620px){
  .ribbon-in span:first-of-type{display:none}
  .ribbon-in{justify-content:center}
}

/* ══ Extension download ════════════════════════════════════════════════════ */
.dl{padding:104px 0;border-top:1px solid var(--border)}
.dl-inner{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:64px;align-items:center}
.dl-eyebrow{
  /* align-self, not just inline-flex: .dl-copy is a flex column, so this is a
     flex item and the default align-items:stretch pulled it to the full column
     width — which is why the pill ran to the right edge instead of hugging
     its label. */
  display:inline-flex;align-self:flex-start;
  align-items:center;gap:7px;margin-bottom:18px;padding:6px 12px;
  border:1px solid var(--border);border-radius:999px;background:rgba(255,255,255,.03);
  font-size:11.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:rgba(255,255,255,.7)}
.dl-eyebrow .ic{width:14px;height:14px}
/* The Chrome mark is a real logo, not a line icon: it ships at 18px and
   carries its own colours, so it needs its own size here or it overhangs the
   pill's cap height. flex:none stops it being squeezed by the label. */
.dl-eyebrow svg{width:14px;height:14px;flex:none}
.dl-copy h2{font-size:50px;font-weight:600;letter-spacing:-.03em;line-height:1.12;text-wrap:balance}
.dl-copy > p{margin-top:18px;font-size:17px;line-height:1.65;color:var(--muted);max-width:52ch}
.dl-actions{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-top:32px}
.dl-meta{margin-top:16px;font-size:13px;color:var(--dim);max-width:46ch}
.dl-vis .mwrap{padding:0}
.dl-vis .mfig{max-width:none}

@media(max-width:1000px){
  .dl-inner{grid-template-columns:1fr;gap:44px}
  .dl{padding:80px 0}
}
@media(max-width:760px){
  .dl-copy h2{font-size:40px}
}

/* ══ Floating nav ══════════════════════════════════════════════════════════
   The header is an island rather than a full-width bar: <nav> stays a fixed,
   transparent full-width layer purely for positioning, and .nav-inner becomes
   the visible surface. base.css paints <nav> itself once scrolled, which would
   put a full-bleed bar behind the island, so that rule is neutralised here and
   the scrolled state is expressed on the island instead. */
nav{padding:14px 0;background:none;border-bottom:0;backdrop-filter:none;-webkit-backdrop-filter:none}
nav.scrolled{background:none;border-bottom:0;backdrop-filter:none;-webkit-backdrop-filter:none}
.nav-inner{
  max-width:1120px;padding:14px 14px 14px 26px;gap:18px;
  background:rgba(11,11,14,.78);
  backdrop-filter:blur(22px) saturate(140%);-webkit-backdrop-filter:blur(22px) saturate(140%);
  border:1px solid rgba(255,255,255,.10);border-radius:16px;
  box-shadow:0 14px 44px rgba(0,0,0,.46),inset 0 1px 0 rgba(255,255,255,.05);
  transition:background .35s,border-color .35s,box-shadow .35s}
/* Over content the island needs more weight than it does over the hero. */
nav.scrolled .nav-inner{
  background:rgba(11,11,14,.92);border-color:rgba(255,255,255,.14);
  box-shadow:0 18px 54px rgba(0,0,0,.6),inset 0 1px 0 rgba(255,255,255,.06)}
.nav-logo svg{height:26px}
.nav-links{margin:0 auto}
.nav-cta{font-size:14px;padding:9px 18px}
.nav-login{font-size:14px}

/* The mega panel hangs off the island, so it clears the island's own edge
   rather than the viewport top. */
.nav-menu{top:calc(100% - 6px)}

@media(max-width:860px){
  nav{padding:10px 0}
  .nav-inner{max-width:none;margin:0 14px;padding:11px 11px 11px 18px;border-radius:16px}
  /* The drawer becomes a second floating card under the island instead of a
     full-bleed sheet, which would not line up with it. */
  .nav-drawer{
    /* Matches the island's own 14px inset. Anchored to <nav>, which is
       full-width, so left/right have to repeat the inset rather than
       inherit it. */
    top:calc(100% + 10px);left:14px;right:14px;padding:14px 18px 20px;
    background:rgba(14,14,19,.82);
    -webkit-backdrop-filter:blur(48px) saturate(185%);backdrop-filter:blur(48px) saturate(185%);
    border:1px solid rgba(255,255,255,.10);border-top:1px solid rgba(255,255,255,.10);
    border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,.6)}
  .nav-drawer a:last-of-type{border-bottom:0}
}

/* ══ Corner language ═══════════════════════════════════════════════════════
   Two radii across the whole site: 8px on anything you press, 16px on any
   surface that holds content, including the floating nav island. Overridden here rather than edited into
   base.css so the original stylesheet stays a clean record of the homepage it
   shipped with.

   The primary CTA (btn-chrome / nav-cta) is set separately below, alongside
   its own hover-glow treatment, but lands on the same 8px. */
.btn-ghost,
.plan-btn,
.sv-install-btn,
.nav-drawer .nav-drawer-cta{border-radius:8px}

/* Content surfaces. .mbox covers both the real media and its placeholder, so
   an image dropped in later inherits the same corner as the card grid. */
.card,
.stp,
.mbox,
.pr-card,
.how-card{border-radius:16px}

/* ══ Testimonials ══════════════════════════════════════════════════════════
   A static row of cards, one per real Chrome Web Store review, each filled
   edge to edge with that reviewer's own photo.

   Known limitation: the source images are the 48x48 avatars the Chrome Web
   Store serves, and a card is roughly 370x400, so they run at a ~8x upscale
   and show visible pixel blocks. Rendering them sharp is a deliberate
   decision, not an oversight — swap in headshots of 800px or better and this
   rule needs no change.

   Replaces the horizontal marquee: base.css still defines .test-scroller and
   .test-track, but nothing renders them any more. Overriding the base
   .test-card here rather than editing base.css keeps the site's overrides in
   one file, which is the pattern the rest of this stylesheet follows. */
/* base.css drives the marquee: .test-track holds the three cards twice and
   travels exactly -50%, so the second set is mid-frame at the wrap and the
   loop is invisible. Only the sizing is set here — the duration is shortened
   from base's 46s because half as many cards means half the distance, which
   at the inherited timing would have crawled. */
.test-track{animation-duration:30s}
/* base.css fades both ends of the scroller into the page background with a
   120px gradient. Off here: the cards carry photographs now, and washing one
   out to black on the way past reads as a rendering fault rather than a
   soft edge. They clip on the scroller's own overflow instead. */
.test-scroller::before,.test-scroller::after{content:none}
.test-track .test-card{flex:0 0 380px;width:380px;margin-right:18px}
.test-card{
  position:relative;isolation:isolate;overflow:hidden;
  display:flex;flex-direction:column;gap:0;
  min-height:520px;padding:22px;border-radius:18px;
  border:1px solid rgba(255,255,255,.10);
  background:#0b0b0e;
  transition:transform .3s var(--ease),border-color .3s var(--ease)}
.test-photo{position:absolute;inset:0;z-index:-1;overflow:hidden;border-radius:inherit}
/* object-position favours the upper part of the frame: cover crops a 1:1
   avatar to this card's taller shape, and centring it would slice the top of
   the head off. */
.test-photo img{
  width:100%;height:100%;object-fit:cover;object-position:50% 28%;display:block;
  filter:saturate(1.25) contrast(1.05)}
/* Darkens towards the foot, where the quote and name sit, so their contrast
   holds no matter how light the photo underneath happens to be. */
.test-photo::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,
    rgba(10,10,13,.10) 0%,
    rgba(10,10,13,.55) 46%,
    rgba(10,10,13,.92) 100%)}
.test-card:hover{
  transform:translateY(-4px);border-color:rgba(255,255,255,.22)}
/* Sits where the reference puts a company logo. These reviewers are
   individuals with no company on record, so the slot carries the rating
   rather than inventing an employer for a named person. The source is stated
   once in the section subhead instead of repeated on all four cards. */
.test-src{display:flex;align-items:center}
.test-src .test-stars{display:flex;gap:3px;font-size:14px;color:#fff}
/* margin-top:auto is what anchors the quote and its attribution to the foot
   of the card, leaving the colour field open above them — the arrangement the
   reference gets by running a portrait down from the top. Putting the auto
   margin on .test-src instead only pushes the block that follows it, which
   left the quote riding up under the stars. */
.test-text{
  flex:none;
  margin:auto 0 20px;font-size:16px;line-height:1.55;font-weight:500;
  color:#fff;text-wrap:pretty}
.test-author{display:flex;align-items:center}
.test-meta{display:flex;flex-direction:column;gap:2px;min-width:0}
.test-name{font-size:14.5px;font-weight:600;color:#fff}
.test-date{font-size:13px;color:rgba(255,255,255,.62)}
/* The track scrolls at every width, so the cards only need to get narrower —
   base.css already drops them to 300px under 640px; this shortens them to
   match so a tall card doesn't dominate a small screen. */
@media(max-width:640px){
  .test-card{min-height:440px}
}

/* ══ Pro badge ═════════════════════════════════════════════════════════════
   Matched to the supplied artwork: an outer gold pill with a thin, even
   surround, and an inner black ROUNDED RECTANGLE — not a second pill. That
   distinction is most of the character; a fully-rounded inner shape reads as
   a generic two-tone chip.

   Proportions are taken from the artwork rather than guessed: the black shape
   fills all but ~4% of the badge height, its corner radius is ~0.3 of its own
   height, and the wordmark is ~0.45 of it. Everything is driven by --pb-h so
   the badge scales as one object.

   Overridden automatically if tools/site/brand/pro-badge.* exists. */
.pro-badge{
  --pb-h:38px;
  display:inline-flex;align-items:center;
  height:var(--pb-h);padding:calc(var(--pb-h) * .085);
  border-radius:999px;
  background:linear-gradient(180deg,#eeb733 0%,#e5a929 100%);
  box-shadow:0 6px 20px rgba(224,164,25,.22)}
.pro-badge-mark{
  display:inline-flex;align-items:center;
  height:100%;padding:0 calc(var(--pb-h) * .42);
  border-radius:calc(var(--pb-h) * .30);
  background:#000;color:#fff;
  font-size:calc(var(--pb-h) * .45);font-weight:600;letter-spacing:-.015em;line-height:1}
.pro-badge-tag{
  padding:0 calc(var(--pb-h) * .40);
  font-size:calc(var(--pb-h) * .36);font-weight:800;letter-spacing:.04em;
  color:#1c1305;line-height:1}

/* ══ Pricing ═══════════════════════════════════════════════════════════════
   One split card, dark throughout. The two halves separate by weight rather
   than by inversion: the left recedes behind its artwork, the right sits
   forward on a lighter ground with a hairline between them.

   Both halves are tall on purpose — the card is the terminal object of the
   page, and giving it height is what stops it reading as another row. */
.price{position:relative;padding:110px 0;overflow:hidden}
.price::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(56% 44% at 50% 4%,rgba(80,136,253,.10),transparent 70%)}
.price .wrap{position:relative;z-index:1}

.pplan{
  display:grid;grid-template-columns:minmax(0,0.9fr) minmax(0,1fr);
  max-width:1020px;margin:0 auto;border-radius:24px;overflow:hidden;
  border:1px solid rgba(255,255,255,.10)}

/* ── left: the argument ────────────────────────────────────────────────── */
/* The banner is the colour gradient and nothing else. The dark overlays that
   used to sit on top of it (a fade to black down the right edge and another
   across the top) are gone: they were there to echo the black areas of the
   source image, but over a full-height panel they just muddied it.

   Replaced wholesale if tools/site/brand/pricing-banner.* exists. */
.pplan-left{
  position:relative;display:flex;flex-direction:column;justify-content:flex-end;
  padding:44px;min-height:700px;overflow:hidden;
  /* Starts on blue, not on near-black. The first two stops used to run
     #0a1430 -> #14306a, which read as a black band across the top of the
     panel behind the badge. The ramp is now blue to mint end to end. */
  background:linear-gradient(180deg,
      #2c66c0 0%,
      #337ac9 14%,
      #3d93c5 30%,
      #43aeaa 45%,
      #47c193 58%,
      #5fd39b 70%,
      #86e4bd 83%,
      #a9edd6 93%,
      #bdf4e4 100%)}

/* A supplied file replaces all of it. cover + center keeps the composition
   intact at any panel size, and black stays underneath so an off-ratio image
   letterboxes into black rather than showing through. */
.pplan-left.has-art{background:var(--banner) center/cover no-repeat,#000}
/* The wireframe was drawn for a black panel. On the gradient it reads as
   noise, so it is dropped rather than restyled into something muddy. */
.pplan-art{display:none}
.pplan-copy{position:relative;z-index:1}
/* The copy sits at the bottom of the panel, which is the pale end of the
   gradient, so it inverts to near-black. White here would be unreadable —
   this is the whole reason the text colour is stated rather than inherited. */
.pplan-copy h2{font-size:44px;font-weight:600;letter-spacing:-.035em;line-height:1.05;color:#04231a}
.pplan-copy h2 strong{font-weight:600;color:#0a4d38;background:none;-webkit-text-fill-color:currentColor}
.pplan-copy p{margin-top:18px;font-size:14.5px;line-height:1.62;color:rgba(4,35,26,.72);max-width:36ch}

/* ── right: the transaction ────────────────────────────────────────────── */
.pplan-right{
  display:flex;flex-direction:column;padding:44px 40px;
  background:#0e0f14;border-left:1px solid rgba(255,255,255,.08)}
.pplan-right > *{flex:none}          /* nothing compresses when space is tight */
/* The badge sits top-left of the banner. margin-bottom:auto is what puts it
   there: the panel is a flex column justified to flex-end, so the auto margin
   absorbs the free space and pushes the headline block to the bottom. */
.pplan-left .pro-badge,
.pplan-left .pro-badge-img{position:relative;z-index:2;align-self:flex-start;margin-bottom:auto}

.pl-opts{display:grid;gap:10px}
/* The billing rows are selectable, not buttons in the CTA sense, so they keep
   an outline — but as an inset shadow so the row height does not change
   between selected and unselected. */
.pl-opt{
  display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:11px;
  width:100%;padding:16px 17px;border-radius:12px;cursor:pointer;text-align:left;
  background:rgba(255,255,255,.03);border:0;
  box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.10);
  font-family:inherit;transition:background .18s,box-shadow .18s}
.pl-opt:hover{background:rgba(255,255,255,.05);box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.2)}
.pl-opt.active{
  background:rgba(72,202,124,.09);
  box-shadow:inset 0 0 0 1.5px rgba(72,202,124,.65),0 0 0 3px rgba(72,202,124,.10)}
.pl-opt-label{font-size:14px;font-weight:600;color:#fff}
.pl-save{
  padding:4px 9px;border-radius:6px;font-size:11.5px;font-weight:700;
  color:#8ef5c4;background:rgba(72,202,124,.16);white-space:nowrap}
.pl-rate{font-size:13.5px;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums}

/* The gap below the list is stated here rather than left to the CTA's auto
   margin: when the card runs short of spare space that margin computes to 0
   and the button lands flush against the last line. */
.pl-list{
  list-style:none;margin:28px 0 0;padding:24px 0 28px;
  border-top:1px solid rgba(255,255,255,.08);display:grid;gap:12px}
.pl-list li{display:flex;align-items:flex-start;gap:11px;font-size:14px;line-height:1.5;color:rgba(255,255,255,.82)}
.pl-list .ic{width:16px;height:16px;flex:none;margin-top:1px;color:var(--green)}

.pl-cta{
  display:block;margin-top:auto;padding:16px;border-radius:8px;text-align:center;
  font-size:14.5px;font-weight:700;color:#04120a;background:var(--grad-diag);
  box-shadow:0 10px 28px rgba(72,202,124,.24);
  transition:transform .2s var(--ease),box-shadow .2s}
.pl-cta:hover{transform:translateY(-1px);box-shadow:0 16px 36px rgba(72,202,124,.34)}
.pl-free{
  display:flex;align-items:baseline;justify-content:center;gap:8px;
  margin-top:12px;padding:13px;border-radius:8px;
  font-size:13.5px;font-weight:600;color:#fff;
  border:1px solid rgba(255,255,255,.12);transition:background .18s,border-color .18s}
.pl-free:hover{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.22)}
.pl-free span{font-weight:400;font-size:12.5px;color:var(--muted)}
.pl-fine{margin-top:18px;text-align:center;font-size:11.5px;line-height:1.6;color:var(--dim)}

@media(max-width:900px){
  .pplan{grid-template-columns:1fr;max-width:520px}
  .pplan-left{min-height:360px;padding:34px;justify-content:flex-end}
  .pplan-right{border-left:0;border-top:1px solid rgba(255,255,255,.08);padding:36px 26px}
  .pplan-copy h2{font-size:34px}
  .pplan-art{width:132%;top:-4%}
}
@media(max-width:560px){
  .price{padding:72px 0}
  .pl-opt{grid-template-columns:1fr auto;row-gap:6px}
  .pl-opt-label{grid-column:1 / -1}
  .pl-save,.pl-rate{grid-column:auto;justify-self:start}
}

/* Supplied badge artwork, when tools/site/brand/pro-badge.* exists. Height is
   fixed and width follows, so swapping a wider or narrower file needs no CSS. */
.pro-badge-img{display:block;height:38px;width:auto}
@media(max-width:560px){.pro-badge-img{height:32px}}

/* ══ Extension panel ═══════════════════════════════════════════════════════
   Copy left, product window right, running off the panel's right edge. The
   crop is the whole idea: a window that continues past the frame reads as a
   screen you are looking into, where a contained thumbnail reads as a picture
   of one. overflow:hidden on the panel does the cutting, so the window is
   simply positioned wider than the space it has.

   The window chrome is drawn in CSS, not baked into the screenshot, so a new
   image can be dropped in without re-shooting the browser frame. */
.dl{padding:40px 0 110px;border-top:0}
.dl-panel{
  position:relative;overflow:hidden;min-height:560px;
  border:1px solid rgba(255,255,255,.09);border-radius:24px;
  background:
    radial-gradient(90% 120% at 8% 0%,rgba(80,136,253,.10),transparent 56%),
    linear-gradient(180deg,#0c0d12,#08080b)}
.dl-copy{
  position:relative;z-index:2;max-width:46%;padding:64px 0 64px 60px;
  display:flex;flex-direction:column;justify-content:center;min-height:560px}
.dl-copy h2{font-size:44px;font-weight:600;letter-spacing:-.034em;line-height:1.1;text-wrap:balance}
/* Second line recedes: the sentence keeps its shape while the emphasis lands
   on the first half, which is the reference's move. */
.dl-copy h2 span{color:rgba(255,255,255,.42)}
.dl-copy > p{margin-top:18px;font-size:16px;line-height:1.65;color:var(--muted);max-width:44ch}

/* ── the bleeding window ───────────────────────────────────────────────── */
.dl-win{
  position:absolute;left:52%;top:74px;width:64%;
  border-radius:14px 0 0 0;overflow:hidden;
  border:1px solid rgba(255,255,255,.12);border-right:0;border-bottom:0;
  background:#0b0c11;
  box-shadow:-30px 30px 80px rgba(0,0,0,.55)}
.dl-win-bar{
  display:flex;align-items:center;gap:7px;padding:0 14px;height:38px;
  background:#14161c;border-bottom:1px solid rgba(255,255,255,.07)}
.dl-dot{width:10px;height:10px;border-radius:50%;flex:none}
.dl-win-url{
  margin-left:12px;padding:4px 12px;border-radius:6px;
  background:rgba(255,255,255,.05);font-size:11.5px;color:var(--dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dl-win-body{min-height:420px;display:flex}
.dl-win-body img{width:100%;height:auto;display:block}
.dl-ph{
  flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:9px;padding:40px 34px;text-align:center;
  background:repeating-linear-gradient(45deg,rgba(255,255,255,.022) 0 12px,transparent 12px 24px)}

@media(max-width:1080px){
  .dl-copy{max-width:52%;padding-left:44px}
  .dl-win{left:56%;width:70%}
}
@media(max-width:900px){
  .dl-panel{min-height:0}
  .dl-copy{max-width:none;min-height:0;padding:44px 32px 8px}
  .dl-win{position:relative;left:auto;top:auto;width:calc(100% - 32px);margin:28px 0 0 32px}
  .dl-win-body{min-height:300px}
  .dl-copy h2{font-size:34px}
}
@media(max-width:620px){
  .dl{padding:20px 0 72px}
  .dl-copy{padding:34px 22px 4px}
  .dl-win{width:calc(100% - 22px);margin-left:22px}
  .dl-win-body{min-height:230px}
}

/* ══ Card visuals ══════════════════════════════════════════════════════════
   Each feature card opens with an inset panel carrying a small illustration,
   the way the reference does. The panel is a recessed surface rather than
   another raised one, so the card still reads as a single object with a
   window cut into it.

   Every visual shares one 200x120 stage and this class vocabulary, so the
   palette lives here and no drawing carries a literal colour. */
.card{padding:14px 14px 26px;display:flex;flex-direction:column}
.card-vis{
  position:relative;overflow:hidden;border-radius:12px;margin-bottom:20px;
  background:
    radial-gradient(90% 80% at 50% 0%,rgba(80,136,253,.10),transparent 62%),
    #08080b;
  border:1px solid rgba(255,255,255,.055)}
.card-vis::after{               /* faint grid, the site's own texture */
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:radial-gradient(rgba(255,255,255,.05) 1px,transparent 1px);
  background-size:14px 14px}
.card h3{padding:0 12px;font-size:17.5px}
.card p{padding:0 12px}
.v{display:block;width:100%;height:auto;position:relative;z-index:1}

/* ── shared vocabulary ─────────────────────────────────────────────────── */
.v-panel{fill:rgba(255,255,255,.045);stroke:rgba(255,255,255,.09)}
.v-ln{fill:rgba(255,255,255,.16)}
.v-dim{fill:none;stroke:rgba(255,255,255,.22)}
.v-div{stroke:rgba(255,255,255,.12)}
.v-ac{fill:var(--blue)}
.v-ac2{fill:var(--green)}
.v-ac-t{fill:var(--blue)}
.v-chip{fill:rgba(72,202,124,.9)}
.v-chipbox{fill:rgba(255,255,255,.05);stroke:rgba(255,255,255,.08)}
.v-lbl{font:600 9px var(--font);fill:rgba(255,255,255,.62)}
.v-num{font:700 9px var(--font);fill:#fff}
.v-big{font:600 34px var(--font);fill:#fff;letter-spacing:-.03em}
.v-sel{fill:none;stroke:var(--blue);stroke-width:1.2;stroke-dasharray:3 2.5}
.v-bub{fill:rgba(80,136,253,.16);stroke:rgba(80,136,253,.34)}
.v-bub2{fill:rgba(72,202,124,.14);stroke:rgba(72,202,124,.30)}
.v-band{fill:rgba(72,202,124,.16)}
.v-shift{fill:rgba(80,136,253,.10);stroke:rgba(80,136,253,.4)}
.v-sw1{fill:#0d0f14;stroke:rgba(255,255,255,.10)}
.v-sw2{fill:var(--blue)}
.v-on-sw{fill:rgba(255,255,255,.9);font:600 15px var(--font)}
.v-pass{fill:rgba(72,202,124,.2)}
.v-pass-t{fill:#8ef5c4}
.v-open{fill:rgba(255,255,255,.28)}
.v-prog{fill:#f59e0b}
.v-tick{stroke:#04120a;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;fill:none}
.v-done{fill:rgba(72,202,124,.07);stroke:rgba(72,202,124,.28)}
.v-high{fill:#ef4444}
.v-med{fill:#f59e0b}
.v-low{fill:var(--blue)}
.v-av{fill:rgba(255,255,255,.14);stroke:#08080b;stroke-width:2}
.v-av2{fill:rgba(80,136,253,.5)}
.v-av3{fill:rgba(72,202,124,.5)}
.v-avplus{fill:rgba(255,255,255,.07);stroke:rgba(255,255,255,.14)}

/* The icon tile stays on personas and steps, where a full illustration would
   crowd a short label. */
.card .ic-tile{display:none}

/* Steps and persona cards take the same treatment as feature cards: one
   recessed visual panel, then the text. Personas moved to a numbered
   accordion instead, which keeps its own icon badge rather than hiding it. */
.stp{padding:14px 14px 26px}
.stp::before{display:none}          /* the old top hairline belonged to a flat card */
.stp h3{padding:0 12px}
.stp p{padding:0 12px}
.stp .ic-tile{display:none}


/* ══ Closing CTA heading ═══════════════════════════════════════════════════
   base.css never set a line-height here, so it fell through to `normal` —
   about 1.4 from this font's metrics, while every other display heading on
   the site is pinned near 1.13. At 73px that difference is very visible, and
   it was the only heading behaving differently.

   Weight goes to 500. BDO Grotesk ships a true Medium, and at this size the
   300 Light was thinning out against the dark ground. */
.cta-inner h2{line-height:1.14;font-weight:500}
.cta-inner h2 strong{font-weight:600}

/* ══ Rotating headline ═════════════════════════════════════════════════════
   The only animated element in the hero. "Give feedback where" never moves;
   the phrase after it slides up and fades between eight variants.

   No layout shift, by construction: every phrase occupies the SAME grid cell,
   so the cell sizes itself to the widest and tallest of them and stays that
   size forever. Nothing is hardcoded, so editing the list cannot break it.

   Timing per phrase: 500ms in, 2750ms hold, 400ms out, then a 100ms pause.
   That is a 3750ms slot; with eight phrases the loop is 30s, and the keyframe
   percentages below are those milliseconds expressed against that 30s. */
/* Selector matches base.css's own `.hero h1` specificity (0,1,1) — as a bare
   class it lost, and the headline silently kept the 105px / 940px rule, which
   wrapped the longest phrase and made the reserved box two lines tall. */
.hero h1.h1-rot-wrap{
  /* 12.635x is the measured width of the longest phrase, so 93px inside an
     1180px column keeps it on one line rather than leaving a hole under the
     short ones. */
  /* 25% up on the cap: 80px -> 100px, column widened to 1460px to hold it.
     Measured in place, "developers miss the difference." is 14.431x its
     font-size and the column is the viewport less 61px, so the largest size
     that still holds one line is (100vw - 61px) / 14.431 — about 6.39vw at
     780px, the tightest point. 6.3vw sits just under that across the range.

     So the full +25% applies from roughly 1590px of viewport, where the cap
     is reached; below that the headline is as large as one line allows. That
     is the trade: going bigger there would wrap the longest phrase, which
     makes the reserved cell two lines tall and leaves the short phrases
     floating in a hole. */
  font-size:clamp(38px,6.3vw,100px);max-width:1460px;line-height:1.06}
.h1-static{display:block;font-weight:300}
/* BDO Grotesk's glyph box is taller than a 1.06 line box, so at 100px the
   descenders of the phrase reach ~50px below the cell and were overrunning
   the subheadline. The padding is in em so it scales with the headline
   instead of needing a value per breakpoint. */
.h1-rot{display:grid;justify-items:center;align-items:start;padding-bottom:.04em}
.h1-p{
  grid-area:1/1;font-weight:600;
  /* background-clip:text paints only where the element's own background box
     overlaps the glyphs. With a 1.06 line-height the box stops above the
     descenders, so the tail of g/p/y got no paint and read as cut off. The
     padding extends the paint box below the baseline; it is on the phrase
     itself, not the grid cell, because the cell's padding is outside the
     background box and would not help. */
  padding-bottom:.10em;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
  opacity:0;transform:translateY(0.34em);
  /* duration and @keyframes are emitted per page by rotationCss(), because
     both depend on the phrase count. Only the slot length is fixed. */
  animation-name:h1Rot;animation-timing-function:var(--ease);
  animation-iteration-count:infinite;animation-fill-mode:both;
  animation-delay:calc(var(--i) * 3750ms)}

/* Reduced motion keeps the first phrase, permanently. The sentence a screen
   reader gets is the same one, because phrases two onward are aria-hidden. */
@media(prefers-reduced-motion:reduce){
  .h1-p{animation:none;opacity:0;transform:none}
  .h1-p:first-child{opacity:1}
}

@media(max-width:760px){
  .hero h1.h1-rot-wrap{font-size:clamp(40px,11vw,60px);line-height:1.08}
}

/* ══ Hero framing ══════════════════════════════════════════════════════════
   The copy is centred in the viewport on its own, and the media begins right
   after it — so a band of the video is always in view without the copy being
   pushed off-centre to make room. Previously the media was part of the same
   centred stack, which dragged the headline upward and buried the video. */
.hero:not(.hero-compact) .hero-sub{margin-top:10px}
.hero:not(.hero-compact) .hero-actions{margin-top:56px}

/* Scoped to the homepage. Inner pages use .hero-compact, which opens on a
   headline rather than a full-screen stage — giving them the 82vh centring
   too just left a large empty band under their nav. Without :not(), this rule
   won over .hero-compact's own padding purely by coming later in the file. */
.hero:not(.hero-compact){justify-content:flex-start;padding-top:calc(var(--ribbon-h) + 104px)}
.hero-copy{
  position:relative;z-index:1;width:100%;
  display:flex;flex-direction:column;align-items:center;justify-content:center}
.hero:not(.hero-compact) .hero-copy{
  /* 82vh, so the media's top edge lands at 82% of the viewport and roughly a
     fifth of it shows above the fold. */
  min-height:calc(82vh - var(--ribbon-h) - 104px)}
.hero-media{margin-top:0}

/* Make it read as a video rather than an image slot. */
.mph-glyph.is-play{color:rgba(255,255,255,.5)}
.mph-glyph.is-play .ic{width:34px;height:34px}

@media(max-width:760px){
  .hero:not(.hero-compact){padding-top:calc(var(--ribbon-h) + 84px)}
  .hero:not(.hero-compact) .hero-copy{min-height:calc(78vh - var(--ribbon-h) - 84px)}
}

/* ══ Footer glow ═══════════════════════════════════════════════════════════
   The wash the original footer had, restored. It sits on .footer-wrap rather
   than on <footer>, because the SVG is wider than the page and needs clipping
   — overflow:hidden on the footer itself would also clip its content.

   The footer's own background is transparent so the glow reads through it;
   the border-top stays as the divider. */
/* No overflow:hidden here on purpose. Clipping the wrapper would cut the glow
   off at the footer's top edge and draw exactly the hard line this is meant to
   remove; letting it spill upward is what carries it under the CTA. The 130%
   width cannot cause a scrollbar because body already has overflow-x:hidden. */
/* Clipping stays: the glow is 130% of page width and unclipping it put up to
   170px of horizontal overflow on the document. The CTA now sits inside this
   wrapper, so there is room above the footer for the glow to spill into. */
.footer-wrap{position:relative;z-index:0;overflow:hidden}
/* Brighter, but pushed down so its hot core clears the link columns. At
   opacity .85 anchored to bottom:0 the peak landed directly behind the footer
   links and dropped their contrast to 1.3 — legible text matters more than
   the last bit of glow, so the intensity stays and the core moves instead. */
.footer-glow-img{
  position:absolute;bottom:-22%;left:50%;transform:translateX(-50%);
  width:140%;max-width:1720px;pointer-events:none;opacity:.85;z-index:0}
footer{position:relative;z-index:1;background:none;border-top:0}

/* The CTA is lifted above the glow's stacking level so its text stays legible
   while the wash passes behind it. Its background is transparent, so the glow
   still shows through around the type — which is what makes the two sections
   read as one continuous block instead of two stacked panels. */
.cta-section{position:relative;z-index:1;border-top:0}

@media(max-width:760px){
  .footer-glow-img{width:210%;opacity:.7;bottom:-16%}
}

/* Supplied screenshot in a card's visual panel. object-fit:contain rather than
   cover, because these are UI captures — cropping one cuts off the very
   control it is meant to show. The panel keeps its own background, so an image
   with a different aspect ratio sits on the texture instead of stretching. */
.card-img{
  display:block;width:100%;height:auto;position:relative;z-index:1;
  object-fit:contain}

/* ══ Button system ═════════════════════════════════════════════════════════
   One set of values for every button on the site: 16px regular, 12px vertical
   and 16px horizontal padding, 8px radius. Sizes were previously set per
   component (14px here, 14.5px there, 15px on the pricing CTA), which is how
   two buttons sitting side by side ended up different heights.

   line-height:1 keeps the box exactly 12+16+12 = 40px tall regardless of the
   font's own metrics, so the padding value is the height you actually get.
   box-sizing is already border-box globally. */
.btn-chrome,
.btn-ghost,
.nav-cta,
.nav-login,
.pl-cta,
.pl-free,
.pb-opt,
.plan-cta,
.plan-btn,
.sv-install-btn,
.nav-drawer .nav-drawer-cta{
  font-size:16px;
  font-weight:400;
  padding:12px 16px;
  border-radius:8px;
  line-height:1;
  /* base.css sets gap:8px/10px on these; with no icon left in most of them
     that gap only widened the box past the 16px side padding. */
  gap:8px;
  /* Every button carries a 1px border, transparent unless it is meant to
     show. Declaring it on all of them keeps them the same height: a border
     adds 2px to an intrinsically-sized box, so giving it only to the
     outlined ones would leave them 2px taller than the filled ones. */
  border:1px solid transparent}

/* base.css builds btn-chrome's outline from two pseudo-elements: ::before is
   a gradient rectangle bled 2px past the button, ::after is a white fill on
   top of it, leaving a 2px gradient ring. That pairing is replaced below —
   both are reused for the hover-glow fill and the travelling border light —
   so nothing here is switched off, only repurposed. */

/* A custom property is what lets the conic-gradient's angle below animate at
   all: browsers can only tween a <angle> smoothly between keyframes once it
   is registered as one. Unsupported browsers just keep the light static,
   which is a fine fallback. */
@property --btn-angle{syntax:'<angle>';inherits:false;initial-value:0deg}

/* Primary CTA: same 8px radius as every other button. The travelling border
   light runs by default rather than waiting for a hover — the ring sits at
   inset -2.5px, so it reads as a white comet against the dark page even
   while the button itself is still white. Hover adds the gradient fill and
   the arrow. */
.btn-chrome,.nav-cta{
  border-radius:8px;
  position:relative;
  isolation:isolate;
  box-shadow:
    0 1px 0 rgba(255,255,255,0) inset,
    0 10px 28px -6px rgba(80,136,253,0),
    0 0 46px -6px rgba(72,202,124,0);
  transition:color .45s var(--ease),transform .3s var(--ease),
             box-shadow .45s var(--ease);
}
.btn-chrome::before,.nav-cta::before{
  content:"";position:absolute;inset:0;z-index:-2;border-radius:inherit;
  background:linear-gradient(rgba(255,255,255,.30),transparent 55%),var(--grad);
  opacity:0;transition:opacity .45s var(--ease);
}
/* The travelling light: a conic-gradient ring, cut down to a ring by masking
   out everything but the padding box (the standard gradient-border trick),
   then spun by animating --btn-angle. A comet shape — soft build, bright
   head, hard cut — reads as motion much more clearly than a flat block of
   colour would, and the drop-shadow lets it bloom past its own 2.5px ring
   instead of staying a hairline. */
.btn-chrome::after,.nav-cta::after{
  content:"";position:absolute;inset:-2.5px;z-index:-1;border-radius:inherit;
  padding:2.5px;
  background:conic-gradient(from var(--btn-angle),
    transparent 0deg,
    transparent 255deg,
    rgba(255,255,255,.55) 300deg,
    #fff 328deg,
    #fff 335deg,
    transparent 345deg);
  -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
  filter:drop-shadow(0 0 6px rgba(255,255,255,.9));
  opacity:1;
  animation:btn-border-loop 2.4s linear infinite;
}
.btn-chrome:hover,.nav-cta:hover{color:#fff;transform:scale(1.03)}
.btn-chrome:hover::before,.nav-cta:hover::before{opacity:1}
.btn-chrome:hover,.nav-cta:hover{
  box-shadow:
    0 1px 0 rgba(255,255,255,.35) inset,
    0 10px 28px -6px rgba(80,136,253,.55),
    0 0 46px -6px rgba(72,202,124,.5);
}
.btn-chrome:active,.nav-cta:active{transform:scale(.98)}
@keyframes btn-border-loop{to{--btn-angle:360deg}}

/* The arrow costs no space at rest: zero width, plus a negative margin that
   cancels the flex gap it would otherwise open beside the label. Hover
   animates both back, so the button grows into the arrow rather than holding
   an empty slot open for it. The slot clips, which is what lets the arrow
   inside run right off its own edge. */
.btn-chrome .btn-arrow,.nav-cta .btn-arrow{
  display:inline-flex;align-items:center;justify-content:center;
  width:0;height:16px;flex:none;overflow:hidden;margin-left:-8px;
  transition:width .28s var(--ease),margin-left .28s var(--ease);
}
.btn-chrome:hover .btn-arrow,.nav-cta:hover .btn-arrow,
.btn-chrome:focus-visible .btn-arrow,.nav-cta:focus-visible .btn-arrow{
  width:16px;margin-left:0;
}
/* Loops rather than settling: the arrow enters from the left, holds, then
   runs out to the right and vanishes before re-entering. Both ends of the
   keyframe sit at opacity 0, so the wrap-around is never seen as a jump. */
.btn-chrome .btn-arrow svg,.nav-cta .btn-arrow svg{
  width:16px;height:16px;flex:none;opacity:0;
}
.btn-chrome:hover .btn-arrow svg,.nav-cta:hover .btn-arrow svg,
.btn-chrome:focus-visible .btn-arrow svg,.nav-cta:focus-visible .btn-arrow svg{
  animation:btn-arrow-shuttle 1.25s var(--ease) infinite;
}
@keyframes btn-arrow-shuttle{
  0%,6%     {transform:translateX(-130%);opacity:0}
  28%,58%   {transform:translateX(0);opacity:1}
  86%,100%  {transform:translateX(130%);opacity:0}
}
@media(prefers-reduced-motion:reduce){
  .btn-chrome::after,.nav-cta::after{animation:none}
  .btn-chrome .btn-arrow,.nav-cta .btn-arrow{transition:none}
  .btn-chrome:hover .btn-arrow svg,.nav-cta:hover .btn-arrow svg,
  .btn-chrome:focus-visible .btn-arrow svg,.nav-cta:focus-visible .btn-arrow svg{
    animation:none;opacity:1;
  }
}

/* Hero pair only, 20% up from the site-wide button values (16px/12px+16px/
   8px radius/8px gap × 1.2), so the nav CTA and every other button on the
   site keep the shared size. */
.hero-actions .btn-chrome,
.hero-actions .btn-ghost{
  padding:14.4px 19.2px;
  border-radius:9.6px;
  gap:9.6px;
}

/* Full-width buttons keep the padding but stay centred rather than shrinking
   to their text. */
.pl-cta,.pl-free,.plan-cta{display:flex;align-items:center;justify-content:center;gap:8px}

/* The secondary line inside "Or start free" is deliberately smaller — it is a
   caption, not part of the label. */
.pl-free span{font-size:13px}

/* No borders on buttons. Secondary ones are separated by a faint fill instead,
   which also keeps every button at exactly 40px: a 1px border adds 2px to the
   box, since box-sizing only governs an explicitly set height, not an
   intrinsic one. The Chrome mark is pinned to 16px for the same reason — at
   18px it made its line box taller than the text beside it. */
/* Secondary buttons are outlined, not filled. */
.btn-ghost,
.pl-free,
.pb-opt{background:transparent;box-shadow:none;border-color:rgba(255,255,255,.20)}
.btn-ghost:hover,.pl-free:hover,.pb-opt:hover{
  background:transparent;border-color:rgba(255,255,255,.42);color:#fff}
.pb-opt.active{
  background:transparent;border-color:rgba(72,202,124,.85);color:#fff;box-shadow:none}

.btn-chrome svg,.nav-cta svg{width:16px;height:16px;flex:none}

/* The toolbar asset is a wide strip, not a page capture, so it is centred in
   the window body at its own scale rather than stretched to fill it. */
/* A full page capture fills the window; the toolbar strip is centred at its
   own scale instead, since stretching a 1232x353 strip to fill a 4:3 frame
   would blow it up past its native resolution. */
.dl-win-body{align-items:stretch}
.dl-shot{width:100%;height:auto;display:block;object-fit:cover;object-position:top center}
.dl-win-body:has(.dl-shot-alt){align-items:center;justify-content:center;padding:34px}
.dl-shot-alt{max-width:560px;object-fit:contain}
