
/* ---------------------------------------------------------------------------
   Fonts, self-hosted.

   Not only for speed. This site's whole argument is that nothing about you
   leaves your phone - and a page making that claim while every visitor's
   browser announces itself to a third party to fetch a typeface is arguing
   against itself. Both faces are SIL OFL 1.1, which permits this.

   Variable fonts: one file per subset covers 400 and 600, so the weight axis
   costs nothing extra. latin-ext is a separate file and only downloads when a
   page actually contains its characters - which is what Romanian needs for
   a s t with cedilla and comma, and English never triggers.
   --------------------------------------------------------------------------- */

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/ibm-plex-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/literata-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/literata-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/*
  Clipa site.

  Tokens are the APP's semantic names, lifted from ui/theme/Color.kt,
  Motion.kt, Spacing.kt and Shape.kt. Keeping the names identical means a
  change in the app has an obvious counterpart here, and a reviewer can
  diff them by eye.
*/

:root {
  /* light — Color.kt ClipaLightColors */
  --bg: #EFF1EC;              /* sage100  */
  --surface: #F7F8F4;         /* sage50   */
  --surface-variant: #E3E7DE; /* sage200  */
  --text: #16201A;            /* inkBlack */
  --text-2: #4F5A52;          /* slate700 */
  /* Was #6E786F, the app's slate600. All 24 uses of this are 13-15px text, and
     at that size it measured 4.03:1 on --bg and 4.30:1 on --surface against the
     4.5:1 AA asks for - so the whole tertiary layer failed, footer links
     included. Three values darker clears it at 4.59 and 4.90 and is not a
     visible change. The app carries the same token and the same problem. */
  --text-3: #656F66;          /* slate600, darkened for AA */
  --accent: #2F5F4B;          /* green700 */
  --accent-tint: #DCEAE1;     /* green050 */
  --accent-deep: #16281F;     /* green900 — hover only, not an app token */
  --outline: #CDD4C6;         /* sage300  */
  --outline-strong: #7E8B7B;  /* sage500  */
  --destructive: #9C4034;     /* brick700 */
  --warning: #8A6A21;         /* used by the order-number callout */

  /* dark band — ClipaDarkColors */
  --ink-950: #0C0F0D;
  --ink-900: #141814;
  --ink-700: #2A322B;
  --ink-350: #7E8980;         /* AA-safe note on --text; see .cta__note */
  --ink-300: #6B766D;
  --ink-200: #9AA69C;
  --ink-050: #E8ECE6;
  --green-400: #86BFA3;

  /* night band — ClipaNightColors */
  --night-bg: #0D0B08;
  --night-text: #E6D9C2;
  --night-dim: #A2937C;
  --night-accent: #C9A96A;
  --night-outline: #241C13;
  --night-outline-strong: #4A3D2A;

  /* motion — Motion.kt, exactly */
  --m-instant: 90ms;
  --m-fast: 160ms;
  --m-base: 240ms;
  --m-slow: 380ms;
  --m-exit: 200ms;
  --e-standard: cubic-bezier(0.2, 0, 0, 1);
  --e-decel: cubic-bezier(0, 0, 0, 1);
  --e-emph-decel: cubic-bezier(0.05, 0.7, 0.1, 1);
  --e-emph-accel: cubic-bezier(0.3, 0, 0.8, 0.15);

  /* spacing — Spacing.kt, 4 dp base */
  --s4: 4px;  --s8: 8px;   --s12: 12px; --s16: 16px; --s20: 20px;
  --s24: 24px; --s32: 32px; --s40: 40px; --s56: 56px; --s80: 80px;

  /* radius — Shape.kt */
  --r-s: 8px; --r-m: 14px; --r-l: 20px; --r-pill: 999px;

  --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-serif: Literata, Georgia, "Times New Roman", serif;
}

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

/* scroll-behavior lives in site.js now, not here.

   Chrome refuses to animate a scroll at all when the OS asks for reduced
   motion - the media query below was only half of it, and removing that half
   changed nothing, because an explicit scrollTo({behavior:'smooth'}) lands
   instantly too. The glide is driven on requestAnimationFrame instead.

   Leaving `scroll-behavior: smooth` here as well would be actively harmful:
   every frame the script scrolls to would itself become a smooth scroll. */
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

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

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-deep); }

/* Literata carries prompts and headlines only. In the app it means "this is a
   sentence to read", and the site keeps that meaning — never UI chrome. */
h1, h2, h3 { font-family: var(--font-serif); font-weight: 600; line-height: 1.25; }
h1 { font-size: clamp(2rem, 1.2rem + 2.8vw, 3.5rem); letter-spacing: -0.02em; margin: 0 0 var(--s16); }
h2 { font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.375rem); letter-spacing: -0.02em; margin: var(--s56) 0 var(--s16); }
h3 { font-family: var(--font-sans); font-size: 1.3125rem; line-height: 1.33; margin: var(--s32) 0 var(--s8); }

p, li { color: var(--text-2); }
strong { color: var(--text); font-weight: 600; }
ul { padding-left: var(--s20); }
li { margin: var(--s4) 0; }
hr { border: 0; border-top: 1px solid var(--outline); margin: var(--s56) 0; }
code {
  font-size: .9em; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--surface-variant); padding: .1em .35em; border-radius: var(--r-s);
}

.eyebrow {
  font-size: 11px; line-height: 14px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .10em; color: var(--text-3);
}

/* ---------------------------------------------------------------------------
   Shell — masthead, frame, footer. Shared by every page including the
   generated legal ones, which is why nothing here assumes a landing page.
   --------------------------------------------------------------------------- */

.frame { max-width: 1060px; margin: 0 auto; padding: 0 var(--s32); }
@media (max-width: 640px) { .frame { padding: 0 var(--s20); } }

/* min-height, not height.

   This was `height: 64px`, which is fine until the nav wraps - and it wraps as
   soon as a fourth item is added to three long ones, which the language toggle
   did. The nav then measured 70px inside a 64px box and, being centred,
   overflowed by 3px at the top and 2px at the bottom: the toggle's baseline sat
   BELOW the header's own border. A fixed height on a box whose contents can
   reflow is a promise the content did not agree to. */
