/* ============================================================
   Feedly — hero block
   ============================================================ */

/* Inter variable — the weight axis is the point here: the display sizes
   run at 360, lighter than regular, which is what gives the page its
   quiet. JetBrains Mono is only for the code card. */
/* One family, three subsets. The browser downloads only the ranges a
   page actually uses, so the Russian build costs 29 KB more and the
   English one costs nothing extra. */
@font-face{font-family:'Inter';src:url('../fonts/inter.woff2') format('woff2');font-weight:300 700;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Inter';src:url('../fonts/inter-cyrillic.woff2') format('woff2');font-weight:300 700;font-style:normal;font-display:swap;
  unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'Inter';src:url('../fonts/inter-cyrillic-ext.woff2') format('woff2');font-weight:300 700;font-style:normal;font-display:swap;
  unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:'JetBrains Mono';src:url('../fonts/jetbrains-mono.woff2') format('woff2');font-weight:100 800;font-style:normal;font-display:swap}

:root{
  --font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Consolas,monospace;
  --font-display:var(--font-sans);

  /* brand — the one colour on an otherwise neutral page */
  --brand:#7D62FF;
  --brand-ink:#6448F0;
  --brand-soft:#F0EDFF;

  /* ink ramp */
  --ink:#161514;
  --ink-2:#454545;
  --muted:#71706F;
  --faint:#8A8987;
  --ink-3:#D7D6D4;

  /* surfaces */
  --bg:#FFFFFF;
  --bg-2:#F6F6F6;
  --linen:#F0EFEE;
  --paper:#FFFFFF;
  --chip:#F2F2F2;
  --chip-h:#ECECEB;
  --line:#ECECEB;

  --r-sm:4px;
  --r:8px;
  --r-lg:12px;

  --nav-h:72px;
  --wrap:1280px;
  --pad:40px;          /* the page gutter; inner blocks clamp up to it */
}

*,*::before,*::after{box-sizing:border-box}

html{-webkit-text-size-adjust:100%}

body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--font-sans);
  font-size:15px;
  line-height:1.6;
  font-optical-sizing:auto;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

a{color:inherit;text-decoration:none}
ul{margin:0;padding:0;list-style:none}

.skip{
  position:absolute;left:-9999px;top:0;z-index:99;
  padding:10px 16px;background:var(--ink);color:#fff;border-radius:0 0 8px 0;
}
.skip:focus{left:0}

:where(a,button):focus-visible{
  outline:2px solid var(--brand);
  outline-offset:3px;
  border-radius:6px;
}

.shell{background:var(--bg)}

/* ─────────────────────────────  nav  ───────────────────────── */

/* The plate spans the window — the bar sits outside .shell for exactly
   that reason — while the row it carries keeps the page's own measure,
   so the mark still lines up with the copy underneath it. */
.nav{
  position:sticky;top:0;z-index:30;
  height:var(--nav-h);
  background:color-mix(in srgb,var(--bg) 82%,transparent);
  backdrop-filter:blur(14px) saturate(1.6);
  -webkit-backdrop-filter:blur(14px) saturate(1.6);
}
.nav__inner{
  height:100%;
  display:grid;
  grid-template-columns:1fr auto 1fr;
  align-items:center;
  gap:24px;
  max-width:var(--wrap);
  margin-inline:auto;
  padding-inline:clamp(20px,4vw,var(--pad));
}

.brand{display:inline-flex;align-items:center;justify-self:start}
/* the lockup carries its own wordmark, so height is the only knob —
   width follows the intrinsic 603×160 ratio */
.brand__logo{
  display:block;flex:none;
  height:28px;width:auto;
  transition:opacity .16s ease;
}
.brand:hover .brand__logo{opacity:.78}

/* ── centre capsule ──────────────────────────────────────────── */

/* Paper, not ink: the capsule is the same white pill as the account
   button opposite it, so the bar reads as one row of controls instead
   of one black slab with two light satellites. */
.hub{
  position:relative;
  justify-self:center;
  display:flex;align-items:center;gap:5px;
  height:52px;padding:5px;
  border-radius:999px;
  background:var(--paper);
  border:1px solid var(--line);
}

.menu__btn{
  display:flex;align-items:center;gap:11px;
  height:42px;padding:0 18px 0 16px;
  border:0;border-radius:999px;
  background:none;
  font:inherit;
  font-size:15px;font-weight:500;letter-spacing:-.005em;
  color:var(--ink);
  cursor:pointer;
  transition:background-color .16s ease;
}
.menu__btn:hover{background:var(--chip)}

/* two bars that cross into a close icon on open */
.burger{position:relative;width:17px;height:11px;flex:none}
.burger i{
  position:absolute;left:0;right:0;height:1.7px;
  border-radius:2px;background:currentColor;
  transition:transform .3s cubic-bezier(.2,.7,.25,1);
}
.burger i:first-child{top:1px}
.burger i:last-child{bottom:1px}
.hub:has(:popover-open) .burger i:first-child{transform:translateY(3.6px) rotate(45deg)}
.hub:has(:popover-open) .burger i:last-child{transform:translateY(-3.6px) rotate(-45deg)}

/* ── reading progress ────────────────────────────────────────── */

@property --scrollp{syntax:"<integer>";inherits:false;initial-value:0}

.prog{
  position:relative;overflow:hidden;
  display:inline-flex;align-items:center;justify-content:center;
  min-width:62px;height:42px;padding:0 14px;
  border-radius:999px;
  background:var(--chip);
}
/* the sweep is brand purple let down towards paper: the figure sits on
   top of it and has to stay legible on both halves of the pill */
.prog__fill{
  position:absolute;inset:0;
  transform-origin:left center;transform:scaleX(0);
  background:color-mix(in srgb,var(--brand) 52%,#fff);
}
.prog__num{
  position:relative;
  font-size:13.5px;font-weight:600;letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;
  color:var(--ink);
  counter-reset:p var(--scrollp);
}
.prog__num::after{content:counter(p) "%"}

@keyframes progFill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes progCount{from{--scrollp:0}to{--scrollp:100}}

/* Both ride the document's own scroll range, so the number and the bar
   are the same measurement rather than two that have to agree. */
@supports (animation-timeline:scroll()){
  .prog__fill{animation:progFill linear both;animation-timeline:scroll(root block)}
  .prog__num {animation:progCount linear both;animation-timeline:scroll(root block)}
}

/* ── dropped sheet ───────────────────────────────────────────────
   A popover, so the browser hands over Esc and click-anywhere-to-close
   and puts it in the top layer — no scripting, no stacking to arrange.
   It hangs below the bar and is only as tall as what is in it.

   The reveal is keyframes rather than transitions: a popover goes from
   display:none to rendered in one jump, and a transition has no first
   frame to leave from in that jump. */

.menu__labels{display:inline-grid;justify-items:start}
.menu__labels span{grid-area:1/1;transition:opacity .18s ease}
.menu__labels .is-close,
.hub:has(:popover-open) .menu__labels .is-open{opacity:0}
.hub:has(:popover-open) .menu__labels .is-close{opacity:1}

.menu__sheet{
  position:fixed;inset:auto;
  top:calc(var(--nav-h) - 2px);left:50%;translate:-50% 0;
  width:min(440px,calc(100vw - 20px));
  max-height:calc(100dvh - var(--nav-h) - 22px);
  overflow:auto;overscroll-behavior:contain;
  margin:0;padding:clamp(24px,2.6vw,30px);
  border-radius:24px;
  background:#F6F4F0;
  border:1px solid var(--line);
  /* the only lifted surface on the page, so the shadow stays a hint */
  box-shadow:0 28px 56px -34px rgba(23,23,26,.4);
}
/* a browser without popover would leave the sheet standing open under
   the bar; better it is simply not there */
@supports not selector(:popover-open){
  .menu__sheet{display:none}
}

.sheet__label{
  margin:0 0 15px;
  font-size:10px;font-weight:600;letter-spacing:.135em;
  text-transform:uppercase;color:var(--faint);
}

/* Hover rolls the word out of the top and its coloured twin in from the
   bottom. The twin is generated from data-t, so the markup carries the
   text once and the link keeps a single accessible name.

   Keyed to [data-t] rather than to .sheet__big, so every tier of the
   sheet behaves the same way — the headline links and both footer lists.
   A link opts in by carrying its twin; the tiers then differ only in
   type size. */
.menu__sheet a[data-t]{
  position:relative;display:block;overflow:hidden;
}
.menu__sheet a[data-t] > span,
.menu__sheet a[data-t]::after{
  display:block;
  transition:transform .44s cubic-bezier(.2,.7,.25,1);
}
.menu__sheet a[data-t]::after{
  content:attr(data-t);
  position:absolute;left:0;top:100%;
  white-space:nowrap;
  color:var(--brand);
}
.menu__sheet a[data-t]:hover > span,
.menu__sheet a[data-t]:hover::after,
.menu__sheet a[data-t]:focus-visible > span,
.menu__sheet a[data-t]:focus-visible::after{transform:translateY(-100%)}

.sheet__big{display:flex;flex-direction:column;gap:2px}
.sheet__big a{
  font-size:clamp(1.5rem,2.3vw,1.95rem);
  line-height:1.34;
  font-weight:500;letter-spacing:-.032em;
  color:var(--ink);
}

.sheet__rule{height:1px;margin:28px 0 26px;border:0;background:var(--line)}

.sheet__grp + .sheet__grp{margin-top:30px}
.sheet__list{display:flex;flex-direction:column;gap:10px}
/* line-height, not the default, so the clipping box is exactly one line
   tall — the twin is positioned at top:100%, and a taller box would let
   it show below the word instead of waiting off-stage */
.sheet__list a{
  font-size:14.5px;line-height:1.5;font-weight:500;
  color:var(--muted);
}

@keyframes sheetOpen{
  from{opacity:0;clip-path:inset(0 0 100% 0 round 24px)}
  to  {opacity:1;clip-path:inset(0 0 0    0 round 24px)}
}
@keyframes sheetItem{
  from{opacity:0;transform:translateY(18px)}
  to  {opacity:1;transform:none}
}

/* the sheet unrolls, the links follow it down on a stagger, the two
   footer groups come last */
.menu__sheet:popover-open{animation:sheetOpen .44s cubic-bezier(.2,.7,.25,1) both}
.menu__sheet:popover-open .sheet__label{animation:sheetItem .5s ease both;animation-delay:80ms}
.menu__sheet:popover-open .sheet__big a{
  animation:sheetItem .6s cubic-bezier(.16,1,.3,1) both;
  animation-delay:calc(var(--i,0)*50ms + 120ms);
}
.menu__sheet:popover-open .sheet__rule{animation:sheetItem .5s ease both;animation-delay:360ms}
.menu__sheet:popover-open .sheet__grp{
  animation:sheetItem .56s cubic-bezier(.16,1,.3,1) both;
  animation-delay:calc(var(--i,0)*80ms + 390ms);
}

/* ── right-hand actions ──────────────────────────────────────── */

.nav__actions{display:flex;align-items:center;gap:12px;justify-self:end}
.nav__actions .btn{height:42px;padding-inline:20px}

.icon-btn{
  display:inline-flex;align-items:center;justify-content:center;
  width:42px;height:42px;flex:none;
  border-radius:50%;
  background:var(--paper);
  border:1px solid var(--line);
  color:var(--ink-2);
  transition:color .16s ease,border-color .16s ease;
}
.icon-btn svg{
  width:19px;height:19px;
  fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
}
.icon-btn:hover{color:var(--brand);border-color:rgba(125,98,255,.45)}

/* ─────────────────────────────  buttons  ───────────────────── */

/* flat pills, no glow — the page carries no other shadow either */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  height:44px;padding:0 22px;
  border-radius:999px;border:1px solid transparent;
  font-size:15px;font-weight:500;letter-spacing:-.006em;
  white-space:nowrap;cursor:pointer;
  transition:background-color .16s ease,color .16s ease,opacity .16s ease;
}
.btn--sm{height:38px;padding:0 18px;font-size:14.5px}

