/* One stylesheet, no framework, no fonts fetched from anywhere.
   A landing page whose job is to load fast on a phone should not
   open five connections before it draws a word. */
:root{
  --bg:#0B0E1A; --bg2:#131A2E; --panel:#161D33; --line:#26304d;
  --text:#F2F5FA; --dim:#AEB6C6; --teal:#00E5C3; --violet:#9B7BFF;
  --amber:#FFC061;
  --max:1120px;
}
*{box-sizing:border-box}
html{
  scroll-behavior:smooth;
  /* Safari on iOS enlarges text when a phone is turned sideways,
     which re-flows a layout that was correct into one that is not. */
  -webkit-text-size-adjust:100%;
  text-size-adjust:100%;
}
/* Every jump link lands under a sticky header otherwise: the reader
   asks for "Pricing" and gets the row above its own heading. One
   line, and it is the header's height at each breakpoint. */
section,[id]{scroll-margin-top:64px}
@media(min-width:900px){section,[id]{scroll-margin-top:70px}}
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{animation-duration:.01ms !important;transition-duration:.01ms !important}
}
body{
  margin:0; background:var(--bg); color:var(--text);
  font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,
    "Helvetica Neue",Arial,sans-serif;
  -webkit-font-smoothing:antialiased;
  /* A long unbroken token — a URL in the legal pages, a compound noun
     in German — is the one thing that can still push a 320px column
     sideways after every box is right. */
  overflow-wrap:break-word;
}
a{color:var(--teal)}
/* Keyboard focus was inheriting the browser default, which is
   invisible against this background on some platforms. */
a:focus-visible,button:focus-visible,summary:focus-visible{
  outline:2px solid var(--teal);outline-offset:2px;border-radius:4px}
/* 16px each side on a phone, 20 above it. On a 320px screen the
   difference is 8px of column — a whole word per line. The safe-area
   inset keeps the text off the rounded corners and the camera notch
   when a phone is held sideways. */
.wrap{max-width:var(--max);margin:0 auto;
  padding-left:max(16px,env(safe-area-inset-left));
  padding-right:max(16px,env(safe-area-inset-right))}
@media(min-width:560px){.wrap{padding-left:max(20px,
  env(safe-area-inset-left));padding-right:max(20px,
  env(safe-area-inset-right))}}
header{position:sticky;top:0;z-index:20;background:rgba(11,14,26,.86);
  backdrop-filter:saturate(140%) blur(10px);border-bottom:1px solid var(--line)}
/* Phone first. Five section links, a brand and a language picker in a
   row put 630px of header into a 360px screen and scrolled the whole
   PAGE sideways — measured, not guessed: tools/mobile_audit.py. So on
   a phone the sections live behind a button, and the row carries only
   what a reader needs to see at a glance: who this is, what language
   they are reading, and the way to everything else. */
header .wrap{display:flex;align-items:center;gap:8px;min-height:56px}
.brand{display:flex;align-items:center;gap:7px;font-weight:700;
  font-size:16px;text-decoration:none;color:var(--text);
  margin-right:auto;min-height:44px;white-space:nowrap}
.brand svg{display:block;flex:none;width:22px;height:22px}
/* A 280px foldable cover screen is the narrowest thing this site is
   asked to draw, and the brand was 3px more than it had. Everything
   above that gets the full size. */
@media(min-width:340px){
  .brand{gap:9px;font-size:18px}
  .brand svg{width:26px;height:26px}
}
/* The picker first, the menu button hard against the edge: that is
   where a thumb reaches for it, and it is the control a reader
   presses most. Ordering only — everything else about the picker is
   in its own section below. */
.menu{order:3}
.menu>summary{
  list-style:none;cursor:pointer;color:var(--dim);
  display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;margin-right:-8px;border-radius:8px;
}
.menu>summary::-webkit-details-marker{display:none}
.menu>summary:hover{color:var(--text);background:rgba(255,255,255,.06)}
.menu[open]>summary{color:var(--text);background:rgba(255,255,255,.08)}
.burger .cross{display:none}
.menu[open] .burger .bars{display:none}
.menu[open] .burger .cross{display:block}
/* The panel hangs off <header>, which is sticky and therefore the
   containing block, so it spans the full width however narrow the
   screen gets and never has to be measured against the button.

   The two display rules are not belt and braces. A bare
   `.menu>nav{display:flex}` OVERRIDES the UA rule that hides a closed
   <details>, so the panel stood permanently open across the top of
   every phone page — and its links measured 4px wide, one character
   per line. Open and closed are stated separately so that the
   desktop rule below can turn the panel back on without also
   deciding whether the disclosure is open. */