.masthead {
  min-height: 64px; display: flex; align-items: center; gap: var(--s24);
  flex-wrap: wrap; padding-block: var(--s8);
  border-bottom: 1px solid var(--outline);
}
.masthead .brand { display: flex; align-items: center; gap: var(--s12); text-decoration: none; }
.masthead nav { margin-left: auto; display: flex; gap: var(--s20); flex-wrap: wrap; }
.masthead nav a { font-size: 15px; color: var(--text-2); text-decoration: none; }
.masthead nav a:hover { color: var(--text); }

/* The language toggle. It is a nav link, not a button: it goes somewhere.
   Set apart from the three page links by weight and a rule, so it does not
   read as a fourth section of the site. */
.masthead .lang {
  margin-left: var(--s4);
  padding-left: var(--s20);
  border-left: 1px solid var(--outline);
  font-weight: 500;
}
@media (max-width: 560px) {
  /* The divider would land at the start of a wrapped line and read as a
     mistake rather than as a separator. */
  .masthead .lang { padding-left: 0; border-left: 0; }
}

/* Once the links cannot sit beside the wordmark, they take a row of their own
   rather than hanging ragged under the last one. Aligned left, on the same
   edge as every other band on the page - a nav that wraps while still pinned
   right ends up indented under nothing, which is what this looked like. */
@media (max-width: 560px) {
  .masthead { padding-block: var(--s12); row-gap: var(--s8); }
  .masthead nav { margin-left: 0; width: 100%; gap: var(--s16) var(--s20); }
}


/* The icon's own idea: a solid field interrupted once. */
.mark {
  /* inline-block, not inline: outside the masthead's flex container nothing
     blockifies this, and width/height silently do nothing on an inline span -
     the mark renders as a hairline. */
  display: inline-block;
  width: 30px; height: 30px; border-radius: var(--r-s);
  background: var(--accent); position: relative; flex: none;
}
.mark::after {
  content: ""; position: absolute; left: 18%; right: 18%; top: 50%;
  height: 1.5px; background: var(--bg); transform: translateY(-50%);
}
.mark-name { font-family: var(--font-serif); font-size: 1.25rem; color: var(--text); text-decoration: none; }

/* Read rather than scanned, so a notch larger than the landing page. */
/* Centred while nothing has a sidebar. Privacy and Terms gain a 236px table
   of contents in the left column, and their layout overrides this - 660 alone
   in a 1060 frame reads as a mistake rather than a measure. */
.prose { max-width: 660px; margin-inline: auto; padding: var(--s56) 0 var(--s80); font-size: 17px; line-height: 28px; }
.prose > h2:first-child, .prose > h1:first-child { margin-top: 0; }

.updated { color: var(--text-3); font-size: 13px; line-height: 19px; margin: 0 0 var(--s40); }

blockquote {
  margin: var(--s24) 0; padding: var(--s12) var(--s20);
  border-left: 2px solid var(--accent); background: var(--surface);
  border-radius: 0 var(--r-s) var(--r-s) 0;
}
blockquote p { margin: var(--s4) 0; }

/* Wide content scrolls inside its own box rather than pushing the page. */
.table-scroll { overflow-x: auto; margin: var(--s16) 0; }
table { border-collapse: collapse; width: 100%; font-size: .95rem; }
th, td { text-align: left; padding: var(--s12) var(--s16); border-bottom: 1px solid var(--outline); color: var(--text-2); }
th { color: var(--text); font-weight: 600; }

.site-footer {
  border-top: 1px solid var(--outline); padding: var(--s32) 0;
  color: var(--text-3); font-size: 14px;
}
.site-footer nav { display: flex; flex-wrap: wrap; gap: var(--s20); margin-bottom: var(--s12); }
.site-footer a { color: var(--text-3); }

/* ---------------------------------------------------------------------------
   Pills — used by 404 and anywhere a short link list needs weight.
   --------------------------------------------------------------------------- */

.pills { display: flex; flex-wrap: wrap; gap: var(--s12); justify-content: center; padding: 0; list-style: none; }
.pill {
  display: inline-block; padding: var(--s8) var(--s16);
  border: 1px solid var(--outline); border-radius: var(--r-pill);
  color: var(--text-2); text-decoration: none; font-size: 15px;
  transition: border-color var(--m-fast) var(--e-standard), color var(--m-fast) var(--e-standard);
}
.pill:hover { border-color: var(--outline-strong); color: var(--text); }

/* ---------------------------------------------------------------------------
   404
   --------------------------------------------------------------------------- */

.notfound { text-align: center; padding: var(--s80) 0; }
.notfound .mark {
  width: 56px; height: 56px; border-radius: var(--r-m); margin: 0 auto var(--s32);
  background: var(--surface-variant); border: 1px solid var(--outline-strong);
}
.notfound .mark::after { background: var(--outline-strong); }
.notfound h1 { font-size: 1.875rem; }
.notfound p { max-width: 40ch; margin: 0 auto var(--s32); }

/* ---------------------------------------------------------------------------
   Landing page. Sections own their own background; the frame only sets width.
   --------------------------------------------------------------------------- */

.section { padding: 112px 0; }
.section--surface { background: var(--surface); }
@media (max-width: 720px) { .section { padding: 72px 0; } }

.hero h1 { max-width: 16ch; }
.lede { font-size: 19px; line-height: 30px; color: var(--text-2); max-width: 54ch; }

/* Honest placeholder until the listing exists. A store badge pointing at a 404
   reads as abandoned, which is worse than saying "not yet". */
.soon {
  display: inline-block; margin-top: var(--s24); padding: var(--s8) var(--s16);
  border: 1px solid var(--outline); border-radius: var(--r-pill);
  font-size: 14px; color: var(--text-3);
}

.shots { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s20); margin: var(--s56) 0; }
.shots figure { margin: 0; }
.shots img { border-radius: var(--r-m); border: 1px solid var(--outline); display: block; }
.shots figcaption { margin-top: var(--s12); font-size: 14px; color: var(--text-3); text-align: center; }

/* Three phones stop being readable well before the page gets narrow, so they
   stack rather than shrink. */
@media (max-width: 720px) {
  .shots { grid-template-columns: 1fr; max-width: 19rem; margin-inline: auto; }
}

