
/* ==========================================================================
   PLATFORM PAGES — SECOND PASS
   --------------------------------------------------------------------------
   Everything in this sheet is shared by the twenty generated pages and by
   mcp.html, and it is appended after pp.css so it can correct it.

     1 · buttons          the homepage control, exactly — no second dialect
     2 · hero gradient    Figma 9935:22953 — rose edges, white core, blue pool
     3 · line art         Figma 9585:16001 — one glowing stroke per card
     4 · bento motion     the homepage bento's language, on the dark cards
     5 · copy tick        the green check holds, and holds its place
     6 · dashboard        the homepage panel, dropped into a page section
   ========================================================================== */


/* ==========================================================================
   1 · BUTTONS
   --------------------------------------------------------------------------
   pp.css and mcp.html each grew their own button, and neither matched the
   homepage's: they cast an outer shadow, they had no pressed state worth the
   name, and the ghost was a plain border swap. The homepage control carries
   every state on INSET shadow alone — rest is a sheen across the top of the
   face, hover strengthens it and presses a hairline round the inside, active
   drives a hard dark inset down from the top and drops the label 1px. It is
   copied here rather than shared because the pages are self-contained files.
   ========================================================================== */
.zr-pp__btn,
.zr-mcp__btn{
  --btn-face    : var(--zr-blue);
  --btn-face-hi : #0F53D6;
  --btn-ink     : #fff;
  --btn-sheen   : rgba(255,255,255,.35);
  --btn-sheen-hi: rgba(255,255,255,.48);
  --btn-press   : rgba(3,22,60,.45);

  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  height:48px; padding-inline:24px;
  border:0; border-radius:var(--zr-r-sm,8px);
  background:var(--btn-face);
  color:var(--btn-ink);
  font-family:inherit; font-weight:500; font-size:16px; line-height:20px;
  white-space:nowrap; cursor:pointer; text-decoration:none;
  box-shadow:inset 0 3px 14px var(--btn-sheen);
  transition:background .16s var(--zr-ease-out-quad,ease),
             box-shadow  .16s var(--zr-ease-out-quad,ease),
             translate   .09s var(--zr-ease-out-quad,ease);
}
.zr-pp__btn:hover,
.zr-mcp__btn:hover{
  background:var(--btn-face-hi);
  box-shadow:inset 0 3px 14px var(--btn-sheen-hi),
             inset 0 0 0 1px rgba(0,0,0,.07);
}
.zr-pp__btn:active,
.zr-mcp__btn:active{
  translate:0 1px;
  box-shadow:inset 0 4px 9px var(--btn-press),
             inset 0 1px 2px var(--btn-press);
}
.zr-pp__btn:focus-visible,
.zr-mcp__btn:focus-visible{ outline:2px solid var(--zr-blue); outline-offset:2px; }

/* the secondary button next to a blue one: a white face on a light ground,
   so it takes the homepage's --light palette plus the hairline the comps
   draw round it. The ring lives in the same inset shadow as the sheen, which
   is what stops it fighting the pressed state. */