.btn--brand{background:var(--brand);color:#fff}
.btn--brand:hover{background:var(--brand-ink)}

.btn--ghost{background:var(--chip);color:var(--ink)}
.btn--ghost:hover{background:var(--chip-h)}

.btn--ink{background:var(--ink);color:#fff}
.btn--ink:hover{opacity:.86}

.arr{
  width:10px;height:10px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .16s ease;
}
.btn:hover .arr{transform:translate(1.5px,-1.5px)}

/* ─────────────────────────────  hero frame  ────────────────── */

/* the whole page hangs off one centred measure. Inner blocks keep
   their own padding, which clamps up to --pad, so that padding is the
   gutter — no double inset. */
.wrap{
  max-width:var(--wrap);
  margin-inline:auto;
}

.hero__inner{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.02fr);
  align-items:center;
}

.hero__copy{
  padding:clamp(56px,8vw,120px) clamp(20px,3vw,48px)
          clamp(56px,8vw,120px) clamp(20px,4vw,var(--pad));
  align-self:center;
}

.hero__visual{
  display:flex;align-items:center;justify-content:center;
  padding:clamp(16px,2.4vw,32px) clamp(16px,2.4vw,var(--pad));
  min-width:0;
}

/* ─────────────────────────────  copy  ──────────────────────── */

.hero__title{
  margin:0;
  font-size:clamp(2rem,4.1vw,3.4rem);
  line-height:1.16;
  letter-spacing:-.022em;
  font-weight:360;
  color:var(--ink);
}

/* one mask per line; the padding/margin pair keeps descenders from
   being clipped by that mask */
.ln{
  display:block;
  overflow:hidden;
  padding-bottom:.14em;
  margin-bottom:-.14em;
}
.w{
  display:inline-block;
  will-change:transform;
  animation:wordUp .95s cubic-bezier(.16,1,.3,1) both;
  animation-delay:calc(var(--i,0)*68ms + 60ms);
}

.hero__lead{
  margin:22px 0 0;
  max-width:38ch;
  font-size:16px;
  line-height:1.6;
  color:var(--muted);
}

.hero__actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:32px}

/* ─────────────────────────────  trust row  ─────────────────── */

.trusted{margin-top:clamp(44px,5vw,64px)}
.trusted__label{
  margin:0;
  font-size:13px;font-weight:400;letter-spacing:0;
  color:var(--faint);
}
.trusted__grid{
  margin-top:22px;
  display:grid;
  grid-template-columns:repeat(4,minmax(0,auto));
  justify-content:start;
  gap:22px clamp(18px,2.6vw,34px);
  max-width:520px;
}
.logo{
  display:inline-flex;align-items:center;gap:7px;
  font-size:14.5px;font-weight:500;letter-spacing:-.012em;
  color:#8E8E96;
  transition:color .18s ease;
}
/* the marks are solid one-colour SVGs; the row tints them with the
   text colour and lifts both together on hover */