/* ---------------------------------------------------------------------------
   Buttons
   --------------------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  height: 52px; padding: 0 var(--s24); border-radius: var(--r-pill);
  font-size: 16px; font-weight: 600; text-decoration: none;
  transition: background var(--m-fast) var(--e-standard),
              border-color var(--m-fast) var(--e-standard),
              color var(--m-fast) var(--e-standard);
}
.btn--primary { background: var(--accent); color: #fff; }
.btn--primary:hover { background: var(--accent-deep); color: #fff; }
.btn--ghost { border: 1px solid var(--outline); color: var(--text); }
.btn--ghost:hover { border-color: var(--outline-strong); color: var(--text); }

/* ---------------------------------------------------------------------------
   Section 1 — hero
   --------------------------------------------------------------------------- */

/* padding-BLOCK, not the padding shorthand.
   
   The section is class="hero frame", and .frame is what puts every band on the
   page onto the same left edge. Writing `padding: 80px 0 112px` here reset the
   horizontal half of that to zero, so the headline, the chip and the buttons
   all sat 32px to the LEFT of the masthead, of every section heading below,
   and of the footer. Measured at 1265px: hero 103, everything else 135.
   
   It reads as a page that starts slightly broken, and it is invisible in the
   rule that causes it - the shorthand looks like it only sets the vertical
   space it names. Set the block axis alone and .frame keeps the inline axis,
   including its narrower gutter under 640px. */
.hero {
  display: grid; grid-template-columns: 1fr 340px; gap: 72px;
  align-items: center; padding-block: var(--s80) 112px;
}
@media (max-width: 960px) {
  .hero { grid-template-columns: 1fr; gap: var(--s56); padding-block: var(--s56) var(--s80); }
  .hero__device { justify-self: center; }
}

.hero__chip {
  display: inline-block; margin: 0 0 var(--s24);
  padding: var(--s8) var(--s16); border: 1px solid var(--outline);
  border-radius: var(--r-pill); background: var(--surface);
  font-size: 13px; line-height: 19px; color: var(--text-3);
}
.hero__title { max-width: 15ch; margin: 0 0 var(--s24); }
.hero .lede { margin: 0 0 var(--s32); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s12); margin: 0 0 var(--s20); }
.hero__note { margin: 0; font-size: 14px; color: var(--text-3); }

/* Each word is its own inline-block so it can be moved without reflowing the
   line. Visible by DEFAULT — the script hides them, not the stylesheet. */
.hero__title .w { display: inline-block; }

.js-reveal .hero__title .w,
.js-reveal [data-reveal="chip"],
.js-reveal [data-reveal="rest"],
.js-reveal [data-reveal="phone"] { opacity: 0; }

.js-reveal .hero__title .w { transform: translateY(6px); }
.js-reveal [data-reveal="phone"] { transform: translateY(16px); }

.js-reveal .hero__title .w.in {
  opacity: 1; transform: none;
  transition: opacity 180ms var(--e-decel), transform 180ms var(--e-decel);
}
.js-reveal [data-reveal].in {
  opacity: 1; transform: none;
  transition: opacity var(--m-base) var(--e-emph-decel), transform var(--m-slow) var(--e-emph-decel);
}

/* Someone who asked for less motion gets the finished state, not a slower
   version of the animation. The script checks this too and skips the
   sequence entirely, so nothing is merely hidden by a transition. */
@media (prefers-reduced-motion: reduce) {
  .js-reveal .hero__title .w,
  .js-reveal [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------------------
   The CSS phone
   --------------------------------------------------------------------------- */

/* One number drives the whole device. The internals are flow-based - the screen
   is a flex column, the prompt sits at 42%, the buttons are margin-top:auto -
   so the frame can be resized without anything inside it being touched. The
   type deliberately does NOT scale: 23px is already the smallest the prompt
   should be read at, and shrinking it to keep a ratio would cost legibility to
   preserve a proportion nobody is measuring. */
.phone {
  --phone-w: 300px;
  width: var(--phone-w); height: calc(var(--phone-w) * 628 / 300); flex: none;
  border-radius: 38px; padding: 10px;
  background: var(--text); box-shadow: 0 24px 60px -28px rgba(22, 32, 26, .45);
}
.phone__screen {
  position: relative; height: 100%; border-radius: 28px;
  background: var(--bg); overflow: hidden;
  display: flex; flex-direction: column;
}
.phone__status {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s16) var(--s20) 0;
  font-size: 12px; font-weight: 600; color: var(--text);
}
.phone__status-icons { width: 42px; height: 8px; border-radius: 4px; background: var(--outline); }

/* 42% optical centre, the app's own figure. True middle reads low. */
.phone__prompt {
  position: absolute; left: 0; right: 0; top: 42%; transform: translateY(-50%);
  padding: 0 var(--s24); text-align: center;
}
.phone__sentence {
  margin: 0; font-family: var(--font-serif);
  font-size: 23px; line-height: 32px; letter-spacing: -0.01em;
  color: var(--text);
}

.phone__actions {
  margin-top: auto; padding: var(--s20); display: grid; gap: var(--s12);
}
.phone__btn {
  display: flex; align-items: center; justify-content: center;
  height: 48px; border-radius: var(--r-pill);
  font-size: 15px; font-weight: 500;
}
.phone__btn--primary { background: var(--accent); color: #fff; }
.phone__btn--secondary { border: 1px solid var(--outline-strong); color: var(--text); }

/* The phone keeps its fixed 300x628 all the way down. 300 + 2x20 of frame
   padding fits a 340px viewport, and every phone in use is wider than that.

   It must NOT be width:100%: .hero__device is justify-self:center, so it is
   shrink-to-fit, and a percentage width inside it resolves against its own
   content. That collapsed the phone to min-content and pushed the prompt
   through the buttons - which only showed up at 375px, not in any preview.

   Below 340px, scale the whole thing rather than reflowing it: the internal
   proportions are the design, and the negative margin reclaims the space the
   transform leaves behind. */
@media (max-width: 339px) {
  .phone { transform: scale(.86); transform-origin: top center; margin-bottom: -88px; }
}

/* ---------------------------------------------------------------------------
   Section 2 — the demo
   --------------------------------------------------------------------------- */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.try { display: grid; grid-template-columns: 1fr 360px; gap: 72px; align-items: start; }
.try__device { position: sticky; top: 104px; justify-self: center; }
@media (max-width: 960px) {
  .try { grid-template-columns: 1fr; gap: var(--s40); }
  .try__device { position: static; }
}

/* The 960-1119 band, from the brief's responsive table.

   Above 1119 the frame is capped at 1060 and centred, so there is slack either
   side. Below 961 the two-column grids collapse. In between, the frame runs
   full-bleed AND still carries a 340px phone beside the copy, which is the
   narrowest the text column ever gets: 524px at a 1000px viewport, against
   600px once the phone comes down to 280.

   The brief also asks for the gutter to drop to 24px here, buying the text
   another 16px. That is NOT done: the 32px gutter was asked for specifically
   after the hero shipped with none, and taking it back in the one band where
   it was noticed would be a strange trade. Shrinking the phone recovers 76px
   on its own, which is four times what the gutter would have returned. */
@media (min-width: 961px) and (max-width: 1119px) {
  .hero { grid-template-columns: 1fr 280px; gap: var(--s56); }
  .hero .phone { --phone-w: 280px; }
  .try { grid-template-columns: 1fr 300px; gap: var(--s56); }
  .try .phone { --phone-w: 280px; }
}


.try__copy > h2 { margin-top: var(--s8); }
.try .lede { margin-bottom: var(--s40); }

.try__field { border: 0; padding: 0; margin: 0 0 var(--s32); }
.try__legend { padding: 0; font-size: 17px; font-weight: 600; color: var(--text); display: flex; align-items: center; gap: var(--s12); }
.try__num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: var(--r-pill);
  background: var(--surface-variant); color: var(--text-2);
  font-size: 13px; font-weight: 600;
}
.try__hint { margin: var(--s8) 0 var(--s16); font-size: 15px; color: var(--text-3); }

/* The controls are real radios. Hidden visually, still focusable, still
   operable with a keyboard and still meaningful with no JS at all. */
.try__apps { display: flex; flex-wrap: wrap; gap: var(--s20); }
.app { display: grid; justify-items: center; gap: var(--s8); cursor: pointer; }
.app input, .sec input {
  position: absolute; opacity: 0; width: 1px; height: 1px;
}
.app__tile {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: var(--r-m);
  background: var(--brand); color: #fff;
  font-size: 15px; font-weight: 600; letter-spacing: .02em;
  border: 1.5px solid transparent;
  transition: box-shadow var(--m-fast) var(--e-standard);
}
.app__name { font-size: 14px; color: var(--text-3); }
.app input:checked ~ .app__tile { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); }
.app input:checked ~ .app__name { color: var(--text); font-weight: 600; }
.app input:focus-visible ~ .app__tile { outline: 2px solid var(--accent); outline-offset: 3px; }

