/* Bamya Call — the landing page nginx serves on the bamya.co call hostnames.
   Deliberately standalone: it lives in landing/, not static/, and shares
   nothing with style.css or the app's modules. Motion is split two ways:
   ambient loops (the hero transcript, the language marquee) are CSS
   keyframes, and everything scroll- or entrance-driven is GSAP + Lenis in
   landing.js, vendored under landing/vendor/. The `js` class on <html> is
   what arms the hidden initial states — without JS the page renders as a
   plain, fully visible document. */

/* The palette is the brand mark's: neon teal into violet on dark charcoal.
   Teal is you — and the AI, which works on your side of the glass; violet is
   the far end. Amber survives only as a status colour (a checklist item
   covered thinly), not as a brand colour. */
:root {
  --bg: #090b0d;
  --raise: #0f1315;
  --raise-2: #141a1c;
  --line: rgba(255, 255, 255, 0.08);
  --line-strong: rgba(255, 255, 255, 0.16);
  --text: #eef2f3;
  --dim: #98a4a8;
  /* Kept level with static/style.css by stylecheck.mjs. Lifted from
     #5e696d, which was under the 4.5:1 WCAG AA floor for text at the
     sizes this sets — the section sub-copy, the footer note, the
     transcript card's language tag. Same hue, same saturation. */
  --faint: #77858a;
  --you: #38d9be;
  --them: #a78bfa;
  --live: #45ecd0;
  --warn: #ffb454;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, Menlo, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Lenis drives the scrolling when JS runs; native smooth scrolling would
   fight it, so it is only the no-JS fallback for the anchor links. */
html:not(.js) { scroll-behavior: smooth; }

body {
  background: var(--bg);
  color: var(--text);
  font: 16px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

::selection { background: rgba(56, 217, 190, 0.30); }

a { color: inherit; }

.wrap {
  width: min(1120px, calc(100% - 48px));
  margin-inline: auto;
}

/* Faint editorial side rules through the whole page on wide screens. */
@media (min-width: 1240px) {
  body::before {
    content: "";
    position: fixed;
    inset: 0;
    margin-inline: auto;
    width: 1120px;
    border-inline: 1px solid rgba(255, 255, 255, 0.04);
    pointer-events: none;
    z-index: 0;
  }
}

/* The two blooms the hero used to own, lifted off it and pinned to the
   viewport: the first section's light is the whole page's light now, sitting
   still behind everything while the panels scroll through it, rather than
   passing by once and leaving the rest on flat --bg. The percentages land
   where they always did — they used to resolve against a panel that was
   100svh and full width, which is the viewport.

   A positioned layer rather than background-attachment: fixed on the body,
   which is the shorter way to write it and is broken on iOS Safari — it sizes
   the background to the document there and does not track the viewport.

   z-index: -1, not 0: the side rules above get to be behind the content by
   tree order, being the first child, but ::after comes after the panels and at
   0 would paint over them. -1 is behind every box on the page and still above
   the canvas, which is where --bg is painted (body's background propagates
   there because html has none — give html a background and this layer is
   covered by it). */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(640px 420px at 72% 26%, rgba(56, 217, 190, 0.10), transparent 70%),
    radial-gradient(560px 420px at 62% 78%, rgba(167, 139, 250, 0.07), transparent 70%);
}

/* ─────────────────────────────────────────────── progress hairline */

/* Scrubbed by GSAP; without JS it simply stays at zero width. */
.progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, var(--you), var(--them));
  transform-origin: 0 50%;
  transform: scaleX(0);
  z-index: 60;
}

/* ─────────────────────────────────────────────── header */

.top {
  position: fixed;
  inset: 0 0 auto 0;
  display: flex;
  align-items: center;
  /* Wraps rather than clips. `body` carries `overflow-x: hidden`, so a row that
     does not fit is not scrolled to - it is cut off and gone, and the things at
     the end of this row are Sign in and the trial button. Measured before this
     line existed: the Bulgarian trial button was clipped and unclickable at
     900px, on the live site, with nothing on the page saying so. That is the
     failure the 520px block below already names in its own comment; it just
     turns out to start at 950 rather than at 520.
     Costs nothing while everything fits, which is every width above about
     1180px with the sections nav shown. */
  flex-wrap: wrap;
  gap: 28px;
  padding: 14px 28px;
  z-index: 50;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}

