/*
 * Checklist item 19 — layout and components for every public page.
 *
 * Read tokens.css first: every colour, radius, shadow and curve is a custom
 * property declared there, including the decision that the palette is warm
 * paper rather than white and that there is no dark mode.
 *
 * This file is a port of the landing-page template the site was rebuilt onto.
 * The template is a React app that leans on Tailwind's preflight and on inline
 * `style` attributes; neither is available here, and the differences that
 * forces are the only places this file departs from it:
 *
 *  - **Not one value arrives as a `style=""` attribute.** The CSP is
 *    `default-src 'none'` with `style-src 'self'`, which bans inline styles
 *    outright — so everything the template expressed inline is a class here:
 *    the stagger delays (`.delay-1`…`.delay-6`), the six scene colours
 *    (`.tone-1`…`.tone-6`, which set one `--tone` custom property that every
 *    tinted surface reads back), and the bar-chart heights (`:nth-child`).
 *    `tests/render.test.ts` and `scripts/preview.ts` both assert that no
 *    rendered document contains a `style` attribute, so this is enforced rather
 *    than merely intended.
 *  - **There is no Tailwind preflight**, so the reset the template took for
 *    granted is written out at the top of this file.
 *  - **Interactive sections degrade without JavaScript.** The template's tabs,
 *    plan switcher and accordion are React state. Here they are server-rendered
 *    with the first item open and `site.js` takes over; `nojs.css`, linked from
 *    a `<noscript>` in the head, expands all of them and hides the controls that
 *    would do nothing. A `<noscript>` containing a `<link>` is CSP-clean, and it
 *    means the no-JS page is complete rather than one sixth of a page.
 *
 * Ordering follows the document: shell, header, home page top to bottom, then
 * the inner-page system, then the download page, then motion, then the two
 * breakpoints. Motion is deliberately last and in one block rather than sprayed
 * through the components, because `prefers-reduced-motion` has to be able to
 * switch all of it off in one place and be believed.
 */

/* ------------------------------------------------------------------ reset - */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* The header is fixed, so an in-page anchor would otherwise land under it. */
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  cursor: pointer;
}

img {
  max-width: 100%;
  height: auto;
}

h1,
h2,
h3,
p,
ul,
ol {
  margin-top: 0;
}

/*
 * The template sets headings very tight — a negative tracking of up to 5px at
 * 80px. That is a Latin decision and it is actively wrong for Chinese: CJK
 * glyphs are full-width by design and pulling them together closes the optical
 * gap the reader uses to segment words. `:lang()` rather than a body class, so
 * a Chinese pull quote inside an English page is also spared.
 */
h1 {
  margin-bottom: 26px;
  font-size: clamp(46px, 6.2vw, 80px);
  font-weight: 600;
  letter-spacing: -5px;
  line-height: 0.96;
}

h2 {
  margin-bottom: 19px;
  font-size: clamp(36px, 4.3vw, 58px);
  font-weight: 500;
  letter-spacing: -3.7px;
  line-height: 0.99;
}

h3 {
  font-size: 34px;
  font-weight: 500;
  letter-spacing: -2.3px;
  line-height: 1.02;
}

:lang(zh-Hans) h1 {
  letter-spacing: -3px;
  line-height: 1.06;
}

:lang(zh-Hans) h2 {
  letter-spacing: -2px;
  line-height: 1.12;
}

:lang(zh-Hans) h3 {
  letter-spacing: -1.2px;
  line-height: 1.2;
}

/* -------------------------------------------------------- shared furniture - */

.section-shell {
  width: min(var(--shell-width), calc(100% - var(--shell-gutter)));
  margin-inline: auto;
}

.main {
  display: block;
}

/* First focusable element on every page. Off-screen until focused, then it
   lands below the fixed header rather than under it. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  z-index: 40;
  padding: 12px 18px;
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: var(--paper);
  font-size: 13px;
  font-weight: 700;
  transition: top var(--duration) var(--ease);
}

.skip-link:focus {
  top: 16px;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

.eyebrow {
  margin: 0 0 16px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.05px;
}

:lang(zh-Hans) .eyebrow {
  font-size: 11px;
  letter-spacing: 0.6px;
}

.lede {
  max-width: 440px;
  color: var(--muted);
  font-size: 16px;
  line-height: 1.42;
}

.button {
  display: inline-flex;
  gap: 9px;
  align-items: center;
  justify-content: center;
  padding: 11px 16px;
  border: 1px solid var(--ink);
  border-radius: var(--radius-pill);
  font-size: 14px;
  font-weight: 700;
  transition: transform var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
}

.button-dark {
  background: #090909;
  color: #fff;
}

.button-light {
  background: #fff;
  color: var(--ink);
}

/* The rotated disc the template puts inside every button and text link. It is
   `aria-hidden` in the markup, so it carries no meaning that has to survive. */
.arrow {
  display: inline-flex;
  width: 20px;
  height: 20px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: currentColor;
  color: var(--ink);
  font-size: 13px;
  line-height: 1;
  transform: rotate(45deg);
}

.button-light .arrow,
.text-link .arrow,
.story-link .arrow,
.case-more .arrow {
  background: var(--ink);
  color: #fff;
}

.text-link {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  margin-top: 16px;
  font-size: 13px;
  font-weight: 700;
}

/* ------------------------------------------------------------------ header - */

/*
 * A floating capsule, not a full-width bar. It is `position: fixed` and centred
 * with a translate, which is also why the width is a `min()` rather than a
 * percentage: at 510px it holds the wordmark, four links and two actions, and
 * below 580px the links move into a sheet underneath it.
 */
.site-header {
  position: fixed;
  top: 22px;
  left: 50%;
  z-index: 20;
  display: flex;
  width: min(690px, calc(100% - 32px));
  height: 58px;
  align-items: center;
  padding: 7px 8px 7px 13px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 253, 0.92);
  box-shadow: var(--shadow-header);
  backdrop-filter: blur(12px);
  transform: translateX(-50%);
}

.brand {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.8px;
}

/* The real app mark, at the size the header draws it. `width`/`height` on the
   element state the file's intrinsic 192×192, so the box is reserved before a
   byte arrives; this only scales it down. */
.brand-mark {
  width: 24px;
  height: 24px;
  border-radius: 7px;
}

.brand-name {
  white-space: nowrap;
}

.nav-links {
  display: flex;
  gap: 20px;
  margin-left: auto;
  margin-right: 16px;
}

.nav-link {
  font-size: 13px;
  font-weight: 600;
}

.nav-link:hover,
.footer-link:hover,
.text-link:hover,
.lang-switch:hover,
.back-link:hover {
  opacity: 0.55;
}

/* The current page, marked `aria-current` in the markup so the underline is not
   the only thing carrying it. */
.nav-link[aria-current="page"] {
  text-decoration: underline;
  text-underline-offset: 5px;
}

.lang-switch {
  margin-right: 10px;
  font-size: 13px;
  font-weight: 700;
}

.header-cta {
  padding: 10px 13px;
  font-size: 13px;
  white-space: nowrap;
}

/* Hidden until the narrow breakpoint, where it becomes the only nav control. */
.menu-toggle {
  display: none;
  margin-left: auto;
  padding: 8px;
  border: 0;
  background: transparent;
}

.menu-bar {
  display: block;
  width: 20px;
  height: 2px;
  background: #111;
}

.menu-bar + .menu-bar {
  margin-top: 4px;
}

/* -------------------------------------------------------------------- hero - */

/*
 * Full width, not `section-shell`. The product shot in the right-hand column
 * runs past the edge of the screen on purpose, and `overflow: hidden` clips at
 * this element — so if the hero were the 1120px measure, the window would be
 * sliced off mid-page with paper beside it instead of continuing off-screen.
 * The measure is applied to `.hero-inner` instead.
 */
.hero {
  position: relative;
  padding: 112px 0 var(--gap-block);
  overflow: hidden;
}