.try__delay { margin: 0 0 var(--s12); font-family: var(--font-serif); font-size: 30px; color: var(--text); }
.try__delay span { font-family: var(--font-sans); font-size: 15px; color: var(--text-3); }
.try__delays { display: flex; flex-wrap: wrap; gap: var(--s8); }
.sec { cursor: pointer; }
.sec__dot {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--r-pill);
  border: 1px solid var(--outline); font-size: 15px; color: var(--text-2);
  transition: background var(--m-fast) var(--e-standard), color var(--m-fast) var(--e-standard);
}
.sec input:checked ~ .sec__dot { background: var(--accent); border-color: var(--accent); color: #fff; }
.sec input:focus-visible ~ .sec__dot { outline: 2px solid var(--accent); outline-offset: 3px; }

.try__run { margin: var(--s32) 0; }
.try__run .btn { border: 0; cursor: pointer; font-family: inherit; }

.try__table { margin-top: var(--s24); font-size: 15px; }
.try__table th { font-weight: 400; color: var(--text-2); }
.try__table td { text-align: right; font-variant-numeric: tabular-nums; color: var(--text); font-weight: 600; }
.try__hold th, .try__hold td { background: var(--accent-tint); }
/* At 1 s the 260 ms floor pushes the sentence out past the delay, so there is
   no hold at all - the buttons arrive while words are still landing. Keeping
   the tint would highlight a beat that does not exist at that setting. */
.try__hold.is-none th, .try__hold.is-none td { background: none; color: var(--text-3); }

/* The abstract feed. Mimicking a real one is a trademark problem and a
   distraction; this only has to read as "a screen you were about to scroll". */
.feed { position: absolute; inset: 0; padding: 52px var(--s20) var(--s20); background: var(--surface); }
.feed__title { margin: 0 0 var(--s16); font-size: 15px; font-weight: 600; color: var(--text-2); }
.feed__block { height: 92px; border-radius: var(--r-s); background: var(--surface-variant); margin-bottom: var(--s12); }
.feed__line { height: 10px; border-radius: 5px; background: var(--surface-variant); margin-bottom: var(--s8); }
.feed__line--short { width: 60%; margin-bottom: var(--s16); }
.feed[hidden] { display: none; }

.phone__count { margin: var(--s16) 0 0; font-size: 13px; color: var(--text-3); }

/* Running state. Words and buttons are visible by default so the phone reads
   as a finished pause with no JS; the script adds .is-running to take over. */
.phone .w { display: inline-block; }
[data-demo-phone].is-running .w { opacity: 0; transform: translateY(4px); }
[data-demo-phone].is-running .w.in {
  opacity: 1; transform: none;
  transition: opacity 180ms var(--e-decel), transform 180ms var(--e-decel);
}
[data-demo-phone].is-running .phone__count { opacity: 0; }
[data-demo-phone].is-running [data-demo-actions] { opacity: 0; pointer-events: none; }
[data-demo-phone].is-running.is-done [data-demo-actions],
[data-demo-phone].is-running.is-done .phone__count {
  opacity: 1;
  transition: opacity var(--m-base) var(--e-emph-decel);
}
[data-demo-feed] { transition: opacity var(--m-exit) var(--e-emph-accel); }
[data-demo-phone].is-running [data-demo-feed] { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  /* The FADE goes. The ARRIVAL does not.

     This used to force opacity:1 on every word, which meant a visitor with
     reduced motion pressed the button and was handed the finished sentence in
     the first frame. The one thing the demo exists to show - words arriving
     over five seconds - was the thing it removed, and the section still said
     "Feel the pause" above a phone where nothing happened.

     site.js has claimed this all along: "Reduced motion still runs the CLOCK -
     the wait is the product, so it is not an animation to be skipped. Only the
     per-word fade goes." The script was right and the stylesheet overrode it.

     The app draws the same distinction in SentenceReveal.kt: under reduced
     motion it drops the word fade but KEEPS the hairline animating, because
     otherwise elapsed time becomes invisible. Time passing is content here,
     not decoration.

     So each word still lands at its scheduled moment - it just lands, rather
     than rising and fading in. */
  [data-demo-phone].is-running .w { transform: none; }
  [data-demo-phone].is-running .w.in { transition: none; }
}

/* ---------------------------------------------------------------------------
   Focus
   --------------------------------------------------------------------------- */

/* The site already decided what a focus ring looks like - 2px solid accent -
   and then applied it to exactly three controls: the two demo radio groups and
   the FAQ summary. Everything else, which is thirty-one of the thirty-four
   remaining focusable elements, fell through to Chrome's default: an orange
   0.8px hairline. Measured by pressing Tab, not by reading the stylesheet;
   programmatic focus does not raise :focus-visible and reports no ring at all,
   which makes this look like a total failure rather than an inconsistent one.

   :focus-visible and not :focus, so a mouse click on a button does not leave a
   ring behind it. Keyboard users get the indicator; pointer users do not. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The anchor glide focuses its target section so a keyboard or screen-reader
   user is told they arrived. That is a landmark, not a control - it takes
   tabindex="-1" and is never tabbed to, so it gets no ring. Real controls are
   unaffected: they match :focus-visible and keep theirs. */
[tabindex="-1"]:focus { outline: none; }

/* A focus indicator needs 3:1 against whatever it sits on (WCAG 1.4.11), and
   no single colour clears that on both of this site's grounds:

     accent    on the page      6.46:1     on the CTA card  2.28:1  FAILS
     green400  on the CTA card  7.96:1     on the page      1.85:1  FAILS

   So the dark bands take the dark-mode accent, the same swap the CTA button
   itself already makes. */
.cta :focus-visible,
.night :focus-visible { outline-color: var(--green-400); }

/* The FAQ summary keeps its inset ring: the rows are flush against the list
   border, and a +2px offset would be drawn over the row above. */

/* ---------------------------------------------------------------------------
   Shared section furniture
   --------------------------------------------------------------------------- */

.section__title { margin: var(--s8) 0 var(--s24); max-width: 22ch; }
.icon { flex: none; }
.link-arrow { display: inline-flex; align-items: center; gap: var(--s8); font-weight: 600; text-decoration: none; }
.link-arrow:hover { text-decoration: underline; }

/* ---------------------------------------------------------------------------
   Section 3 — how it works
   --------------------------------------------------------------------------- */

.steps {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s40);
  list-style: none; padding: 0; margin: var(--s56) 0 var(--s80);
}
@media (max-width: 860px) { .steps { grid-template-columns: 1fr; gap: var(--s32); } }
.step { border-top: 1px solid var(--outline); padding-top: var(--s16); }
/* --outline-strong is a BORDER token and measured 3.15:1 as text here. The
   step numbers are content - they are the only thing naming the order - so they
   take the text token meant to recede rather than the one meant to draw a line. */