.top-nav {
  display: flex;
  gap: 22px;
  margin-inline: auto;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.top-nav a {
  color: var(--dim);
  text-decoration: none;
  transition: color 0.2s;
}

.top-nav a:hover { color: var(--text); }

/* The EN | БГ switcher was here and is gone from the markup, so its rules went
   with it rather than sitting in SELECTOR_EXCEPTIONS: a rule naming nothing is
   indistinguishable from one nobody has reached yet, which is what
   stylecheck.mjs exists to catch. /bg is still served and is still its own page
   at its own path — it is simply no longer linked from the English one. */

/* 1000px, not 720: with Pricing in the bar the sections nav and the buttons
   stop fitting on one line at about 1180, and what the header does then is take
   a second row - a 136px fixed bar at the top of every screen. The nav is a
   convenience on a page that scrolls to the same places; the height is paid for
   on every screen at that width, so the nav is what gives way. */
@media (max-width: 1000px) {
  .top-nav { display: none; }
  .top { justify-content: space-between; }
}

/* Sign in gives up its place before the header has to take a second row.
   Adding Pricing to this bar made five things where there were four, and the
   fifth pushed the trial button onto a line of its own from about 800px down.
   The 520px block below already decided which of these is expendable and why -
   the trial is what the page is for, and somebody who wants Sign in has an
   account and a full-size button in the closing section. This is that same
   decision, taken 380px earlier because the row is that much fuller now. */
@media (max-width: 900px) {
  .top .cta[href="/auth/login"] { display: none; }
}

/* The header carries four things — mark, Pricing, Sign in, Start free — and
   below about 520px they do not fit: measured on a 390px phone the trial
   button ended 101px past the right edge, clipped rather than wrapped, because
   `body` has `overflow-x: hidden`. A control that is invisible AND unclickable
   is worse than one that was never drawn, and nothing on the page said so.

   So the row is tightened, and Sign in gives up its place. That is the right
   one to drop of the two: the trial button is what this page is for, and the
   reader who wants Sign in already has an account and is one full-size button
   away from it in the closing section. Both ghosts go here, Pricing with them:
   the price list is a page away from a reader who has decided, and the mark is
   what is left beside the trial. */
@media (max-width: 520px) {
  .top { gap: 12px; padding: 12px 14px; }
  .top .cta { padding: 10px 14px; font-size: 12px; letter-spacing: 0.04em; }
  .top .cta.ghost { display: none; }
}

/* The Bamya mark, redrawn rather than imported: same boxed shape as the rest
   of the family, none of the app's CSS. */
/* Set in the system's own face at its heaviest weight — no bordered lockup, no
   inverted chip, nothing to load. Which face that is differs per platform and
   that is the point: it is the same voice as the rest of the page, since --sans
   is what every other word here is set in. The tracking is pulled in because
   at this weight the default spacing reads as three words rather than one. */
.mark {
  font-family: var(--sans);
  font-weight: 900;
  font-size: 2.03rem;
  letter-spacing: -0.045em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
  line-height: 1;

  /* How long the mark reads "bamya" before it becomes the product — see the
     arrival, below. Four animations are keyed off it and they have to agree
     to the millisecond, since the swap is timed to land inside the first
     smear of the perpetual one; it is written here, once, and inherited. */
  --mark-hold: 2s;
}

/* The favicon's waveform tile, inline before the wordmark. Sized in em so the
   footer's smaller mark scales it for free. The tile is pure black with no
   border, both on request; the bars take --you and --them, which are the same
   two colours the favicon hard-codes. */
.mark-icon {
  width: 0.98em;
  height: 0.98em;
  margin-right: 0.26em;
  vertical-align: -0.14em;
}

/* The letters are white and only the colon is coloured — until it swings, when
   the same split the section titles carry runs through the words. It is built
   out of two dots rather than typed. It has to be built: rotating a real ":"
   would swing it around the middle of its line box, and that is nowhere near
   the middle of its two dots — they would orbit off-centre. Two absolutely
   placed dots in a box of their own turn around the point between them.

   An empty inline-block takes its baseline from its bottom margin edge, so the
   lower dot sits on the text baseline exactly where a typeset colon does, with
   no nudging. The side margins are what the dots need when they swing through
   horizontal: they reach 0.16em past the box either side, and the mark's
   negative tracking eats 0.045em of whatever is given here. */
.mark-colon {
  display: inline-block;
  position: relative;
  vertical-align: baseline;
  width: 0.2em;
  height: 0.52em;
  margin: 0 0.28em;
  animation: mark-turn 7s infinite;
  /* After the shorthand, which would otherwise reset these to `ease` and 0s.
     The delay is what makes the dots' FIRST sweep the arrival rather than an
     idle swing two seconds after the reader has already read the word. */
  animation-timing-function: var(--mark-spring);
  animation-delay: var(--mark-hold);
}

.mark-colon::before,
.mark-colon::after {
  content: "";
  position: absolute;
  left: 0;
  width: 0.2em;
  height: 0.2em;
  border-radius: 50%;
}

/* Violet over mint, the pair in the order the page reads it — though half a
   turn later it is the other way up, which is the point. */
.mark-colon::before { top: 0; background: var(--them); }
.mark-colon::after { bottom: 0; background: var(--you); }

/* Not a constant spin — a swing and a rest, twice a cycle. Each sweep is half a
   turn, which is what puts the other colour on top, so the beat is: swap,
   hold, swap back, hold.

   The swing is a damped spring, sampled — the step response of an actual
   second-order system at a damping ratio of 0.68, which is what gives it the
   one clear overshoot to 190 degrees and the small settle back that a bezier
   cannot express at all. A bezier has two control points and can only ever
   cross its target once; anything that goes past and comes back the way a
   thing with weight does needs the curve stated as samples. linear() is
   exactly the tool for that: a measured curve, written down.

   It sits on the element rather than in the keyframes, so it is written once
   and both sweeps take it. The rests override it with `linear` — a segment
   with no timing function of its own inherits this one, and the rests must not
   creep. Reduced motion stops the whole thing dead through the global rule at
   the foot of the file, leaving an ordinary two-colour colon. */
.mark-colon {
  --mark-spring: linear(
    0, 0.084, 0.2686, 0.4793, 0.6719, 0.8254, 0.9346, 1.0033, 1.0398, 1.0533,
    1.0525, 1.044, 1.0328, 1.0218, 1.0125, 1.0056, 1.0011, 0.9985, 0.9973,
    0.9971, 1);
}

@keyframes mark-turn {
  0%   { transform: rotate(0deg); }
  18%  { transform: rotate(180deg); animation-timing-function: linear; }
  50%  { transform: rotate(180deg); }
  68%  { transform: rotate(360deg); animation-timing-function: linear; }
  100% { transform: rotate(360deg); }
}

/* The titles' split, run through the wordmark on the other axis and keyed to
   the colon: nothing at all while the dots stand vertical, widest as they pass
   through horizontal. The two words take it in OPPOSITE directions, so violet
   ends up on the outside of the mark and mint hugs the colon, and the whole
   thing pulls apart and closes again with each swing rather than shaking.

   --abx is registered, which is what makes it interpolate — an unregistered
   custom property animates in steps, not smoothly. Where @property is missing
   the var() fallback holds the mark at zero and it simply never splits. */
@property --abx {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

.mark-b {
  text-shadow: calc(var(--abx, 0) * -1em) 0 0 var(--them),
               calc(var(--abx, 0) *  1em) 0 0 var(--you);
}

/* "amya" takes the tail's direction rather than one of its own, so the smear
   runs on unbroken through the swap: what tears apart is what comes back. */
.mark-call,
.mark-amya {
  text-shadow: calc(var(--abx, 0) *  1em) 0 0 var(--them),
               calc(var(--abx, 0) * -1em) 0 0 var(--you);
}

/* Same 7s clock as the swing, started in the same style pass, so the two stay
   in step without either one knowing about the other.

   The stops are |sin(angle)| SAMPLED off the same spring the dots turn on, not
   a guessed rise and fall — they are two readings of one simulation, which is
   the only way the peak can be trusted to land on the horizontal. An
   evenly-spaced arch would peak around the middle of the sweep, by which time
   the spring has already carried the dots to 150 degrees and is on its way
   back. The late ripple is that overshoot: the dots pass 180, swing to 190 and
   settle, and the words breathe once more, faintly, as they do. linear joins
   the samples — the shape is in the numbers, so easing between them would bend
   it twice. */
.mark { animation: mark-split 7s linear infinite; }
/* After the shorthand, for the reason the colon's carries one: the arrival is
   the first beat of this clock, not a separate effect laid over it. Both marks
   take it so the two stay in step; the footer's is below the fold for those
   two seconds anyway. */
.mark { animation-delay: var(--mark-hold); }

@keyframes mark-split {
  0%,    50%    { --abx: 0; }
  1.5%,  51.5%  { --abx: 0.053; }   /*  36deg */
  2.2%,  52.2%  { --abx: 0.082; }   /*  65deg */
  3%,    53%    { --abx: 0.089; }   /*  98deg — dots horizontal, widest */
  3.8%,  53.8%  { --abx: 0.071; }   /* 128deg */
  4.5%,  54.5%  { --abx: 0.047; }   /* 149deg */
  6%,    56%    { --abx: 0.004; }   /* 177deg */
  7.5%,  57.5%  { --abx: 0.013; }   /* 188deg — overshoot, the second breath */
  9%,    59%    { --abx: 0.015; }   /* 189deg */
  11%,   61%    { --abx: 0.009; }   /* 185deg */
  13%,   63%    { --abx: 0.003; }   /* 182deg */
  15%,   65%    { --abx: 0; }
  18%,   68%, 100% { --abx: 0; }
}

/* The mark arrives as the company and becomes the product: "bamya" for two
   seconds, then the tail tears apart and b:call resolves out of it.

   The b is the anchor and does not move a pixel through any of it, which is
   what the absolute positioning here is for. "amya" is taken out of the flow
   entirely, so .mark-tail is ":call" wide from the first frame and the mark
   never changes width — nothing in the header reflows, and the b is held in
   place by the tile in front of it, which never moves either. Everything that
   changes is to the b's right. Animating a width instead would have shifted
   the nav beside it and, with the mark centred at any width, the b itself.

   The swap is still keyed to the perpetual animation, held back by
   --mark-hold: the dots turn in on their spring, the whole mark smears on the
   sampled arch it always smears on, and the identity changes at 0.089em — the
   peak of that arch, where there is least to see. What the two words do on top
   of that is their own, and much louder: the arch is an ambient breath at a
   twentieth of an em and this is a tear, so it is stated where it happens
   rather than by pushing the shared curve somewhere it must not go for the
   other 6.8 seconds of every cycle.

   It is a chained filter rather than a text-shadow, and that is the only
   mechanism that could carry it: the colon is two coloured BOXES, not glyphs,
   so a text-shadow reaches "call" and leaves the dots undistorted. Chained
   drop-shadows also compound — each one shadows the result of the last, so
   two functions draw four ghosts and the smear reads as a streak rather than
   two copies. It travels LEFT, into the b, which is the one thing on the mark
   that is not going anywhere.

   Both ends are filter: none, so nothing survives the arrival. A filter left
   standing at zero offsets is still a filter: it holds a stacking context and
   a rasterised layer around the wordmark for the life of the page, under a
   text-shadow that is animating on every one of those 7s cycles.

   Base state is the FINAL form: amya at zero, tail at one. A browser that runs
   no animation at all gets b:call from the first frame rather than a wordmark
   stranded on the wrong word, and so does a reduced-motion visitor — see the
   foot of the file, where the whole thing is taken off. It has to be taken off
   by name: the arrival is a HOLD, and crushing an animation's duration leaves
   its delay untouched, so the global rule alone would have left the mark
   sitting on "bamya" for two seconds and then cutting.

   The box that POSITIONS the outgoing word cannot be the box that fades the
   incoming one, which is why there are two. opacity applies to a whole
   subtree, not to one element's own painting, so a fading .mark-tail took the
   "amya" nested inside it along — computed opacity 1, rendered nothing, the
   only frame where both had to be legible at once. .mark-tail is the box and
   never fades; .mark-form is the settled wordmark and is the thing that
   arrives. .mark-form needs no layout of its own — an inline wrapper leaves
   the colon and the word laid out exactly as they were. */
.mark-tail {
  position: relative;
  display: inline-block;
}

.mark-form {
  animation: mark-form-in 0.42s ease-out backwards;
  /* Into the back of the tear, not after it: the form is already pulling into
     focus while the word that was there is still coming apart, so the two
     overlap and it is never a cut between two still words. */
  animation-delay: calc(var(--mark-hold) + 0.16s);
}

.mark-amya {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  /* Toward the b: what is left of the word folds into the letter that stays. */
  transform-origin: left center;
  animation: mark-amya-out 0.21s ease-in backwards;
  animation-delay: var(--mark-hold);
}

/* 0.21s is 3% of the 7s clock — exactly where mark-split peaks, so the last of
   the word is spent under the widest breath of the shared arch. ease-in holds
   it legible and then drops it, rather than fading from the first millisecond.

   The streak is widest at 55%, NOT at the end, and that is the whole trick of
   an exit: a filter is painted at the element's own opacity, so a smear stated
   on the last keyframe is a smear drawn at opacity zero. It has to peak while
   there is still something to smear, and what the final keyframe carries is
   only where the ghosts were heading. */
@keyframes mark-amya-out {
  0% {
    opacity: 1;
    transform: scaleX(1);
    filter: none;
  }
  55% {
    opacity: 0.5;
    transform: scaleX(0.86);
    filter: drop-shadow(-0.14em 0 0.02em var(--them))
            drop-shadow(-0.29em 0 0.06em var(--you));
  }
  100% {
    opacity: 0;
    transform: scaleX(0.7);
    filter: drop-shadow(-0.22em 0 0.05em var(--them))
            drop-shadow(-0.46em 0 0.11em var(--you));
  }
}

/* The same streak read backwards: the form arrives already torn, trailing the
   pair back toward the b, and pulls into focus. It starts at opacity 0 because
   `backwards` fill paints the first keyframe for the whole two-second hold, and
   a ghost of "call" under "bamya" is the one thing this must never show. */
@keyframes mark-form-in {
  0% {
    opacity: 0;
    filter: drop-shadow(-0.24em 0 0.05em var(--them))
            drop-shadow(-0.48em 0 0.12em var(--you));
  }
  30% {
    opacity: 0.7;
    filter: drop-shadow(-0.17em 0 0.04em var(--them))
            drop-shadow(-0.34em 0 0.08em var(--you));
  }
  65% {
    opacity: 0.94;
    filter: drop-shadow(-0.07em 0 0.02em var(--them))
            drop-shadow(-0.14em 0 0.03em var(--you));
  }
  100% {
    opacity: 1;
    filter: none;
  }
}

.mark.small { font-size: 1.58rem; }

/* ─────────────────────────────────────────────── buttons */

.cta {
  display: inline-block;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 12px 22px;
  border-radius: 999px;
  transition: transform 0.15s, background 0.2s, border-color 0.2s, color 0.2s;
}

.cta:active { transform: translateY(1px); }

.cta.solid {
  background: var(--text);
  color: var(--bg);
  border: 1px solid var(--text);
}

.cta.solid:hover { background: #fff; border-color: #fff; }

.cta.ghost {
  color: var(--text);
  border: 1px solid var(--line-strong);
}

.cta.ghost:hover { border-color: var(--text); }

.cta.lg { font-size: 15px; padding: 16px 34px; }

/* ─────────────────────────────────────────────── panels (the carousel) */

.panel {
  min-height: 100svh;
  display: grid;
  /* An explicit 100% track, not auto: an auto track is sized from content,
     so the language marquee's max-content width would widen it past the
     viewport and the centred .wrap would centre inside THAT. */
  grid-template-columns: 100%;
  align-items: center;
  position: relative;
  padding: 110px 0 70px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.panel:last-of-type { border-bottom: 0; }

/* ─────────────────────────────────────────────── shared type */

.kicker {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 26px;
}

.pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--live);
  animation: pulse 2.2s ease-out infinite;
}

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(69, 236, 208, 0.5); }
  70%  { box-shadow: 0 0 0 12px rgba(69, 236, 208, 0); }
  100% { box-shadow: 0 0 0 0 rgba(69, 236, 208, 0); }
}