/*
 * Two columns, and the copy column is CAPPED at 470px rather than being a
 * fraction — every pixel given to the copy is a pixel of interface pushed off
 * the screen. The window is anchored to the left of the right-hand track and
 * runs off the viewport, so the copy's width is what decides how much of the
 * application is on screen; a pure fraction would hand more of it away on every
 * wider monitor, exactly where there is room to show more.
 *
 * 470 is the narrowest width at which the Chinese headline still sets
 * `让 Claude Code` on one line at its full clamped size. Below the cap the
 * headline is shrinking on its own `vw` term, which is why the percentage floor
 * is allowed to take over on narrower shells — at 43px the same line needs
 * about 300px and the column has more than that everywhere the two-column
 * layout still applies.
 *
 * `minmax(0, …)` on the right track, not `1fr`: a grid track's default
 * `min-width: auto` grows to fit its content, so the oversized window would
 * stretch its own column, push the layout wide and produce a horizontal
 * scrollbar on the whole document. Pinning the minimum to 0 is what lets the
 * window overflow visually without affecting anything's size.
 *
 * `align-items: start` so the panel takes the top of the row; the copy opts
 * back out with `align-self: center` and carries a trailing margin instead —
 * see `.hero-copy` for why the slack between a 380px column of text and an
 * 880px panel must not be split evenly.
 */
.hero-inner {
  /* Tighter than the feature panels below. Those are pictures with a caption
     beside them and can afford a frame; this one is trying to get an
     application in front of someone, and every millimetre of gradient here is
     a millimetre of interface that is not on the screen. */
  --shot-pad-block: clamp(18px, 2.6vw, 38px);
  --shot-pad-inline: clamp(20px, 3vw, 44px);
  display: grid;
  align-items: start;
  gap: 40px;
  grid-template-columns: minmax(0, clamp(300px, 44%, 470px)) minmax(0, 1fr);
}

/* The faint vertical rule field behind the hero. Inset negatively so the rules
   run past the shell's gutters instead of stopping at them. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0 -150px;
  background: repeating-linear-gradient(90deg, transparent 0 58px, rgba(48, 48, 42, 0.035) 59px 60px);
  pointer-events: none;
}

/*
 * The measure is the grid track (see `.hero-inner`), not a `max-width` here.
 *
 * ABOVE the optical centre, which is what the trailing margin buys. The copy is
 * ~380px against a ~880px panel, and the two ways of resolving that 500px of
 * slack are both wrong on their own: centred, it puts 250px of empty paper
 * above the eyebrow and pushes the buttons onto the fold of a laptop screen;
 * top-aligned, the text is stranded at the ceiling with a 500px hole beneath
 * it. The margin is a phantom block under the copy that gets centred with it,
 * so the slack lands roughly a third above and two thirds below — the text
 * reads as high in the frame, and the space that is left is at the bottom of
 * the section where the next one is coming anyway.
 */
.hero-copy {
  position: relative;
  z-index: 1;
  align-self: center;
  margin-bottom: 180px;
}

/*
 * Smaller than the global `h1`, because this one no longer has the page to
 * itself: it shares the hero with the product shot, so it sets inside a ~515px
 * column rather than across 1120px. At the inherited 80px the Chinese headline
 * broke as `让 Claude / Code`, splitting the product's name across two lines —
 * the one break that must not happen in the first sentence on the site.
 *
 * The `vw` term still does the work between breakpoints; only the ceiling and
 * the floor moved.
 */
.hero-title {
  margin-bottom: 24px;
  font-size: clamp(42px, 4.6vw, 64px);
}

.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 32px;
}

.hero-note {
  margin: 18px 0 0;
  color: var(--muted);
  font-size: 13px;
}

/* ---------------------------------------------------------- platform strip - */

/*
 * The template's customer-logo marquee, carrying the one claim we can make: the
 * three desktops every release is built for. The track is duplicated in the
 * markup and translated by exactly -50%, which is what makes the loop seamless.
 */
.platform-line {
  padding-bottom: var(--gap-tight);
  overflow: hidden;
}

.platform-line-title {
  margin: 0 0 14px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1px;
  text-align: center;
}

/* The same correction `.eyebrow` already carries, and which this one missed.
   At 11px with 1px of tracking, full-width Chinese characters have nothing
   left between them and the line sets as one solid block: the tracking that
   opens up Latin small caps closes up CJK. */
:lang(zh-Hans) .platform-line-title {
  font-size: 12px;
  letter-spacing: 0.6px;
}

.logo-track {
  display: flex;
  width: max-content;
  gap: 43px;
  padding-left: 43px;
  color: #c7c6c1;
  font-size: 17px;
  font-weight: 700;
  white-space: nowrap;
}

/* `site.js` marks the visitor's own platform. With JavaScript off, all three
   render un-highlighted, which is the complete and correct page. */
.logo-item.is-detected {
  color: var(--ink);
}

.platform-line-note {
  margin: 16px 0 0;
  color: var(--muted);
  font-size: 13px;
  text-align: center;
}

/* ------------------------------------------------------------------- steps - */

.steps {
  display: grid;
  gap: 7px;
  grid-template-columns: 1.55fr 1fr 1fr;
}

.step-card {
  position: relative;
  min-height: 285px;
  padding: 23px 25px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--sand);
}

/* Above `.setup-card`, which hangs out of the top of the first card and is
   6px further left than this — so without the stacking context the "0" of "01"
   is painted over and the card reads as step 1 while the others read 02, 03. */
.step-number {
  position: absolute;
  z-index: 1;
  top: 22px;
  right: 22px;
  color: #73736f;
  font-size: 12px;
}

.step-title {
  margin: 105px 0 12px;
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -1.7px;
  line-height: 1.05;
}

:lang(zh-Hans) .step-title {
  letter-spacing: -1px;
}

.step-body {
  max-width: 210px;
  margin-bottom: 0;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.36;
}

/* The illustration hanging out of the top of the first card. */
.setup-card {
  position: absolute;
  top: 0;
  right: 28px;
  width: 230px;
  height: 180px;
  padding: 24px 19px;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  background: linear-gradient(135deg, #ffdac9, #e9e2fe 70%, #fff);
}

.setup-title {
  font-size: 12px;
}

.setup-line {
  height: 30px;
  margin-top: 10px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.85);
}

/* ---------------------------------------------------------------- features - */

.feature-section {
  padding: var(--gap-section) 0 var(--gap-block);
}

.section-heading {
  text-align: center;
}

.section-title {
  margin-bottom: 18px;
}

.section-intro {
  max-width: 430px;
  margin: 0 auto;
  color: var(--muted);
  font-size: 16px;
  line-height: 1.42;
}

.feature-tabs {
  display: flex;
  justify-content: space-between;
  margin: 48px auto 12px;
  padding: 6px;
  border-radius: var(--radius-pill);
  background: var(--tint);
}

.feature-tab {
  padding: 13px 18px;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: #676762;
  font-size: 13px;
}

.feature-tab.is-active {
  background: #fff;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
  color: var(--ink);
  font-weight: 700;
}

.feature-stack {
  display: grid;
}

/*
 * All five panels are rendered. With `boot.js` in play they are stacked in one
 * grid cell — so switching tabs cannot change the section's height and shove
 * the page under the reader — and four of them carry `hidden`, which also
 * removes them from the a11y tree. Without it they are five rows, in order,
 * which is a longer section and a complete one. See boot.js.
 */
.feature-display {
  display: grid;
  min-height: 345px;
  grid-template-columns: 1.05fr 1fr;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  background: var(--card);
}

.js .feature-display {
  grid-area: 1 / 1;
}

.js .feature-display[hidden] {
  display: none;
}

/* The tab bar and the plan switcher drive nothing without the script, and the
   plus/minus is a control for an accordion that is already open. */
html:not(.js) .feature-tabs,
html:not(.js) .plan-switcher,
html:not(.js) .faq-sign {
  display: none;
}