.step__num { font-family: var(--font-serif); font-size: 15px; color: var(--text-3); }
.step h3 { margin: var(--s12) 0 var(--s8); }
.step p { margin: 0; font-size: 16px; line-height: 26px; }

.why {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s40);
  background: var(--surface-variant); border-radius: var(--r-l); padding: var(--s40);
}
@media (max-width: 860px) { .why { grid-template-columns: 1fr; padding: var(--s24); } }
.why__arg h3 { margin-top: 0; }
.why__arg p { font-size: 16px; line-height: 26px; }
.why__compare { display: grid; gap: var(--s16); align-content: center; }

.compare {
  display: flex; align-items: center; gap: var(--s16);
  background: var(--surface); border: 1px solid var(--outline);
  border-radius: var(--r-m); padding: var(--s16) var(--s20);
}
.compare--chosen { border: 1.5px solid var(--accent); }
.compare__title { margin: 0; font-size: 15px; font-weight: 600; color: var(--text); }
.compare__body { margin: var(--s4) 0 0; font-size: 14px; line-height: 20px; color: var(--text-3); }

/* A ring you can watch without reading, against a sentence you cannot. The
   two marks are the argument in miniature, so they are drawn rather than
   described. */
.compare__ring {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--outline); border-top-color: var(--outline-strong);
  flex: none;
}
.compare__lines { display: grid; gap: 5px; width: 34px; flex: none; }
.compare__lines i { display: block; height: 3px; border-radius: 2px; background: var(--accent); }
.compare__lines i:nth-child(2) { width: 78%; }
.compare__lines i:nth-child(3) { width: 45%; background: var(--outline); }

/* ---------------------------------------------------------------------------
   Section 5 — what it does not do
   --------------------------------------------------------------------------- */

.nots { display: grid; grid-template-columns: 1fr 1fr; gap: 72px; align-items: start; }
@media (max-width: 900px) { .nots { grid-template-columns: 1fr; gap: var(--s40); } }
.nots__copy p { font-size: 16px; line-height: 26px; }

.nots__list {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--outline); border-radius: var(--r-m); background: var(--surface);
}
.nots__list li {
  display: flex; gap: var(--s16); align-items: flex-start; margin: 0;
  padding: var(--s16) var(--s20); border-bottom: 1px solid var(--outline);
}
.nots__list li:last-child { border-bottom: 0; }
/* Colour on the mark, never on the words — seven red sentences would read as
   seven warnings rather than seven reassurances. */
.nots__icon { color: var(--destructive); display: flex; padding-top: 2px; }
.nots__title { margin: 0; font-size: 16px; font-weight: 600; color: var(--text); }
.nots__body { margin: var(--s4) 0 0; font-size: 14px; line-height: 21px; color: var(--text-3); }

/* ---------------------------------------------------------------------------
   Section 7 — pricing
   --------------------------------------------------------------------------- */

.pricing__lede { max-width: 60ch; }
.pricing__regional { max-width: 60ch; font-size: 14px; color: var(--text-3); margin: 0 0 var(--s40); }