h1 {
  font-size: clamp(2.7rem, 6.2vw, 5.2rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  font-weight: 800;
  margin-bottom: 28px;
  text-wrap: balance;
}

h1 em {
  font-style: normal;
  color: var(--live);
}

h2 {
  font-size: clamp(2rem, 4.4vw, 3.4rem);
  line-height: 1.06;
  letter-spacing: -0.03em;
  font-weight: 800;
  margin-bottom: 22px;
  text-wrap: balance;
}

h2.big { font-size: clamp(2.6rem, 7vw, 5.6rem); margin-bottom: 38px; }

/* A section title is pure white only while its panel is IN PLACE. Off the
   settle point — above or below — the brand pair separates out along the
   travel and stretches with the distance, so a title arrives into focus and
   leaves it again. landing.js writes --ab (a signed number, panels away from
   settled) and nothing else; the offsets are em, so h2.big splits as far as it
   is big, and 0 here is the whole no-JS and reduced-motion story. */
h1, h2 {
  --ab: 0;
  --ab-lead: var(--you);
  --ab-trail: var(--them);
  text-shadow:
    calc(var(--ab) * -0.34em) calc(var(--ab) * -1em) 0 var(--ab-lead),
    calc(var(--ab) *  0.34em) calc(var(--ab) *  1em) 0 var(--ab-trail);
}

/* "next." is already mint, so the mint edge would be an edge against itself.
   The pair swaps on that word alone — the split still reads as two colours
   pulling apart, and the direction is carried by the violet either way. */
.hero h1 em { --ab-lead: var(--them); --ab-trail: var(--live); }

.sec-copy > p {
  max-width: 34em;
  color: var(--dim);
  font-size: clamp(1rem, 1.35vw, 1.13rem);
}

.sec-copy > p + p { margin-top: 14px; }

.sec-copy .sub { font-size: 0.95rem; color: var(--faint); }

/* The local-only promise gets its own box rather than a subordinate clause. */
.callout {
  margin-top: 18px;
  max-width: 34em;
  border: 1px dashed rgba(56, 217, 190, 0.4);
  background: rgba(56, 217, 190, 0.06);
  border-radius: 12px;
  padding: 16px 18px;
}

.callout span {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--you);
  margin-bottom: 7px;
}

.callout p { color: var(--text); font-size: 0.98rem; line-height: 1.55; }

.callout + p { margin-top: 16px; }

.lede {
  color: var(--dim);
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  max-width: 36em;
  margin-bottom: 34px;
}

.hl-you  { color: var(--you); }
.hl-them { color: var(--them); }

.sec-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 26px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.sec-head .index { color: var(--faint); }

.sec-head .tag { color: var(--dim); }

.sec-head::after {
  content: "";
  flex: 0 0 56px;
  height: 1px;
  background: var(--line-strong);
}

.actions { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }

.centered-row { justify-content: center; }

.fineprint {
  margin-top: 22px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--faint);
}

.centered { text-align: center; }

.centered .sec-head { justify-content: center; }

.centered .sec-head::after { display: none; }

.centered > p, .sec-copy.centered > p { margin-inline: auto; }

/* ─────────────────────────────────────────────── layout patterns */

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(40px, 6vw, 90px);
  align-items: center;
}

.split.flip .sec-copy { order: 2; }
.split.flip .visual  { order: 1; }

@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }
  .split.flip .sec-copy { order: 1; }
  .split.flip .visual  { order: 2; }
}

/* ─────────────────────────────────────────────── reveals (GSAP-driven)

   The `js` class hides everything the script will animate in; GSAP's inline
   styles then take over. Without JS the class is absent (or removed for
   reduced motion) and everything below renders visible. */