html:not(.js) .feature-stack,
html:not(.js) .plan-stack {
  gap: 10px;
}

/* The card's picture side. The surface and the padding come from `.shot`; all
   this adds is the vertical centring and the reset of the figure's margin. */
.feature-art {
  align-items: center;
  margin: 0;
}

.feature-copy {
  align-self: center;
  padding: 52px;
}

.feature-label,
.price-label {
  display: inline-block;
  padding: 6px 9px;
  border-radius: var(--radius-pill);
  background: var(--yellow);
  font-size: 10px;
  font-weight: 700;
}

/* 420, not 360: at 360 the Chinese heading wrapped between the two characters
   of 会话, which is a word break rather than a line break. */
.feature-heading {
  max-width: 420px;
  margin: 22px 0 18px;
}

.feature-body {
  max-width: 370px;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.42;
}

/*
 * A viewport onto the screenshot rather than the whole screenshot.
 *
 * The captures are 2960×1860 — a 1480×930 window at 2×. Shown whole in this
 * column they render at ~19%, which puts the app's 13px UI text under 3px: five
 * blocks of grey smudge. There is no layout that fixes that, because fitting
 * 1480 logical pixels into 560 is a 62% reduction no matter how the tracks are
 * cut. The only way to make the detail legible at the size it is displayed is
 * to show less of it.
 *
 * That reduction is also exactly why the workbench is not one of these panels
 * any more: it is the whole window rather than a pane inside it, so there is no
 * rectangle of it worth cropping to. It runs full-bleed under the hero instead
 * — see `.preview-stage`.
 *
 * The crop is done in CSS, not by cutting new PNGs: the `<img>` keeps its real
 * intrinsic 2960×1860, which is what `renderImage` promises and what stops the
 * browser reserving a wrong box. Layout stability comes from `aspect-ratio`
 * here instead — the image is out of flow, so the frame's height is known
 * before a byte of it arrives.
 *
 * Each `.media-crop-*` states its crop rectangle in source pixels and derives
 * the three values from it, so retuning a crop means editing the four numbers
 * in the comment and nothing else:
 *
 *    width = 2960 / crop-w        left = -crop-x / crop-w
 *    top   = -crop-y / crop-h     (all as percentages of this box)
 *
 * Every crop keeps the same 140:94 aspect so the five panels stay a set.
 */
.media-crop {
  position: relative;
  width: 100%;
  aspect-ratio: 140 / 94;
  overflow: hidden;
  border-radius: var(--radius-md);
  background-color: #fff;
  box-shadow: var(--shadow-panel);
}

.media-img {
  display: block;
  position: absolute;
  height: auto;
  /* `img { max-width: 100% }` in the reset would clamp the zoom back to 1:1. */
  max-width: none;
}

/* 2300×1544 at (0, 30): the project list, the board's toolbar and the first
   five columns. Wider than the settings crops — the alternatives that reach the
   app's own scale drop the board to two and a half columns, and a board with
   two columns is not a board. */
.media-crop-board .media-img {
  width: calc(2960 / 2300 * 100%);
  left: calc(0 / 2300 * -100%);
  top: calc(30 / 1544 * -100%);
}

/* 1400×940 at (980, 240): the Skills pane — search, the installed skills with
   their checkboxes, and the Personal/System/Recommended tabs. */
.media-crop-skills .media-img {
  width: calc(2960 / 1400 * 100%);
  left: calc(980 / 1400 * -100%);
  top: calc(240 / 940 * -100%);
}

/* 1400×940 at (1050, 150): the Scheduled tasks page down to the last of the
   three suggestions and their times. */
.media-crop-scheduled .media-img {
  width: calc(2960 / 1400 * 100%);
  left: calc(1050 / 1400 * -100%);
  top: calc(150 / 940 * -100%);
}

/* 1400×940 at (978, 300): the Model services pane. 978 rather than a round
   1000 puts the crop's right edge on the settings sheet's own edge, so no
   sliver of the window behind it shows. */
.media-crop-providers .media-img {
  width: calc(2960 / 1400 * 100%);
  left: calc(978 / 1400 * -100%);
  top: calc(300 / 940 * -100%);
}

/* 1400×940 at (978, 420): appearance, interface language and reply language,
   which is what the copy beside it talks about. */
.media-crop-settings .media-img {
  width: calc(2960 / 1400 * 100%);
  left: calc(978 / 1400 * -100%);
  top: calc(420 / 940 * -100%);
}

/* ------------------------------------------------------------ app preview - */

/*
 * The scenery panel: a soft, rounded surface that the app window sits ON.
 *
 * This is the difference between a screenshot and a product shot, and it is the
 * one thing the page was missing. A window dropped straight onto the paper has
 * to carry its own separation — a border, a shadow — and it still reads as part
 * of the document, because the surface behind it is the document. Give it a
 * surface of its own, with air on all four sides, and it reads as an object
 * being presented. Every attempt to fix this with the window's own styling was
 * treating the symptom.
 *
 * The backdrop is a CSS gradient rather than a photograph, and that is a
 * constraint turned into a decision: the CSP is `img-src 'self'` with no
 * `data:`, so a photograph would be a real file to ship, localise-proof and
 * keep in sync. A gradient costs nothing, never 404s, and tints from the same
 * `--tone` table the six scenes already use — so a panel's colour is stated in
 * one place and cannot drift between the pages that draw it.
 */
/*
 * The panel's air, as two inherited custom properties rather than a literal.
 *
 * The hero needs to line its copy column up with the top edge of the window
 * INSIDE the panel, and needs its own tighter values. Both of those want the
 * number in a place a sibling can read, so it lives on the inherited chain —
 * `.hero-inner` overrides it once and the copy and the panel move together.
 * Declaring it on `.shot` would not work: `.shot`'s own declaration beats an
 * inherited one, so the panel would keep the default while the copy moved.
 */
:root {
  --shot-pad-block: clamp(20px, 4vw, 56px);
  --shot-pad-inline: clamp(20px, 4vw, 56px);
}

.shot {
  --tone: #ffbd83;
  display: flex;
  justify-content: center;
  padding: var(--shot-pad-block) var(--shot-pad-inline);
  overflow: hidden;
  border-radius: var(--radius-xl);
  /* Below the copy, so the headline stays legible if a narrow viewport ever
     brings the two close enough to touch. */
  z-index: 0;
  /* Three stops, not two: a flat two-stop ramp reads as a colour swatch. The
     off-centre highlight is what makes it read as light falling on a surface. */
  background:
    radial-gradient(120% 90% at 22% 8%, rgba(255, 255, 255, 0.72), transparent 58%),
    radial-gradient(90% 120% at 88% 100%, color-mix(in srgb, var(--tone) 55%, transparent), transparent 62%),
    linear-gradient(150deg, color-mix(in srgb, var(--tone) 26%, var(--sand)), var(--tint));
}

/*
 * The hero's panel, and the one place a `.shot` is allowed to leave the page's
 * measure.
 *
 * `width: max-content` plus `justify-self: start` is what produces the bleed:
 * the panel takes its natural width from the window inside it, anchors to the
 * left of its column, and runs past the right edge of the viewport, where
 * `.hero`'s `overflow: hidden` cuts it. Nothing about that affects layout,
 * because the track it sits in has `min-width: 0`.
 *
 * The right corners are squared off. A rounded corner that is never on screen
 * is a corner nobody sees, and rounding one end while the other is cut by the
 * viewport is what makes a bleed look like a mistake rather than a choice.
 */