.logo img{
  width:17px;height:17px;flex:none;
  opacity:.55;
  transition:opacity .18s ease;
}
.logo:hover img{opacity:.9}
.logo:hover{color:#55555C}

/* ─────────────────────────────  visual  ────────────────────── */

.viz{
  width:100%;
  max-width:720px;
  height:auto;
  overflow:visible;
}

/* assembly axes */
.axes path{
  stroke:#D6D5D3;stroke-width:2;
  stroke-dasharray:5 10;stroke-linecap:round;
  fill:none;
}

/* slab artwork */
.art .chip rect{fill:#FEFEFD;stroke:#E3E2E0;stroke-width:3}
.art .glyph{fill:none;stroke:#A8A5A0;stroke-width:4;stroke-linecap:round;stroke-linejoin:round}
.art .ink{stroke:#8A8680}
.art .ink-fill{fill:#7E7B75}

.dots circle{fill:none;stroke:#6A4CF2;stroke-width:4.5}
.dots circle.solid{fill:#6A4CF2}

/* wall labels */
.lbl{
  font-size:21px;font-weight:500;letter-spacing:.02em;
  fill:#7C7974;
}
.lbl--ai{fill:#3C2492}

/* call-outs */
.callouts text{
  font-size:23px;font-weight:500;letter-spacing:-.005em;
  fill:#6C6C75;
}
.callouts .lead{
  stroke:#C6C4C1;stroke-width:2.6;
  stroke-dasharray:0.1 8;stroke-linecap:round;
  fill:none;
}
.cal--ai .lead{stroke:#6A4CF2}
.cal--ai text{fill:#4A4A52}

/* ───────────────────────  the four steps  ───────────────────── */

.steps__inner{
  padding:clamp(56px,8vw,120px) clamp(20px,4vw,var(--pad));
}

/* left-ranged like every other head on the page — a centred one read
   as a different page */
.steps__title{
  margin:12px 0 clamp(32px,4vw,54px);
  font-size:clamp(1.75rem,3.1vw,2.5rem);
  line-height:1.16;
  letter-spacing:-.026em;
  font-weight:500;
  color:var(--ink);
}

.steps__grid{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(22px,2.4vw,32px);
  margin:0;padding:0;list-style:none;
}
/* four steps only survive one row down to about here; below it they go
   two-by-two rather than leaving a widow on a second row */
@media (max-width:1150px){
  .steps__grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:620px){
  .steps__grid{grid-template-columns:minmax(0,1fr)}
}

/* the step index carries the order the cards are in — without it the
   row reads as four features rather than one sequence */
.step__no{
  display:flex;align-items:center;gap:12px;
  margin:20px 0 0;
  font-size:12px;font-weight:500;letter-spacing:.05em;
  font-variant-numeric:tabular-nums;
  color:var(--faint);
}
.step__no::after{
  content:"";flex:1;height:1px;background:var(--line);
}

.step__title{
  margin:12px 0 0;
  font-size:19px;font-weight:500;letter-spacing:-.022em;
  color:var(--ink);
}
.step__text{
  margin:10px 0 0;
  max-width:34em;
  font-size:15px;line-height:1.6;
  color:var(--muted);
}

/* ── the stills each panel carries ─────────────────────────── */

.step__art{
  position:relative;
  height:clamp(196px,15.6vw,224px);
  border-radius:22px;
  /* flat fill, no border. A gradient plus an outline is two devices
     doing one job; a single quiet plane lets the white cards inside
     read as the content and the panel as the space around it. */
  background:#F5F5F7;
  overflow:hidden;
}


/* ── 1 · the sources you pick, and what starts arriving ─────── */

.src__row{
  --cycle:9s;--slot:1.8s;              /* five sources, one fifth each */
  position:absolute;z-index:2;
  left:50%;top:clamp(16px,2vw,24px);
  transform:translateX(-50%);
  display:flex;align-items:center;gap:8px;
}
.src,.src--add{
  display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;flex:none;
  border-radius:11px;
  background:var(--paper);
  border:1px solid rgba(23,23,26,.055);
  box-shadow:0 1px 1.5px rgba(23,23,26,.04);
  color:#A8A59F;
}
.src img{width:19px;height:19px;display:block}
.src--add{
  background:var(--chip);border-style:dashed;box-shadow:none;
  color:var(--faint);font-size:17px;line-height:1;
}
.src{
  animation:srcOn var(--cycle) linear infinite both;
  animation-delay:calc(var(--i,0)*var(--slot));
}

/* the feed hangs off the bottom edge on purpose — the tile clips the
   last row, so the stream reads as longer than the frame */
.feed{
  position:absolute;
  left:clamp(12px,1.5vw,18px);right:clamp(12px,1.5vw,18px);
  top:clamp(72px,7.8vw,92px);
  display:grid;gap:8px;
  margin:0;padding:0;list-style:none;
}
.feed__row{
  display:flex;align-items:center;gap:9px;
  padding:9px 10px;
  border-radius:12px;
  background:var(--paper);border:1px solid rgba(23,23,26,.05);
  animation:feedLive 9s ease-in-out infinite both;
  animation-delay:calc(var(--i,0)*.5s);
}
.feed__mark{
  display:grid;place-items:center;
  width:24px;height:24px;flex:none;
  border-radius:7px;
}
/* the marks bring their own colour now, so the tinted plate they used
   to sit on only muddied them */
.feed__mark img{width:16px;height:16px;display:block}
.feed__body{flex:1;min-width:0}
.feed__ln{display:block;height:4px;border-radius:3px;background:#E6E5E3}
.feed__ln + .feed__ln{margin-top:5px}
.feed__ln--s{width:56%}

/* the rating is the one number on the row: it is what makes the row a
   review rather than a placeholder card */
.feed__rate{
  display:inline-flex;align-items:center;gap:4px;flex:none;
  font-size:11.5px;font-weight:600;letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;
  color:var(--ink-2);
}
.feed__rate svg{width:11px;height:11px;fill:#C98500}
.feed__rate--low{color:#D03B3B}
.feed__rate--low svg{fill:#D03B3B}

/* Lift and ring rather than recolour: the tiles carry real marks now,
   and tinting a bitmap does nothing. */
@keyframes srcOn{
  0%      {border-color:rgba(23,23,26,.055);box-shadow:0 1px 1.5px rgba(23,23,26,.04);transform:none}
  4%,16%  {border-color:rgba(125,98,255,.5);box-shadow:0 6px 16px -10px rgba(125,98,255,.85);transform:translateY(-5px)}
  22%,100%{border-color:rgba(23,23,26,.055);box-shadow:0 1px 1.5px rgba(23,23,26,.04);transform:none}
}
@keyframes feedLive{
  0%,6%    {border-color:#E8E7E5;box-shadow:0 1px 2px rgba(23,23,26,.03)}
  13%,24%  {border-color:rgba(125,98,255,.34);box-shadow:0 8px 18px -14px rgba(125,98,255,.9)}
  33%,100% {border-color:#E8E7E5;box-shadow:0 1px 2px rgba(23,23,26,.03)}
}

/* ── 2 · raw feedback in, named themes out ──────────────────── */

/* ── 2–4 · the three stills that carry variants ──────────────
   Each of these cards shows the same job done for a different trade —
   FMCG, HoReCa, retail — one at a time. The variants are stacked in a
   single grid cell so the card height is set by the tallest and never
   jumps, and they share one clock, so all three cards turn the page
   together and the row reads as one demo rather than three loops. */
.vstack{position:absolute;inset:0;display:grid;padding:clamp(14px,1.6vw,20px)}
.vstack > .v{
  grid-area:1/1;
  display:flex;flex-direction:column;justify-content:center;
  gap:9px;
  animation:vSwap var(--vcycle) ease-in-out infinite both;
  animation-delay:calc(var(--i,0) * var(--vstep));
}
.step__art{--vcycle:13.5s;--vstep:4.5s}

@keyframes vSwap{
  0%       {opacity:0;transform:translateY(6px)}
  3.5%     {opacity:1;transform:none}
  29.5%    {opacity:1;transform:none}
  33.333%  {opacity:0;transform:translateY(-6px)}
  100%     {opacity:0;transform:translateY(-6px)}
}

/* the trade this pass is showing */
.vcap{
  margin:0;
  font-size:10.5px;font-weight:500;letter-spacing:.05em;
  text-transform:uppercase;
  color:var(--faint);
}

/* ── 2 · one review, read and labelled ─────────────────────── */

.read__q{
  margin:0;
  padding:11px 12px;
  border-radius:12px;
  background:var(--paper);border:1px solid rgba(23,23,26,.05);
  font-size:12.5px;line-height:1.55;
  color:var(--ink-2);
}
/* the marked phrases are what the model acted on, so they are
   underlined as well as tinted — never colour alone */
.rmk{
  padding:0 2px;border-radius:3px;
  background:rgba(208,59,59,.13);
  border-bottom:1.5px solid rgba(208,59,59,.75);
  color:var(--ink);
}

.read__tags{display:flex;flex-wrap:wrap;gap:5px}
.tag{
  display:inline-flex;align-items:center;gap:6px;
  padding:4px 9px;
  border-radius:999px;
  background:var(--paper);border:1px solid rgba(23,23,26,.05);
  font-size:11px;font-weight:500;letter-spacing:-.01em;
  color:var(--ink-2);
}
.tag__dot{width:6px;height:6px;flex:none;border-radius:50%;background:var(--brand)}
.tag__dot--neg{background:#D03B3B}
.tag__dot--pos{background:#2E9E6B}
.tag--crit{
  background:#FBEDED;border-color:rgba(208,59,59,.28);
  color:#D03B3B;
}

/* ── 3 · the themes those labels collapse into ─────────────── */

.themes{
  display:grid;gap:11px;
  margin:0;padding:0;list-style:none;
}
/* Label and figure on one line, the share of volume on a hairline track
   beneath. The old version put each row in its own bordered pill with
   the bar as a wash behind the text, which made three cards inside a
   card and left the number fighting its own background. */
.theme{
  display:grid;
  grid-template-columns:1fr auto;
  align-items:baseline;
  gap:2px 10px;
  font-size:12.5px;font-weight:500;letter-spacing:-.012em;
  color:var(--ink);
}
.theme__n{
  font-size:12px;font-weight:500;font-variant-numeric:tabular-nums;
  color:var(--ink-2);
}
.theme__bar{
  grid-column:1/-1;
  height:3px;border-radius:2px;
  background:rgba(23,23,26,.07);
  overflow:hidden;
}
.theme__fill{
  display:block;height:100%;width:var(--w,50%);
  border-radius:inherit;
  background:var(--brand);
  transform-origin:left center;
  animation:barGrow var(--vcycle) cubic-bezier(.2,.7,.25,1) infinite both;
  animation-delay:calc(var(--i,0) * var(--vstep));
}
/* the second and third bars read as the tail of the ranking, so they
   step down in weight rather than repeating the same purple */
.theme:nth-child(2) .theme__fill{background:#A99BFF}
.theme:nth-child(3) .theme__fill{background:#CFC8FF}

@keyframes barGrow{
  0%      {transform:scaleX(0)}
  12%     {transform:scaleX(1)}
  100%    {transform:scaleX(1)}
}


/* ── the share ring ───────────────────────────────────────────
   Ramp validated with the dataviz palette checker against this card's
   #F5F5F7 panel: lightness band, chroma floor, CVD separation and the
   normal-vision floor all pass. The one warning left is contrast of the
   pale step against the panel, which the checker allows only if every
   slice is labelled in text — hence the legend, not a bare ring. It is a
   sequential ramp because the slices are ordered shares of one thing;
   "Other" sits outside the ramp in neutral grey, as a leftover bucket
   should. */
.share{
  --s1:#4A31C6; --s2:#7F5FF8; --s3:#AB9AF7; --s4:#C4C2BE;
  display:flex;align-items:center;gap:14px;
}
.share__ring{width:88px;height:88px;flex:none}
.share__ring circle{fill:none;stroke-width:12}
.share__track{stroke:#E4E3E1}

.share__legend{
  flex:1;min-width:0;
  display:grid;gap:5px;
  margin:0;padding:0;list-style:none;
}
.share__legend li{
  display:flex;align-items:center;gap:7px;
  font-size:11px;letter-spacing:-.01em;
  color:var(--muted);
}
.share__legend i{width:8px;height:8px;flex:none;border-radius:2.5px;background:var(--sw)}
.share__legend b{
  margin-left:auto;font-weight:500;
  color:var(--ink-2);font-variant-numeric:tabular-nums;
}

/* ── 4 · a question, and the answer with its receipts ──────── */

.ask__q{
  display:flex;align-items:center;gap:0;
  margin:0;
  padding:10px 12px;
  border-radius:12px;
  background:var(--paper);border:1px solid rgba(23,23,26,.05);
  font-size:12.5px;font-weight:500;letter-spacing:-.012em;
  color:var(--ink);
}
.ask__spark svg{width:13px;height:13px;fill:currentColor}
.ask__caret{
  width:1.5px;height:13px;flex:none;margin-left:1px;
  background:var(--brand);
  animation:askCaret 1.1s steps(1,end) infinite;
}
.ask__line{
  margin:0;
  font-size:12.5px;line-height:1.5;letter-spacing:-.012em;
  color:var(--ink);
}
.ask__meta{
  margin:-4px 0 0;
  font-size:11px;font-variant-numeric:tabular-nums;
  color:var(--faint);
}

/* the receipts: what the answer was read out of */
.ask__cites{display:flex;flex-wrap:wrap;gap:5px}
.cite{
  display:inline-flex;align-items:center;gap:5px;
  padding:3px 8px 3px 4px;
  border-radius:999px;
  background:var(--paper);border:1px solid rgba(23,23,26,.05);
  font-size:10.5px;font-weight:500;letter-spacing:-.008em;
  color:var(--ink-2);
}
.cite img{width:13px;height:13px;display:block;border-radius:3px}

@keyframes askCaret{50%{opacity:0}}



/* the section label every block above its heading — the grid column is
   a leftover default that .eyebrow--plain clears where there is no grid */
.eyebrow{
  grid-column:2;
  margin:0;
  padding:20px 0 20px clamp(24px,3.4vw,48px);
  font-size:13.5px;font-weight:500;color:var(--ink-2);
}

/* ───────────────────────────  always on  ───────────────────── */

.band{
  /* one timeline for the whole block. --step is --cycle / 12; the twelve
     sources are indexed 0…11 and everything animated derives its delay
     from --i, so nothing can drift out of sync. Adding a source means a
     pod, a label and a longer --cycle — the ring itself does not care. */
  --cycle:21.6s;
  --step:1.8s;


  /* the pill's backdrop-filter needs colour to bite on — these two
     tints sit directly behind it */
  background:
    radial-gradient(46% 52% at 22% 62%,rgba(125,98,255,.10) 0%,rgba(125,98,255,0) 70%),
    radial-gradient(38% 44% at 8% 22%,rgba(95,211,228,.10) 0%,rgba(95,211,228,0) 72%),
    radial-gradient(100% 76% at 76% 40%,#F1ECFF 0%,rgba(241,236,255,0) 64%),
    linear-gradient(180deg,#FAFAFA 0%,#F2F2F2 100%);
  border-top:1px solid var(--line);
}

.band__inner{
  display:grid;
  grid-template-columns:minmax(0,.94fr) minmax(0,1.06fr);
  align-items:center;
  gap:clamp(20px,3vw,52px);
  /* folk gives this block a lot of air — the graphic reads as an object
     in a room rather than something filling the frame */
  padding:clamp(60px,7.6vw,116px) clamp(20px,2.4vw,34px)
          clamp(60px,7.6vw,116px) clamp(24px,4vw,var(--pad));
}

.band__title{
  margin:0;
  font-size:clamp(1.7rem,3vw,2.4rem);
  line-height:1.22;
  letter-spacing:-.018em;
  font-weight:360;
  color:var(--ink);
}

.band__lead{
  margin:20px 0 0;
  max-width:40ch;
  font-size:16px;
  line-height:1.6;
  color:var(--muted);
}

.band__actions{
  display:flex;flex-wrap:wrap;align-items:center;gap:12px;
  margin-top:34px;
}

.learn{
  display:inline-flex;align-items:center;gap:8px;
  margin-top:26px;
  font-size:15px;font-weight:500;letter-spacing:-.005em;
  color:var(--brand);
}
.learn svg{
  width:17px;height:10px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .16s ease;
}
.learn:hover svg{transform:translateX(3px)}

/* ── the "Using …" pill ───────────────────────────────────────
   inline-grid with every label in cell 1/1: the pill sizes to the
   longest source name once and holds that width as they cycle, so
   the row beside it never reflows. */
/* The one piece of glass that can be real: an HTML box, so it gets a
   true backdrop-filter and actually refracts the band's aurora behind
   it. The inset highlight is the lit top edge, the inset shadow the
   shaded bottom one — same two-shoulder trick as the SVG rim. */
.using{
  display:inline-grid;
  height:50px;
  padding:0 22px 0 12px;
  border-radius:999px;
  background:rgba(255,255,255,.52);
  border:1px solid rgba(255,255,255,.75);
  -webkit-backdrop-filter:blur(18px) saturate(180%);
          backdrop-filter:blur(18px) saturate(180%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),
    inset 0 -1px 0 rgba(140,136,128,.14),
    0 1px 2px rgba(23,23,26,.04),
    0 10px 26px -16px rgba(23,23,26,.4);
}

/* no backdrop-filter, no glass — fall back to the solid surface rather
   than a washed-out translucent one */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .using{background:var(--paper);border-color:var(--line)}
}
.using__slot{
  grid-area:1/1;
  display:inline-flex;align-items:center;gap:11px;
  font-size:15px;font-weight:600;letter-spacing:-.005em;
  color:var(--ink-2);
  white-space:nowrap;
  /* hidden by default and NO fill: now that the keyframes open at full
     opacity, a backwards fill would show every label that is still
     waiting out its delay — all of them, for the first cycle */
  opacity:0;
  animation:slotSwap var(--cycle) linear infinite;
  /* Pod --i reaches the capsule — station 9 of 12, i.e. 75% of the ring
     — at (9 - --i) steps, and now HOLDS there rather than sweeping
     through, so the window has to START on the arrival, not straddle it.
     Centring it half a step earlier (8.5) was correct only while the pod
     passed the capsule at speed; against a dwell it named the incoming
     source while the previous one was still sitting in the slot.
     Signs differ across the twelve labels, which is fine — the loop
     settles either way. */
  animation-delay:calc((9 - var(--i,0)) * var(--step));
}
.using__mark{
  width:26px;height:26px;flex:none;
  padding:3px;
  border-radius:8px;
  background:rgba(255,255,255,.7);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.85),0 1px 2px rgba(23,23,26,.08);
}

.using__tile{
  display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;flex:none;
  border-radius:9px;
  background:var(--brand-soft);
  border:1px solid color-mix(in srgb,var(--brand) 24%,transparent);
  color:var(--brand);
}
.using__tile svg{width:18px;height:18px}

/* ── constellation ───────────────────────────────────────────── */

.sprite{position:absolute}

.band__visual{
  display:flex;align-items:center;justify-content:center;
  min-width:0;
}
.orbit{
  width:100%;
  max-width:604px;
  height:auto;
  /* the departing pod sinks past the cropped bottom edge while it fades,
     which is the point — it must not be clipped on the way out */
  overflow:visible;
}

.ic{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round}

/* The rail is a hint, not a diagram. folk's is barely there, and once
   the pods carry the eye it only has to stop the motion reading as
   random — anything heavier turns the block into a flowchart. */
/* Two crossed masks rather than a gradient on the stroke, which is how
   folk fades theirs. A stroke gradient can only vary along one axis, so
   it kept the rail bright on the lower right where the ring curves back
   under the capsule and no pod is left to justify it.

   Horizontal mask: the left half carries no visible pod, so it is cut
   outright. Vertical mask: eased in at the top, solid through the
   capsule, then gone by 80% — just past the one pod below it. */
.orbit .chain{
  fill:none;stroke:#BCB6AB;stroke-width:1.3;
  stroke-linecap:round;
  opacity:.3;
  -webkit-mask-image:
    linear-gradient(90deg,transparent 0 42%,rgba(0,0,0,.25) 48%,#000 56%,#000 100%),
    linear-gradient(180deg,rgba(0,0,0,.45) 0,#000 26%,#000 62%,rgba(0,0,0,.3) 72%,transparent 80%,transparent 100%);
          mask-image:
    linear-gradient(90deg,transparent 0 42%,rgba(0,0,0,.25) 48%,#000 56%,#000 100%),
    linear-gradient(180deg,rgba(0,0,0,.45) 0,#000 26%,#000 62%,rgba(0,0,0,.3) 72%,transparent 80%,transparent 100%);
  -webkit-mask-composite:source-in;
          mask-composite:intersect;
}
/* the brand lens keeps a touch of tint so it reads as the one piece of
   glass that belongs to us rather than to a connector */
.orbit .glass--brand{stroke:color-mix(in srgb,var(--brand) 30%,transparent);stroke-width:2.5}

/* the halo gradient is painted from --brand too, so a palette swap
   never has to reach into the markup */
#oHalo stop{stop-color:var(--brand)}
.orbit .capsule{fill:url(#gGlass);stroke:url(#gRim);stroke-width:2}

/* dashed ring, slowly turning. fill-box excludes the stroke, so the
   box centre is the circle centre and it spins in place. */
/* ── the lens ring ────────────────────────────────────────────
   The dotted spinner this replaces read as a loading state. What sits
   here instead is folk's trick: one conic sweep of the brand colour,
   drawn twice — blurred for the halo, sharp for the edge — so the light
   round the rim is uneven and the lens looks lit rather than outlined.

   Both are masked into a ring, which is what lets them sit ON TOP of
   the SVG (HTML has the conic gradient, SVG does not) without covering
   the wordmark: the middle of the mask is simply transparent.

   The wrapper is the SVG's own box, so these percentages are the
   viewBox's: centre 258/700 across, 409/818 down, outer 174/700 wide. */
.orbit-wrap{
  position:relative;
  width:100%;
  max-width:604px;
  line-height:0;
}
/* the capsule, as a share of the graphic's own box */
.lens-clip{
  position:absolute;
  left:23.286%;top:38.386%;
  width:65.428%;height:23.228%;
  border-radius:9999px;
  overflow:hidden;
  pointer-events:none;
}

/* placed against the clip box now, not the whole graphic */
.lens{
  position:absolute;
  left:20.742%;top:50%;
  width:38.012%;aspect-ratio:1;
  transform:translate(-50%,-50%);
  border-radius:50%;
  pointer-events:none;
  /* ONE concentrated lobe rather than several soft ones. Light spread
     evenly round a rim averages out into a border and reads as nothing,
     which is what the earlier six-stop version did — the eye needs a
     bright arc with somewhere dark to travel into. */
  background:conic-gradient(from 270deg,
    color-mix(in srgb,var(--brand)  6%,transparent)   0deg,
    color-mix(in srgb,var(--brand)  9%,transparent)  55deg,
    color-mix(in srgb,var(--brand) 70%,transparent) 112deg,
    color-mix(in srgb,var(--brand) 12%,transparent) 172deg,
    color-mix(in srgb,var(--brand)  6%,transparent) 260deg,
    color-mix(in srgb,var(--brand)  6%,transparent) 360deg);
  /* the sweep is declared per layer below, alongside each one's beat */
}
/* Every arrival gives the rim a beat of light. The beat is exactly one
   --step long and needs no phase offset: a pod settles into the capsule
   at 75% of its own cycle, and successive pods are one --step apart, so
   every arrival falls on a multiple of --step. Peak at 0% and again at
   100% is therefore the arrival itself, and the loop closes on it.

   It rides alongside the sweep instead of replacing it — the sweep owns
   transform, the beat owns opacity and blur, so the two never fight over
   a property. */
.lens--glow{
  -webkit-mask-image:radial-gradient(closest-side,transparent 72%,#000 90%);
          mask-image:radial-gradient(closest-side,transparent 72%,#000 90%);
  filter:blur(9px);
  opacity:.56;
  animation:
    lensSweep calc(var(--step) * 2) linear infinite,
    lensBeatGlow var(--step) ease-out infinite;
}
/* the outer edge fades too, or the sharp copy ends on a drawn circle */
.lens--rim{
  -webkit-mask-image:radial-gradient(closest-side,transparent 88.5%,#000 92%,#000 97%,transparent 100%);
          mask-image:radial-gradient(closest-side,transparent 88.5%,#000 92%,#000 97%,transparent 100%);
  opacity:.55;
  animation:
    lensSweep calc(var(--step) * 2) linear infinite,
    lensBeatRim var(--step) ease-out infinite;
}

@keyframes lensBeatGlow{
  0%   {opacity:.88;filter:blur(13px)}
  38%  {opacity:.5; filter:blur(9px)}
  82%  {opacity:.5; filter:blur(9px)}
  100% {opacity:.88;filter:blur(13px)}
}
@keyframes lensBeatRim{
  0%   {opacity:.95}
  38%  {opacity:.55}
  82%  {opacity:.55}
  100% {opacity:.95}
}

/* view-box + origin 0 0 means translate() in the keyframes is read as
   plain viewBox coordinates, and the scale that follows it pivots on the
   pod's own centre — so a station is just the pair of numbers you would
   read off the drawing. The negative delay is what fills the rail: pod
   --i starts --i steps in, i.e. already parked at station --i. */
.pod{
  color:var(--brand);
  transform-box:view-box;transform-origin:0 0;
  /* linear, and it has to be. Every pod shares this timing and their
     delays are exact multiples of --step, so any easing applies to all
     twelve in lockstep: a steppy curve here stopped the entire ring dead
     for 400ms and then lurched it at five times the average speed. The
     per-source rhythm is carried by the pill and the lens beat instead,
     where a pause costs nothing. */
  animation:march var(--cycle) linear infinite both;
  animation-delay:calc(var(--i,0) * var(--step) * -1);
  will-change:transform,opacity,filter;
}
/* ── glass ────────────────────────────────────────────────────
   The body is deliberately not opaque: the aurora and the rail have
   to read through it, otherwise this is just a white circle. The rim
   is the part that does the work — lit on one shoulder, shaded on the
   other, so the eye reads a thick edge rather than a drawn outline. */
/* Ozon ships its mark as a hard-edged square while every other app icon
   on the page has rounded corners, so on the glass it reads as a sticker
   rather than a tile. Keyed to the file, so it holds in all three places
   the mark appears — ring, pill and catalogue chip — and the asset
   itself stays untouched. */
.pod image[href$="ozon.png"]{clip-path:inset(0 round 22%)}
img[src$="ozon.png"]{border-radius:22%}

.glass{fill:url(#gGlass)}
.glass--brand{fill:url(#gGlass);stroke:none}
.glass__rim{fill:none;stroke:url(#gRim);stroke-width:2.4}
.glass__sheen{fill:url(#gSheen);opacity:.55}

.aurora{opacity:.62}
.aurora__b{
  transform-box:fill-box;transform-origin:center;
  animation:auroraDrift 26s ease-in-out infinite;
  animation-delay:calc(var(--i,0) * -8s);
}

/* Twelve stations round a ring, one per 100/12 of the cycle. Read the
   percentages downwards and the station numbers run backwards — that is
   deliberate. It puts the right-hand side of the ring travelling UPWARD:
   a pod rises out of the bottom, reaches the capsule at 75%, and keeps
   climbing past it.

   The ladder is therefore NOT symmetric about the capsule. One station
   below it is visible (the arrival) and four above it (the departure,
   fading as it goes), which is the distribution the reference has: a
   single source coming up into frame and a trail of them dispersing over
   the top. The rest of the ring sits at zero and is where the queue is
   quietly recycled.

   Scale, opacity and blur move together — that is the whole trick, a pod
   reads as distant rather than merely faint. 100% restates 0% exactly,
   so the loop closes with nothing to hide. Timing is linear on purpose:
   see .pod for why any easing here stalls all twelve pods at once. */
@keyframes march{
  0%       {transform:translate(258px,64px) scale(0.74);opacity:.48;filter:blur(0.7px);animation-timing-function:linear}
  5%       {transform:translate(258px,64px) scale(0.74);opacity:.48;filter:blur(0.7px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  8.333%   {transform:translate(124px,110px) scale(0.64);opacity:0;filter:blur(1.2px);animation-timing-function:linear}
  13.333%  {transform:translate(124px,110px) scale(0.64);opacity:0;filter:blur(1.2px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  16.667%  {transform:translate(26px,236px) scale(0.58);opacity:0;filter:blur(1.8px);animation-timing-function:linear}
  21.667%  {transform:translate(26px,236px) scale(0.58);opacity:0;filter:blur(1.8px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  25%      {transform:translate(-10px,409px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:linear}
  30%      {transform:translate(-10px,409px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  33.333%  {transform:translate(26px,582px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:linear}
  38.333%  {transform:translate(26px,582px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  41.667%  {transform:translate(124px,708px) scale(0.56);opacity:0;filter:blur(1.9px);animation-timing-function:linear}
  46.667%  {transform:translate(124px,708px) scale(0.56);opacity:0;filter:blur(1.9px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  50%      {transform:translate(258px,754px) scale(0.62);opacity:0;filter:blur(1.6px);animation-timing-function:linear}
  55%      {transform:translate(258px,754px) scale(0.62);opacity:0;filter:blur(1.6px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  58.333%  {transform:translate(392px,708px) scale(0.7);opacity:0;filter:blur(1px);animation-timing-function:linear}
  63.333%  {transform:translate(392px,708px) scale(0.7);opacity:0;filter:blur(1px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  66.667%  {transform:translate(490px,582px) scale(0.82);opacity:.55;filter:blur(0.5px);animation-timing-function:linear}
  71.667%  {transform:translate(490px,582px) scale(0.82);opacity:.55;filter:blur(0.5px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  75%      {transform:translate(526px,409px) scale(1);opacity:1;filter:blur(0px);animation-timing-function:linear}
  80%      {transform:translate(526px,409px) scale(1);opacity:1;filter:blur(0px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  83.333%  {transform:translate(490px,236px) scale(0.93);opacity:.92;filter:blur(0.12px);animation-timing-function:linear}
  88.333%  {transform:translate(490px,236px) scale(0.93);opacity:.92;filter:blur(0.12px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  91.667%  {transform:translate(392px,110px) scale(0.84);opacity:.72;filter:blur(0.35px);animation-timing-function:linear}
  96.667%  {transform:translate(392px,110px) scale(0.84);opacity:.72;filter:blur(0.35px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  100%     {transform:translate(258px,64px) scale(0.74);opacity:.48;filter:blur(0.7px);animation-timing-function:linear}
}

/* one station wide — 100/12 of the cycle — so a label lives exactly as
   long as its pod stands in the capsule */
@keyframes marchTurned{
  0%       {transform:translate(258px,64px) scale(0.62);opacity:0;filter:blur(1.2px);animation-timing-function:linear}
  5%       {transform:translate(258px,64px) scale(0.62);opacity:0;filter:blur(1.2px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  8.333%   {transform:translate(124px,110px) scale(0.56);opacity:0;filter:blur(1.8px);animation-timing-function:linear}
  13.333%  {transform:translate(124px,110px) scale(0.56);opacity:0;filter:blur(1.8px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  16.667%  {transform:translate(26px,236px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:linear}
  21.667%  {transform:translate(26px,236px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  25%      {transform:translate(-10px,409px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:linear}
  30%      {transform:translate(-10px,409px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  33.333%  {transform:translate(26px,582px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:linear}
  38.333%  {transform:translate(26px,582px) scale(0.54);opacity:0;filter:blur(2.1px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  41.667%  {transform:translate(124px,708px) scale(0.56);opacity:0;filter:blur(1.8px);animation-timing-function:linear}
  46.667%  {transform:translate(124px,708px) scale(0.56);opacity:0;filter:blur(1.8px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  50%      {transform:translate(258px,754px) scale(0.62);opacity:0;filter:blur(1.2px);animation-timing-function:linear}
  55%      {transform:translate(258px,754px) scale(0.62);opacity:0;filter:blur(1.2px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  58.333%  {transform:translate(392px,708px) scale(0.76);opacity:.45;filter:blur(0.5px);animation-timing-function:linear}
  63.333%  {transform:translate(392px,708px) scale(0.76);opacity:.45;filter:blur(0.5px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  66.667%  {transform:translate(490px,582px) scale(0.9);opacity:.85;filter:blur(0.15px);animation-timing-function:linear}
  71.667%  {transform:translate(490px,582px) scale(0.9);opacity:.85;filter:blur(0.15px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  75%      {transform:translate(526px,409px) scale(1);opacity:1;filter:blur(0px);animation-timing-function:linear}
  80%      {transform:translate(526px,409px) scale(1);opacity:1;filter:blur(0px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  83.333%  {transform:translate(490px,236px) scale(0.9);opacity:.85;filter:blur(0.15px);animation-timing-function:linear}
  88.333%  {transform:translate(490px,236px) scale(0.9);opacity:.85;filter:blur(0.15px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  91.667%  {transform:translate(392px,110px) scale(0.76);opacity:.45;filter:blur(0.5px);animation-timing-function:linear}
  96.667%  {transform:translate(392px,110px) scale(0.76);opacity:.45;filter:blur(0.5px);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  100%     {transform:translate(258px,64px) scale(0.62);opacity:0;filter:blur(1.2px);animation-timing-function:linear}
}

/* The windows tile edge to edge, so the swap is a cut rather than a
   crossfade. Ramping in over 1.1% left a ~240ms hole at every arrival —
   the outgoing label had reached zero and the incoming had not started —
   and the pill blinked empty once a second and a half. Dropping the old
   one 12ms early keeps the two from ever overlapping either. */
@keyframes slotSwap{
  0%     {opacity:1}
  8.28%  {opacity:1}
  8.333% {opacity:0}
  100%   {opacity:0}
}
/* Half a turn per --step, gently eased at both ends: the lobe swings to
   the other side of the rim as each source lands. A constant slow spin
   read as nothing happening; a hard ease-in-out read as a stutter, so
   the curve is shallow enough that the lobe never actually stops. */
@keyframes lensSweep{
  0%   {transform:translate(-50%,-50%) rotate(0deg);animation-timing-function:linear}
  30%  {transform:translate(-50%,-50%) rotate(0deg);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  50%  {transform:translate(-50%,-50%) rotate(180deg);animation-timing-function:linear}
  80%  {transform:translate(-50%,-50%) rotate(180deg);animation-timing-function:cubic-bezier(.42,0,.35,1)}
  100% {transform:translate(-50%,-50%) rotate(360deg)}
}

/* slow, unsynchronised drift — the "liquid" half of liquid glass */
@keyframes auroraDrift{
  0%,100%{transform:translate(0,0) scale(1)}
  33%    {transform:translate(26px,-20px) scale(1.09)}
  66%    {transform:translate(-22px,16px) scale(.95)}
}

/* ─────────────────────────────  faq  ───────────────────────── */

.faq{--faq-col:30%}

.faq__grid{
  display:grid;
  grid-template-columns:minmax(0,var(--faq-col)) minmax(0,1fr);
}

/* same 30% split as the works block, so the two dividers line up down
   the page */
.faq__side{
  display:flex;flex-direction:column;justify-content:space-between;gap:40px;
  padding:clamp(26px,2.8vw,40px) clamp(20px,2.4vw,34px)
          clamp(32px,3.6vw,52px) clamp(20px,4vw,var(--pad));
}
.faq__label{
  margin:0;
  font-size:13.5px;font-weight:500;color:var(--ink-2);
  position:sticky;top:clamp(24px,3vw,44px);
}
.faq__side .learn{margin-top:0}

.faq__main{
  padding:clamp(26px,2.8vw,40px) clamp(24px,4vw,var(--pad))
          clamp(32px,3.6vw,56px) clamp(24px,3.4vw,48px);
  min-width:0;
}

.faq__title{
  margin:0 0 clamp(34px,4.4vw,64px);
  font-size:clamp(1.7rem,3vw,2.4rem);
  line-height:1.22;
  letter-spacing:-.018em;
  font-weight:360;
  color:var(--ink);
}

.faq__list{border-bottom:1px solid var(--line)}
.faq__item{border-top:1px solid var(--line)}

.faq__q{
  display:flex;align-items:center;gap:clamp(16px,3vw,40px);
  padding:clamp(19px,2.1vw,26px) 0;
  font-size:16px;
  font-weight:400;letter-spacing:-.008em;
  color:var(--ink);
  cursor:pointer;
  list-style:none;                       /* the marker, three ways */
  transition:color .16s ease;
}
.faq__q::-webkit-details-marker{display:none}
.faq__q::marker{content:""}
.faq__q:hover{color:var(--brand)}

/* + that loses its upright bar on open. Both bars are the same element
   rotated, so they cannot drift out of alignment. */
.faq__sign{
  position:relative;
  width:17px;height:17px;flex:none;
  margin-left:auto;
  color:var(--faint);
  transition:color .16s ease;
}
.faq__sign::before,
.faq__sign::after{
  content:"";position:absolute;
  top:50%;left:0;right:0;height:1.7px;margin-top:-.85px;
  border-radius:2px;background:currentColor;
  transition:transform .32s cubic-bezier(.2,.7,.25,1);
}
.faq__sign::after{transform:rotate(90deg)}
.faq__q:hover .faq__sign{color:var(--brand)}
.faq__item[open] .faq__sign{color:var(--ink)}
.faq__item[open] .faq__sign::after{transform:rotate(90deg) scaleX(0)}

.faq__a p{
  margin:0;
  padding:0 clamp(16px,3vw,40px) clamp(24px,2.6vw,32px) 0;
  max-width:52em;
  font-size:15px;line-height:1.62;
  color:var(--muted);
}

/* The slide only exists where a keyword height can be interpolated —
   guarding it keeps every other browser on the plain instant open
   instead of a row clamped to zero height. */
@supports (interpolate-size:allow-keywords){
  :root{interpolate-size:allow-keywords}

  .faq__item::details-content{
    block-size:0;
    overflow:clip;
    opacity:.4;
    transition:
      block-size .42s cubic-bezier(.2,.7,.25,1),
      opacity .3s ease,
      content-visibility .42s allow-discrete;
  }
  .faq__item[open]::details-content{block-size:auto;opacity:1}
}

/* ─────────────────────────────  motion  ────────────────────── */

@keyframes wordUp{
  from{opacity:0;transform:translate3d(0,110%,0)}
  to{opacity:1;transform:translate3d(0,0,0)}
}

@keyframes rise{
  from{opacity:0;transform:translateY(14px)}
  to{opacity:1;transform:none}
}
.rise{animation:rise .7s cubic-bezier(.2,.7,.25,1) both;animation-delay:var(--d,0ms)}

@keyframes vizIn{
  from{opacity:0;transform:translateY(22px) scale(.985)}
  to{opacity:1;transform:none}
}
.hero__visual .viz{animation:vizIn .9s .15s cubic-bezier(.2,.7,.25,1) both}

@keyframes drift{
  0%,100%{transform:translateY(0)}
  50%{transform:translateY(-9px)}
}
.lyr{animation:drift 7.5s var(--d,0s) ease-in-out infinite;will-change:transform}

@keyframes pulse{
  0%,100%{opacity:.85}
  50%{opacity:1}
}
.bloom{animation:pulse 4.5s ease-in-out infinite}

/* The always-on band sits well below the fold, where a load-time
   reveal would be over long before anyone reached it — so it is driven
   by scroll position instead. The words keep their own stagger by
   sliding each one's range along by --i. Browsers without scroll-driven
   animations never enter this block and get the finished state, so
   there is no hidden start state left stranded. */
@supports (animation-timeline:view()){
  .rv{
    animation:rise 1s linear both;
    animation-timeline:view();
    animation-range:entry 6% cover 26%;
  }
  .band__title .w,
  .faq__title .w{
    animation-delay:0s;
    animation-timeline:view();
    animation-range:entry calc(4% + var(--i,0)*4.5%) entry calc(54% + var(--i,0)*4.5%);
  }
}

/* Delays matter as much as durations here: without zeroing them the
   staggered reveals still hold their hidden start state for up to half
   a second, which is exactly what reduced-motion asks us not to do. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-delay:0ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    transition-delay:0ms !important;
  }

  /* The conveyor has no meaningful end state — the rule above would
     land every pod on its hidden last keyframe and empty the ring.
     Switching the animation off entirely is enough here: each pod
     carries its own station as a transform attribute, so the ring
     renders as the still frame of the running one. The pill keeps the
     source parked in the capsule, which is pod 9 by construction. */
  .pod,.lens{animation:none !important}
  .using__slot{animation:none !important;opacity:0}
  .using__slot[style*="--i:9"]{opacity:1}

  /* same problem in the four stills: stopped mid-loop the feed would
     empty, the theme bars would collapse and the fourth card would sit
     on its thinking dots forever. Pin the composed frame instead. */
  .src,.feed__row,.theme__fill,.ask__caret,.v,
  .qr__stars i,.wave i{
    animation:none !important;
  }
  .qr__stars i{background:#E8A93C}
  .wave i{transform:scaleY(.62)}
  /* the tiles carry marks now, so the lit one is shown by its ring */
  .src[style*="--i:0"]{
    border-color:rgba(125,98,255,.5);
    box-shadow:0 6px 16px -10px rgba(125,98,255,.85);
  }
  .theme__fill{transform:none}
  /* the three cards page through trades; stopped, they would stack all
     three on top of each other. Keep the first pass showing. */
  .v{opacity:0;transform:none}
  .v[style*="--i:0"]{opacity:1}

  /* duration is ignored on a scroll timeline, so the reveals have to be
     put back on the clock before the zeroing above can reach them */
  @supports (animation-timeline:view()){
    .rv,.band__title .w,.faq__title .w{animation-timeline:auto !important}
  }
}

/* ─────────────────────────────  responsive  ────────────────── */

@media (max-width:1024px){
  :root{--nav-h:76px}
  .nav__inner{gap:12px}
  .hub{height:48px}
  .menu__btn,.prog,.icon-btn,.nav__actions .btn{height:38px}
  .icon-btn{width:38px}
  .hero__inner{grid-template-columns:minmax(0,1fr)}
  .hero__visual{
    border-left:0;
    border-top:1px solid var(--line);
    padding:34px 20px 44px;
  }
  .hero__copy{padding-block:clamp(40px,7vw,64px)}
  .hero__title{font-size:clamp(2.4rem,7.4vw,3.6rem)}
  .viz{max-width:520px}

  .eyebrow{grid-column:1;padding-left:clamp(24px,4vw,var(--pad))}

  /* Stacked, the ring goes FIRST and the column centres under it — folk's
     phone treatment, and it is the right one: the graphic is what you
     land on, and the copy then reads as its caption instead of as a
     column that happens to have a picture stuck underneath. */
  .band__inner{
    grid-template-columns:minmax(0,1fr);
    gap:clamp(20px,4vw,36px);
    padding-inline:clamp(24px,4vw,var(--pad));
  }
  .band__visual{order:-1}
  .band__copy{text-align:center}
  .band__lead{margin-inline:auto}
  .band__actions{justify-content:center}
  /* .learn is inline-flex, so text-align on the column already centres
     it — no auto margins needed */
  .orbit{max-width:520px}

  /* ── the phone turns the whole orbit a quarter turn ──────────
     folk marks their orbit container rotate-90 / lg:rotate-0, and it is
     the right call: a ring that is wider than it is tall wastes a narrow
     screen, and turned, the capsule stands up — brand lens on top, the
     arriving source directly beneath it, the queue climbing from below.

     One transform on the stage does it, so the stations, the rail masks
     and the depth ladder all come along without a second set of numbers.
     Rotating about the lens centre keeps the lens put; the translate
     after it re-centres the now-landscape drawing inside a portrait
     viewBox. Note the transform list applies right to left — rotate
     first, then shift. */
  /* Turned on its side, the desktop ladder (three one way, one the
     other) reads as a lopsided picture. This set is symmetric about the
     capsule instead — two sources climbing in, two leaving — so the
     arrangement balances around the lens and the outermost pair is what
     the crop cuts in half. */
  .pod{animation-name:marchTurned}

  /* the aurora lives inside the svg, so the crop slices it and leaves a
     hard horizontal edge across the block. The band's own background
     already carries the colour here. */
  .aurora{display:none}

  /* Full bleed, so the crop lands on the screen edge. Inset by the
     column's padding, the same crop cut a clean vertical line through a
     soft glass circle with background still showing beyond it, and that
     reads as a seam rather than as an icon leaving the frame. */
  .band__visual{
    margin-inline:calc(clamp(24px,4vw,var(--pad)) * -1);
  }
  .orbit-wrap{max-width:none}

  /* The sheen is a crescent meant to sit on the top of the lens. The
     stage turns a quarter, so it ends up as a white sliver down the
     right-hand side; wind it back about its own centre. The gradients
     are deliberately left alone — their direction was never the fault. */
  .glass__sheen{
    transform:rotate(-90deg);
    transform-box:fill-box;transform-origin:center;
  }

  .stage{
    transform-box:view-box;
    transform-origin:258px 409px;
    transform:translate(92px,-157px) scale(1.17) rotate(90deg);
  }

  /* the drawing is 542 tall once turned, in a box that is still 818 —
     crop the empty bands rather than letterbox them */
  .orbit-wrap{
    aspect-ratio:700/580;
    overflow:hidden;
  }
  /* absolute, not a centred grid item: the row would size to the taller
     svg and align-items would then centre it inside that row rather than
     inside the cropped box, pinning it to the top */
  .orbit-wrap > .orbit{
    position:absolute;left:0;top:50%;
    width:100%;max-width:none;
    transform:translateY(-50%);
  }

  /* every mark counter-rotates about its own centre, or the logos lie on
     their side — the same trick folk uses on its orbit children */
  .pod image{
    transform:rotate(-90deg);
    transform-box:fill-box;transform-origin:center;
  }
  /* .mark carries its placement in a transform attribute, so the
     counter-rotation has to restate it rather than replace it */
  .mark{
    transform:translate(258px,409px) rotate(-90deg);
    transform-box:view-box;transform-origin:0 0;
  }

  /* the lens is HTML and sits outside the stage, so it is repositioned
     rather than rotated. Both figures are measured off the rendered
     disc, not derived — the crop and the stage shift both feed into
     them and arithmetic got it wrong twice. */
  /* the capsule stands up, so its clip box does too; the ring keeps the
     size it had, because the clip is what stops the spill now */
  .lens-clip{left:34.122%;top:3.765%;width:31.757%;height:92.389%}
  .lens{left:50%;top:20.742%;width:91.629%}

  /* the label rail folds into a row above the questions, same move the
     works block makes */
  .faq__grid{grid-template-columns:minmax(0,1fr)}
  .faq__side{
    border-right:0;
    border-bottom:1px solid var(--line);
    flex-direction:row;align-items:center;flex-wrap:wrap;gap:20px;
    padding-block:clamp(20px,2.8vw,30px);
  }
  .faq__label{position:static}
  .faq__side .learn{margin-left:auto}
  .faq__main{padding-inline:clamp(24px,4vw,var(--pad))}
}

/* Below ~560px the call-outs stop being legible, so they are dropped,
   the stack is over-scaled, and the now-empty right-hand strip of the
   viewBox is cropped away by the column. */
/* ── the hero on a phone ──────────────────────────────────────
   Stacked, the copy alone filled the first screen and the mockup began
   somewhere past the fold. Three things fix that: the artwork comes
   first, it is compressed, and everything under it is tightened.

   The stack is 480 units wide and 1223 tall — no scale turns that into
   a short block, so the plates are pulled toward the middle instead.
   Each carries its index, and the shift is (n − 2) × the amount the gap
   closes by, which holds the middle plate still and shuts the stack
   symmetrically around it. The container then crops the empty bands the
   move leaves at the top and bottom. */
@media (max-width:560px){
  .hero__inner{
    display:flex;flex-direction:column;align-items:stretch;
    padding:34px clamp(20px,4vw,var(--pad)) 52px;
  }
  /* .hero__copy dissolves so the artwork can slot between its children */
  .hero__copy{display:contents}
  .hero__actions{order:1}
  .hero__visual{order:2}
  .trusted{order:3}

  .hero__visual{
    position:relative;
    margin:30px 0 6px;
    border-top:0;
    aspect-ratio:21 / 20;   /* the compressed stack, cropped of its empty bands */
    overflow:hidden;
    padding:0;
    justify-content:flex-start;
  }
  .viz{
    position:absolute;
    left:0;top:-20.7%;
    width:100%;max-width:none;flex:none;margin:0;
  }
  .callouts{display:block}
  .cal .lead{display:none}
  .cal text{transform:translateX(-72px);font-size:33px}

  /* the plates were pulled together, so their labels have to travel the
     same distance or the two stop lining up */
  .cal{transform:translateY(calc((var(--n,2) - 2) * -82px))}

  /* drift animates the same transform; on the phone the plates hold
     their new station instead */
  .lyr{
    animation:none;
    transform:translateY(calc((var(--n,2) - 2) * -82px));
  }

  .hero__title{font-size:clamp(1.9rem,8.2vw,2.4rem)}
  .hero__lead{margin-top:20px;font-size:15px}
  .hero__actions{gap:8px;margin-top:26px;flex-wrap:nowrap}
  .hero__actions .btn{flex:1 1 0;min-width:0;padding:0 12px;font-size:14px}

  /* the trust row was four tall rows of two; eight small marks in two
     rows costs a third of the height */
  .trusted{margin-top:30px}
  .trusted__grid{
    grid-template-columns:repeat(4,minmax(0,1fr));
    gap:14px 10px;
    max-width:none;
  }
  .logo{font-size:12.5px;gap:5px}
  .logo img{width:15px;height:15px}
}

@media (max-width:600px){
  :root{--rail:14px}
  .brand__logo{height:26px}
  .nav__actions{gap:8px}
  /* three capsules do not fit beside the mark. The demo button is the one
     to drop here rather than the account: the same call to action sits in
     the hero a screen-inch below, while signing in has nowhere else to go. */
  .prog,.nav__actions .btn{display:none}
  .hub{padding:4px}
  .menu__btn{padding:0 14px 0 12px;gap:9px}
  .menu__sheet{padding-inline:20px}
  .sheet__big a{font-size:clamp(1.45rem,7vw,1.8rem)}
  .hatch{height:26px}
  .hero__title{font-size:clamp(2rem,8.8vw,2.9rem)}
  .hero__lead{font-size:15.5px}
  .hero__actions{gap:10px}
  .hero__actions .btn{width:100%}
  .trusted__grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:14px 10px;max-width:none}

  .band__title{font-size:clamp(2rem,8.6vw,2.8rem)}
  .band__lead{font-size:15.5px}

  /* One CTA on a phone, at its own width rather than stretched — a
     full-bleed bar plus a status pill plus a text link is three stacked
     rows of chrome under a graphic that is already doing the talking.
     The pill goes; the source it would have named is legible in the
     capsule anyway. */
  .band__actions{gap:10px}
  .using{display:none}

  .faq__title{font-size:clamp(1.85rem,8.4vw,2.6rem)}
  .faq__side{gap:14px}
  .faq__q{font-size:16px;gap:16px}
  .faq__a p{font-size:15.5px;padding-right:0}
}

/* ───────────────────────────  developers  ──────────────────── */

.dev__head{
  padding:clamp(56px,8vw,120px) clamp(20px,4vw,var(--pad)) clamp(26px,3vw,40px);
}
.eyebrow--plain{grid-column:auto;padding:0;color:var(--faint);font-size:14px}

.dev__title{
  margin:14px 0 0;
  font-size:clamp(1.7rem,3vw,2.4rem);
  line-height:1.22;
  letter-spacing:-.018em;
  font-weight:360;
  color:var(--ink);
}

/* soft sky plate — the top strip of the brand photograph, so the
   section shares its light with the footer */
.dev__plate{
  margin:0 clamp(24px,4vw,var(--pad));
  border-radius:4px;
  background:#E6DEF7 image-set(url('../img/sections/developers-plate.jpg') 1x) center / cover no-repeat;
  display:grid;place-items:center;
  padding:clamp(48px,8vw,120px) clamp(16px,4vw,48px);
  min-height:clamp(320px,42vw,620px);
}

/* wide enough that the longest command still clears the caret on one
   line — the type-on clips to the pane, so a tight fit would eat the
   tail of the command instead of wrapping it */
.dev__stack{width:100%;max-width:740px}

/* ── tabs ─────────────────────────────────────────────────────── */

.dev__tabs{position:relative;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:20px}
.dev__radio{position:absolute;opacity:0;pointer-events:none;width:0;height:0}

/* The travelling pill. It only exists once the script has measured the
   checked tab (.is-glide); until then — and with no JS at all — each
   tab paints its own background instead, so the selected state never
   depends on the script having run. */
.dev__glide{display:none}
.dev__tabs.is-glide .dev__glide{
  display:block;
  position:absolute;z-index:0;
  top:var(--gy,0);left:0;
  width:var(--gw,0);height:var(--gh,42px);
  border-radius:11px;
  background:var(--paper);
  box-shadow:0 1px 2px rgba(23,23,26,.06),0 8px 20px -12px rgba(23,23,26,.5);
  transform:translateX(var(--gx,0));
  transition:transform .42s cubic-bezier(.2,.8,.24,1),
             width .42s cubic-bezier(.2,.8,.24,1),
             top .42s cubic-bezier(.2,.8,.24,1);
}
.dev__tabs.is-glide .dev__tab{position:relative;z-index:1}
.dev__tabs.is-glide .dev__radio:checked + .dev__tab{background:none;box-shadow:none}

/* the unselected tabs sit well back — only the label fades, never the
   badge, so the row still reads as four products rather than one
   product and three ghosts */
.dev__tab{
  display:inline-flex;align-items:center;gap:9px;
  height:42px;padding:0 17px;
  border-radius:11px;
  font-size:15px;font-weight:500;letter-spacing:-.005em;
  color:rgba(23,23,26,.4);
  cursor:pointer;
  transition:background-color .16s ease,color .16s ease,box-shadow .16s ease;
}
.dev__tab svg{
  width:17px;height:17px;flex:none;
  opacity:.9;
  transition:opacity .16s ease;
}
.dev__tab .st{
  fill:none;stroke:#8C8A93;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;
}
.dev__tab:hover{color:rgba(23,23,26,.72)}

.dev__radio:checked + .dev__tab{
  background:var(--paper);
  color:var(--ink);
  box-shadow:0 1px 2px rgba(23,23,26,.06),0 8px 20px -12px rgba(23,23,26,.5);
}
.dev__radio:checked + .dev__tab svg{opacity:1}
.dev__radio:checked + .dev__tab .st{stroke:#4A4952}
.dev__radio:focus-visible + .dev__tab{outline:2px solid var(--brand);outline-offset:2px}

/* ── code card ────────────────────────────────────────────────── */

/* One plane, no chrome: the bar carries no fill and no rule under it,
   so the dots and the Copy control read as sitting on the same sheet
   as the command rather than in a title bar bolted above it. */
.term{
  background:var(--paper);
  border-radius:16px;
  overflow:hidden;
  box-shadow:0 1px 2px rgba(23,23,26,.05),0 30px 60px -34px rgba(40,30,90,.5);
}
.term__bar{
  display:flex;align-items:center;justify-content:space-between;
  height:56px;padding:0 18px 0 22px;
}
/* the three lights step down in tone rather than sitting flat */
.term__dots{display:inline-flex;gap:8px}
.term__dots i{width:11px;height:11px;border-radius:50%;background:#B4B2B8}
.term__dots i:first-child{background:#828088}
.term__dots i:nth-child(2){background:#A09EA6}

.term__copy{
  display:inline-flex;align-items:center;gap:9px;
  height:32px;padding:0 4px;
  border:0;border-radius:8px;background:none;
  font:inherit;font-size:15px;font-weight:500;
  color:var(--faint);cursor:pointer;
  transition:color .15s ease;
}
.term__copy svg{
  width:16px;height:16px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;
}
.term__copy:hover{color:var(--ink-2)}
.term__copy.is-done{color:var(--brand)}

.term__body{padding:6px 26px 34px}

.term__pane{
  display:none;
  margin:0;
  font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Consolas,monospace;
  font-size:14.5px;line-height:1.75;
  color:#22222A;
  white-space:pre-wrap;
  word-break:break-word;
  padding-left:48px;
  position:relative;
}
.term__chev{
  position:absolute;left:8px;top:0;
  font-size:16px;
  color:#C4C2CC;
  font-family:var(--font-sans);
}

/* ── the pane switch ──────────────────────────────────────────
   Swapping panes is a display change, so the animation restarts by
   itself every time a tab is picked — no class to add, nothing to
   reset. The caret is the pane's own ::after: it waits out the
   type-on, then blinks. */

.term__pane{animation:paneIn .34s cubic-bezier(.2,.7,.25,1) both}

.term__pane::after{
  content:"";
  display:inline-block;vertical-align:-2px;
  width:8px;height:16px;margin-left:3px;
  background:var(--brand);
  opacity:.9;
  animation:caret 1.1s steps(1) infinite;
}

@keyframes paneIn{
  from{opacity:0;transform:translateY(5px)}
  to{opacity:1;transform:none}
}
@keyframes caret{0%,50%{opacity:.9}50.01%,100%{opacity:0}}

/* Character stepping only where the command is certain to hold one
   line — the sweep runs left to right, which would read as nonsense
   across a wrap. Below this width the pane just rises in. */
@media (min-width:960px){
  .term__pane{
    white-space:pre;
    /* the box is border-box like everything else, so the width has to
       carry the gutter as well as the command and its caret */
    width:calc(var(--n,60)*1ch + 11px + 48px);
    max-width:100%;
    overflow:hidden;
    animation:type calc(var(--n,60)*14ms) steps(var(--n,60),end) both;
  }
  .term__pane::after{animation-delay:calc(var(--n,60)*14ms)}
  @keyframes type{from{width:48px}}
}

@media (prefers-reduced-motion:reduce){
  .dev__tabs.is-glide .dev__glide{transition:none}
  .term__pane::after{animation:none;opacity:.9}
}

/* map the checked radio to its pane */
.dev:has(#dev-mcp:checked)   .term__pane[data-pane=dev-mcp],
.dev:has(#dev-codex:checked) .term__pane[data-pane=dev-codex],
.dev:has(#dev-sdk:checked)   .term__pane[data-pane=dev-sdk],
.dev:has(#dev-cli:checked)   .term__pane[data-pane=dev-cli]{display:block}

/* no :has() — show the first pane so the card is never empty */
@supports not selector(:has(*)){
  .term__pane[data-pane=dev-mcp]{display:block}
  .dev__tabs{display:none}
}

.dev__actions{
  display:flex;align-items:center;flex-wrap:wrap;gap:14px 26px;
  padding:clamp(28px,3.4vw,46px) clamp(24px,4vw,var(--pad)) clamp(36px,4.4vw,60px);
}
.dev__link{
  font-size:15px;font-weight:500;color:var(--ink-2);
  transition:color .15s ease;
}
.dev__link:hover{color:var(--brand)}

.arr--right{width:12px;height:10px}

/* ──────────────────────  ai agent · ru only  ─────────────────
   The Russian stand-in for the developer block. Same slot, same
   two-column shape as the always-on band, but the visual is a
   thread instead of a diagram: the argument is that the answer
   reads like a person wrote it, and only a real message shows
   that. `hidden` has to win over the layout below it, hence the
   guard — a section set to grid would otherwise ignore it. */
[hidden]{display:none !important}

.agent__head{
  padding:clamp(44px,5.4vw,80px) clamp(24px,4vw,var(--pad)) clamp(26px,3vw,42px);
  text-align:center;
}
/* two-tone: the promise in ink, what it costs you to believe it let
   back a step — the same trick the reference uses to stop a
   two-line headline reading as one long shout */
.agent__title{
  margin:0;
  font-family:var(--font-display);
  font-size:clamp(1.9rem,3.5vw,2.85rem);
  line-height:1.12;
  letter-spacing:-.035em;
  font-weight:900;
  color:var(--ink);
}
.agent__title span{display:block;color:var(--faint)}
.agent__lead{
  margin:clamp(16px,1.8vw,24px) auto 0;
  max-width:46ch;
  font-size:clamp(15px,1.2vw,17px);
  line-height:1.6;
  color:var(--muted);
}

/* ── the agent window ───────────────────────────────────────────
   An app window on the plate rather than a chat card: the point of
   this block is that the agent answers out of your own data, and
   only real product furniture — a query line, the themes it counted,
   a quote you can check — says that. */

.awin{
  width:100%;max-width:660px;
  border-radius:16px;
  background:var(--paper);
  border:1px solid var(--line);
  overflow:hidden;
  box-shadow:0 1px 2px rgba(23,23,26,.05),0 34px 64px -34px rgba(40,30,90,.5);
}

.awin__bar{
  display:flex;align-items:center;gap:14px;
  padding:0 16px;height:44px;
  border-bottom:1px solid var(--line);
  background:linear-gradient(180deg,#FBFAFC 0%,#F7F6FA 100%);
}
.awin__crumb{
  font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Consolas,monospace;
  font-size:11.5px;letter-spacing:-.01em;
  color:var(--faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

/* the question, typed in */
/* ── the turns ──────────────────────────────────────────────
   Asymmetric on purpose, the way a chat with an assistant is: what
   you said is a pill on the right, what the agent said is plain text
   under its mark on the left. That asymmetry is most of what makes
   this read as a conversation rather than as search results. */
.awin__you{
  align-self:flex-end;
  max-width:80%;
  margin:0;
  padding:9px 14px;
  border-radius:16px 16px 4px 16px;
  background:var(--brand-soft);
  border:1px solid color-mix(in srgb,var(--brand) 18%,transparent);
  font-size:13.5px;line-height:1.45;letter-spacing:-.012em;
  color:var(--ink);
}

.awin__turn{display:flex;align-items:center;gap:8px;margin-top:2px}
.awin__mark{
  width:20px;height:20px;flex:none;
  border-radius:50%;
  background:conic-gradient(from 210deg,var(--brand),#B9A9FF,var(--brand));
}
.awin__by{
  font-size:11px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint);
}

/* the answer itself: no bubble, so it reads as the assistant writing
   into the page rather than passing a note */
.awin__say{
  margin:0;
  font-size:14.5px;line-height:1.55;letter-spacing:-.012em;
  color:var(--ink);
}
/* the block cursor only exists while the words are still arriving */
.awin__say.is-writing::after{
  content:;
  display:inline-block;vertical-align:-2px;
  width:7px;height:15px;margin-left:3px;
  background:var(--ink);
  animation:awinCaret 1s steps(1) infinite;
}

/* Plain white, and a fixed height: the answer arriving must not push
   the window open — the pane scrolls to keep the newest line in sight
   the way the app itself would. The bar is hidden because the scroll
   is driven, not asked for, but the pane stays scrollable by hand. */
.awin__body{
  display:flex;flex-direction:column;gap:14px;
  padding:16px 12px 20px 18px;
  height:clamp(268px,30vh,308px);
  overflow-y:auto;
  scroll-behavior:smooth;
  overscroll-behavior:contain;
  background:var(--paper);

  /* The bar is left on and styled slim: it is the thing that tells the
     reader this pane is a running conversation and not a static card.
     `stable` reserves its track from the start, so nothing reflows the
     moment the answer grows past the fold. */
  scrollbar-gutter:stable;
  scrollbar-width:thin;
  scrollbar-color:rgba(23,23,26,.22) transparent;
}
/* the same bar for engines that predate the standard properties —
   where both are understood, the standard ones above win */
.awin__body::-webkit-scrollbar{width:7px}
.awin__body::-webkit-scrollbar-track{background:transparent}
.awin__body::-webkit-scrollbar-thumb{
  background:rgba(23,23,26,.22);
  border-radius:999px;
}
.awin__body:hover{scrollbar-color:rgba(23,23,26,.34) transparent}
.awin__body::-webkit-scrollbar-thumb:hover{background:rgba(23,23,26,.34)}
/* A flex column of fixed height squeezes its children to fit before it
   will admit to overflowing — which is why the last row was being
   crushed under the quote and the pane reported nothing to scroll.
   Holding every line at its own height is what turns the overflow into
   a scroll. */
.awin__body > *{flex:0 0 auto}


/* ── the closing proof strip ──────────────────────────────────
   These three classes were in the markup with no rules behind them,
   which is why the figure and its label ran together as one word. */
.lead__proof{
  display:flex;align-items:center;gap:clamp(14px,2.4vw,22px);
  margin-top:18px;
  padding:12px 18px;
  border-radius:var(--r-lg);
  background:var(--bg-2);
}
.lead__brand{
  flex:none;
  font-size:11px;font-weight:600;letter-spacing:.14em;
  color:var(--muted);
}
.lead__delta{
  display:flex;flex-direction:column;gap:1px;
  min-width:0;
}
.lead__delta b{
  font-size:15px;font-weight:600;letter-spacing:-.012em;
  color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.lead__delta > span{
  font-size:13px;line-height:1.4;
  color:var(--muted);
}
.awin__body:hover{scrollbar-color:rgba(23,23,26,.34) transparent}
.awin__body::-webkit-scrollbar-thumb:hover{background:rgba(23,23,26,.34)}

/* A flex column of fixed height squeezes its children to fit before it
   will admit to overflowing — which is why a row was being crushed
   under the quote and the pane reported nothing to scroll. Holding
   every line at its own height is what turns overflow into a scroll. */
.awin__body > *{flex:0 0 auto}

/* the count the answer rests on, set as a table rather than as prose */
.awin__table{display:grid;gap:1px;background:var(--line);border-radius:10px;overflow:hidden}
.awin__head,.awin__row{
  display:grid;
  grid-template-columns:minmax(0,1fr) 52px 64px;
  align-items:center;gap:10px;
  padding:9px 12px;
  background:var(--paper);
}
.awin__head{
  padding-block:7px;
  background:#F4F2ED;
  font-size:10px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint);
}
.awin__head span:not(:first-child),
.awin__row span:not(:first-child),
.awin__row b{text-align:right;font-variant-numeric:tabular-nums}
.awin__row{font-size:13px;letter-spacing:-.012em;color:var(--ink)}
.awin__row span:first-child{display:flex;flex-direction:column;gap:2px;min-width:0}
.awin__row i{
  font-style:normal;font-size:10.5px;letter-spacing:-.008em;
  color:var(--faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.awin__row b{font-size:12.5px;font-weight:600}
.awin__row .up{color:#D03B3B}      /* more complaints is worse, so red */
.awin__row .down{color:#0F8F66}

.awin__quote{
  margin:0;
  padding:11px 13px;
  border-radius:10px;
  border-left:2px solid var(--brand);
  background:#F7F5F1;              /* the pane is white, so the quote
                                      needs a ground of its own */
  font-size:12.5px;line-height:1.5;letter-spacing:-.012em;
  color:var(--ink-2);
}
.awin__quote cite{
  display:block;margin-top:5px;
  font-style:normal;font-size:10.5px;
  color:var(--faint);
}

/* the composer is a still, but it is what makes the pane read as
   something you talk to rather than something you searched */
.awin__ask{
  display:flex;align-items:center;gap:10px;
  padding:11px 12px 11px 16px;
  border-top:1px solid var(--line);
  background:var(--paper);
}
.awin__field{
  flex:1;min-width:0;
  font-size:13.5px;letter-spacing:-.012em;
  color:var(--faint);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.awin__send{
  display:grid;place-items:center;
  width:30px;height:30px;flex:none;
  border-radius:50%;
  background:var(--ink);
}
.awin__send svg{
  width:16px;height:16px;
  fill:none;stroke:#fff;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;
}

/* Each line is out of flow until its turn — expressed as the absence
   of .is-in, so every line keeps the display its own rule gives it.
   The resting state is declared here and the fade only fills forwards,
   so a frame where the animation never ran shows the line rather than
   a hole. */
.awin [data-step]:not(.is-in){display:none}
.awin [data-step].is-in{
  opacity:1;
  animation:awinIn .3s cubic-bezier(.2,.7,.25,1) forwards;
}

@keyframes awinIn{
  from{opacity:0;transform:translateY(7px)}
  to  {opacity:1;transform:none}
}
@keyframes awinCaret{0%,50%{opacity:1}50.01%,100%{opacity:0}}

@media (max-width:560px){
  .awin{max-width:none}
  .awin__crumb{font-size:10.5px}
  .awin__row{grid-template-columns:minmax(0,1fr) 42px 56px}
  .awin__body{height:clamp(260px,72vw,320px)}
}
@media (prefers-reduced-motion:reduce){
  .awin__say.is-writing::after{animation:none}
}

/* ── step 3 · the themes those labels collapse into ─────────── */

.themes__cap{
  position:absolute;left:0;right:0;top:clamp(14px,1.6vw,20px);
  margin:0;
  text-align:center;
  font-size:10.5px;font-weight:600;letter-spacing:.04em;
  color:var(--faint);
}
@keyframes themeGlow{
  0%,100% {opacity:.72}
  30%     {opacity:1}
}

/* ── step 4 · where it lands ────────────────────────────────── */

.dest{
  --cycle:7.5s;
  position:absolute;inset:0;
  display:grid;align-content:center;gap:10px;
  padding:0 clamp(12px,1.5vw,18px);
  margin:0;list-style:none;
}
.dest__row{
  display:flex;align-items:center;gap:10px;
  height:46px;padding:0 12px;
  border-radius:12px;
  background:var(--paper);border:1px solid #E8E7E5;
  font-size:13.5px;font-weight:500;letter-spacing:-.012em;
  color:var(--ink);
  animation:destOn var(--cycle) ease-in-out infinite both;
  animation-delay:calc(var(--i,0) * var(--cycle) / 3);
}
/* --i is set once on the row and inherits, so the tile and the pill
   cannot fall out of step with the row they belong to */
.dest__tile{
  display:grid;place-items:center;
  width:28px;height:28px;flex:none;
  border-radius:8px;
  background:var(--chip);
  color:#8A8880;
  animation:destTile var(--cycle) ease-in-out infinite both;
  animation-delay:calc(var(--i,0) * var(--cycle) / 3);
}
.dest__tile svg{
  width:17px;height:17px;
  fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;
}
.dest__state{
  margin-left:auto;
  padding:4px 9px;border-radius:999px;
  background:var(--chip);
  font-size:11px;font-weight:600;letter-spacing:-.008em;
  color:var(--muted);
  white-space:nowrap;
  animation:destPill var(--cycle) ease-in-out infinite both;
  animation-delay:calc(var(--i,0) * var(--cycle) / 3);
}

/* one row at a time takes its delivery: the tile picks up the brand,
   the pill fills, and the whole row lifts a hair off the plate */
@keyframes destOn{
  0%,4%    {border-color:#E8E7E5;transform:none;
            box-shadow:0 1px 2px rgba(23,23,26,.04)}
  12%,30%  {border-color:rgba(125,98,255,.4);transform:translateY(-2px);
            box-shadow:0 10px 20px -14px rgba(125,98,255,.9)}
  40%,100% {border-color:#E8E7E5;transform:none;
            box-shadow:0 1px 2px rgba(23,23,26,.04)}
}
@keyframes destTile{
  0%,4%    {background:var(--chip);color:#8A8880}
  12%,30%  {background:var(--brand-soft);color:var(--brand)}
  40%,100% {background:var(--chip);color:#8A8880}
}
@keyframes destPill{
  0%,4%    {background:var(--chip);color:var(--muted)}
  12%,30%  {background:var(--brand);color:#fff}
  40%,100% {background:var(--chip);color:var(--muted)}
}

/* ════════════════════════════════════════════════════════════════
   FOOTER ON A PHONE

   The legal row is a single flex line built for a desktop width. On
   a 390px screen it breaks into four ragged lines: the copyright,
   the language chip stranded on its own, then the links wrapping
   with "DPA" alone at the bottom. Below 640px it becomes a small
   grid instead — links across the top, copyright and the language
   chip sharing the line under them.

   Written at `.foot .foot__legal` rather than `.foot__legal` on
   purpose: the base rules currently live in the restore*.css files,
   which load after this one. The extra element keeps these winning
   whichever file the footer ends up in.
   ════════════════════════════════════════════════════════════════ */
@media (max-width:640px){
  .foot .foot__legal{
    display:grid;
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:
      "links links"
      "copy  lang";
    align-items:center;
    gap:18px 14px;
    padding:20px clamp(20px,5vw,28px) 26px;
  }
  .foot .foot__legal ul{grid-area:links;gap:10px 18px;font-size:13px}
  .foot .foot__legal p{grid-area:copy;font-size:12.5px;line-height:1.4}
  .foot .foot__legal .lang{grid-area:lang;justify-self:end}

  /* a 44px target: the chip is 26px tall, which is fine to look at
     and too small to hit */
  .foot .foot__legal .lang{position:relative}
  .foot .foot__legal .lang::after{
    content:'';position:absolute;inset:-9px -6px;
  }

  /* the links are the one thing here anyone taps */
  .foot .foot__legal a{
    display:inline-block;padding:5px 0;
    color:var(--ink-2);
  }
}

/* ── the dot field ───────────────────────────────────────────────
   Seventeen rows at a 21px pitch is 357px of mostly-empty field on a
   phone — taller than the footer above it. The script drops both the
   row count and the pitch below 640px; this only trims the padding
   around it to match, and hands the canvas its own gestures.

   touch-action:none is what makes a dragged finger draw in the field
   instead of scrolling the page past it. It is safe here and nowhere
   else on the page: the field is the last thing in the document, so
   there is nothing below it a visitor could be prevented from
   reaching. */
.foot .mesh{touch-action:none;-webkit-user-select:none;user-select:none}

@media (max-width:640px){
  .foot .foot__mesh{padding:14px clamp(16px,4vw,24px) 18px}
}

/* ════════════════════════════════════════════════════════════════
   ФИНАЛЬНЫЙ БЛОК: КНОПКИ В КОЛОНКЕ

   Ниже 560px строка кнопок должна становиться колонкой во всю ширину.
   restored.css так и говорит — `flex-direction:column; align-items:
   stretch` в медиа-запросе. Но restore-2.css подключается последним и
   несёт базовое `.finale__actions{align-items:flex-end}` без медиа:
   специфичность у них одинаковая, медиа-запрос её не повышает, и
   выигрывает то правило, что идёт позже по порядку файлов.

   В итоге flex-direction остаётся column, а выравнивание становится
   flex-end. Чёрная кнопка растягивается (у неё width:100%), пилюля
   мессенджеров держит свою clamp(296px,28vw,352px) — и на 559px это
   470px против 296px, обе прижаты к правому краю центрированного
   блока. Это и есть «поплывшая» вёрстка.

   Пишется здесь, а не в restore*.css, по двум причинам: те файлы
   сейчас переписываются параллельно, и лишний класс в селекторе
   (0,2,0 против 0,1,0) оставит правило рабочим в любом порядке, в
   каком они в итоге сольются в main.css.
   ════════════════════════════════════════════════════════════════ */
@media (max-width:560px){
  .finale .finale__actions{align-items:stretch}

  /* пилюля перестаёт быть фиксированной и берёт ширину колонки —
     половинки внутри считаются в процентах, так что растягивание им
     ничего не ломает */
  .finale .finale__actions .splitwrap,
  .finale .finale__actions .split{width:100%}
}

/* ── фиолетовое кольцо вокруг пилюли мессенджеров ────────────────
   У .split осталась рамка в 1px, которой потом задали
   border-color:transparent, а фон #6E33E8 по умолчанию красится под
   рамку (background-clip:border-box). Прозрачная граница показывает
   этот фон наружу — вокруг пилюли идёт фиолетовый ободок в пиксель,
   которого в макете нет: половинки должны доходить до самого края.

   Фон под ними всё равно нужен как подложка, поэтому убирается
   рамка, а не фон. Границы у .split нет ни в одном состоянии, так
   что снять её можно целиком; box-sizing:border-box глобальный,
   высота пилюли от этого не меняется. */
.splitwrap .split,
.lead .split{border-width:0}


/* ── Сброс нативного вида кнопок, которые НЕ .btn ──────────────────────────
   В <head> страниц сброс написан как `button:where(.btn){…border:0…}`, поэтому
   кнопкам с другими классами доставались фон и рамка браузера: у .learn это
   выглядело серой плашкой вокруг ссылки, у .split__half — лишней обводкой
   внутри пилюли MAX/Telegram.
   Селектор нулевой специфичности: собственные правила (например разделитель
   `.split__half + .split__half{border-left:…}`) продолжают действовать. */
button:where(.learn, .split__half, .ag__q, .menu__btn, .lead__x, .lead__back, .qnav) {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  appearance: none;
  -webkit-appearance: none;
}