.js .reveal,
.js .hero .kicker,
.js .hero .lede,
.js .hero .actions,
.js .hero .fineprint,
.js .hero h1,
.js .transcript,
.js .scroll-cue { opacity: 0; }

/* The headline splits into rising words: outer clips, inner translates.

   The clip is the word's own box, and at a line-height this tight that box is
   SHORTER than the glyphs in it — the half-leading goes negative, so a
   descender hangs below the box and was being cut flat ("say" lost the tail of
   its y). The padding puts that room back and the negative margin takes it out
   of the layout again: with vertical-align: bottom the margin edge is what
   aligns, so the glyphs do not move and the line box does not grow. The rise
   starts below the deeper box for the same reason — see landing.js. */
.hero h1 .w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.hero h1 .wi { display: inline-block; }

/* Once the words have landed the clip has no further job, and the focus split
   needs room on every side — it is the widest thing the headline draws. This
   is why the hero can carry the effect at all. */
.hero h1.landed .w { overflow: visible; }


/* ─────────────────────────────────────────────── hero */

/* No background of its own — it is the fixed body::after layer now, so what
   was the hero's light stays put for the whole scroll. */

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(40px, 5vw, 80px);
  align-items: center;
}

@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; }
}

.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 26px;
  width: 22px;
  height: 34px;
  margin-left: -11px;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
}

.scroll-cue::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 7px;
  width: 3px;
  height: 7px;
  margin-left: -1.5px;
  border-radius: 2px;
  background: var(--dim);
  animation: cue 2s ease-in-out infinite;
}

@keyframes cue {
  0%, 100% { transform: translateY(0);   opacity: 1; }
  60%      { transform: translateY(9px); opacity: 0.2; }
}

/* ─────────────────────────────────────────────── the live transcript card */

.transcript {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.45);
}

.t-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 12px;
  color: var(--dim);
}

.t-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ff5b5b;
  animation: rec 1.6s ease-in-out infinite;
}

@keyframes rec { 50% { opacity: 0.25; } }

.t-live { text-transform: uppercase; letter-spacing: 0.14em; color: var(--text); }

.t-title {
  margin-inline: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.t-lang { color: var(--faint); }

/* A header with no live dot/badge in front (a static list, not a call in
   progress) has only two children in .t-head, and .t-title's margin-inline:
   auto - meant to centre it between the dot and the badge - has nothing to
   push against on the right, so all the free space collapses onto the left
   and the title ends up shoved flush against the badge instead. .plain drops
   the auto-centring so the title sits left as plain text, with the badge
   pushed right by its own margin instead. */
.t-head.plain .t-title { margin-inline: 0; }
.t-head.plain .t-lang { margin-left: auto; }

.t-body {
  padding: 20px 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: 300px;
}

.t-line { display: grid; grid-template-columns: 84px 1fr; gap: 0 12px; align-items: baseline; }

/* Both the original and a translation under it live in the text column; only
   the tag sits in the first one. */
.t-line p { grid-column: 2; }
.t-line .t-tag { grid-column: 1; grid-row: 1; }

.t-tag {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: right;
  /* The column is 84px - wide enough for the longest single word among the
     tags ("TRANSLATES", "OBJECTION") to sit on one line, with a second word
     wrapping cleanly below it rather than the word itself splitting mid
     character. overflow-wrap: anywhere is the fallback for a future tag with
     a word too long even for that: it breaks mid-word instead of overflowing
     into the paragraph beside it with no gap at all. */
  overflow-wrap: anywhere;
}

.t-line.you  .t-tag { color: var(--you); }
.t-line.them .t-tag { color: var(--them); }
.t-line.hint .t-tag { color: var(--live); }

.t-line p { font-size: 14.5px; line-height: 1.5; }

.t-line.hint p {
  color: var(--live);
  border: 1px dashed rgba(69, 236, 208, 0.35);
  border-radius: 10px;
  padding: 8px 12px;
  font-size: 13.5px;
  background: rgba(69, 236, 208, 0.05);
}

.t-trans { color: var(--live); font-size: 13px !important; margin-top: 4px; opacity: 0.85; }

.t-typing { display: flex; gap: 5px; padding-left: 74px; }

.t-typing i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--faint);
  animation: typing 1.2s ease-in-out infinite;
}

.t-typing i:nth-child(2) { animation-delay: 0.18s; }
.t-typing i:nth-child(3) { animation-delay: 0.36s; }

@keyframes typing { 40% { transform: translateY(-4px); opacity: 1; } }

/* The conversation loops: each line owns a slice of one 16s cycle. */
.l1 { animation: line-1 16s infinite; }
.l2 { animation: line-2 16s infinite; }
.l3 { animation: line-3 16s infinite; }
.l4 { animation: line-4 16s infinite; }
.l5 { animation: line-5 16s infinite; }

@keyframes line-1 {
  0%, 2%   { opacity: 0; transform: translateY(10px); }
  6%, 92%  { opacity: 1; transform: none; }
  97%, 100% { opacity: 0; transform: translateY(-6px); }
}

@keyframes line-2 {
  0%, 16%  { opacity: 0; transform: translateY(10px); }
  21%, 92% { opacity: 1; transform: none; }
  97%, 100% { opacity: 0; transform: translateY(-6px); }
}

@keyframes line-3 {
  0%, 38%  { opacity: 0; transform: translateY(10px); }
  43%, 92% { opacity: 1; transform: none; }
  97%, 100% { opacity: 0; transform: translateY(-6px); }
}

@keyframes line-4 {
  0%, 60%  { opacity: 0; transform: translateY(10px) scale(0.98); }
  65%, 92% { opacity: 1; transform: none; }
  97%, 100% { opacity: 0; transform: translateY(-6px); }
}

@keyframes line-5 {
  0%, 6%   { opacity: 0; }
  10%, 88% { opacity: 1; }
  92%, 100% { opacity: 0; }
}

/* ─────────────────────────────────────────────── roster (botless) */

.roster {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
}

.r-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-radius: 10px;
}

.r-row:not(.bot):hover { background: var(--raise-2); }

.r-avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  flex: 0 0 auto;
}

.r-avatar.a1 { background: rgba(167, 139, 250, 0.16); color: var(--them); }
.r-avatar.a2 { background: rgba(167, 139, 250, 0.16); color: var(--them); }
.r-avatar.a3 { background: rgba(56, 217, 190, 0.16); color: var(--you); }

.r-name { font-size: 15px; }

.r-glyph { margin-left: auto; width: 9px; height: 9px; border-radius: 50%; }

.r-glyph.on { background: var(--live); box-shadow: 0 0 10px rgba(69, 236, 208, 0.5); }

.r-row.bot {
  border: 1px dashed var(--line-strong);
  color: var(--faint);
  opacity: 0.75;
}

.r-row.bot .r-avatar { background: transparent; border: 1px dashed var(--line-strong); color: var(--faint); }

.r-row.bot .r-name { text-decoration: line-through; font-family: var(--mono); font-size: 13.5px; }