.hero-shot {
  justify-self: start;
  width: max-content;
  padding-right: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

/*
 * The window.
 *
 * `--window-scale` is a multiplier on the app's real 1480×930: the box is
 * `1480px × scale` and the iframe inside is a full-size 1480px document under
 * `transform: scale()` of the same value.
 *
 * The scale must be a table of fixed values, and that is a CSS limitation with
 * no way around it: `calc()` cannot divide a length by a length, so there is no
 * expression for "the number that turns 1480px into the width of this box".
 * Sizing the iframe itself is not an option either — an iframe lays its
 * document out at its element width, so a 900px iframe re-runs the app's media
 * queries and renders a narrow-window layout no desktop user has ever seen.
 *
 * Each step is picked for the NARROWEST viewport in its band, so the window
 * always fits inside the panel's padding. See the table further down.
 */
.preview-window {
  --window-scale: 0.86;
  position: relative;
  width: calc(1480px * var(--window-scale));
  max-width: 100%;
  aspect-ratio: 1480 / 930;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background-color: #fff;
  /* Two shadows: a tight one that draws the window's edge against the panel,
     and a wide soft one that lifts it off. One shadow cannot do both. */
  box-shadow:
    0 1px 2px rgba(13, 13, 14, 0.16),
    0 24px 50px -12px rgba(13, 13, 14, 0.34);
}

.preview-frame {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  border: 0;
  transform-origin: 0 0;
  transform: scale(var(--window-scale));
}

/* ------------------------------------------------------------ integrations - */

.integrations {
  position: relative;
  display: grid;
  align-items: start;
  grid-template-columns: 1fr 0.7fr;
  /* The 45px top is structural, not a seam: `::before` below draws the column
     divider from `top: 0`, and this is what lets the line start above the first
     line of text instead of beside it. The seam above this section is owned by
     `.feature-section`'s bottom, as everywhere else. */
  padding: 45px 0 var(--gap-block);
}

.integrations::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 1px;
  height: 85%;
  background: linear-gradient(90deg, transparent, rgba(82, 82, 69, 0.04), transparent);
}

.integrations-copy {
  max-width: 460px;
}

.integrations-title {
  font-size: clamp(36px, 4vw, 52px);
}

.integrations-body {
  color: var(--muted);
  font-size: 16px;
  line-height: 1.42;
}

.integrations-copy .button {
  margin: 16px 0 50px;
}

/*
 * A named counter, not the built-in `list-item`.
 *
 * `list-item` is incremented by the browser for every `<li>` on its own, so a
 * `counter-increment: list-item` in the marker's `::before` increments it a
 * second time and the list renders 02, 04, 06. Verified in headless Chrome —
 * the template gets away with it only because its list is styled by Tailwind
 * and never reaches this rule.
 */
.integrations-steps {
  margin: 0;
  padding: 0;
  counter-reset: integration-step;
  list-style: none;
}

.integrations-step {
  margin: 18px 0;
  color: #6c6c68;
  font-size: 15px;
}

.integrations-step::before {
  content: counter(integration-step, decimal-leading-zero);
  display: inline-flex;
  width: 25px;
  height: 25px;
  align-items: center;
  justify-content: center;
  margin-right: 11px;
  border-radius: 50%;
  background: #0b0b0b;
  color: #fff;
  counter-increment: integration-step;
  font-size: 9px;
}

.integration-grid {
  display: grid;
  gap: 17px;
  grid-template-columns: repeat(2, 115px);
  justify-content: end;
  padding-top: 18px;
}

.integration-icon {
  display: flex;
  height: 115px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-lg);
  background: #f0eee8;
  color: #ef795b;
  font-size: 46px;
}

.icon-0,
.icon-3 { color: #ef795b; }
.icon-1,
.icon-4 { color: #7498f5; }
.icon-2,
.icon-7 { color: #b186f5; }
.icon-5 { color: #43bdb8; }
.icon-6 { color: #ed668c; }

/* ----------------------------------------------------------------- pricing - */

.pricing-section {
  margin-bottom: 120px;
  padding: 78px 64px 54px;
  overflow: hidden;
  border-radius: var(--radius-2xl);
  background:
    radial-gradient(circle at 66% 75%, #933d4c 0, transparent 24%),
    radial-gradient(circle at 35% 80%, #4b84a0 0, transparent 26%),
    var(--deep);
  color: #fff;
}

.pricing-section .eyebrow {
  color: #d9dbe0;
}

.price-heading {
  max-width: 620px;
}

.price-title {
  margin-bottom: 18px;
}

.price-lede {
  max-width: 430px;
  color: var(--muted-dark);
  font-size: 16px;
  line-height: 1.42;
}

.plan-switcher {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(3, 1fr);
  margin: 44px 0 12px;
}

.plan-tab {
  position: relative;
  min-height: 82px;
  padding: 15px;
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-md);
  background: transparent;
  color: #d3d6dc;
  text-align: left;
}

.plan-tab.is-active {
  background: #fff;
  color: #111;
}

.plan-name {
  display: block;
  font-size: 15px;
}

.plan-sub {
  display: block;
  margin-top: 5px;
  font-size: 11px;
}

.plan-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 4px 6px;
  border-radius: 7px;
  background: var(--yellow);
  color: #111;
  font-size: 9px;
  font-style: normal;
}

.plan-stack {
  display: grid;
}

.price-card {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 24px;
  padding: 32px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-lg);
  background: rgba(16, 20, 29, 0.65);
  backdrop-filter: blur(12px);
}

.js .price-card {
  grid-area: 1 / 1;
}

.js .price-card[hidden] {
  display: none;
}

.price-card .price-label {
  background: #e8edf6;
  color: #222;
}

.price-main {
  align-self: start;
}

.price {
  display: flex;
  gap: 7px;
  align-items: baseline;
  margin-top: 15px;
}

.price-value {
  font-size: clamp(34px, 3.4vw, 46px);
  letter-spacing: -2px;
}

.price-unit {
  color: var(--muted-dark);
  font-size: 12px;
}

.price-note {
  max-width: 300px;
  color: var(--muted-dark);
  font-size: 14px;
  line-height: 1.4;
}

.price-card .button {
  margin-top: 10px;
  border-color: #fff;
}

.price-features {
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
}

.price-feature {
  margin: 14px 0;
  color: #f3f4f5;
  font-size: 13px;
}

.tick {
  margin-right: 8px;
  color: #eeec77;
  font-weight: 700;
}

.price-aside {
  display: flex;
  flex-direction: column;
  align-items: end;
  justify-content: end;
  color: var(--muted-dark);
  font-size: 11px;
  text-align: right;
}

.price-aside-title {
  color: #fff;
  font-size: 19px;
  letter-spacing: -0.6px;
}

.price-aside-note {
  max-width: 220px;
  margin-top: 6px;
}

/* ------------------------------------------------------------ scene cards -- */

.stories {
  padding-bottom: var(--gap-block);
}

.story-heading {
  text-align: left;
}

.story-heading .section-intro {
  max-width: 470px;
  margin-left: 0;
}

.story-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(3, 1fr);
  margin-top: 45px;
}

.story-card {
  position: relative;
  min-height: 300px;
  padding: 20px 20px 52px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--sand);
}

.story-kicker {
  color: #74736e;
  font-size: 11px;
}

/* The orb reads `--tone`, which a `.tone-N` class on the card sets. */
.story-orb,
.case-cover,
.story-cover {
  background:
    radial-gradient(circle at 70% 30%, rgba(255, 255, 255, 0.55) 0 10%, transparent 11%),
    linear-gradient(135deg, var(--tone), #f7f6f1);
}

.story-orb {
  position: relative;
  width: 90px;
  height: 90px;
  margin: 24px auto 22px;
  border-radius: 50%;
  filter: saturate(0.75);
}

.story-orb::after {
  content: "";
  position: absolute;
  top: 16px;
  left: 16px;
  width: 57px;
  height: 57px;
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 50%;
}

.story-name {
  margin-bottom: 8px;
  font-size: 21px;
  letter-spacing: -1.2px;
}

.story-teaser {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.35;
}

.story-link {
  position: absolute;
  bottom: 18px;
  display: inline-flex;
  gap: 7px;
  align-items: center;
  font-size: 11px;
  font-weight: 700;
}

.story-link .arrow,
.case-more .arrow {
  width: 17px;
  height: 17px;
  font-size: 11px;
}

.all-stories {
  display: block;
  margin-top: 26px;
  text-align: center;
}

/* -------------------------------------------------------------- design notes - */

.voices {
  padding-bottom: var(--gap-block);
}

.voices-eyebrow {
  margin-bottom: 22px;
}

.voice-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(3, 1fr);
}

.voice-card {
  position: relative;
  min-height: 260px;
  padding: 27px;
  border-radius: var(--radius-lg);
  background: #fff;
}

.voice-quote {
  font-size: 17px;
  letter-spacing: -0.7px;
  line-height: 1.25;
}

:lang(zh-Hans) .voice-quote {
  letter-spacing: 0;
  line-height: 1.5;
}

.voice-by {
  position: absolute;
  bottom: 22px;
  display: flex;
  gap: 11px;
  align-items: center;
}

/* The template puts a portrait here. There is nobody to photograph, so the slot
   holds a tinted glyph and the line beside it names the subject rather than a
   person — which is also why nothing on this site claims a quotation from one. */
.voice-mark {
  display: flex;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--tone), #f7f6f1);
  font-size: 15px;
}

.voice-role {
  color: var(--muted);
  font-size: 12px;
}

/* --------------------------------------------------------------------- faq - */

.faq-section {
  display: grid;
  align-items: start;
  gap: 70px;
  grid-template-columns: 0.75fr 1.25fr;
  padding-bottom: var(--gap-block);
}

.faq-title {
  margin-bottom: 18px;
}

.faq-lede {
  max-width: 340px;
  color: var(--muted);
  font-size: 16px;
  line-height: 1.42;
}

.faq-intro .button {
  margin-top: 17px;
}

.faq-list {
  padding: 10px;
  border-radius: var(--radius-xl);
  background: var(--tint);
}

.faq-item {
  margin-bottom: 7px;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--sand);
}