.zr-pp__btn--ghost,
.zr-mcp__btn--ghost{
  --btn-face    : #fff;
  --btn-face-hi : #F4F6FA;
  --btn-ink     : var(--pp-ink,#1F2328);
  --btn-sheen   : rgba(255,255,255,.9);
  --btn-sheen-hi: rgba(255,255,255,1);
  --btn-press   : rgba(15,23,42,.24);
  border:1px solid var(--pp-hair,#E4E9F0);
}
.zr-pp__btn--ghost:hover,
.zr-mcp__btn--ghost:hover{ border-color:#C9D6F2; }

/* a quiet filled surface, for a third action that is not an outline */
.zr-pp__btn--soft{
  --btn-face    : #F1F4F9;
  --btn-face-hi : #E7ECF4;
  --btn-ink     : var(--pp-ink,#1F2328);
  --btn-sheen   : rgba(255,255,255,.7);
  --btn-sheen-hi: rgba(255,255,255,.9);
  --btn-press   : rgba(15,23,42,.22);
}
/* on a dark card the white face is the primary one */
.zr-dk .zr-pp__btn--ghost,
.zr-pp__onDark .zr-pp__btn--ghost{
  --btn-ink:#0B0C10; border-color:transparent;
}
.zr-pp__btn svg,
.zr-mcp__btn svg{ width:20px; height:20px; flex:0 0 auto; }

@media (prefers-reduced-motion:reduce){
  .zr-pp__btn, .zr-mcp__btn{ transition:background .16s linear, box-shadow .16s linear; }
  .zr-pp__btn:active, .zr-mcp__btn:active{ translate:0 0; }
}


/* ==========================================================================
   2 · HERO GRADIENT                                     Figma 9935:22953
   --------------------------------------------------------------------------
   The comp is a single panel: a white core, rose and lilac pooling in from
   both outer edges, a blue field rising from the bottom, a row of vertical
   glass slats running the full height, and a fine star speckle over all of
   it. Three layers are added to the existing backdrop and the burst is
   pulled back, so it reads as one field rather than a star pasted on a wash.

   Only the generated pages get this. index.html keeps its own hero.
   ========================================================================== */
.zr-pp__hero{
  --edge   : #F3C2CF;   /* the rose that comes in from both sides  */
  --edge2  : #CDBDF4;   /* the lilac just inboard of it            */
  --pool   : #6E97DA;   /* the blue field rising from the bottom   */
  --pool-h : 46%;       /* how far up the card that field reaches  */
}

/* the outer curtains — rose then lilac, mirrored left and right, kept well
   clear of the middle so the headline always sits on white */
.zr-pp__edges{
  inset:0;
  background:
    radial-gradient(38% 62% at -4% 26%,  var(--edge)  0%, rgba(255,255,255,0) 70%),
    radial-gradient(38% 62% at 104% 26%, var(--edge)  0%, rgba(255,255,255,0) 70%),
    radial-gradient(30% 72% at 9% 44%,   var(--edge2) 0%, rgba(255,255,255,0) 72%),
    radial-gradient(30% 72% at 91% 44%,  var(--edge2) 0%, rgba(255,255,255,0) 72%);
  opacity:.5;
  animation:zr-pp-drift 46s var(--zr-ease,ease-in-out) infinite reverse;
}

/* the blue pool at the foot of the card. The comp runs it to a solid edge;
   here it stops short of full strength because the card sits on a white page
   and a hard band would cut the hero off from the section under it. */
.zr-pp__pool{
  left:0; right:0; bottom:0; height:var(--pool-h);
  background:linear-gradient(180deg,
    rgba(255,255,255,0)            0%,
    color-mix(in srgb, var(--pool) 26%, transparent) 54%,
    color-mix(in srgb, var(--pool) 62%, transparent) 100%);
}
@supports not (background:color-mix(in srgb, red 50%, blue)){
  .zr-pp__pool{ background:linear-gradient(180deg, rgba(110,151,218,0) 0%, rgba(110,151,218,.26) 54%, rgba(110,151,218,.62) 100%); }
}

/* the glass slats. Columns of a hair of white, blurred — the comp's own
   "curtain", which is what gives that wash its grain. One
   repeating-linear-gradient draws the lot; the mask keeps them to the outer
   thirds, where the colour is, because over the white core they would read
   as banding. Plain alpha, not an overlay blend: white on a near-white
   ground under `overlay` composites to nothing at all. */
.zr-pp__slats{
  inset:-2% -1%;
  background:repeating-linear-gradient(90deg,
    rgba(255,255,255,0)   0px,
    rgba(255,255,255,0)   34px,
    rgba(255,255,255,.62) 34px,
    rgba(255,255,255,.62) 36px,
    rgba(255,255,255,.26) 36px,
    rgba(255,255,255,.26) 82px);
  /* two masks intersected — the outer thirds across, and the top two thirds
     down. Without the second one the slats stripe the blue pool at the foot
     of the card, which reads as banding rather than as glass. */
  -webkit-mask-image:linear-gradient(90deg,#000 0%,rgba(0,0,0,.55) 20%,rgba(0,0,0,0) 36%,rgba(0,0,0,0) 64%,rgba(0,0,0,.55) 80%,#000 100%),
                     linear-gradient(180deg,#000 0%,#000 42%,rgba(0,0,0,0) 84%);
          mask-image:linear-gradient(90deg,#000 0%,rgba(0,0,0,.55) 20%,rgba(0,0,0,0) 36%,rgba(0,0,0,0) 64%,rgba(0,0,0,.55) 80%,#000 100%),
                     linear-gradient(180deg,#000 0%,#000 42%,rgba(0,0,0,0) 84%);
  -webkit-mask-composite:source-in;  mask-composite:intersect;
  filter:blur(1px);
  opacity:.6;
}

/* with a field this busy the burst only has to be the light in it */
.zr-pp__burst{ opacity:calc(.46 * var(--mark-boost)); }
.zr-pp__glow { opacity:calc(.13 * var(--mark-boost)); }

/* …and the white pool under the copy carries a little further, because the
   rose now reaches further in than the old tint did */
.zr-pp__veil{
  background:radial-gradient(58% 84% at 14% 48%, rgba(255,255,255,.97) 0%, rgba(255,255,255,.78) 44%, rgba(255,255,255,0) 78%);
}
.zr-pp__hero--center .zr-pp__veil{
  background:radial-gradient(64% 62% at 50% 42%, rgba(255,255,255,.96) 0%, rgba(255,255,255,.7) 48%, rgba(255,255,255,0) 80%);
}

/* per-page edge colours, so ten palettes still read as ten palettes */
.zr-pp--tracking  .zr-pp__hero{ --edge:#F0C3D2; --edge2:#C3C8F6; --pool:#6E97DA; }
.zr-pp--prompts   .zr-pp__hero{ --edge:#F4C6C1; --edge2:#CFC0F2; --pool:#7391D8; }
.zr-pp--optimize  .zr-pp__hero{ --edge:#F6C9BC; --edge2:#C8CDF4; --pool:#6F9AD6; }
.zr-pp--workflows .zr-pp__hero{ --edge:#E9C4E2; --edge2:#BFC9F5; --pool:#6A8FD8; }
.zr-pp--copilot   .zr-pp__hero{ --edge:#F2C4CE; --edge2:#C6C4F3; --pool:#7396DC; }
.zr-pp--content   .zr-pp__hero{ --edge:#F1C8D3; --edge2:#C9C6F2; --pool:#7194D9; }
.zr-pp--sol       .zr-pp__hero{ --edge:#EFC6C6; --edge2:#C4CBF4; --pool:#6D93D7; }
.zr-pp--vs        .zr-pp__hero{ --edge:#F3C5CC; --edge2:#CBC5F1; --pool:#7295DA; }

@media (prefers-reduced-motion:reduce){
  .zr-pp__edges{ animation:none; }
}


/* ==========================================================================
   3 · LINE ART                                          Figma 9585:16001
   --------------------------------------------------------------------------
   The comp draws each step as one open stroke with a bloom under it — a
   chain link, two buildings, two speech bubbles — floating on the card with
   no frame of its own. Two things make it read: the stroke fades toward the
   top (a mask, not a gradient stroke, so one <svg> serves every colour) and
   the bloom pools at the foot of the mark rather than ringing it.

   Deliberately simplified against the comp: no inner detail beyond what
   names the object, 1.6 stroke throughout, and every mark drawn inside the
   same 120-unit box so a row of them lines up without nudging.
   ========================================================================== */
.zr-il{
  position:relative; display:flex; flex-direction:column;
  padding:28px 28px 30px; border-radius:20px;
  border:1px solid var(--pp-hair); background:#fff;
  box-shadow:0 1px 2px rgba(16,24,40,.04);
}
.zr-il__art{
  position:relative; display:grid; place-items:center;
  height:190px; margin-bottom:26px;
  --il      : #8B5CF6;   /* the stroke            */
  --il-glow : #A78BFA;   /* the bloom under it    */
}
.zr-il__art--violet{ --il:#8B5CF6; --il-glow:#A78BFA; }
.zr-il__art--blue  { --il:#3B82F6; --il-glow:#60A5FA; }
.zr-il__art--green { --il:#22C08A; --il-glow:#4ADE9E; }
.zr-il__art--amber { --il:#F59E0B; --il-glow:#FBBF24; }

/* the bloom: a wide, soft pool sitting under the lower half of the mark */
.zr-il__art::before{
  content:""; position:absolute; z-index:0; pointer-events:none;
  left:50%; top:56%; translate:-50% -50%;
  width:190px; height:150px;
  background:radial-gradient(50% 50% at 50% 50%, var(--il-glow) 0%, rgba(255,255,255,0) 72%);
  opacity:.5; filter:blur(22px);
}
.zr-il__art svg{
  position:relative; z-index:1;
  width:132px; height:132px;
  color:var(--il);
  fill:none; stroke:currentColor;
  stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round;
  /* the comp's stroke is almost gone at the top of the mark and full weight
     at the bottom. A mask does that once, for every colour. */
  -webkit-mask-image:linear-gradient(180deg, rgba(0,0,0,.42) 0%, #000 58%);
          mask-image:linear-gradient(180deg, rgba(0,0,0,.42) 0%, #000 58%);
  filter:drop-shadow(0 6px 16px color-mix(in srgb, var(--il-glow) 55%, transparent));
}
@supports not (color:color-mix(in srgb, red 50%, blue)){
  .zr-il__art svg{ filter:drop-shadow(0 6px 16px rgba(167,139,250,.55)); }
}
.zr-il h3{ margin:0 0 8px; font-size:21px; font-weight:500; line-height:1.25; letter-spacing:-.02em; color:var(--pp-ink); }
.zr-il p { margin:0; font-size:15px; line-height:24px; color:var(--pp-dim); text-wrap:pretty; }

/* the mark drifts a hair, and lifts when the card is pointed at — the same
   idle the homepage bento runs, at a quarter of the amplitude */
.zr-il__art svg{
  animation:zr-il-float 9s ease-in-out var(--ph,0s) infinite;
  transition:scale .45s var(--zr-ease-out-quart,ease);
}
.zr-il:hover .zr-il__art svg{ scale:1.035; }
.zr-il:hover .zr-il__art::before{ opacity:.66; }
.zr-il__art::before{ transition:opacity .45s var(--zr-ease-out-quart,ease); }
@keyframes zr-il-float{
  0%,100%{ translate:0 0; }
  50%    { translate:0 -6px; }
}
@media (prefers-reduced-motion:reduce){
  .zr-il__art svg{ animation:none; }
}

/* out of step with each other, or three marks bobbing together read as the
   whole row breathing */
.zr-il-row > :nth-child(1) .zr-il__art svg{ --ph:0s; }
.zr-il-row > :nth-child(2) .zr-il__art svg{ --ph:-3s; }
.zr-il-row > :nth-child(3) .zr-il__art svg{ --ph:-6s; }

/* three of them across, one under the other on a phone */
.zr-il-row{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:16px; }
@media (max-width:900px){ .zr-il-row{ grid-template-columns:minmax(0,1fr); } }
@media (max-width:620px){
  .zr-il__art{ height:150px; }
  .zr-il__art svg{ width:104px; height:104px; }
}


/* ==========================================================================
   4 · BENTO MOTION
   --------------------------------------------------------------------------
   The dark cards were static pictures on a white page: a hard-edged slab
   with nothing moving in it. They now run the homepage bento's language —
   a diagonal light beam, a star overlay on screen blend, an idle breathe
   deliberately out of phase per card — plus one thing the homepage does not
   need: an ambient bloom under the grid in each card's own hue, which is
   what stops four black rectangles reading as pasted onto the page.

   Everything is transform and opacity. Nothing loops faster than 9s.
   ========================================================================== */
.zr-bento{ position:relative; isolation:isolate; }

/* the bloom the grid sits in — the cards' own colours, bled out past their
   edges so the dark blocks arrive out of a wash instead of off an edge */
.zr-bento::before{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  inset:-7% -5% -9%;
  background:
    radial-gradient(34% 52% at 16% 18%, rgba(46,48,150,.20)  0%, rgba(255,255,255,0) 72%),
    radial-gradient(30% 46% at 84% 26%, rgba(18,92,80,.16)   0%, rgba(255,255,255,0) 72%),
    radial-gradient(36% 44% at 30% 92%, rgba(122,20,44,.14)  0%, rgba(255,255,255,0) 74%),
    radial-gradient(32% 40% at 72% 88%, rgba(20,74,142,.14)  0%, rgba(255,255,255,0) 74%);
  filter:blur(26px);
  opacity:0;
  transition:opacity 1.1s var(--zr-ease-out-quart,ease) .1s;
}
.zr-bento.is-in::before{ opacity:1; }

/* the cards arrive one after another rather than as a slab. The grid itself
   still carries data-reveal, so this is a second, shorter curve on top of
   the page's own — 14px, not another 20. */
.zr-bento > *{
  opacity:0; translate:0 14px;
  transition:opacity .78s var(--zr-ease-out-quart,ease) var(--sd,0ms),
             translate .78s var(--zr-ease-out-quart,ease) var(--sd,0ms);
}
.zr-bento.is-in > *{ opacity:1; translate:0 0; }
.zr-bento > :nth-child(1){ --sd:60ms; }
.zr-bento > :nth-child(2){ --sd:150ms; }
.zr-bento > :nth-child(3){ --sd:240ms; }
.zr-bento > :nth-child(4){ --sd:330ms; }
.zr-bento > :nth-child(5){ --sd:420ms; }

.zr-dk{
  /* soft edge rather than a cut one: the ring is the card's own light, and
     the shadow under it is what seats it on the page */
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07),
             0 18px 44px -24px rgba(11,12,16,.55);
  will-change:scale;
}
.zr-bento.is-in .zr-dk,
.zr-dk.is-in{
  animation:zr-dk-breathe var(--br,14s) ease-in-out var(--ph,0s) infinite;
}
/* tiny on purpose, and staggered — in step it would read as a page zoom */
@keyframes zr-dk-breathe{
  0%,100%{ scale:1; }
  50%    { scale:1.006; }
}
.zr-bento > :nth-child(1){ --ph:0s;    --br:14s; }
.zr-bento > :nth-child(2){ --ph:-3.5s; --br:15s; }
.zr-bento > :nth-child(3){ --ph:-7s;   --br:13s; }
.zr-bento > :nth-child(4){ --ph:-1.8s; --br:16s; }
.zr-bento > :nth-child(5){ --ph:-5.2s; --br:15s; }

/* the diagonal beam, straight off the homepage card (Rectangle 6845) */
.zr-dk::before{
  content:""; position:absolute; z-index:0; pointer-events:none;
  top:-30%; right:18%; width:132px; height:170%;
  rotate:-24deg;
  background:linear-gradient(180deg,
    rgba(255,255,255,0) 0%, rgba(255,255,255,.12) 34%,
    rgba(255,255,255,.15) 58%, rgba(255,255,255,0) 100%);
  filter:blur(28px);
  opacity:0;
  transition:opacity 1.2s var(--zr-ease-out-quart,ease) .18s;
}
.zr-bento.is-in .zr-dk::before,
.zr-dk.is-in::before{ opacity:1; }

/* the star overlay. --ll-stars is the homepage's own tile and is already on
   every page; the offset is per-card so the field never repeats across two
   cards side by side. */
.zr-dk__stars{
  position:absolute; inset:-1px; z-index:0; pointer-events:none;
  background-image:var(--ll-stars);
  background-size:420px 420px;
  background-position:var(--sp,0 0);
  opacity:.10; mix-blend-mode:screen;
}
.zr-bento > :nth-child(1) .zr-dk__stars{ --sp:0 0; }
.zr-bento > :nth-child(2) .zr-dk__stars{ --sp:130px 60px; }
.zr-bento > :nth-child(3) .zr-dk__stars{ --sp:40px 200px; }
.zr-bento > :nth-child(4) .zr-dk__stars{ --sp:200px 120px; }
.zr-bento > :nth-child(5) .zr-dk__stars{ --sp:90px 280px; }

/* the copy and the mock both sit above the beam and the stars */
.zr-dk__viz, .zr-dk h3, .zr-dk p{ position:relative; z-index:1; }

/* ── what moves inside the cards ─────────────────────────────────────────
   One rule for all of it: the piece that carries words keeps moving for as
   long as the card is on screen; everything else plays once as the card
   arrives and then holds. A caption that never settles is unreadable, and a
   bar that grows every nine seconds is a distraction.
   ──────────────────────────────────────────────────────────────────────── */

/* bars grow out of the baseline, once, as the grid arrives */
.zr-bento .zr-bar::after{
  transform-origin:left center;
  scale:0 1;
  transition:scale 1.1s var(--zr-ease-out-quart,ease) var(--bd,180ms);
}
.zr-bento.is-in .zr-bar::after{ scale:1 1; }
.zr-bento .zr-models__row:nth-child(1) .zr-bar::after,
.zr-bento .zr-mkts__row:nth-child(1) .zr-bar::after{ --bd:180ms; }
.zr-bento .zr-models__row:nth-child(2) .zr-bar::after,
.zr-bento .zr-mkts__row:nth-child(2) .zr-bar::after{ --bd:260ms; }
.zr-bento .zr-models__row:nth-child(3) .zr-bar::after,
.zr-bento .zr-mkts__row:nth-child(3) .zr-bar::after{ --bd:340ms; }
.zr-bento .zr-models__row:nth-child(4) .zr-bar::after,
.zr-bento .zr-mkts__row:nth-child(4) .zr-bar::after{ --bd:420ms; }
.zr-bento .zr-mkts__row:nth-child(5) .zr-bar::after{ --bd:500ms; }
.zr-bento .zr-mkts__row:nth-child(6) .zr-bar::after{ --bd:580ms; }

/* the crawler log — the words card on Tracking. It scrolls for ever: the
   five lines are printed twice and the reel travels exactly one set, so the
   seam never shows. No gap on the reel, padding on the lines instead, or
   -50% would land five pixels short of the second copy. */
.zr-log{
  overflow:hidden; display:block; gap:0; height:114px; padding-block:8px;
  /* lines fade in at the top and out at the bottom rather than being cut
     off by the card's edge */
  -webkit-mask-image:linear-gradient(180deg, rgba(0,0,0,0) 0%, #000 16%, #000 84%, rgba(0,0,0,0) 100%);
          mask-image:linear-gradient(180deg, rgba(0,0,0,0) 0%, #000 16%, #000 84%, rgba(0,0,0,0) 100%);
}
.zr-log__reel{ display:block; }
.zr-log__reel > span{ display:block; padding-block:5px; }
.zr-bento.is-in .zr-log__reel{ animation:zr-log-reel 13s linear infinite; }
@keyframes zr-log-reel{
  from{ translate:0 0; }
  to  { translate:0 -50%; }
}

/* the alert feed — the words card on Tracking's right. Each row lights in
   turn, four seconds apart, and the dot pulses while it is the live one. */
.zr-bento.is-in .zr-alerts li{ animation:zr-feed-lift 9.6s ease-in-out infinite; }
.zr-bento.is-in .zr-alerts li:nth-child(1){ animation-delay:0s; }
.zr-bento.is-in .zr-alerts li:nth-child(2){ animation-delay:-7.2s; }
.zr-bento.is-in .zr-alerts li:nth-child(3){ animation-delay:-4.8s; }
.zr-bento.is-in .zr-alerts li:nth-child(4){ animation-delay:-2.4s; }
@keyframes zr-feed-lift{
  0%, 6%   { opacity:.44; }
  12%, 22% { opacity:1; }
  34%, 100%{ opacity:.62; }
}

/* the FAQ mock on Workflows — each question opens in turn */
.zr-bento.is-in .zr-faqmock span{ animation:zr-faq-open 10.5s ease-in-out infinite; }
.zr-bento.is-in .zr-faqmock span:nth-of-type(1){ animation-delay:0s; }
.zr-bento.is-in .zr-faqmock span:nth-of-type(2){ animation-delay:-7s; }
.zr-bento.is-in .zr-faqmock span:nth-of-type(3){ animation-delay:-3.5s; }
@keyframes zr-faq-open{
  0%, 4%  { background-color:rgba(255,255,255,0); }
  10%, 26%{ background-color:rgba(255,255,255,.55); }
  34%,100%{ background-color:rgba(255,255,255,0); }
}
.zr-bento.is-in .zr-faqmock span i{ animation:zr-faq-sign 10.5s ease-in-out infinite; animation-delay:inherit; }
.zr-bento.is-in .zr-faqmock span:nth-of-type(1) i{ animation-delay:0s; }
.zr-bento.is-in .zr-faqmock span:nth-of-type(2) i{ animation-delay:-7s; }
.zr-bento.is-in .zr-faqmock span:nth-of-type(3) i{ animation-delay:-3.5s; }
@keyframes zr-faq-sign{
  0%, 4%  { rotate:0deg; }
  10%, 26%{ rotate:135deg; }
  34%,100%{ rotate:0deg; }
}

/* the URL list on Workflows — the row being worked runs a highlight across */
.zr-bento.is-in .zr-win__rows li{ animation:zr-row-scan 12s ease-in-out infinite; }
.zr-bento.is-in .zr-win__rows li:nth-child(1){ animation-delay:0s; }
.zr-bento.is-in .zr-win__rows li:nth-child(2){ animation-delay:-9.6s; }
.zr-bento.is-in .zr-win__rows li:nth-child(3){ animation-delay:-7.2s; }
.zr-bento.is-in .zr-win__rows li:nth-child(4){ animation-delay:-4.8s; }
.zr-bento.is-in .zr-win__rows li:nth-child(5){ animation-delay:-2.4s; }
@keyframes zr-row-scan{
  0%, 3%  { opacity:.55; }
  8%, 18% { opacity:1; }
  26%,100%{ opacity:.72; }
}

/* the language stack on Workflows — the last row finishes drafting, and the
   chip flips to published, then the loop starts over */
.zr-bento.is-in .zr-chip--wait{ animation:zr-wait-pulse 3.2s ease-in-out infinite; }
@keyframes zr-wait-pulse{
  0%,100%{ opacity:.6; }
  50%    { opacity:1; }
}

/* the "Live" pip, on any card that carries one */
.zr-bento.is-in .zr-chip--green i{ animation:zr-pip 2.4s ease-in-out infinite; }
@keyframes zr-pip{
  0%,100%{ opacity:1;  scale:1; }
  50%    { opacity:.45; scale:.82; }
}

@media (prefers-reduced-motion:reduce){
  .zr-bento .zr-dk,
  .zr-bento .zr-dk::before,
  .zr-bento .zr-log__reel,
  .zr-bento .zr-alerts li,
  .zr-bento .zr-faqmock span,
  .zr-bento .zr-faqmock span i,
  .zr-bento .zr-win__rows li,
  .zr-bento .zr-chip--wait,
  .zr-bento .zr-chip--green i{ animation:none !important; }
  .zr-bento::before, .zr-dk::before{ opacity:1; transition:none; }
  /* the page's own reduced-motion rule only reaches [data-reveal]; the
     staggered cards are a second layer on top of it and would otherwise be
     left at opacity 0 with no transition to bring them back */
  .zr-bento > *{ opacity:1; translate:none; transition:none; }
}


/* ==========================================================================
   5 · THE COPY TICK                                     Figma 9585:16066
   --------------------------------------------------------------------------
   Two problems, both of them the same problem. The tick replaced the copy
   glyph in flow, so the row reflowed by a pixel or two on every copy; and
   the copied state was cleared on a timer, so a prompt you copied, scrolled
   past and came back to had forgotten it. The two glyphs are now stacked in
   one 24px well — nothing moves — and the state sticks: it is only dropped
   when a different button is copied.
   ========================================================================== */
/* The two glyphs are the same size and sit at the end of a 12px flex gap, so
   pulling the tick back over the copy icon by exactly one icon plus one gap
   stacks them in the same well. Nothing is display:none any more, so the
   swap is a cross-fade rather than a jump, and the row cannot reflow even a
   pixel while a page is being scrolled. */
.zr-try__q .zr-menu__copy,
.zr-try__q .zr-menu__tick,
.zr-ability__prompt .zr-menu__copy,
.zr-ability__prompt .zr-menu__tick{
  transition:opacity .2s var(--zr-ease-out-quad,ease),
             scale   .2s var(--zr-ease-out-quad,ease);
}
.zr-try__q .zr-menu__tick{
  display:block; margin-left:-30px;      /* 18px icon + 12px gap */
  opacity:0; scale:.72; color:#16A34A;
}
.zr-ability__prompt .zr-menu__tick{
  display:block; margin-left:-32px;      /* 20px icon + 12px gap */
  opacity:0; scale:.72; color:#16A34A;
}
.zr-try__q.is-copied .zr-menu__copy,
.zr-ability__prompt.is-copied .zr-menu__copy{ display:block; opacity:0; scale:.72; }
.zr-try__q.is-copied .zr-menu__tick,
.zr-ability__prompt.is-copied .zr-menu__tick{ opacity:1; scale:1; }

@media (prefers-reduced-motion:reduce){
  .zr-try__q .zr-menu__copy,  .zr-try__q .zr-menu__tick,
  .zr-ability__prompt .zr-menu__copy, .zr-ability__prompt .zr-menu__tick{ transition:none; }
}


/* ==========================================================================
   6 · THE DASHBOARD, IN A PAGE SECTION
   --------------------------------------------------------------------------
   The homepage's panel is lifted whole onto the platform pages, opened on
   the view that page is about. The panel's own CSS and its interaction
   script already ship on every page; all that is needed here is the frame it
   sits in, which is the homepage's `.zr-shot` given a height (on the
   homepage it takes its height from the hero).
   ========================================================================== */
.zr-pp__panel{
  position:relative; isolation:isolate;
  /* the same wash the bento sits in, so the window arrives out of light
     rather than off a white page */
  padding-block:clamp(10px,2vw,26px);
}
.zr-pp__panel::before{
  content:""; position:absolute; z-index:-1; inset:6% -4% -2%;
  background:
    radial-gradient(40% 60% at 22% 30%, rgba(20,99,245,.16) 0%, rgba(255,255,255,0) 72%),
    radial-gradient(36% 54% at 80% 40%, rgba(143,120,246,.14) 0%, rgba(255,255,255,0) 74%);
  filter:blur(24px);
}
/* the homepage floats this over its hero video, so the face is 60% white on
   a backdrop-filter. In a page section there is nothing behind it to blur,
   and a 60% white card on a white page has no edge at all. */
.zr-pp__panel .zr-shot{
  margin:0 auto;
  width:min(1120px,100%);
  background:rgba(255,255,255,.82);
  border-color:rgba(255,255,255,.9);
  box-shadow:0 40px 90px -38px rgba(16,49,110,.34), 0 0 0 1px rgba(16,49,110,.06);
}
/* the panel keeps its 1091:620 face, so it can never be squashed */
.zr-pp__panel .zr-shot__frame{ background:#F7F9FC; }
/* …until the page is narrow enough that 1091:620 leaves 190px of app inside
   a 340px card, which shows the toolbar and nothing under it. Below 760 the
   ratio is dropped for a real height; the panel's own container queries take
   it from there — sidebar to icons at 820, out at 620, last trims at 460. */
@media (max-width:760px){
  .zr-pp__panel .zr-shot__frame{ aspect-ratio:auto; height:min(560px, 72svh); }
}
@media (max-width:460px){
  .zr-pp__panel .zr-shot__frame{ height:min(480px, 68svh); }
}
/* …and under desktop the frame holds a still of the panel rather than the
   panel (see .zr-shot__still), so the height those two rules buy is no
   longer needed: it takes the picture's own face instead of cropping a tall
   slice out of it. Last, so it wins on source order over both. */
@media (max-width:1024px){
  .zr-pp__panel .zr-shot__frame{ aspect-ratio:12 / 7; height:auto; }
}
/* a caption under the panel that says it is live, because it is */
.zr-pp__panel-note{
  display:flex; align-items:center; justify-content:center; gap:8px;
  margin:16px 0 0; font-size:13.5px; color:var(--pp-dim);
}
.zr-pp__panel-note i{
  width:7px; height:7px; border-radius:50%; background:#16A34A; flex:0 0 auto;
  animation:zr-pip 2.4s ease-in-out infinite;
}
@media (prefers-reduced-motion:reduce){ .zr-pp__panel-note i{ animation:none; } }
/* the caption says the panel is live; under desktop it is a still, so the
   caption goes with it. After the note's own rules, or it loses to them. */
@media (max-width:1024px){
  .zr-pp__panel-note{ display:none; }
  /* and the sentence in the copy above that invites you to drive it */
  .zr-pp__live{ display:none; }
}


/* ==========================================================================
   7 · THE BRAND MARKS
   --------------------------------------------------------------------------
   The sprite now carries the real logos (gen.py's patch_marks), so the
   platforms that were standing behind a generic question mark — Qwen,
   Llama, Brave, DuckDuckGo, Naver, Baidu — get their own colour like every
   other engine on the list. Marks are single-path and painted by
   currentColor, so one --_c is the whole of it.
   ========================================================================== */
.zr-eng--qwen    { --_c:#615CED; }
.zr-eng--llama   { --_c:#0467DF; }
.zr-eng--brave   { --_c:#FB542B; }
.zr-eng--ddg     { --_c:#DE5833; }
.zr-eng--naver   { --_c:#03C75A; }
.zr-eng--baidu   { --_c:#2932E1; }
.zr-eng--grok    { --_c:#111111; }
.zr-eng--deepseek{ --_c:#4D6BFE; }
.zr-eng--mistral { --_c:#FA520F; }
/* on a dark card the near-black marks would disappear */
.zr-dk .zr-eng--grok{ --_c:#E8E8EA; }


/* ==========================================================================
   8 · THE WALL OF LOVE, ON A GENERATED PAGE
   --------------------------------------------------------------------------
   The section is the homepage's, verbatim — same markup, same CSS, same
   script (the marquee clone and the observer that lights the heart both come
   across in mcp.html's <script>). Two things about its *context* differ, and
   they are what makes it read weaker here than it does at home:

   · on index.html the section above the heart is dark, so the blue and
     violet glows have something to come up against. Here the page above it
     is white, and at .65 the glow all but disappears.
   · the homepage titles it "Wall of Love" — two short words. Every generated
     page gives it a real sentence, and at a 579px measure those wrap to
     three cramped lines across the heart's cleft. At 880 the sentence sets
     on one line and stops orphaning its last word.
   · the heart sat 96 Figma px into the section, but its glow is thrown a
     further 204 px straight up (two drop-shadows, offset -36/-70 and blurred
     64/134). overflow:hidden cut that off dead straight, so the top of the
     heart arrived as a seam across the page. The heart is dropped to 210 and
     the fade and the heading follow it down, which puts the whole glow
     inside the section without needing overflow-clip-margin.
   ========================================================================== */
.zr-pp ~ .wol,
.wol{ --wol-glow:.92; --wol-heart-top:210; --wol-fade-top:444; }
.wol__title{ width:min(880px, 92vw); }
.wol__head{ padding-top:clamp(190px, 30vw, 430px); }


/* ==========================================================================
   9 · THE MCP PAGE, LIKE THE OTHERS
   --------------------------------------------------------------------------
   mcp.html is the one platform page that predates the shared hero, so it
   draws its own backdrop (.zr-mcp__bg, the exported star and the script-fed
   sparkles) instead of the aurora. It keeps those — the star is the page's
   signature — and gains the comp's gradient on top of them, off the same
   three layers and the same variables every other hero uses.
   ========================================================================== */
.zr-mcp__bg{ overflow:hidden; }
.zr-mcp__bg > span{ position:absolute; display:block; }
.zr-mcp__hero{
  --edge  : #E9C6D8;
  --edge2 : #C6C6F5;
  --pool  : #6C92D9;
  --pool-h: 44%;
}
/* the star is already the light in this hero, so the curtains stay quieter
   here than they do on a page whose burst is a wash */
.zr-mcp__bg .zr-pp__edges{ opacity:.42; }
.zr-mcp__bg .zr-pp__slats{ opacity:.55; }

/* the dashboard section on the MCP page uses the same frame as the others */
.zr-mcp .zr-pp__panel{ margin-top:8px; }