.r-note {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ─────────────────────────────────────────────── flow diagram (private) */

.flow { width: 100%; height: auto; }

.f-node rect {
  fill: var(--raise);
  stroke: var(--line-strong);
}

.f-node.dashed rect { stroke-dasharray: 5 5; }

.f-node text {
  fill: var(--text);
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 600;
}

.f-node text.f-dim {
  fill: var(--faint);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
}

.f-wire {
  fill: none;
  stroke-width: 1.5;
}

.f-wire.audio {
  stroke: var(--you);
  stroke-dasharray: 7 7;
  animation: wire 1.4s linear infinite;
}

.f-wire.text { stroke: var(--faint); stroke-dasharray: 2 6; }

@keyframes wire { to { stroke-dashoffset: -14; } }

.f-label {
  fill: var(--faint);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.f-label.audio-l { fill: var(--you); }

/* ─────────────────────────────────────────────── languages */

.marquee {
  margin: 46px 0 40px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.chips-row {
  display: flex;
  gap: 10px;
  width: max-content;
  animation: slide 46s linear infinite;
}

.chips-row.rev { animation-direction: reverse; }

@keyframes slide { to { transform: translateX(-50%); } }

.chips-row span {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 8px 16px;
  white-space: nowrap;
  flex: 0 0 auto;
}

/* The chips drive the demo card, so the rows hold still while aiming. */
.marquee:hover .chips-row { animation-play-state: paused; }

.chips-row span.is-clickable {
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s;
}

.chips-row span.is-clickable:hover { color: var(--text); border-color: var(--line-strong); }

.chips-row span.is-lang {
  color: var(--live);
  border-color: rgba(69, 236, 208, 0.5);
}

.lang-hint {
  text-align: center;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 16px;
}

.trans-demo {
  max-width: 560px;
  margin: 0 auto;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 26px 30px;
  text-align: left;
}

/* The green leading line IS the translation — that is the product mechanic
   the card demonstrates — so it is the line the chips change. The spoken
   original holds still underneath. */
.td-lead {
  color: var(--live);
  font-size: 1.15rem;
  font-weight: 600;
  transition: opacity 0.24s ease;
}

.trans-demo.is-swapping .td-lead { opacity: 0; }

.td-orig {
  color: var(--faint);
  font-size: 0.85rem;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

.td-lang {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 6px;
}

.td-text { color: inherit; }

/* ─────────────────────────────────────────────── assist */

.assist-card {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
}

.assist-card p span {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 7px;
}

.a-q { color: var(--them); font-size: 1.05rem; }

.a-q span { color: rgba(167, 139, 250, 0.6); }

.a-a {
  color: var(--text);
  font-size: 0.98rem;
  border: 1px dashed rgba(69, 236, 208, 0.35);
  background: rgba(69, 236, 208, 0.05);
  border-radius: 12px;
  padding: 14px 16px;
}

.a-a span { color: var(--live); }

.a-chips { display: flex; gap: 8px; }

.a-chips span {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dim);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 6px 13px;
}

/* .assist and .obj-demo now sit in the same section, and two identical boxed
   panels side by side read as one repeated idea instead of two - the
   objection handler is the section's one wow card. .plain drops the card
   surface (background, border, shadow) so this reads as an inline example
   under the copy instead of a second competing panel; the .a-a answer keeps
   its own dashed highlight, so the content still has a visual anchor. */
.assist-card.plain {
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
}

/* ─────────────────────────────────────────────── after-the-call cards */

.cards {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin-top: 46px;
}

@media (max-width: 1000px) {
  .cards { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .cards { grid-template-columns: 1fr; }
}

.card {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 28px 26px;
  text-align: left;
  transition: border-color 0.25s, transform 0.25s;
}

.card:hover { border-color: var(--line-strong); transform: translateY(-3px); }

.card h3 {
  font-size: 1.1rem;
  letter-spacing: -0.01em;
  margin-bottom: 10px;
}

.card p { color: var(--dim); font-size: 0.95rem; }

/* ─────────────────────────────────────────────── use cases */

.uc { margin-top: 48px; }

.uc-tabs {
  position: relative;
  display: flex;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 36px;
}

.uc-tab {
  position: relative;
  z-index: 1;
  appearance: none;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--dim);
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 11px 22px;
  cursor: pointer;
  transition: color 0.25s, border-color 0.25s;
}

.uc-tab:hover { color: var(--text); border-color: var(--line-strong); }

.uc-tab.is-active { color: var(--bg); border-color: transparent; }

/* The sliding pill behind the active tab, moved by landing.js. Without JS it
   stays parked under the first tab, whose .is-active colours still apply. */
.uc-ink {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: var(--text);
  border-radius: 999px;
  transition: transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1), width 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
  z-index: 0;
}

html:not(.js) .uc-ink { display: none; }

html:not(.js) .uc-tab.is-active { background: var(--text); border-color: var(--text); }

.uc-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.uc-track::-webkit-scrollbar { display: none; }

.uc-slide {
  scroll-snap-align: start;
  padding: 6px 2px;
}

.uc-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 4.5vw, 70px);
  align-items: center;
  text-align: left;
}

@media (max-width: 900px) {
  .uc-grid { grid-template-columns: 1fr; }
}

.uc-copy h3 {
  font-size: 1.55rem;
  letter-spacing: -0.02em;
  margin-bottom: 12px;
}

.uc-copy p { color: var(--dim); max-width: 30em; }

.uc-knows {
  margin-top: 20px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.uc-knows span {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-right: 4px;
}

.uc-knows em {
  font-style: normal;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 13px;
  white-space: nowrap;
}

.uc-mock { box-shadow: none; }

/* ─────────────────────────────────────────────── checklist mock */

.stack { display: flex; flex-direction: column; gap: 14px; }

.check-card {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 20px 22px;
}

.ck-head {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 14px;
}

.check-card ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.check-card li {
  position: relative;
  padding-left: 24px;
  font-size: 14.5px;
}

.check-card li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 9px;
  height: 9px;
  border-radius: 50%;
}

.check-card li.ok::before { background: var(--live); box-shadow: 0 0 8px rgba(69, 236, 208, 0.45); }
.check-card li.thin::before { background: var(--warn); }
.check-card li.no::before { background: #ff5b5b; }

.check-card li.thin,
.check-card li.no { color: var(--dim); }

.ck-note {
  margin-top: 14px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
}

/* ─────────────────────────────────────────────── cursor spotlight */

.js .spot { position: relative; }

.js .spot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(240px 240px at var(--mx, 50%) var(--my, 50%),
              rgba(56, 217, 190, 0.08), transparent 65%);
  opacity: 0;
  transition: opacity 0.35s;
}

.js .spot:hover::after { opacity: 1; }

/* ─────────────────────────────────────────────── scrolled header */

.top { transition: padding 0.3s, background 0.3s; }

.top.scrolled {
  padding-top: 10px;
  padding-bottom: 10px;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
}

/* ─────────────────────────────────────────────── closer + footer */

.closer {
  background:
    radial-gradient(700px 460px at 50% 62%, rgba(167, 139, 250, 0.09), transparent 70%);
}

.foot { border-top: 1px solid var(--line); padding: 26px 0; }

.foot-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.foot-note { font-family: var(--mono); font-size: 12px; color: var(--faint); }

.foot-note a { color: var(--dim); text-decoration: none; }

.foot-note a:hover { color: var(--text); }

/* ─────────────────────────────────────────────── legal pages */

/* landing/legal/privacy.html and terms.html, generated by build_legal.mjs out
   of docs/legal/*.md. They are the landing's own furniture — the same header,
   the same footer, the same mark, this same stylesheet — wrapped around a long
   document, so the two never read as two different companies.

   What they deliberately do NOT have is landing.js: no GSAP, no Lenis, no
   scroll progress and no reveal states. A legal document has nothing to
   animate, and the entrance states are armed by the `js` class those pages
   never set, so everything here is visible from the first frame by
   construction rather than by an override. */

.legal {
  /* .top is fixed and the landing's first panel is 100svh, so nothing on the
     index has ever needed to clear it. A document that starts at the top
     does. */
  padding: 128px 0 90px;
}

/* A reading measure, not the landing's 1120px layout width: these are
   paragraphs somebody reads end to end, and 1120px of 16px prose is about 150
   characters a line. .wrap still supplies the gutter and the centring. */
.legal-wrap { max-width: 820px; }

.legal h1 {
  font-size: clamp(2.1rem, 5vw, 3.2rem);
  margin-bottom: 18px;
}

.legal-meta {
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 2;
  letter-spacing: 0.04em;
  color: var(--faint);
}

.legal-meta strong { color: var(--dim); font-weight: 500; }

/* ── the draft banner ──
   Amber, which on this page means exactly what --warn means everywhere in the
   app: nothing is broken and nothing is reassuring either. It is drawn by the
   generator only while a placeholder is unresolved, and it disappears on its
   own when they are filled — there is no switch to remember to flip. */
.legal-draft {
  margin: 30px 0 0;
  padding: 16px 18px;
  border: 1px solid color-mix(in srgb, var(--warn) 42%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--warn) 8%, transparent);
  color: var(--dim);
  font-size: 14.5px;
}

.legal-draft strong { color: var(--warn); }

/* Each unsupplied fact, in the running text. Bright enough that no reader
   mistakes one for prose — which is the whole reason it is marked rather than
   left as bare brackets somebody's eye slides over. */