.faq-item:last-child {
  margin-bottom: 0;
}

.faq-item.is-open {
  background: #fff;
}

.faq-question {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  padding: 24px 28px;
  border: 0;
  background: transparent;
  font-size: 16px;
  text-align: left;
}

.faq-q {
  padding-right: 16px;
}

/*
 * A plus that becomes a minus, drawn as two bars rather than as two glyphs.
 *
 * The template swaps the character in React (`+` closed, `−` open), which is
 * not available to a server-rendered page whose open item can change without a
 * re-render. Rotating a `+` by 180° — the obvious CSS translation — is a
 * no-op: the glyph is symmetric under that rotation, so the control animates
 * and looks identical at both ends. Collapsing the vertical bar is the version
 * that actually reads.
 */
.faq-sign {
  position: relative;
  display: block;
  width: 18px;
  height: 18px;
  flex: none;
}

.faq-sign::before,
.faq-sign::after {
  content: "";
  position: absolute;
  border-radius: 2px;
  background: #aaa9a2;
}

.faq-sign::before {
  top: 8px;
  left: 0;
  width: 18px;
  height: 2px;
}

.faq-sign::after {
  top: 0;
  left: 8px;
  width: 2px;
  height: 18px;
}

.faq-a {
  max-width: 600px;
  margin: 0;
  padding: 0 28px 24px;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
}

/* ------------------------------------------------------------- closing band - */

.closing-section {
  display: grid;
  grid-template-columns: 1fr 0.85fr;
  gap: 40px;
  margin-bottom: 85px;
  padding: 70px;
  border-radius: var(--radius-2xl);
  background: var(--dark);
  color: #fff;
}

.closing-section .eyebrow {
  color: #c5cbd6;
}

.closing-copy {
  max-width: 520px;
}

.closing-title {
  margin-bottom: 18px;
}

.closing-body {
  max-width: 400px;
  color: #d3d7df;
  font-size: 16px;
  line-height: 1.42;
}

.closing-actions {
  display: flex;
  flex-direction: column;
  align-items: start;
  align-self: center;
  gap: 18px;
}

.closing-link {
  font-size: 14px;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* ------------------------------------------------------------------ footer - */

.site-footer {
  padding-bottom: 35px;
}

.footer-cta {
  padding: 10px 0 60px;
  border-bottom: 1px solid #dbd9d3;
}

.footer-heading {
  max-width: 520px;
  margin-bottom: 26px;
}

.footer-action {
  margin-bottom: 24px;
}

.footer-note {
  max-width: 430px;
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}

.footer-links {
  display: flex;
  gap: 90px;
  justify-content: flex-end;
  min-height: 170px;
  margin-top: -170px;
}

.footer-column {
  display: grid;
  align-content: start;
  gap: 12px;
}

.footer-column-title {
  margin-bottom: 8px;
  font-size: 11px;
}

.footer-link {
  color: #676762;
  font-size: 13px;
}

.footer-bottom {
  display: flex;
  gap: 22px;
  align-items: center;
  justify-content: flex-end;
  padding-top: 21px;
  border-top: 1px solid #dbd9d3;
  color: #6e6d68;
  font-size: 11px;
}

.footer-bottom .brand {
  margin-right: auto;
  color: var(--ink);
}

.footer-tagline {
  max-width: 260px;
}

.footer-copyright {
  white-space: nowrap;
}

/* ------------------------------------------------------- inner-page system - */

/* Every page other than the home page. The 1px of padding stops the first
   section's margin collapsing through the top of the document. */
.inner-page {
  padding-top: 1px;
}

.inner-hero {
  min-height: 470px;
  padding: 150px 0 80px;
}

.inner-title {
  max-width: 840px;
}

.inner-lede {
  max-width: 520px;
  color: var(--muted);
  font-size: 18px;
  line-height: 1.45;
}

.centered-hero {
  min-height: 430px;
  padding-top: 165px;
  text-align: center;
}

.centered-hero .inner-lede,
.centered-hero .inner-title {
  margin-inline: auto;
}

.centered-hero.is-compact {
  min-height: 370px;
}

.hero-stat {
  display: flex;
  max-width: 360px;
  gap: 14px;
  align-items: center;
  margin-top: 44px;
  padding-top: 15px;
  border-top: 1px solid var(--line);
}

.hero-stat-value {
  font-size: 22px;
  letter-spacing: -1px;
  white-space: nowrap;
}

.hero-stat-label {
  color: var(--muted);
  font-size: 12px;
}

.mission {
  display: grid;
  align-items: end;
  gap: 70px;
  grid-template-columns: 1.15fr 0.85fr;
  padding: 80px 0 95px;
  border-top: 1px solid var(--line);
}

.mission-heading {
  margin: 0;
}

.mission-body {
  margin: 0;
  color: var(--muted);
  font-size: 18px;
  line-height: 1.42;
}

.numbers {
  display: grid;
  gap: 9px;
  grid-template-columns: repeat(4, 1fr);
  padding-bottom: 125px;
}

.number-card {
  min-height: 205px;
  padding: 24px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--sand);
}

.number-value {
  font-size: 40px;
  letter-spacing: -2px;
}

.number-label {
  margin: 32px 0 7px;
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.9px;
  line-height: 1.2;
}

.number-body {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
}

.partners {
  position: relative;
  margin-bottom: 120px;
  padding: 64px;
  overflow: hidden;
  border-radius: var(--radius-xl);
  color: #fff;
}

.partners::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle, #a9616c 0, transparent 26%),
    radial-gradient(circle at 22% 85%, #4b88a1 0, transparent 20%),
    var(--deep);
}

.partners > * {
  position: relative;
}

.partners .eyebrow {
  color: #d9dfe9;
}

.partners-title {
  margin-bottom: 46px;
}

.partner-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 40px;
  overflow: hidden;
  color: #dde0e4;
  font-size: 17px;
  font-weight: 700;
}

.partner-item {
  white-space: nowrap;
}

.team-section {
  padding-bottom: 125px;
}

.team-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(4, 1fr);
  margin-top: 44px;
}

.team-card {
  padding: 12px 12px 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--sand);
}