.plans { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s24); max-width: 820px; }
@media (max-width: 780px) { .plans { grid-template-columns: 1fr; } }

.plan {
  position: relative; background: var(--surface);
  border: 1px solid var(--outline); border-radius: var(--r-l); padding: var(--s32);
}
.plan--full { border: 2px solid var(--accent); }
.plan__tab {
  position: absolute; top: 0; left: var(--s32); transform: translateY(-50%);
  margin: 0; padding: var(--s4) var(--s12); border-radius: var(--r-pill);
  background: var(--accent); color: #fff; font-size: 12px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
}
.plan__name { margin: 0; font-size: 15px; font-weight: 600; color: var(--text-2); }
.plan__price { margin: var(--s8) 0 0; font-family: var(--font-serif); font-size: 40px; line-height: 1; color: var(--text); }
.plan__unit { margin: var(--s8) 0 var(--s24); font-size: 14px; color: var(--text-3); }
.plan__list { list-style: none; margin: 0; padding: 0; }
.plan__list li {
  display: flex; gap: var(--s12); align-items: flex-start;
  margin: 0 0 var(--s12); font-size: 15px; line-height: 23px; color: var(--text-2);
}
.plan__list .icon { color: var(--accent); margin-top: 2px; }

.pricing__note { margin: var(--s24) 0 0; max-width: 60ch; font-size: 14px; color: var(--text-3); }


/* ---------------------------------------------------------------------------
   Section 4 - the night band

   THE DEFAULT IS NIGHT. .is-light is added by the script before the observer
   runs, and removed when the band is 35% visible. Same inversion as the hero:
   with no JS the band is already dark, which is the state that makes the point.

   700ms, slower than any duration in the app. This stands in for a change of
   hour, not a UI response, and everything moves together so it reads as one
   event rather than a dozen.
   --------------------------------------------------------------------------- */

.night { background: var(--night-bg); color: var(--night-text); }
.night, .night h2, .night .eyebrow, .night p,
.night .phone, .night .phone__screen, .night .phone__status,
.night .phone__status-icons, .night .phone__sentence,
.night .phone__btn--primary, .night .phone__btn--secondary,
.night .night__pills li {
  transition: background-color 700ms var(--e-standard),
              color 700ms var(--e-standard),
              border-color 700ms var(--e-standard);
}
.night h2 { color: var(--night-text); }
.night .eyebrow { color: var(--night-accent); }
.night p { color: var(--night-dim); }
.night .phone { background: var(--night-outline-strong); }
.night .phone__screen { background: var(--night-bg); }
.night .phone__status { color: var(--night-dim); }
.night .phone__status-icons { background: var(--night-outline); }
.night .phone__sentence { color: var(--night-text); }
.night .phone__btn--primary { background: var(--night-accent); color: var(--night-bg); }
.night .phone__btn--secondary { border-color: var(--night-outline-strong); color: var(--night-text); }