.menu>nav{
  position:absolute;left:0;right:0;top:100%;
  flex-direction:column;
  background:#0F1424;border-bottom:1px solid var(--line);
  padding:4px 20px 10px;box-shadow:0 16px 34px rgba(0,0,0,.45);
}
.menu:not([open])>nav{display:none}
.menu[open]>nav{display:flex}
nav a{color:var(--dim);text-decoration:none;font-size:16px;
  padding:13px 2px;border-bottom:1px solid rgba(255,255,255,.06)}
nav a:last-child{border-bottom:0}
nav a:hover{color:var(--text)}
.btn{display:inline-block;background:var(--teal);color:#04121a;
  font-weight:700;padding:12px 20px;border-radius:8px;text-decoration:none;
  font-size:15px}
.btn:hover{filter:brightness(1.07)}
.btn.ghost{background:transparent;color:var(--text);
  border:1px solid var(--line);font-weight:600}
h1{font-size:clamp(32px,5.4vw,56px);line-height:1.08;margin:0 0 18px;
  letter-spacing:-.02em}
h2{font-size:clamp(24px,3.4vw,34px);line-height:1.2;margin:0 0 14px;
  letter-spacing:-.01em}
h3{font-size:18px;margin:0 0 8px}
p{margin:0 0 16px;color:var(--dim)}
.lead{font-size:clamp(17px,2.1vw,20px);color:var(--dim);max-width:60ch}
/* The free month, said once and said loudly. It is the only amber on
   the page on purpose: teal is what the product IS and amber is what
   the reader GETS, and a second amber anywhere would spend the
   distinction. 11:1 against the page ground, so it is emphasis a
   reader with low vision also receives. */
.free{color:var(--amber);font-weight:700}
section{padding:46px 0;border-bottom:1px solid var(--line)}
.hero{padding:40px 0 44px;background:
  radial-gradient(900px 420px at 15% 0%,rgba(0,229,195,.12),transparent 62%),
  radial-gradient(760px 420px at 92% 12%,rgba(155,123,255,.12),transparent 60%)}
.hero .cta{display:flex;gap:12px;flex-wrap:wrap;margin-top:8px}
/* The last section: the offer and the one button, centred, after the
   reader has been through every argument the page makes. */
.closing{text-align:center}
.closing .cta{display:flex;justify-content:center;margin-top:14px}
.badge{display:inline-flex;align-items:center;gap:8px;font-size:13px;
  color:var(--teal);border:1px solid rgba(0,229,195,.34);
  background:rgba(0,229,195,.07);padding:6px 12px;border-radius:999px;
  margin-bottom:20px}
/* The hero holds two things: the words and the product.

   ON A PHONE THEY STACK, WORDS FIRST. That is also the source order,
   so nothing here reorders anything — the headline and the two
   buttons are what a small screen opens on, and the illustration
   follows them instead of pushing them under the fold.

   Side by side only from 900px, which is where the copy still has a
   comfortable measure after giving half the row away. Below it the
   two columns would each be too narrow to be worth having. */
.hero-grid{display:grid;gap:30px;align-items:center}
.hero-art{width:100%;max-width:520px;justify-self:center}
/* The drawing carries its own transparency and sits on the hero's
   gradient, so it gets no background, no border and no radius: the
   moment it has a box, the box is visible around a picture that was
   cut out precisely so it would not have one. */
.hero-art img{display:block;width:100%;height:auto}
/* A single sweep of light across the hero, once, when the page opens.

   IT DOES NOT EXIST ON A PHONE. Not hidden — absent: `display:none`
   means no element to paint and no animation to run on the device with
   the least to spare, and the effect was asked for on desktop only.

   It runs ONCE. Anything that keeps moving has to offer a way to stop
   it (WCAG 2.2.2) and would compete with the headline for the rest of
   the visit; two seconds and gone competes with nothing. The global
   reduced-motion rule near the top of this file already collapses
   every animation, and the rule below removes this one outright, so a
   reader who has asked for stillness gets a still page.

   It passes BEHIND the words and behind the drawing — the hero grid
   sits a layer above — so it reads as light in the room rather than a
   line drawn over the copy. The illustration is a cut-out, so the
   sweep glows through the gaps around the laptop and the people. */