.team-portrait {
  display: flex;
  height: 200px;
  align-items: center;
  justify-content: center;
  margin-bottom: 18px;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--tone), #f7f6f2);
}

.team-mark {
  display: flex;
  width: 68px;
  height: 68px;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.42);
  font-size: 26px;
}

.team-name {
  margin: 0 8px 6px;
  font-size: 18px;
  letter-spacing: -1px;
}

.team-role {
  margin: 0 8px;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
}

.inner-cta {
  margin-bottom: 90px;
  padding: 62px 66px;
  border-radius: var(--radius-2xl);
  background: var(--dark);
  color: #fff;
}

.inner-cta .eyebrow {
  color: #d5d9e3;
}

.inner-cta-title {
  margin-bottom: 25px;
}

.inner-cta .button {
  border-color: #fff;
}

/* --------------------------------------------------------- the scenes index - */

.case-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(3, 1fr);
  padding-bottom: 125px;
}

.case-card {
  position: relative;
  min-height: 400px;
  padding: 20px 20px 52px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--sand);
}

.case-kicker {
  color: #6f6f6b;
  font-size: 11px;
}

.case-cover {
  display: flex;
  height: 178px;
  align-items: center;
  justify-content: center;
  margin: 18px 0 20px;
  overflow: hidden;
  border-radius: var(--radius-md);
}

.case-glyph {
  display: flex;
  width: 100px;
  height: 100px;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.8);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.46);
  font-size: 44px;
  font-style: normal;
}

.case-name {
  margin-bottom: 8px;
  font-size: 27px;
  letter-spacing: -1.5px;
}

.case-teaser {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.4;
}

.case-more {
  position: absolute;
  bottom: 18px;
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 12px;
  font-weight: 700;
}

/* ----------------------------------------------------------- a single scene - */

.story-hero {
  padding: 130px 0 80px;
}

.back-link {
  display: inline-block;
  margin-bottom: 55px;
  font-size: 13px;
  font-weight: 700;
}

.story-cover {
  display: flex;
  height: 400px;
  align-items: center;
  justify-content: center;
  margin-top: 52px;
  overflow: hidden;
  border-radius: var(--radius-2xl);
}

.story-glyph {
  display: flex;
  width: 215px;
  height: 215px;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.47);
  font-size: 100px;
  font-style: normal;
}

.story-body {
  display: grid;
  align-items: start;
  gap: 100px;
  grid-template-columns: 1.1fr 0.9fr;
  padding-bottom: 100px;
}

.story-lead {
  max-width: 550px;
  color: var(--muted);
  font-size: 17px;
  line-height: 1.5;
}

.story-aside {
  padding: 28px;
  border-radius: var(--radius-lg);
  background: var(--tint);
}

.story-result {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 16px 0;
  border-top: 1px solid #d8d5ce;
  font-size: 14px;
}

.story-tick {
  display: flex;
  width: 21px;
  height: 21px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--yellow);
  font-size: 11px;
}

.quote-band {
  margin-bottom: 105px;
  padding: 52px 80px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  background: var(--card);
  text-align: center;
}

.quote-text {
  max-width: 760px;
  margin: 0 auto 20px;
  font-size: 30px;
  letter-spacing: -1.8px;
  line-height: 1.13;
}

:lang(zh-Hans) .quote-text {
  letter-spacing: -0.5px;
  line-height: 1.4;
}

.quote-source {
  color: var(--muted);
  font-size: 12px;
}

/* -------------------------------------------------------------- licenses -- */

.license-list {
  padding-bottom: 125px;
}

.license-item {
  display: grid;
  gap: 60px;
  grid-template-columns: 0.7fr 1fr;
  padding: 32px 0;
  border-top: 1px solid var(--line);
}

.license-item:last-child {
  border-bottom: 1px solid var(--line);
}

.license-title {
  margin: 0;
  font-size: 25px;
  letter-spacing: -1.3px;
}

.license-body {
  max-width: 600px;
  margin: 0;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.5;
}

/* ----------------------------------------------------------- download page - */

.page-head {
  padding: 150px 0 50px;
}

.page-head-inner {
  max-width: 720px;
}

.page-title {
  margin-bottom: 20px;
}

.page-intro {
  max-width: 560px;
  color: var(--muted);
  font-size: 18px;
  line-height: 1.45;
}

.release-meta {
  display: flex;
  gap: 40px;
  margin: 36px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.meta-item {
  display: grid;
  gap: 4px;
}

.meta-label {
  color: var(--muted);
  font-size: 11px;
}

.meta-value {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
}

.platform-sections {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(3, 1fr);
  padding-bottom: 60px;
}

.platform-card {
  padding: 26px 24px 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  background: var(--sand);
  transition: border-color var(--duration-slow) var(--ease), box-shadow var(--duration-slow) var(--ease);
}

/* `site.js` marks the platform the visitor is on. Nothing is hidden or
   reordered — the card simply stops being one of three identical boxes. */
.platform-card.is-detected {
  border-color: var(--ink);
  background: var(--card);
  box-shadow: var(--shadow-card);
}

.platform-name {
  margin-bottom: 6px;
  font-size: 25px;
  letter-spacing: -1.3px;
}

.platform-note {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.4;
}

.package-list {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
}

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

.package-main {
  display: grid;
  gap: 8px;
  justify-items: start;
}

.package-cta {
  width: 100%;
}

.package-label {
  font-size: 15px;
  font-weight: 700;
}

.package-hint {
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
}

.package-filename {
  margin: 0;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 11px;
  overflow-wrap: anywhere;
}

.package-missing {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 12px;
}

.package.is-missing .package-label {
  color: var(--muted);
}

.package-meta {
  margin: 12px 0 0;
}

.meta-row {
  display: grid;
  gap: 4px;
  margin-top: 8px;
}

.hash-value {
  display: block;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: var(--tint);
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

.notes {
  padding-bottom: 60px;
}

.notes-title {
  margin-bottom: 14px;
  font-size: 25px;
  letter-spacing: -1.3px;
}

/* Operator-supplied plain text. `pre-wrap` is what preserves the line breaks
   without injecting a single <br> into an escaped string. */
.notes-body {
  max-width: 700px;
  padding: 24px 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--card);
  color: var(--muted);
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
}

.info-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, 1fr);
  padding-bottom: 110px;
}

.info-card {
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--sand);
}

.info-title {
  margin-bottom: 10px;
  font-size: 21px;
  letter-spacing: -1.1px;
}

.info-body {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
}

.info-lead {
  color: var(--ink);
}

.empty-state {
  max-width: 560px;
  margin-inline: auto;
  padding: 48px 40px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  background: var(--sand);
  text-align: center;
}

.empty-title {
  margin-bottom: 12px;
  font-size: 30px;
  letter-spacing: -1.6px;
}

.empty-body {
  margin-bottom: 24px;
  color: var(--muted);
  font-size: 15px;
}

/* ------------------------------------------------------------ scene tones - */

/*
 * Six values for one custom property. Everything tinted per scene — the orb on
 * a home card, the cover on the index, the hero art on a scene page, the mark
 * beside a design note — reads `--tone` back, so a scene's colour is stated
 * once here and cannot drift between three surfaces.
 */