.night.is-light { background: var(--bg); color: var(--text); }
.night.is-light h2 { color: var(--text); }
.night.is-light .eyebrow { color: var(--text-3); }
.night.is-light p { color: var(--text-2); }
.night.is-light .phone { background: var(--text); }
.night.is-light .phone__screen { background: var(--bg); }
.night.is-light .phone__status { color: var(--text); }
.night.is-light .phone__status-icons { background: var(--outline); }
.night.is-light .phone__sentence { color: var(--text); }
.night.is-light .phone__btn--primary { background: var(--accent); color: #fff; }
.night.is-light .phone__btn--secondary { border-color: var(--outline-strong); color: var(--text); }

.night__grid { display: grid; grid-template-columns: 1fr 340px; gap: 72px; align-items: center; }
@media (max-width: 960px) {
  .night__grid { grid-template-columns: 1fr; gap: var(--s40); }
  .night__device { justify-self: center; }
}

.night__pills { list-style: none; display: flex; flex-wrap: wrap; gap: var(--s12); padding: 0; margin: var(--s32) 0 0; }
.night__pills li {
  padding: var(--s8) var(--s16); border-radius: var(--r-pill);
  border: 1px solid var(--night-outline-strong); color: var(--night-dim); font-size: 14px;
}
.night.is-light .night__pills li { border-color: var(--outline); color: var(--text-3); }

@media (prefers-reduced-motion: reduce) {
  .night, .night *, .night.is-light, .night.is-light * { transition: none !important; }
}

/* ---------------------------------------------------------------------------
   Section 6 - what changed
   --------------------------------------------------------------------------- */

.changed { display: grid; grid-template-columns: 340px 1fr; gap: 72px; align-items: center; }
/* Heading first in the DOM for screen readers; card first visually. The two
   orders disagree on purpose. */
.changed__copy { order: 2; }
.changed__card { order: 1; }
@media (max-width: 900px) {
  .changed { grid-template-columns: 1fr; gap: var(--s40); }
  .changed__copy { order: 1; }
  .changed__card { order: 2; }
}

.changed__card { background: var(--bg); border: 1px solid var(--outline); border-radius: var(--r-l); padding: var(--s32); }
.changed__figure { margin: var(--s8) 0 0; font-family: var(--font-serif); font-size: 54px; line-height: 1; color: var(--text); }
.changed__label { margin: var(--s4) 0 var(--s32); font-size: 15px; color: var(--text-3); }

.chart { display: flex; align-items: flex-end; gap: var(--s8); height: 96px; }
.chart__bar {
  flex: 1; border-radius: 3px;
  background: #2F5F4B8C;              /* ChartAlpha - the app's own 55% */
  height: var(--h);
  transition: height 300ms var(--e-emph-decel);
}
.chart__bar--peak { background: var(--accent); }
/* Set by the script before the observer fires. Without JS the bars are simply
   drawn at their height - never an empty box waiting for something that will
   not arrive. */
.chart.is-pending .chart__bar { height: 0; }

.chart__days { display: flex; justify-content: space-between; margin: var(--s8) 0 var(--s24); font-size: 13px; color: var(--text-3); }

.changed__note { border-top: 1px solid var(--outline); padding-top: var(--s16); }
.changed__note-title { margin: 0; font-size: 15px; font-weight: 600; color: var(--text); }
.changed__note-body { margin: var(--s4) 0 0; font-size: 14px; line-height: 21px; color: var(--text-3); }

@media (prefers-reduced-motion: reduce) {
  .chart.is-pending .chart__bar { height: var(--h); }
  .chart__bar { transition: none; }
}

/* ---------------------------------------------------------------------------
   Section 8 - FAQ
   --------------------------------------------------------------------------- */

.faq { display: grid; grid-template-columns: 340px 1fr; gap: 72px; align-items: start; }
.faq__head { position: sticky; top: 104px; }
@media (max-width: 900px) {
  .faq { grid-template-columns: 1fr; gap: var(--s32); }
  .faq__head { position: static; }
}

.faq__list { border: 1px solid var(--outline); border-radius: var(--r-m); background: var(--bg); }
.faq__item { border-bottom: 1px solid var(--outline); }
.faq__item:last-child { border-bottom: 0; }

.faq__q {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s16);
  padding: var(--s20); cursor: pointer; list-style: none;
  font-size: 16px; font-weight: 600; color: var(--text);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.faq__chev { color: var(--text-3); flex: none; transition: transform var(--m-base) var(--e-standard); }
.faq__item[open] .faq__chev { transform: rotate(180deg); }

/* The panel is a plain block and stays one.

   This was a 0fr -> 1fr grid transition, which is the standard trick for
   animating to an unknown height - and it never ran. A native <details> does
   not render its content at all while closed, so there is no start frame to
   transition FROM; measured, the element jumped 67px to 159px with nothing in
   between, and the panel still computed to 92px while shut. The CSS was
   correct and inert.

   site.js animates the height directly instead, and keeps the element open
   until the closing animation has finished. Without the script <details> opens
   and closes natively, instantly and correctly, which is the right unenhanced
   behaviour. */
.faq__panel > div { overflow: hidden; }
.faq__panel p { margin: 0; padding: 0 var(--s20) var(--s20); font-size: 15px; line-height: 24px; }

/* While a panel is closing the element is still [open], so the chevron cannot
   be driven from that or it would rotate a beat late. The script sets is-open
   at the moment of the click, which is when the reader asked for it. */
.js-faq .faq__item .faq__chev { transform: none; }
.js-faq .faq__item.is-open .faq__chev { transform: rotate(180deg); }

/* The chevron keeps its transition under reduced motion, because the panel it
   points at now animates too - see faq() in site.js. A still arrow beside a
   moving panel reads as a bug rather than as restraint. */


/* ---------------------------------------------------------------------------
   Section 9 - the closing card

   The app's own dark pair, not an invented palette: --ink-050 on --text with
   --green-400 for the action. The card should read as Clipa's dark mode.
   --------------------------------------------------------------------------- */

.cta {
  background: var(--text); border-radius: 24px;
  padding: var(--s80) 64px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: var(--s24);
}
@media (max-width: 720px) { .cta { padding: var(--s40) var(--s24); } }

.cta__title { margin: 0; color: var(--ink-050); max-width: 18ch; }
.cta__body { margin: 0; color: var(--ink-200); max-width: 52ch; font-size: 17px; line-height: 27px; }
.cta__action { margin: 0; }
.cta__btn { background: var(--green-400); color: var(--ink-950); }
.cta__btn:hover { background: #9BCDB2; color: var(--ink-950); }
/* --ink-300 measured 3.53:1 on the card. --ink-200 clears it but is already the
   body colour above, which would flatten the two into one; --ink-350 is the
   first step that clears 4.5 (4.60) and stays clear of the body's 6.62. */
.cta__note { margin: 0; color: var(--ink-350); font-size: 14px; max-width: 46ch; }

/* The launch-notice form. One field and a button, side by side where there is
   room and stacked where there is not - a full-width field above a full-width
   button is the shape a phone keyboard expects. */
.cta__form {
  display: flex; flex-wrap: wrap; gap: var(--s12);
  justify-content: center; width: 100%; max-width: 460px; margin: 0;
}
.cta__input {
  flex: 1 1 220px; min-width: 0;
  padding: var(--s12) var(--s20);
  border-radius: var(--r-pill); border: 1px solid var(--ink-300);
  background: var(--ink-950); color: var(--ink-050);
  font-family: var(--font-sans); font-size: 16px; line-height: 24px;
}
/* 16px is not a style choice: iOS Safari zooms the whole page when a focused
   input is smaller, and the reader then has to pinch back out. */
.cta__input::placeholder { color: var(--ink-300); }
.cta__input:hover { border-color: var(--ink-200); }
.cta__form .cta__btn { flex: 0 0 auto; }
@media (max-width: 460px) {
  .cta__input, .cta__form .cta__btn { flex: 1 1 100%; }
}

/* The answer lands where the form is, not at the top of the page. */
.cta__status { margin: 0; font-size: 15px; line-height: 22px; color: var(--ink-050); }
.cta__status[data-state="error"] { color: var(--green-400); }


/* ---------------------------------------------------------------------------
   Footer - three columns on the landing page
   --------------------------------------------------------------------------- */

.footer { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: var(--s40); }
@media (max-width: 720px) { .footer { grid-template-columns: 1fr 1fr; } .footer__brand { grid-column: 1 / -1; } }

.footer__brand .brand { display: inline-flex; align-items: center; gap: var(--s12); }
.footer__brand p { margin: var(--s16) 0 0; max-width: 34ch; font-size: 14px; line-height: 21px; color: var(--text-3); }
.footer__copy { margin-top: var(--s12) !important; }

.footer__col { display: flex; flex-direction: column; gap: var(--s8); }
.footer__col .eyebrow { margin: 0 0 var(--s4); }
.footer__col a { font-size: 14px; color: var(--text-2); text-decoration: none; }
.footer__col a:hover { color: var(--text); text-decoration: underline; }

/* ---------------------------------------------------------------------------
   Blog

   Articles live in the same .prose column as the support pages: 660px, 17/28,
   Literata headings. Nothing here invents a colour, a size or a radius; it is
   the existing system applied to long-form reading.
   --------------------------------------------------------------------------- */

.post__header { margin-bottom: var(--s40); }
.post__header .eyebrow a,
.blog__header .eyebrow { color: var(--text-3); text-decoration: none; }
.post__header .eyebrow a:hover { color: var(--accent); }

/* A long title at the landing page's h1 size would run to four lines on a
   phone and push the first sentence below the fold. */
.post__title { font-size: clamp(2rem, 1.4rem + 2.2vw, 2.875rem); margin: var(--s8) 0 var(--s16); }
.post__dek { font-size: 19px; line-height: 30px; color: var(--text-2); margin: 0 0 var(--s16); }
.post__meta { font-size: 14px; color: var(--text-3); margin: 0; padding-bottom: var(--s24); border-bottom: 1px solid var(--outline); }

/* Section headings in an article are statements, and statements are long. */
.post__body h2 { font-size: clamp(1.375rem, 1.15rem + 0.8vw, 1.75rem); margin: var(--s56) 0 var(--s16); }
.post__body p { margin: 0 0 var(--s20); }
.post__body a { overflow-wrap: anywhere; }

/* The standalone line. In the documents these were short paragraphs with tight
   spacing; on the page they are the author's pivot points, so they read as a
   different instrument - serif, slightly larger - and a run of them stays
   together as one gesture instead of five separate paragraphs. */
.post__body .beat {
  font-family: var(--font-serif); font-size: 20px; line-height: 30px;
  color: var(--text); margin: 0 0 var(--s8);
}
.post__body p:not(.beat) + .beat { margin-top: var(--s32); }
.post__body .beat + p:not(.beat) { margin-top: var(--s32); }

.post-figure {
  margin: var(--s40) 0; padding: var(--s24) var(--s24) var(--s20);
  background: var(--surface); border: 1px solid var(--outline); border-radius: var(--r-m);
}
.post-figure svg { display: block; width: 100%; max-width: 480px; height: auto; margin: 0 auto; overflow: visible; }
.post-figure figcaption {
  margin-top: var(--s16); padding-top: var(--s16); border-top: 1px solid var(--outline);
  font-size: 14px; line-height: 21px; color: var(--text-3);
}
/* Inside the SVGs. Sizes are user units, so they scale with the figure. */
.fig-eyebrow { font: 600 13px var(--font-sans); letter-spacing: 0.08em; fill: var(--text-3); }
.fig-body { font: 500 19px var(--font-serif); fill: var(--text); }
.fig-label { font: 500 14px var(--font-sans); fill: var(--text-2); }
.fig-note { font: italic 400 14px var(--font-sans); fill: var(--text-3); }
.fig-on-accent { fill: var(--bg); }
.fig-on-accent-soft { fill: var(--accent-tint); }
.fig-on-tint { fill: var(--accent); }
.fig-box { fill: var(--surface-variant); stroke: var(--outline); }
.fig-box--accent { fill: var(--accent); stroke: none; }
.fig-box--goal { fill: var(--bg); stroke: var(--outline-strong); stroke-dasharray: 5 4; }
.fig-line-dashed { fill: none; stroke: var(--outline-strong); stroke-width: 1.5; stroke-dasharray: 4 4; }
.fig-arrow { stroke: var(--accent); stroke-width: 2.5; }
.fig-head { fill: var(--accent); }
.fig-axis { stroke: var(--outline-strong); stroke-width: 1; }
.fig-bar { fill: var(--accent); }
.fig-badge--strong { fill: var(--accent); }
.fig-badge--some { fill: var(--accent-tint); }
.fig-badge--none { fill: none; stroke: var(--outline-strong); stroke-width: 1.5; }

/* On a phone the SVG scales to about two thirds, which took the small labels
   down to 8-9px. So the card spends the frame's gutter, and the small text is
   set larger here to land near 12px once scaled. Checked for collisions at
   375px against every box it sits in. */
@media (max-width: 640px) {
  .post-figure {
    margin-inline: calc(-1 * var(--s20)); padding: var(--s16) var(--s12);
    border-radius: 0; border-inline: 0;
  }
  .post-figure figcaption { padding-inline: var(--s8); }
  .post-figure .fig-eyebrow { font-size: 14px; }
  .post-figure .fig-label, .post-figure .fig-note { font-size: 15px; }
}

/* The one line that names the product. Set apart so it reads as a sign-off,
   not as the last sentence of the argument. */
.post__closing {
  margin: var(--s40) 0 0; padding-top: var(--s24); border-top: 1px solid var(--outline);
  font-family: var(--font-serif); font-style: italic; font-size: 19px; line-height: 30px; color: var(--text);
}

.post__sources { margin-top: var(--s56); }
.post__sources h2 { font-family: var(--font-sans); font-size: 13px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-3); margin: 0 0 var(--s16); }
.post__sources ol { margin: 0; padding-left: var(--s20); }
.post__sources li { font-size: 14px; line-height: 21px; color: var(--text-2); margin-bottom: var(--s12); }
.post__sources a { overflow-wrap: anywhere; }

.post__next {
  display: block; margin-top: var(--s56); padding: var(--s24);
  border: 1px solid var(--outline); border-radius: var(--r-m); background: var(--surface);
  text-decoration: none; color: inherit;
}
.post__next:hover { border-color: var(--outline-strong); }
.post__next-title { display: block; margin: var(--s8) 0; font-family: var(--font-serif); font-weight: 600; font-size: 22px; line-height: 30px; color: var(--text); }
.post__next-dek { display: block; font-size: 15px; line-height: 23px; color: var(--text-2); }

/* The index. Three entries today; the list is built to read the same at thirty. */
.blog__header { margin-bottom: var(--s40); }
.blog__header h1 { font-size: clamp(2rem, 1.4rem + 2.2vw, 2.875rem); margin: var(--s8) 0 var(--s16); }
.blog__lede { font-size: 19px; line-height: 30px; color: var(--text-2); margin: 0; }
.blog__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--outline); }
.blog__item { border-bottom: 1px solid var(--outline); }
.blog__item a { display: block; padding: var(--s24) 0; text-decoration: none; color: inherit; }
.blog__title { display: block; font-family: var(--font-serif); font-weight: 600; font-size: 24px; line-height: 32px; color: var(--text); }
.blog__item a:hover .blog__title { color: var(--accent); }
.blog__dek { display: block; margin: var(--s8) 0; font-size: 16px; line-height: 25px; color: var(--text-2); }
.blog__meta { display: block; font-size: 14px; color: var(--text-3); }