.legal-todo {
  background: color-mix(in srgb, var(--warn) 22%, transparent);
  color: var(--warn);
  font-family: var(--mono);
  font-size: 0.86em;
  padding: 1px 5px;
  border-radius: 4px;
}

/* ── contents ── */

.legal-toc {
  margin: 36px 0 8px;
  padding: 20px 22px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--raise);
}

.legal-toc-head {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 12px;
}

/* No list marker and no counter: every section heading in both documents
   already opens with its own number ("1. Who we are"), and a generated one
   beside it would read "1 1. Who we are". */
.legal-toc ol {
  columns: 2;
  column-gap: 30px;
  list-style: none;
}

.legal-toc li {
  break-inside: avoid;
  font-size: 14px;
  line-height: 1.5;
  margin-bottom: 7px;
}

/* An annex is a document of its own hung off the end of the Terms, and is the
   one entry with no number of its own — so it takes a rule above it rather
   than sitting unmarked in the run. */
.legal-toc li.is-annex {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.legal-toc a {
  color: var(--dim);
  text-decoration: none;
  transition: color 0.2s;
}

.legal-toc a:hover { color: var(--text); }

/* ── the document ── */

.legal-body {
  margin-top: 40px;
  color: var(--dim);
  font-size: 15.5px;
  line-height: 1.78;
}

.legal-body p { margin-bottom: 18px; }

.legal-body strong { color: var(--text); font-weight: 600; }

.legal-body em { color: var(--text); font-style: italic; }

.legal-body code {
  font-family: var(--mono);
  font-size: 0.86em;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--raise-2);
  color: var(--dim);
}

.legal-body a {
  color: var(--you);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--you) 40%, transparent);
}

.legal-body a:hover { border-bottom-color: var(--you); }

/* A section heading, not a landing display heading: the bare h1/h2 rules above
   are sized for a panel that holds four words. `scroll-margin-top` is what
   makes the contents links land below the fixed header instead of under it. */
.legal-body h2 {
  font-size: clamp(1.25rem, 2.4vw, 1.5rem);
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--text);
  margin: 44px 0 16px;
  scroll-margin-top: 96px;
}

.legal-body h3 {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--faint);
  margin: 30px 0 12px;
  scroll-margin-top: 96px;
}

/* Annex A and Annex B are their own documents; they open with a rule the width
   of the measure rather than merely more space. */
.legal-body h2.legal-annex {
  margin-top: 58px;
  padding-top: 30px;
  border-top: 1px solid var(--line-strong);
}

/* The # beside a heading. Absolutely positioned so it costs no layout and
   cannot push a heading off its measure, and invisible until the heading is
   hovered — a document full of live # glyphs reads as scaffolding. */
.legal-anchor {
  position: absolute;
  margin-left: -1.1em;
  color: var(--faint);
  text-decoration: none;
  opacity: 0;
  transition: opacity 0.2s;
}

.legal-body h2, .legal-body h3 { position: relative; }

.legal-body h2:hover .legal-anchor,
.legal-body h3:hover .legal-anchor,
.legal-anchor:focus-visible { opacity: 1; }

.legal-body ul {
  list-style: none;
  margin: 0 0 20px;
  padding-left: 20px;
}

.legal-body li {
  position: relative;
  margin-bottom: 10px;
}

.legal-body li::before {
  content: "";
  position: absolute;
  left: -20px;
  top: 0.72em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--you) 55%, transparent);
}

.legal-body hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 34px 0;
}

/* ── tables ──
   Every table in these documents is a wide one — a processor and where it is,
   a purpose and its lawful basis. The measure is 820px and some of them are
   not, so the table scrolls inside its own box rather than pushing the page
   sideways. */
.legal-table {
  overflow-x: auto;
  margin: 0 0 24px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--raise);
}

.legal-table table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.legal-table th {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--faint);
  text-align: left;
  white-space: nowrap;
}

.legal-table th, .legal-table td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  line-height: 1.6;
}

.legal-table tbody tr:last-child td { border-bottom: 0; }

.legal-tail {
  margin-top: 54px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
  font-size: 13.5px;
  color: var(--faint);
}

.legal-tail a { color: var(--dim); }

/* Where you are standing, said the same way wherever it is said: the two legal
   pages mark the current document in the header nav, and the price list marks
   its own Pricing button. That button is in the bar on every page — it has to
   be, or the header changes shape as you move around it — so something has to
   say the press would go nowhere. The pair of declarations is what .lang-switch
   used for the current language before it was removed. */
.top-nav a[aria-current="page"] { color: var(--text); }

.top .cta.ghost[aria-current="page"] {
  color: var(--text);
  background: var(--raise);
}

@media (max-width: 720px) {
  .legal { padding-top: 104px; }
  /* One column: two columns of 14px text inside a 340px card is four words a
     line, and the contents becomes harder to scan than the document. */
  .legal-toc ol { columns: 1; }
}

/* ─────────────────────────────────────────────── motion off */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .t-line, .t-typing { opacity: 1 !important; transform: none !important; }

  /* The rule above does not reach the mark's arrival, and quietly makes it
     worse: it crushes the durations and leaves the DELAYS in place, so the
     wordmark would still sit on "bamya" for two seconds and then cut to
     b:call in a single frame — motion removed, surprise kept. Taking the
     name off lets the base state stand, which is the settled form, from the
     first frame. No !important needed: nothing above sets animation-name. */
  .mark-amya, .mark-form { animation: none; }
}

/* ─────────────────────────────────────────── objection list (objections)

   The company's own list, with the entry that just landed lit up and the
   exchange underneath it. Same surface as .roster and .assist-card - raised
   panel, hairline, 16px radius, the one deep shadow this page uses - because
   it is the same kind of object and inventing a second card treatment for it
   would read as a different product. */

.obj-demo {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
}

.ob-head {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}

/* Teal, not violet: this is your side's material, written by you in advance. */
.ob-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--you);
  flex: 0 0 auto;
}

.ob-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }

.ob-list li {
  font-size: 14.5px;
  color: var(--dim);
  padding: 9px 13px;
  border: 1px solid transparent;
  border-radius: 10px;
  transition: color 0.35s, background 0.35s, border-color 0.35s;
}

/* The one that just landed. A border and a lift rather than a colour swap:
   the row is a thing you wrote, not a status. */
.ob-list li.is-live {
  color: var(--text);
  background: var(--raise-2);
  border-color: var(--line-strong);
}

.ob-list li.is-clickable { cursor: pointer; }

.ob-exchange { display: flex; flex-direction: column; gap: 12px; }

/* Fades on the swap, exactly as .trans-demo does - the two demos cycle on the
   same idea and should not disagree about how a swap looks. */
.obj-demo.is-swapping .ob-exchange { opacity: 0; }
.ob-exchange { transition: opacity 0.24s ease; }

.ob-said,
.ob-answer { margin: 0; font-size: 0.98rem; line-height: 1.5; }

.ob-said span,
.ob-answer span {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 7px;
}

/* Their words in the far-end colour, your answer in yours - the same two
   colours the transcript uses, so the picture reads without a legend. */
.ob-said { color: var(--them); }
.ob-said span { color: rgba(167, 139, 250, 0.6); }

.ob-answer {
  color: var(--text);
  border: 1px dashed rgba(69, 236, 208, 0.35);
  background: rgba(69, 236, 208, 0.05);
  border-radius: 12px;
  padding: 14px 16px;
}

.ob-answer span { color: var(--live); }
.ob-answer em { font-style: normal; color: var(--live); }

/* ─────────────────────────────────────────── both-ways wire (integrations)

   Two lanes running in opposite directions between two nodes. A card like the
   others on this page rather than bare lanes on the background: it is the same
   kind of object, and floating rails in the middle of a column read as an
   artefact rather than a diagram.

   The chips travel by interpolating `left` AND `transform` together — at the
   half-way frame that is left: 50% with translateX(-50%), which is the chip
   centred in the lane. The obvious version animates the transform alone and
   needs the lane's width in the keyframe, which CSS will only give you through
   a container query; written as `100cqw` with no container declared, `cqw`
   falls back to the VIEWPORT and every chip shot off the end of its own rail.
   Two animated properties beat one that has to be told how wide the box is. */

.wire-demo {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
}

.wd-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
}