.tone-1 { --tone: #ffbd83; }
.tone-2 { --tone: #9bc6fb; }
.tone-3 { --tone: #f3abc1; }
.tone-4 { --tone: #9ce0bc; }
.tone-5 { --tone: #e1c192; }
.tone-6 { --tone: #bca8e9; }

/* ------------------------------------------------------------------ motion - */

/*
 * One block, on purpose. `prefers-reduced-motion` at the bottom of this file
 * has to be able to switch all of it off and be believed, and that is only
 * checkable if every animated property is written in one place.
 */
.button:hover {
  box-shadow: var(--shadow-press);
  transform: translate(-2px, -2px);
}

.button:active {
  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
  transform: translate(1px, 2px) scale(0.98);
}

.logo-track {
  animation: logo-ticker 25s linear infinite;
}

.step-card {
  transition:
    border-color var(--duration-slow) var(--ease),
    box-shadow var(--duration-slow) var(--ease),
    background-color var(--duration-slow) var(--ease),
    transform var(--duration-slow) var(--ease);
}

.step-card:hover {
  border-color: #d0cec6;
  background: #fff;
  box-shadow: var(--shadow-card);
  transform: translateY(-6px);
}

.setup-card {
  transition: transform var(--duration-slow) var(--ease);
}

.step-card:hover .setup-card {
  transform: translateY(6px) rotate(-1deg);
}

.feature-tab {
  transition:
    background-color var(--duration-slow) var(--ease),
    color var(--duration-slow) var(--ease),
    transform var(--duration) var(--ease);
}

.feature-tab:hover {
  color: var(--ink);
  transform: translateY(-1px);
}

/* The panel that just became visible fades up. Applied to the panel itself
   rather than to a wrapper so the animation restarts on every switch. */
/* `tab-in` starts at `scale(0.98)`, which is safe here and would not be on a
   full-bleed element: scaling something that reaches both edges of the viewport
   detaches it from them for the length of the animation and flashes a strip of
   page background down each side. That is why `.preview-stage` is not in this
   list and never should be. */
.feature-display,
.price-card {
  animation: tab-in var(--duration-slow) var(--spring) both;
}

.text-link .arrow {
  transition: transform var(--duration) var(--ease);
}

.text-link:hover .arrow {
  transform: rotate(45deg) translate(1px, -1px);
}

.integration-icon {
  transition:
    transform var(--duration-slow) var(--spring),
    box-shadow var(--duration-slow) var(--ease),
    background-color var(--duration-slow) var(--ease);
}

.integration-icon:hover {
  background: #fff;
  box-shadow: 0 13px 21px rgba(23, 20, 14, 0.1);
  transform: translateY(-8px) rotate(-3deg);
}

.integration-icon:nth-child(even):hover {
  transform: translateY(-8px) rotate(3deg);
}

.plan-tab {
  transition:
    background-color var(--duration-slow) var(--ease),
    color var(--duration-slow) var(--ease),
    border-color var(--duration-slow) var(--ease),
    transform var(--duration) var(--ease);
}

.plan-tab:hover {
  border-color: rgba(255, 255, 255, 0.72);
  transform: translateY(-2px);
}

.story-card,
.case-card,
.number-card,
.team-card,
.voice-card {
  transition:
    transform var(--duration-slow) var(--spring),
    box-shadow var(--duration-slow) var(--ease);
}

.story-card:hover,
.case-card:hover,
.number-card:hover,
.team-card:hover,
.voice-card:hover {
  box-shadow: var(--shadow-card);
  transform: translateY(-6px);
}

.voice-mark {
  transition: transform var(--duration-slow) var(--ease);
}

.voice-card:hover .voice-mark {
  transform: rotate(-6deg) scale(1.06);
}

.faq-item {
  transition: background-color var(--duration-slow) var(--ease);
}

.faq-sign::before,
.faq-sign::after {
  transition: transform var(--duration-slow) var(--ease), background-color var(--duration-slow) var(--ease);
}

/*
 * The accordion animates on `grid-template-rows`, which is the only way to go
 * from nothing to auto height without measuring the content in JavaScript.
 * `.faq-a` needs `min-height: 0` and `overflow: hidden` for the 0fr row to be
 * able to clip it at all.
 */
.faq-answer {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--duration-slow) var(--ease);
}

.faq-a {
  min-height: 0;
  overflow: hidden;
  transition: padding var(--duration-slow) var(--ease);
}

/* Collapsed only once the script that can reopen it is known to have run. */
.js .faq-answer {
  grid-template-rows: 0fr;
}

.js .faq-a {
  padding-bottom: 0;
}

.js .faq-item.is-open .faq-answer {
  grid-template-rows: 1fr;
}

.js .faq-item.is-open .faq-a {
  padding-bottom: 24px;
}

.faq-item.is-open .faq-sign::before {
  background: var(--ink);
}

.faq-item.is-open .faq-sign::after {
  background: var(--ink);
  transform: scaleY(0);
}

/*
 * Scroll entrances. Every element carrying `data-reveal` starts here and is
 * released by `site.js` adding `is-visible`. The initial state is in CSS rather
 * than applied by the script, so there is no frame in which the page is drawn
 * at full opacity and then hidden — but it also means a broken script would
 * leave the page blank, which is why `site.js` releases everything immediately
 * if the observer is unavailable.
 */
.js .reveal-up,
.js .hero-reveal {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity var(--duration-reveal) var(--spring),
    transform var(--duration-reveal) var(--spring);
}

.js .reveal-up.is-visible,
.js .hero-reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Stagger, as classes rather than as six inline `transition-delay` values. */
.delay-1 { transition-delay: 80ms; }
.delay-2 { transition-delay: 160ms; }
.delay-3 { transition-delay: 240ms; }
.delay-4 { transition-delay: 320ms; }
.delay-5 { transition-delay: 400ms; }
.delay-6 { transition-delay: 480ms; }

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

@keyframes card-float {
  0%,
  100% {
    transform: translate3d(0, 0, 0) rotate(1.5deg);
  }
  50% {
    transform: translate3d(-2px, -10px, 0) rotate(0.2deg);
  }
}

@keyframes chart-float {
  0%,
  100% {
    transform: translate3d(0, 0, 0) rotate(-3deg);
  }
  50% {
    transform: translate3d(5px, -9px, 0) rotate(-1.2deg);
  }
}

@keyframes tab-in {
  from {
    opacity: 0;
    transform: translate3d(0, 14px, 0) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

/* ------------------------------------------------------------- breakpoints - */

/*
 * The window scale table. See `.preview-window` for why this cannot be an
 * expression. Kept in one block, ahead of the layout breakpoints, so it reads
 * as the single table it is rather than as four unrelated tweaks.
 *
 * In the hero the window bleeds, so these are not "the largest that fits" —
 * they are the smallest scale at which the app's 13px interface text is still
 * readable, which is what the panel is there to show. What falls off the right
 * edge is margin.
 */
@media (max-width: 1167px) {
  .preview-window {
    --window-scale: 0.78;
  }
}

@media (max-width: 1023px) {
  .preview-window {
    --window-scale: 0.7;
  }
}

/*
 * The hero stops being two columns here: below this the copy column is too
 * narrow for the headline to set at a size worth reading, and a headline is
 * what a hero is for. Stacked, the panel goes full width and stops bleeding —
 * there is no column to bleed out of, and a bleed with nothing beside it is
 * just a cropped picture.
 */
@media (max-width: 900px) {
  .hero-inner {
    gap: 44px;
    grid-template-columns: minmax(0, 1fr);
  }

  /* No slack to distribute once the columns stack: the copy is above the panel
     rather than beside it, so the phantom block is just a gap on top of the
     grid's own gap. */
  .hero-copy {
    margin-bottom: 0;
  }

  .hero-shot {
    justify-self: stretch;
    width: auto;
    padding-right: var(--shot-pad-inline);
    border-top-right-radius: var(--radius-xl);
    border-bottom-right-radius: var(--radius-xl);
  }

  .preview-window {
    --window-scale: 0.44;
  }
}

/*
 * Below this the whole window stops being worth showing: 1480 logical pixels
 * inside a 390px phone is a scale of 0.26, at which the app's 13px interface
 * text is 3px of grey and the picture says nothing at all.
 *
 * So the phone gets a slice instead — the sidebar and the start of the
 * conversation, at a scale where they still read as an interface. The window
 * takes a squarer box and its own `overflow: hidden` does the cropping. This is
 * the same trade the five screenshot crops make, for the same reason: at a
 * small size the only way to keep detail legible is to show less of it.
 */
@media (max-width: 650px) {
  .preview-window {
    --window-scale: 0.6;
    width: 100%;
    aspect-ratio: 4 / 3;
  }
}

@media (max-width: 980px) {
  .site-header {
    width: min(620px, calc(100% - 32px));
  }

  .nav-links {
    gap: 14px;
  }

  /* The height was reserved for the absolute illustration. It is in flow now
     and brings its own. */
  .hero {
    min-height: 0;
  }
}

@media (max-width: 850px) {
  /* The scale narrows with the measure. A 152px gap is a third of a phone
     screen: at that size the reader loses the thread between a section and the
     one that answers it, which is the opposite of what the gap is for. The
     steps stay proportional to each other, so the page reads the same — just
     closer. */
  :root {
    --gap-tight: 56px;
    --gap-block: 76px;
    --gap-section: 104px;
  }

  .steps {
    grid-template-columns: 1fr 1fr;
  }

  .step-card:first-child {
    grid-column: span 2;
  }

  .feature-display,
  .closing-section,
  .integrations,
  .mission,
  .story-body,
  .license-item {
    gap: 40px;
    grid-template-columns: 1fr;
  }

  .integrations::before {
    display: none;
  }

  .integration-grid {
    justify-content: start;
  }

  .story-grid,
  .case-grid,
  .numbers,
  .team-grid,
  .platform-sections {
    grid-template-columns: repeat(2, 1fr);
  }

  .voice-grid {
    grid-template-columns: 1fr;
  }

  .voice-card {
    min-height: 200px;
  }

  .faq-section {
    gap: 32px;
    grid-template-columns: 1fr;
  }

  .price-card {
    grid-template-columns: 1fr 1fr;
  }

  .price-aside {
    align-items: start;
    grid-column: span 2;
    text-align: left;
  }

  .footer-links {
    min-height: 0;
    margin-top: 30px;
    justify-content: flex-start;
  }

  .footer-bottom {
    flex-wrap: wrap;
    justify-content: flex-start;
  }

  .footer-bottom .brand {
    width: 100%;
  }

  .inner-cta,
  .partners {
    padding: 50px;
  }

  .info-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 580px) {
  :root {
    --shell-gutter: 28px;
    --gap-tight: 44px;
    --gap-block: 60px;
    --gap-section: 84px;
  }

  html {
    scroll-padding-top: 78px;
  }

  h1 {
    letter-spacing: -3px;
  }

  h2 {
    letter-spacing: -2.2px;
  }

  .site-header {
    top: 12px;
    width: calc(100% - 24px);
  }

  .nav-links {
    position: absolute;
    top: 62px;
    left: 0;
    display: none;
    width: 100%;
    padding: 13px;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: #fff;
    box-shadow: var(--shadow-panel);
  }

  .nav-links.is-open {
    display: grid;
    gap: 11px;
  }

  /* The sheet is opened by a button the script wires up. Without it the button
     is removed and the links sit inside the capsule, which wraps to two rows. */
  html:not(.js) .site-header {
    height: auto;
    flex-wrap: wrap;
    padding: 10px 14px;
    border-radius: var(--radius-lg);
  }

  html:not(.js) .menu-toggle {
    display: none;
  }

  html:not(.js) .nav-links {
    position: static;
    display: flex;
    width: auto;
    flex-wrap: wrap;
    gap: 12px;
    padding: 0;
    border: 0;
    background: transparent;
    box-shadow: none;
  }

  .header-cta {
    display: none;
  }

  .menu-toggle {
    display: block;
  }

  /* Only the hamburger takes the free space. Giving the language switch a
     second `margin-left: auto` splits it between the two and leaves the button
     stranded in the middle of the capsule. */
  .lang-switch {
    margin-left: 12px;
    margin-right: 4px;
  }

  /*
   * 900/540, not the template's 780/430.
   *
   * The template's hero copy is a two-line headline and one short line of
   * lede. Ours is a two-line headline plus four lines of lede, a button row
   * and a note, which reaches ~500px — so at `top: 430px` the illustration
   * landed on top of the second button and the note under it. Measured in a
   * 375px viewport.
   */
  .hero {
    min-height: 900px;
    padding-top: 128px;
  }

  .steps {
    grid-template-columns: 1fr;
    margin-top: 12px;
  }

  .step-card:first-child {
    grid-column: auto;
  }

  .step-card {
    min-height: 0;
  }

  /* The 105px of clearance exists for the illustration hanging into the first
     card. The other two have nothing there, so on one column it is just a hole
     above the heading. */
  .step-title {
    margin-top: 56px;
  }

  .step-card:first-child .step-title {
    margin-top: 150px;
  }

  .setup-card {
    right: 13px;
    opacity: 0.85;
    transform: scale(0.8);
    transform-origin: top right;
  }

  .feature-section {
    padding: 95px 0 70px;
  }

  .feature-tabs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    border-radius: var(--radius-lg);
  }

  .feature-tab {
    padding: 11px;
    font-size: 12px;
  }

  .feature-copy {
    padding: 32px 24px;
  }

  .feature-heading {
    font-size: 28px;
  }

  .integrations {
    padding-bottom: 75px;
  }

  .integration-grid {
    grid-template-columns: repeat(2, 90px);
  }

  .integration-icon {
    height: 90px;
    font-size: 38px;
  }

  .pricing-section {
    margin-bottom: 75px;
    padding: 48px 20px 24px;
    border-radius: var(--radius-lg);
  }

  .plan-switcher {
    grid-template-columns: 1fr;
  }

  .price-card {
    gap: 26px;
    grid-template-columns: 1fr;
    padding: 24px;
  }

  .price-aside {
    grid-column: auto;
  }

  .story-grid,
  .case-grid,
  .numbers,
  .team-grid,
  .platform-sections {
    grid-template-columns: 1fr;
  }

  .story-card,
  .case-card {
    min-height: 0;
  }

  .story-orb {
    width: 70px;
    height: 70px;
    margin: 16px 0;
    float: right;
  }

  .story-orb::after {
    top: 12px;
    left: 12px;
    width: 42px;
    height: 42px;
  }

  .faq-section {
    padding-bottom: 75px;
  }

  .faq-question {
    padding: 20px 17px;
    font-size: 14px;
  }

  .faq-a {
    padding: 0 17px;
  }

  .faq-item.is-open .faq-a {
    padding-bottom: 20px;
  }

  .closing-section {
    margin-bottom: 55px;
    padding: 42px 22px;
  }

  .footer-links {
    gap: 45px;
  }

  .footer-cta {
    padding-bottom: 40px;
  }

  .inner-hero {
    min-height: 400px;
    padding-top: 125px;
  }

  .centered-hero {
    min-height: 360px;
    padding-top: 135px;
  }

  .inner-lede {
    font-size: 16px;
  }

  .mission {
    padding: 60px 0 70px;
  }

  .mission-body {
    font-size: 16px;
  }

  .numbers {
    padding-bottom: 80px;
  }

  .number-card {
    min-height: 0;
  }

  .partners,
  .inner-cta {
    margin-bottom: 70px;
    padding: 42px 24px;
  }

  .team-section {
    padding-bottom: 80px;
  }

  .team-portrait {
    height: 180px;
  }

  .story-hero {
    padding: 108px 0 60px;
  }

  .back-link {
    margin-bottom: 40px;
  }

  .story-cover {
    height: 250px;
    margin-top: 36px;
  }

  .story-glyph {
    width: 140px;
    height: 140px;
    font-size: 64px;
  }

  .story-body {
    padding-bottom: 70px;
  }

  .quote-band {
    margin-bottom: 70px;
    padding: 34px 22px;
  }

  .quote-text {
    font-size: 22px;
  }

  .license-list {
    padding-bottom: 75px;
  }

  .license-item {
    gap: 13px;
    padding: 24px 0;
  }

  .page-head {
    padding: 125px 0 40px;
  }

  .release-meta {
    gap: 26px;
  }

  .quote-band,
  .notes-body {
    padding-inline: 22px;
  }
}

/* ----------------------------------------------------- reduced motion, last - */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  }