.hero{position:relative;overflow:hidden}
.hero-grid{position:relative;z-index:1}
.hero-pulse{display:none}
@media(min-width:900px){
  .hero-pulse{
    display:block;position:absolute;z-index:0;pointer-events:none;
    /* Wider than any screen and parked well off the left edge, in
       viewport units so it starts outside the window whatever the
       window is. `.hero` clips it, which is what keeps a 100vw box
       positioned past the right edge from adding a scrollbar. */
    top:50%;right:-5vw;width:100vw;height:170px;
    margin-top:-85px;

    /* THE BRIGHT CORE IS AT THE FAR END, not the middle. A streak of
       light has a head and a tail behind it, and putting the peak in
       the centre had a second consequence worth recording: half the
       gradient led the core, so the beam only entered the screen at
       the halfway point of its travel and faded out again before it
       reached the drawing. Nobody would have seen it arrive. */
    /* THE BOX PAINTS NOTHING. It is 170px tall now, to hold the
       waveform, so a background on it is a 170px slab of gradient —
       which is exactly what it drew before this was a variable: a
       hard-edged teal rectangle where a line of light belonged. The
       core and its halo take the gradient from here, so there is one
       source of truth and they cannot drift apart. */
    --beam:linear-gradient(90deg,
      transparent 0%,
      rgba(155,123,255,.12) 38%,
      rgba(155,123,255,.26) 62%,
      rgba(0,229,195,.48) 78%,
      rgba(0,229,195,.88) 88%,
      #E9FFFA 92%,
      rgba(0,229,195,.5) 96%,
      transparent 100%);
    background:none;
    animation:hero-sweep .9s cubic-bezier(.16,.62,.24,1) .12s 1 both;
  }
  /* The glow is the same gradient again, taller and blurred. One
     source of truth for the colour: the core and its halo cannot
     drift apart. */
  /* The axis: the bright horizontal line the streak rides. It is a
     child rather than the box's own background because the box is now
     tall enough to hold the waveform, and a background would have
     filled all 170px of it. */
  .hero-pulse::after{content:"";position:absolute;left:0;right:0;
    top:50%;height:2px;margin-top:-1px;background-image:var(--beam)}
  /* And its halo: the same gradient again, taller and blurred, so the
     core and its glow cannot drift apart. */
  .hero-pulse::before{content:"";position:absolute;left:0;right:0;
    top:50%;height:22px;margin-top:-11px;
    background-image:var(--beam);filter:blur(11px);opacity:.55}
  /* The meter. It rides the axis at the head end of the sweep, and it
     is masked so the tail dissolves rather than ending on a hard edge
     wherever the last bar happens to fall. */
  .hero-bars{position:absolute;top:0;right:3%;width:54%;height:100%;
    display:flex;align-items:center;justify-content:space-between;
    gap:2px;
    -webkit-mask-image:linear-gradient(90deg,transparent,
      rgba(0,0,0,.5) 34%,#000 72%);
    mask-image:linear-gradient(90deg,transparent,
      rgba(0,0,0,.5) 34%,#000 72%);
    transform-origin:50% 50%;
    /* The level over TIME, laid over the level along the streak: in
       already loud, a surge as it crosses the middle of the screen,
       then down to almost nothing. 30% is solved, not eyeballed —
       with this geometry and this easing curve that is where the
       meter is actually at mid-screen, because the curve is
       front-loaded and by the middle of the TIMELINE the streak has
       already covered 91% of its journey. Change the curve and this
       number moves. */
    animation:hero-level .9s cubic-bezier(.3,.1,.2,1) .12s 1 both}
  /* Every bar breathes on its own clock — its own phase AND its own
     rate. Bars rising and falling together are a cartoon equaliser;
     what a microphone drives never lines up. */
  .hero-bars i{display:block;flex:1 1 0;min-width:0;height:var(--h);
    border-radius:2px;transform-origin:50% 50%;
    /* Brightest at the axis, falling away above and below it, the way
       a bar is lit from the line it stands on. */
    background:linear-gradient(180deg,
      rgba(0,229,195,.10),#5BF3DC 20%,#EAFFFB 50%,#5BF3DC 80%,
      rgba(0,229,195,.10));
    /* EIGHT, NOT `infinite`. The sweep is over in about a second and
       the box is then hidden — but a hidden element's animations keep
       ticking, and this is 52 of them running for the rest of the
       visit for nobody. Eight half-periods is 1.12s at the fastest bar
       and 2.22s at the slowest, so every bar outlives the sweep and
       none outlives it by much. */
    animation:hero-bar var(--t) ease-in-out var(--d) 8 alternate}
  @keyframes hero-bar{from{transform:scaleY(.34)}to{transform:scaleY(1)}}
  @keyframes hero-level{
    0%{transform:scaleY(1)}
    30%{transform:scaleY(1.3)}
    58%{transform:scaleY(.9)}
    100%{transform:scaleY(.3)}
  }
}
/* Travels its own width plus a screen, so it begins wholly outside the
   window and comes to rest with the core just past the drawing's right
   edge — which is where it fades. Stated in vw, so it is the same
   journey on any screen rather than a pixel count tuned to one. */
@keyframes hero-sweep{
  0%{transform:translateX(-105vw);opacity:0}
  7%{opacity:1}
  /* Lightning does not hold a steady brightness — it stutters. Two
     dips are enough to read as a strike; more reads as a broken
     animation. */
  16%{opacity:.45}
  24%{opacity:1}
  58%{opacity:.8}
  66%{opacity:1}
  78%{opacity:.62}
  90%{opacity:.34}
  /* Gone, and gone for good: `both` holds this last frame, and
     visibility takes the element out of the paint entirely rather
     than leaving a transparent box the compositor still carries. */
  100%{transform:translateX(0);opacity:0;visibility:hidden}
}
@media(prefers-reduced-motion:reduce){.hero-pulse{display:none}}
/* The drawing lifts slightly under a pointer.

   GUARDED BY THE POINTER, NOT BY A WIDTH. A tablet can be 1100px wide
   and have no pointer at all, and a `:hover` that a finger triggers
   STICKS — the picture would stay raised until something else was
   tapped. `hover:hover` asks the device the right question.

   drop-shadow, not box-shadow: the illustration is a cut-out, so the
   shadow has to follow the silhouette. A box-shadow would draw a
   rectangle under a picture that has no rectangle in it. */
@media(hover:hover) and (pointer:fine){
  .hero-art img{transition:transform .38s cubic-bezier(.22,.7,.28,1),
    filter .38s ease}
  .hero-art:hover img{transform:translateY(-7px) scale(1.012);
    filter:drop-shadow(0 20px 30px rgba(0,229,195,.16))
           drop-shadow(0 4px 10px rgba(0,0,0,.4))}
}
@media(prefers-reduced-motion:reduce){
  .hero-art img,.hero-art:hover img{transform:none}
}
@media(min-width:900px){
  .hero-grid{grid-template-columns:minmax(0,1fr) minmax(0,.92fr);gap:44px}
  .hero-art{max-width:none;justify-self:end}
}
/* Above about 1400px the page had stopped growing at all. The
   container is capped at --max, and both display clamps have saturated
   by 1040px, so every further pixel of window became margin: a 1728px
   screen showed the content in 65% of itself and 35% of nothing.

   That is why the site read better at 125% browser zoom. Zoom shrinks
   the viewport TOWARD the cap, so the empty margins collapse — a
   change in proportion, not in size, and the half of it you cannot get
   by sitting closer to the screen.

   Every value here starts at exactly what it was at 1400px, so there
   is no jump at the breakpoint and nothing below it renders any
   differently than before. Small laptops were the constraint. */
@media(min-width:1400px){
  :root{--max:clamp(1120px,80vw,1360px)}
  h1{font-size:clamp(56px,4vw,66px)}
  h2{font-size:clamp(34px,2.43vw,40px)}
  .lead{font-size:clamp(20px,1.43vw,22px)}
}
.grid{display:grid;gap:22px}
@media(min-width:760px){.grid.c2{grid-template-columns:1fr 1fr}
  .grid.c3{grid-template-columns:repeat(3,1fr)}
  section{padding:62px 0}
  .hero{padding:64px 0 56px}}
/* Wide enough for the five links, the brand and the picker to sit in
   one row with room left over. Below this the button is the nav; the
   breakpoint is where the CONTENT stops fitting, not a device name. */
@media(min-width:900px){
  section{padding:76px 0}
  .hero{padding:84px 0 64px}
  header .wrap{gap:16px;min-height:62px}
  .brand{font-size:19px}
  .menu>summary{display:none}
  /* Above the breakpoint there is no disclosure to open: the links
     are the navigation, whatever state the <details> was left in.

     Two rules because browsers hide a closed <details> two different
     ways. Older engines put display:none on the children, which the
     second rule overrides. Chrome 131 and Safari 18.4 hide the
     ::details-content pseudo with content-visibility instead — a
     rule about DISPLAY cannot reach that, and the measured result
     was a nav box 0px wide whose links still ran 200px past the
     right edge of a 1024px screen. Both mechanisms, both engines. */
  .menu::details-content{content-visibility:visible;display:contents}
  .menu>nav,.menu:not([open])>nav{display:flex;position:static;
    flex-direction:row;gap:22px;align-items:center;background:none;
    border:0;padding:0;box-shadow:none}
  nav a{font-size:14.5px;padding:8px 2px;border-bottom:0}
}
.card{background:var(--panel);border:1px solid var(--line);
  border-radius:12px;padding:22px}
.card h3{color:var(--text)}
.card p{margin:0;font-size:15px}
/* 86vw left a sliver of the next shot showing, which is the point on
   a wide screen and clutter on a narrow one where that sliver is all
   the margin there is. */
/* <picture> is inline by default, so without this the figure gets a
   few pixels of baseline gap under every screenshot. */
/* The dot is 8px because that is the right size to LOOK at. The
   button around it is 28px because that is the smallest thing a
   thumb can reliably hit (WCAG 2.5.8 asks 24), so the hit area is
   padding and the dot is a background, not a box with a size. */
table{width:100%;border-collapse:collapse;font-size:15px}
th,td{padding:12px 10px;border-bottom:1px solid var(--line);text-align:left}
@media(min-width:560px){th,td{padding:13px 12px}}
/* The first column names the row, so it is the one that must stay
   legible when the table is scrolled sideways on a phone. */
th:first-child,td:first-child{padding-left:0}
th{color:var(--dim);font-weight:600;font-size:13.5px;
  text-transform:uppercase;letter-spacing:.05em}
td.yes{color:var(--teal);font-weight:600}
td.no{color:#8b93a7}
.price{display:flex;gap:16px;flex-wrap:wrap}
@media(min-width:560px){.price{gap:20px}}
/* 260px could not fit inside a 320px screen once padding was taken
   off, so the basis has to be smaller than the narrowest column the
   site can offer. */
.plan{flex:1 1 240px;min-width:0;background:var(--panel);border:1px solid var(--line);
  border-radius:12px;padding:24px}
.plan.best{border-color:var(--teal);box-shadow:0 0 0 1px rgba(0,229,195,.3)}
.plan .amount{font-size:38px;font-weight:700;letter-spacing:-.02em}
.plan .per{color:var(--dim);font-size:14px}
/* Scoped to the FAQ. It used to be a bare `details` selector, which
   also reached the two disclosures in the header and gave the
   language picker a stray bottom border and 16px of padding nobody
   asked for. Both header controls carry a class; this one is theirs.
   The padding is on the SUMMARY so the tap target is the row you
   would tap, not a box with a small line of text inside it. */
details.q{border-bottom:1px solid var(--line)}
details.q summary{cursor:pointer;font-weight:600;list-style:none;
  padding:15px 26px 15px 0;position:relative}
details.q summary::-webkit-details-marker{display:none}
details.q summary::after{content:"+";position:absolute;right:2px;
  top:15px;color:var(--dim)}
details.q[open] summary::after{content:"–"}
details.q p{margin:0 0 16px}
footer{padding:36px 0 max(48px,env(safe-area-inset-bottom));
  color:var(--dim);font-size:14px}
footer a{color:var(--dim);text-decoration:none}
/* Only the Terms/Privacy/Contact row is spaced. This used to be on
   `footer a`, which also hit the company name in the copyright line
   and opened a gap before "All rights reserved." Scoping it beats
   overriding it: a link added to the footer later inherits nothing
   it has to undo. */
footer a:hover{color:var(--text)}
footer .links{display:flex;flex-wrap:wrap;gap:4px 18px}
/* 44 in BOTH directions. The height was set and the width was left to
   the word, which is fine until the word is two characters: Chinese
   "支持" measured 29px wide on every page, a target under the guidance
   in the one language where every footer label is short. Centring it
   keeps the 18px gaps looking even rather than lopsided. */
footer .links a{padding:8px 0;min-height:44px;min-width:44px;
  display:inline-flex;align-items:center;justify-content:center}

/* The language picker. A <details>, so it opens with no JavaScript, and
   deliberately quiet: it is a way out of the wrong language, not a
   feature to advertise. */
.langs{position:relative;order:2}
.langs summary{
  list-style:none;cursor:pointer;color:var(--dim);font-size:13px;
  display:flex;align-items:center;gap:6px;padding:12px 8px;
  border:1px solid transparent;border-radius:6px;white-space:nowrap;
}
.langs summary::-webkit-details-marker{display:none}
.langs summary::after{
  content:"";width:5px;height:5px;margin-left:2px;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(45deg) translate(-2px,-2px);opacity:.7;
}
.langs summary:hover{color:var(--text);border-color:rgba(255,255,255,.14)}
.langs[open] summary{color:var(--text);border-color:rgba(255,255,255,.18)}
.langs i{font-style:normal;font-size:15px;line-height:1}
/* The button is flag + CODE on a narrow screen and flag + NAME from
   400px up. Nothing is lost: the menu underneath always spells the
   nine out in full, in their own scripts. */
.langs .lname{display:none}
.langs .lcode{font-weight:700;letter-spacing:.04em;font-size:12.5px}
@media(min-width:400px){
  .langs summary{gap:7px;padding:12px 10px}
  .langs .lname{display:inline}
  .langs .lcode{display:none}
}
.langmenu{
  position:absolute;right:0;top:calc(100% + 6px);z-index:40;
  background:#0F1424;border:1px solid rgba(255,255,255,.12);
  border-radius:8px;padding:5px;min-width:184px;
  box-shadow:0 12px 32px rgba(0,0,0,.45);
  display:flex;flex-direction:column;
  /* The picker used to sit at the LEFT of the header, where a menu
     anchored right ran off that edge on a phone, so the menu was
     flipped to the left below 640px. It sits at the right now, and
     that flip is what pushed the menu 57px past the right edge of a
     360px screen. Anchored to the control at every width, capped to
     the viewport, it fits from 280px up. */
  max-width:calc(100vw - 24px);
}
.langmenu a{
  display:flex;align-items:center;gap:9px;margin:0;
  padding:11px 10px;border-radius:5px;font-size:14px;
  color:var(--dim);text-decoration:none;white-space:nowrap;
}
.langmenu a:hover{background:rgba(255,255,255,.07);color:var(--text)}
.langmenu a[aria-current="true"]{color:var(--text);font-weight:600}
.legal{max-width:76ch}
.legal h2{margin-top:38px}
.legal h3{margin-top:24px;color:var(--text)}
.legal p,.legal li{color:var(--dim)}
.legal ul{padding-left:20px}

/* Store badges. Centred, the same height, each with its state written
   underneath — so the row reads as three equal platforms and not as one
   product with two footnotes. The ones that are not links say so
   instead of looking like links that are broken.

   One height rule, and it works because the ARTWORK was fixed rather
   than compensated for. Google ships its badge on a 646x250 canvas with
   41px of transparent padding on every side; against Apple's SVG, which
   is trimmed to its edges, that made the same CSS height put a third
   less ink on the page. Two rounds of nudging a number followed. The
   file is cropped to its own 564x168 now, so both badges are what they
   measure and neither needs a special case. */
.stores{display:flex;gap:20px 30px;flex-wrap:wrap;
  justify-content:center;align-items:flex-start;margin-top:26px}
.store{display:flex;flex-direction:column;align-items:center;gap:9px;
  text-align:center;text-decoration:none}
.store img{height:52px;width:auto;display:block}
/* OPTICALLY, NOT MATHEMATICALLY. All five badges render at the same 52
   points and measure the same 74 including their caption — checked in
   pixels, because the eye said otherwise and the eye was right about
   something. Google's artwork carries a white outline and a two-line
   lockup, "GET IT ON" over "Google Play", so its wordmark sits smaller
   than the App Store's single line and reads as a smaller badge beside
   it. Six points of height put the lockups level. Google's guidelines
   set a minimum size and a clear-space rule, not a fixed one. */
.store.play img{height:58px}
@media (max-width:520px){.store.play img{height:49px}}
.store.soon{opacity:.55}
/* The state under a badge is a caption, not a second link: inside the
   anchor it inherited the link underline and read as one. */
.store em{font-style:normal;font-size:13px;color:var(--dim);
  letter-spacing:.02em;text-decoration:none;line-height:1.35;
  /* NEVER WIDER THAN THE BADGE IT DESCRIBES. Each caption names a state
     and a platform, and "In development for macOS" on one line is wider
     than the 156 points of artwork above it — so the row grew past its
     own gutters in the languages with longer words. The break is in the
     copy; this is the ceiling that catches a translation which finds a
     longer way to say it. */
  max-width:160px}
.store.live em{color:var(--teal)}

@media (max-width:520px){.store img{height:44px}}

/* The plan worth choosing says so, beside its name rather than under
   it: a reader scanning two prices should not have to read a paragraph
   to find which one is the offer. */
.plan h3{display:flex;align-items:center;justify-content:space-between;
  gap:12px}
.plan .tag{font-size:12px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--bg);background:var(--teal);
  border-radius:999px;padding:4px 10px;white-space:nowrap}
.plan .save{display:inline-block;margin-top:8px;font-size:13px;
  font-weight:600;color:var(--teal)}

/* The A/B demonstration. A dubbing app's strongest argument is not
   visual, and this is the only control on the page that makes a sound —
   so it is the only one that looks like a device rather than a link. */
.hear{display:flex;flex-wrap:wrap;gap:12px;align-items:center;
  margin:22px 0 14px}
.hearplay{display:inline-flex;align-items:center;gap:10px;
  min-height:52px;padding:0 22px 0 18px;border:0;border-radius:999px;
  background:var(--teal);color:#04121a;font:inherit;font-weight:700;
  font-size:16px;cursor:pointer}
.hearplay:hover{filter:brightness(1.07)}
/* The icon is drawn rather than fetched: one more request for a
   triangle would be one more than this page makes. */
.hearicon{width:0;height:0;border-left:13px solid currentColor;
  border-top:8px solid transparent;border-bottom:8px solid transparent;
  margin-left:2px}
.hear.playing .hearicon{width:12px;height:15px;border:0;margin-left:0;
  background:linear-gradient(currentColor 0 0) 0/4px 100% no-repeat,
             linear-gradient(currentColor 0 0) 100%/4px 100% no-repeat}
/* Two states of one thing, so they share a frame. Separate buttons
   would read as two different clips. */
.hearswitch{display:inline-flex;border:1px solid var(--line);
  border-radius:999px;padding:4px;background:var(--panel)}
.hearswitch button{min-height:44px;padding:0 18px;border:0;
  border-radius:999px;background:transparent;color:var(--dim);
  font:inherit;font-size:15px;font-weight:600;cursor:pointer;
  white-space:nowrap}
.hearswitch button[aria-pressed="true"]{background:rgba(255,255,255,.09);
  color:var(--text)}
.hearswitch button:hover{color:var(--text)}
.small{font-size:13px;color:var(--dim);margin:0}
@media(min-width:560px){.hear{gap:16px}}
/* The transcript under the A/B switch. Not a translation into the
   reader's language — it is what was said, in the language it was said
   in, which is the only honest thing a transcript can be. And it is
   the only part of this section a deaf visitor can use. */
.hearline{margin:2px 0 14px;font-size:16px;color:var(--text);
  max-width:52ch;min-height:1.6em}

/* Keyboard focus has to be visible on the LABEL, because the input it
   belongs to is the thing that is hidden. */
/* The tracks live outside .wrap so they can run full width, which puts
   them out of reach of a sibling selector — so the switch is done from
   the body instead, keyed on which radio is checked. */
/* ── THE GALLERY ────────────────────────────────────────────────────

   NOTHING ON THIS PAGE MOVES BY ITSELF. What was here before was two
   videos playing and fourteen screenshots turning over once every three
   seconds, on the landing page, in nine languages. It was the product
   spending a stranger's data before they had asked for anything, and on
   a phone it was most of the page's weight.

   A card is now a still, a badge saying what opening it gives you, and
   a link. The picture arrives when the card is scrolled to; the film
   arrives only when somebody presses it.

   The wall is a grid that counts its own columns: `auto-fill` with a
   minimum means a wide screen gets four and a phone gets one, without a
   breakpoint per device. Phone screenshots are tall, so their track is
   narrower — a 9:19.5 frame shown at a Mac frame's width is a stripe of
   picture between two stripes of nothing. */
/* FLEX, NOT GRID, and the reason is the LAST row. A grid's column
   count is decided by the container, so five cards across three tracks
   leaves two of them in tracks one and two with an empty third beside
   them — hard against the left edge, under a full row. `justify-content`
   cannot fix it, because it centres the tracks and not the items in a
   short line. A flex line centres itself, and every line stretches its
   own items to one height without being asked.

   The count per row is a max-width rather than a track count: three
   across from 960px, two from 660, one below that, and the same card
   everywhere. The films row on the landing page is three and its sets
   row is four, which is the only place the counts differ. */
.gwall{display:flex;flex-wrap:wrap;justify-content:center;
  gap:18px;margin:26px 0 0}
@media(min-width:900px){.gwall{gap:22px}}
.gcard{flex:1 1 260px;max-width:100%;
  display:flex;flex-direction:column;gap:12px;
  text-decoration:none;color:var(--text);
  border:1px solid var(--line);border-radius:14px;
  background:var(--panel);padding:10px 10px 16px;
  transition:border-color .16s ease,transform .16s ease}
@media(min-width:660px){
  .gwall>.gcard{max-width:calc(50% - 9px)}
}
@media(min-width:900px){
  .gwall>.gcard{max-width:calc(33.333% - 15px)}
  .gwall-strip>.gcard{max-width:calc(25% - 17px)}
}
.gcard:hover{border-color:rgba(0,229,195,.45)}
@media(hover:hover){.gcard:hover{transform:translateY(-2px)}}
@media(prefers-reduced-motion:reduce){
  .gcard{transition:none}
  .gcard:hover{transform:none}
}
/* The picture's box. `aspect-ratio` on the frame rather than on the
   image: every shot in a set shares the card's shape, so the wall does
   not step up and down as one device's screenshots turn out to be a
   few pixels taller than another's. `flex:0 0 auto` so a card that was
   stretched to match its neighbours grows at the TEXT and never at the
   picture — a taller box would crop a different amount off each frame
   and the row would stop being one set. */
.gshot{position:relative;display:block;border-radius:9px;flex:0 0 auto;
  overflow:hidden;background:var(--bg2);aspect-ratio:16 / 10}
.g-phone .gshot{aspect-ratio:3 / 4}
.gshot img{width:100%;height:100%;display:block;object-fit:cover;
  object-position:50% 0}
.g-phone .gshot img{object-position:50% 12%}
/* The badge says what the card IS before it is opened: a length for a
   film, a count for a set. Bottom right, over a plate rather than bare,
   so it stays legible on a bright frame and on a dark one. */
.gbadge{position:absolute;right:8px;bottom:8px;
  display:inline-flex;align-items:center;gap:5px;
  padding:4px 9px;border-radius:999px;
  background:rgba(6,10,20,.82);color:#fff;
  font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;
  backdrop-filter:blur(6px)}
.gbadge.gplay{color:var(--teal)}
.gmeta{display:flex;flex-direction:column;gap:6px;padding:0 6px}
.gmeta strong{font-size:17px;line-height:1.3;font-weight:650}
.gopen{color:var(--teal);font-size:13px;font-weight:600;
  text-transform:uppercase;letter-spacing:.06em}
.gbody{color:var(--dim);font-size:15px;line-height:1.55}
/* THE THREE FILMS ARE ONE ROW OF THREE EQUAL THINGS, so they share a
   picture box whatever shape the recording is. The phone's is 496x1080
   and took the card to nearly three times the height of the other two,
   which left the Mac and the tablet sitting above a panel of empty
   navy — a row that reads as broken rather than as varied. Its poster
   is fitted inside the shared box instead of filling it: a tall strip
   on a dark ground, which is what a vertical film looks like. */
.gwall-films .gshot,
.gwall-strip .g-phone .gshot{aspect-ratio:16 / 10}
.gwall-films .g-phone .gshot img,
.gwall-strip .g-phone .gshot img{object-fit:contain;background:#05070E;
  object-position:50% 50%}
.gwall-strip .gcard{padding-bottom:14px}
/* The two rows of the strip belong to each other, so they sit closer
   together than either does to the heading above or the door below. */
.gwall-strip{margin-top:18px}
.gmore{margin-top:26px;text-align:center}
.gallery h2{margin-top:52px}
.gallery h2:first-of-type{margin-top:36px}
.gnote{margin-top:34px}
.gallery .cta{margin-top:30px}

/* ── THE VIEWER ─────────────────────────────────────────────────────

   A real <dialog>, so Escape closes it, focus is trapped inside it and
   the page behind it goes inert — three behaviours that are a hundred
   lines of script to reproduce badly. The backdrop is the element's
   own ::backdrop, which is why a click landing on the dialog box and
   not on anything inside it counts as a click outside.

   The frame is capped by the VIEWPORT, not by the picture: a film is
   1280 wide and a phone screenshot is 3467 tall, and both have to fit
   between the same two edges without the page scrolling behind them. */
.lb{width:min(1120px,94vw);max-width:none;max-height:92vh;
  padding:0;border:1px solid var(--line);border-radius:16px;
  background:var(--panel);color:var(--text);overflow:hidden}
.lb::backdrop{background:rgba(4,7,14,.82);backdrop-filter:blur(3px)}
.lb[open]{display:flex;flex-direction:column}
/* THE BOX IS RESERVED BY THE STAGE, not by the picture. An <img> with
   `width:auto` is 0x0 until its bytes arrive, whatever its width and
   height attributes say — the attributes are a presentational hint and
   `auto` beats them. So the dialog opened as a sliver of caption and
   jumped to full height when the file landed, under the reader's
   cursor. The viewer now sets `aspect-ratio` on this box from the
   shape the build measured, and the picture fills what is already
   there. Capped by height as well, because a phone screenshot is
   1600x3467 and would otherwise ask for a box four screens tall. */
.lbstage{display:flex;align-items:center;justify-content:center;
  background:#05070E;width:100%;min-height:0;
  max-height:calc(92vh - 160px);overflow:hidden}
.lbstage video,.lbstage img{display:block;width:100%;height:100%;
  object-fit:contain}
.lbfoot{display:flex;align-items:flex-start;gap:16px;
  padding:14px 16px 16px;border-top:1px solid var(--line);
  flex:0 0 auto}
.lbwords{min-width:0;flex:1 1 auto}
.lbwords strong{display:block;font-size:17px;margin-bottom:5px}
.lbbody{color:var(--dim);font-size:14.5px;line-height:1.55;
  margin:0;max-width:74ch}
.lbnav{display:flex;align-items:center;gap:4px;flex:0 0 auto}
.lbcount{color:var(--dim);font-size:14px;min-width:4.5ch;
  text-align:center;font-variant-numeric:tabular-nums}
.lbstep,.lbx{display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:10px;cursor:pointer;
  background:transparent;color:var(--text);
  border:1px solid var(--line);font:inherit}
.lbstep:hover,.lbx:hover{background:rgba(255,255,255,.08)}
/* The close button floats over the picture rather than taking a row of
   its own: at 92vh every row costs the picture. */
.lbx{position:absolute;top:10px;right:10px;z-index:2;
  background:rgba(6,10,20,.72);backdrop-filter:blur(6px)}
@media(max-width:640px){
  .lb{width:100vw;max-height:100vh;border-radius:0;border:0}
  .lbfoot{flex-direction:column;gap:10px}
  .lbnav{align-self:flex-end}
  .lbstage video,.lbstage img{max-height:calc(100vh - 200px)}
}
/* The feature index at the foot of the gallery. Prose rather than a
   list, because it is one paragraph a reader skims and an answer engine
   quotes whole — a bulleted version of the same words reads as a spec
   sheet and gets quoted a line at a time. Set narrower and a touch
   smaller than the page's body copy: it is reference, not argument. */
.gfeat{color:var(--dim);font-size:15.5px;line-height:1.7;max-width:74ch}
/* The offer, at the top of the gallery as well as the foot of it.
   Somebody arriving here from a search has read a page of pictures and
   had nothing to press until the bottom — four screens between being
   convinced and being able to act. Wraps to two lines on a phone
   rather than shrinking either half. */
.goffer{display:flex;flex-wrap:wrap;align-items:center;gap:14px;
  margin:20px 0 0}