.wd-node {
  background: var(--raise-2);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  padding: 13px 12px;
  text-align: center;
}

.wd-node strong { display: block; font-size: 14.5px; font-weight: 600; }

.wd-node span {
  display: block;
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

.wd-lanes { display: flex; flex-direction: column; gap: 12px; }

/* The rail each chip travels. `overflow: hidden` is what makes a chip appear at
   one end and be gone at the other rather than spilling past the node. */
.wd-lane {
  position: relative;
  height: 36px;
  border-top: 1px dashed var(--line-strong);
  border-bottom: 1px dashed var(--line-strong);
  overflow: hidden;
}

.wd-chip {
  position: absolute;
  top: 50%;
  left: 0;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 999px;
  padding: 5px 11px;
  opacity: 0;
  transform: translateY(-50%);
  animation: wd-travel 10.5s linear infinite;
}

/* Teal leaves us, violet arrives from them - the two colours the transcript
   already uses for the two sides, so the picture needs no legend. */
.wd-lane.out .wd-chip {
  color: var(--you);
  border: 1px solid rgba(56, 217, 190, 0.4);
  background: rgba(56, 217, 190, 0.07);
}

.wd-lane.in .wd-chip {
  color: var(--them);
  border: 1px solid rgba(167, 139, 250, 0.4);
  background: rgba(167, 139, 250, 0.07);
  animation-name: wd-travel-back;
}

/* Negative delays: each chip starts mid-flight, so the rail is populated on
   the first frame instead of filling up over the first ten seconds. */
.wd-chip.d2 { animation-delay: -7s; }
.wd-chip.d3 { animation-delay: -3.5s; }

/* ONE chip in flight per lane, not three. Each crosses during its own third of
   the cycle and waits out the other two thirds parked and invisible, which the
   three delays below then chain into a continuous single file.

   Written that way because the rail is about 230px and a label like "Who you're
   meeting" is 150 of them: three chips evenly spread along a path shorter than
   two of them laid end to end is three chips printed on top of each other, which
   is exactly what it looked like. A diagram whose parts overlap is not a
   simpler diagram, it is an unreadable one. */
@keyframes wd-travel {
  0%   { left: 0;    transform: translate(0, -50%);       opacity: 0; }
  4%   { opacity: 1; }
  29%  { opacity: 1; }
  33%  { left: 100%; transform: translate(-100%, -50%);   opacity: 0; }
  100% { left: 100%; transform: translate(-100%, -50%);   opacity: 0; }
}

@keyframes wd-travel-back {
  0%   { left: 100%; transform: translate(-100%, -50%);   opacity: 0; }
  4%   { opacity: 1; }
  29%  { opacity: 1; }
  33%  { left: 0;    transform: translate(0, -50%);       opacity: 0; }
  100% { left: 0;    transform: translate(0, -50%);       opacity: 0; }
}

.wd-note {
  margin: 0;
  text-align: center;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

/* ─────────────────────────────────────────── two devices (multi-device)

   One records, the other answers a beat later. The beat is the whole picture,
   so the phone's answer is delayed against the laptop's line rather than both
   arriving together. */

.dev-demo {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 0.8fr);
  align-items: end;
  gap: 18px;
}

/* Narrow enough to read as a handset rather than as a second, smaller card.
   Both sit on the same baseline, which is what `align-items: end` above is
   for - a phone floating level with the top of a laptop is neither object. */
.dv-phone { max-width: 240px; justify-self: end; width: 100%; }

.dv-laptop,
.dv-phone {
  background: var(--raise);
  border: 1px solid var(--line);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
  display: flex;
  flex-direction: column;
}

.dv-laptop { border-radius: 14px; padding: 12px 12px 10px; }
.dv-phone { border-radius: 22px; padding: 10px 10px 12px; }

.dv-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 2px 4px 10px;
  border-bottom: 1px solid var(--line);
}

/* The one live thing on the laptop, on the page's existing rec keyframe. */
.dv-rec {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warn);
  box-shadow: 0 0 10px rgba(255, 180, 84, 0.5);
  animation: rec 1.6s ease-in-out infinite;
}

.dv-screen { padding: 12px 4px 4px; display: flex; flex-direction: column; gap: 10px; }

.dv-line { margin: 0; font-size: 13.5px; line-height: 1.45; }

.dv-line span {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 4px;
}

.dv-line.them { color: var(--text); }
.dv-line.them span { color: var(--them); }
.dv-line.you { color: var(--dim); }
.dv-line.you span { color: var(--you); }

.dv-notch {
  width: 46px;
  height: 4px;
  border-radius: 999px;
  background: var(--line-strong);
  margin: 2px auto 8px;
}

.dv-tag {
  margin: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--live);
}

.dv-ans {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text);
}

.dv-chk {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 12px;
  color: var(--dim);
}

.dv-chk i {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  border: 1px solid rgba(56, 217, 190, 0.5);
  background: rgba(56, 217, 190, 0.12);
  flex: 0 0 auto;
}

/* The phone's contents arrive after the laptop's, which is the second the copy
   beside this promises. `both` so they hold their end state between loops. */
.dv-phone .dv-screen > * {
  opacity: 0;
  animation: dv-land 9s ease-in-out infinite both;
}

.dv-phone .dv-ans { animation-delay: 0.35s; }
.dv-phone .dv-chk { animation-delay: 0.7s; }

@keyframes dv-land {
  0%   { opacity: 0; transform: translateY(8px); }
  8%   { opacity: 1; transform: translateY(0); }
  88%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(0); }
}

.dv-foot {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: center;
  padding-top: 10px;
}

/* Stacked, the two lanes and the two devices need the room the split gave
   them. Same breakpoint the rest of the page changes shape at. */
@media (max-width: 900px) {
  /* `.wd-row` and not `.wire-demo` - the grid moved down a level when the demo
     became a card, and a stacking rule left pointing at the wrapper does not
     fail, it silently does nothing: the two nodes stayed side by side with a
     90px rail between them and the chips were cut in half by their own lane. */
  .wd-row { grid-template-columns: minmax(0, 1fr); }
  .dev-demo { grid-template-columns: minmax(0, 1fr); align-items: stretch; }
  .dv-phone { max-width: none; justify-self: stretch; }
}

/* Nothing here may move for a reader who asked for stillness. The travelling
   chips and the arriving answer are the two that would. */
@media (prefers-reduced-motion: reduce) {
  .wd-chip,
  .dv-rec,
  .dv-phone .dv-screen > * { animation: none; }
  .wd-chip { opacity: 1; position: static; transform: none; left: auto; }
  .wd-lane { height: auto; display: flex; gap: 8px; padding: 8px 0; flex-wrap: wrap; }
  .dv-phone .dv-screen > * { opacity: 1; }
}

/* ─────────────────────────────────────────────────── pricing (en/pricing.html)

   Its own page: a price list is what somebody links to on its own, and it is
   where a buyer arrives directly.

   A TABLE, not three cards. Cards make a reader hold three lists in their head
   and compare them from memory; a row per feature puts the comparison on the
   page, which is the thing somebody choosing a tier is actually doing. Real
   table markup so it is read as tabular data, with a word beside every tick for
   anyone who cannot see the glyph. */

.cmp-wrap { margin-top: 40px; overflow-x: auto; }

/* Shrink-to-fit and centred, rather than stretched across the whole 1320px
   wrap. Stretched, the feature column absorbed every spare pixel and pushed the
   three tier columns hard against the right-hand edge - so the thing being
   compared sat in the corner while the names it belonged to were a screen away
   from it. Sized to its own contents the table centres as one block, which puts
   the tick columns about the middle of the page with the names immediately to
   their left, where the eye crosses from one to the other in one movement. */
.cmp {
  width: auto;
  max-width: 100%;
  min-width: 620px;
  margin-inline: auto;
  border-collapse: collapse;
  text-align: left;
}

/* The whole row lights up under the pointer, not the cell. Reading this table
   is a horizontal act - a name on the left, a mark three columns over - and the
   row is what carries the eye between them. */
.cmp tbody tr:not(.cmp-group):hover { background: var(--raise); }

/* Sticky column heads, but only where the table already fits: `overflow-x` on
   the wrapper makes it a scroll container on BOTH axes, which is what would
   break the stickiness. Above 1000px there is nothing to scroll sideways, so
   the wrapper stops being one and the heads pin under the site header. */
@media (min-width: 1001px) {
  .cmp-wrap { overflow: visible; }
  .cmp thead th {
    position: sticky;
    top: 60px;
    z-index: 2;
    /* Opaque, unlike the site header above it. A translucent bar over prose is
       a nice effect; over a table it lets the rows it is covering show through
       its own column headings, and two sets of words in one box is not a
       reading of either. */
    background: var(--bg);
  }
}

.cmp th,
.cmp td {
  border-bottom: 1px solid var(--line);
  padding: 12px 14px;
  vertical-align: top;
}

.cmp thead th {
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 16px;
}

/* The feature column carries the name and, in the detailed table, the line that
   says what it is. Left, and the only column that is. */
.cmp-what { width: 320px; font-weight: 400; }

/* An empty column on the right, the same width as the names on the left, so the
   three tick columns sit on the page's own centre line rather than 130px to the
   right of it. `aria-hidden` on every one of its cells: it is a margin that
   happens to be made of table cells, and a screen reader should no more
   announce it than it would announce a gutter. Gone below 560px, where the
   table needs every pixel it can get to fit without scrolling sideways. */
.cmp-pad { width: 320px; padding: 0; }

/* The `?` on every row, to the LEFT of the name. Absolutely positioned rather
   than made a flex item, so the name and its three-word note keep the block
   layout they already had and no wrapper element has to be introduced between
   them and the rules that style them. The padding on the cell is what makes
   room for it. Only tbody rows carry one, so the "Feature" head is untouched. */
.cmp tbody .cmp-what { position: relative; padding-left: 44px; }

.cmp-help {
  position: absolute;
  left: 12px;
  top: 11px;
  display: none;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: transparent;
  color: var(--dim);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

/* Shown only once something is listening for the press. `js` cannot be the
   signal: it is stripped again under reduced motion, which would take the
   explanations away from exactly the readers most likely to want them. */
.help-on .cmp-help { display: block; }

.cmp-help:hover,
.cmp-help:focus-visible { color: var(--text); border-color: var(--you); background: var(--raise-2); }

.cmp-what b { display: block; font-size: 14.5px; font-weight: 600; color: var(--text); }

.cmp-what span {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--faint);
}

/* Wide enough for the note under the price to sit on ONE line. It was 132px,
   which left 104px inside the cell's own padding for a line that measures 119px
   at this size - so all three heads read "30 H ·" over "1 PERSON", breaking the
   hours off the people they are for. The width is the room; the `nowrap` below
   is the guarantee, since the table algorithm narrows these columns again as
   soon as the viewport does. */
.cmp-col { width: 152px; text-align: center; }

.cmp-tier {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}

.cmp-price {
  display: block;
  margin-top: 6px;
  font-size: 1.45rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* The hours and the seat count are stated HERE and nowhere else in the table -
   they used to be the first two rows as well, which said one number twice. So
   this is set to be read rather than skimmed past: white, and a fifth larger
   than the other micro-labels. */
.cmp-note {
  display: block;
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text);
}

/* Only Pro carries one - the parenthetical explaining that the seat and hour
   counts above are a floor, not a cap. Sentence case and dim, unlike
   .cmp-note: it is a qualifier on the fact above it, not a second fact of the
   same weight, and forcing it onto one line would fight the column width the
   short cells below it actually need. */
.cmp-subnote {
  display: block;
  margin-top: 4px;
  font-size: 13.5px;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  white-space: normal;
  color: var(--dim);
}

/* The middle column is marked on its own head rather than tinted down its whole
   length: a coloured column reads as a column that is different in kind, and
   these three differ only in what is ticked in them. */
.cmp-col.is-featured .cmp-tier { color: var(--you); }

.cmp-group th {
  border-bottom: 0;
  padding: 30px 14px 6px;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.cmp tbody td { text-align: center; }

/* Teal for yes, because it is the only colour on this page that means anything
   and here it means included. A dash rather than a cross for no: a column of
   crosses reads as a list of what you are being denied, and the honest shape of
   these tiers is that each is the one below it plus more. */
.is-yes span[aria-hidden] { color: var(--you); font-size: 15px; }
.is-no span[aria-hidden] { color: var(--line-strong); font-size: 15px; }

.is-num {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text);
}

/* A word for every mark, for anyone reading the page aloud: a row of bare ticks
   is a row of nothing when the glyph cannot be seen. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The trial, under the table rather than as a fourth column: it is not a tier
   somebody chooses between, it is how everybody starts. */
.tier-free {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-top: 26px;
  border: 1px dashed rgba(56, 217, 190, 0.35);
  background: rgba(56, 217, 190, 0.05);
  border-radius: 16px;
  padding: 20px 24px;
}

.tier-free p { margin: 0; font-size: 14.5px; line-height: 1.55; color: var(--dim); max-width: 62ch; }

.tier-free b { color: var(--text); font-weight: 600; }

.tier-free .cta { flex: 0 0 auto; }

/* ---- the integration note ---- */

.int-note {
  margin-top: 40px;
  max-width: 860px;
  margin-inline: auto;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 28px 30px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
}

.int-note p { margin: 0; font-size: 15px; line-height: 1.6; color: var(--dim); }

.int-note b { color: var(--text); font-weight: 600; }

/* The one paragraph that is a definition rather than a term, set apart so the
   commercial terms above it read as the commercial terms. */
.int-what {
  border-top: 1px solid var(--line);
  padding-top: 16px;
}

/* ---------------------------------------------------------------- help modal
   What a `?` on a comparison row opens. One dialog, filled from the button
   that was pressed. It is deliberately small and centred rather than a sheet:
   forty words does not want the whole window, and the row you pressed should
   still be visible around it. */

.hm-back {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.62);
  overscroll-behavior: contain;
}

/* Beats the `display: flex` above on specificity, so the attribute alone
   opens and closes it and there is no second class to keep in step. */
.hm-back[hidden] { display: none; }

.hm-card {
  position: relative;
  width: min(420px, 100%);
  padding: 26px 28px 28px;
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  background: var(--raise);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}

.hm-kicker {
  margin: 0 0 10px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

/* --sans, not the label voice: this is the row's own name, which is prose. */
.hm-title { margin: 0 0 12px; font-size: 1.15rem; font-weight: 600; color: var(--text); }

.hm-body { margin: 0; font-size: 14px; line-height: 1.62; color: var(--dim); }

.hm-close {
  position: absolute;
  top: 10px;
  right: 12px;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--faint);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.hm-close:hover,
.hm-close:focus-visible { color: var(--text); background: var(--raise-2); }

/* On a phone the table FITS rather than scrolling sideways. A comparison whose
   columns are off the right-hand edge, with nothing on screen saying so, is a
   list of features with the answer missing - which is the one thing this page
   exists to give. What gives way instead is the three-word explanation under
   each feature name and the price repeated in the column head: the name carries
   the meaning, and the price is two rows down the table anyway. */
@media (max-width: 560px) {
  .cmp { min-width: 0; }
  .cmp-pad { display: none; }
  .cmp th, .cmp td { padding: 10px 5px; }
  /* The `?` stays — it is worth more here than anywhere, since the note under
     each name is the thing this width gives up. It keeps a 20px target and
     takes its room from the cell's own padding rather than from the name. */
  .cmp tbody .cmp-what { padding-left: 26px; }
  .cmp-help { left: 2px; top: 9px; }
  .cmp-what b { font-size: 12.5px; line-height: 1.35; }
  .cmp-what span { display: none; }
  .cmp-col { width: 58px; }
  .cmp-tier { font-size: 9.5px; letter-spacing: 0.06em; }
  .cmp-price { font-size: 0.95rem; margin-top: 4px; }
  .cmp-note { display: none; }
  .cmp-subnote { display: none; }
  .is-num { font-size: 9.5px; letter-spacing: 0; text-transform: none; }
  .is-yes span[aria-hidden], .is-no span[aria-hidden] { font-size: 13px; }
  .cmp-group th { padding: 22px 5px 4px; font-size: 9.5px; }
  .int-note { padding: 22px 20px; }
}
