/* ==========================================================================
   one7x.com — site layout
   Built on one7x-kit.css. Every value below resolves to a kit token.
   Signature moves: hairline first / shadow last · dense, not airy.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Accent — the mark's red, in a warmer family, approved 2026-08-02.
   The logo ring runs hue 253 (blue-violet) to hue 358 (red) and contains no orange.
   The first pass took hue 356 straight off it and came out an oxblood crimson.
   Asked for something in Todoist's family instead — warmer, brighter, hue 6-7.

   Todoist's own #E44332 is 4.08:1 with white on it and fails, so this is that exact
   hue taken one step deeper until it clears. Computed, not eyeballed:
     light  #C93A28  hue 7 · white on it 5.10:1 · as text on paper 5.01:1
     dark   #FF6B57  hue 7 · on the page 6.84:1 · on the inverted band 5.61:1

   --accent-bg is separate from --signal because a red light enough to READ on ink is
   too light to put white text ON. Text accents lift in dark; button fills do not.
   The button gradient lightens its top edge, so that edge is what gets measured —
   the middle passing tells you nothing about the part the eye lands on first.
   -------------------------------------------------------------------------- */
:root[data-accent="crimson"] {
  --signal: #C93A28;
  --signal-deep: #A32D1E;
  --signal-tint: #FDEDEA;
  --accent-bg: #BE3524;
}
:root[data-theme="dark"][data-accent="crimson"] {
  --signal: #FF4438;
  --signal-deep: #FF5F52;
  --signal-tint: #3D1A13;
  --accent-bg: #BE3524;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"])[data-accent="crimson"] {
    --signal: #FF4438;
    --signal-deep: #FF5F52;
    --signal-tint: #3D1A13;
    --accent-bg: #BE3524;
  }
}

/* Neutral dark surfaces — see the note in the header comment of the design block below.
   Same lightness as the kit's, without the blue cast that shows up next to the accent. */
/* --------------------------------------------------------------------------
   Status text colours. Found undeclared 2026-08-25 by sampling the rendered
   pill rather than reading the stylesheet: --ok-text and --warn-text are used
   in six places here and were declared nowhere, so every status pill has been
   rendering with an undefined colour — inherited text and a transparent dot —
   since this site shipped. design-system/accents.md already carried an open
   flag saying "one7x.com defines these locally"; it did not. A doc that
   references a mechanism reads as if it implements it.

   The values are the ones accents.md already measured and recorded, sampled
   off a rendered page rather than computed: on paper 4.84:1 and 5.10:1, on
   ink 9.07:1 and 8.70:1. Raw --ok / --warn stay for dots and fills, where the
   3:1 non-text floor applies and they pass.
   -------------------------------------------------------------------------- */
:root {
  --ok-text: #1C8055;
  --warn-text: #96620A;
}
:root[data-theme="dark"] {
  --ok-text: #34C98B;
  --warn-text: #E0A33A;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ok-text: #34C98B;
    --warn-text: #E0A33A;
  }
}

:root {
  --ink-neutral-1: #232322;   /* band top     — was #21212A */
  --ink-neutral-2: #191918;   /* band bottom  — was #17171B */
  --ink-neutral-3: #201F1E;   /* raised on ink — was --ink-elev #1E1E26 */
}
:root[data-theme="dark"] {
  --bg: #0F0F0E; --bg-elev: #191918;
  --pearl: #191918; --mist: #2B2B29;
  --border: #2B2B29; --border-strong: #3B3B38;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0F0F0E; --bg-elev: #191918;
    --pearl: #191918; --mist: #2B2B29;
    --border: #2B2B29; --border-strong: #3B3B38;
  }
}
/* --------------------------------------------------------------------------
   Fonts, served from this origin.
   These cost three round trips before: a render-blocking stylesheet on one Google
   host, then the files themselves from a second. Both are gone.
   Four files, not eight: these are the true variable faces (one per family per
   character range), so a single file covers every weight from 100 to 900 — which is
   also why the eight fixed-weight declarations this replaced were redundant.
   Only the latin pair downloads for English; latin-ext waits for a page that needs
   an accented character. Both latin files are preloaded in every <head>.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/assets/fonts/inter-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: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/assets/fonts/inter-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: 'Inter Tight';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/assets/fonts/inter-tight-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: 'Inter Tight';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/assets/fonts/inter-tight-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;
}

/* ==========================================================================
   DESIGN NOTE — read this before changing anything below.

   Colour: kit tokens, plus TWO deliberate overrides, both at the top of this file.
   (1) the accent, because the kit's light rust is a brown; (2) the dark surfaces,
   because the kit's are cool-biased and read blue beside a warm accent.

   ⚠️ THE ACCENT GOES ON ELEMENTS, NOT ON BACKGROUNDS. The bloom and the band wash are
   a light source, not a fill. They were at 24% and covered half a band, which measured
   as a 37-point RGB spread — an orange panel, not a dark one. They now peak around 11%
   in a corner and every band reads grey across most of its width. The test is the raw
   spread, not HSL saturation: at near-white or near-black, one or two points of RGB
   difference computes as 30% "saturation" and is completely invisible. Sample the pixel
   and read max(R,G,B) − min(R,G,B): under 7 is grey, 25 or more is a colour.

   ⚠️ ONE TEMPERATURE PER PAGE. Every dark ground here is neutral or a shade warm.
   The kit's #1E1E26 / #17171B / #0E0E10 all sit near hue 235, which is invisible on
   its own — but put the orange glow on one side of a band and simultaneous contrast
   drags the other side hard toward blue. Measured before the fix: the inverted band
   ran hue 233 on the left and hue 17 on the right. One band, two temperatures, and it
   read as a mistake because it was one. If you add a dark surface, sample it: R, G and
   B within about two points of each other, or warmer. Never cooler.

   The mark's violet→red appears in exactly one place: the 3px rule at the top of the
   page. That one IS deliberate — it is the logo's own gradient, directly beneath the
   logo. It is not a second accent and must not be used anywhere else.

   Type: Inter Tight 800 against Inter, with a much WIDER size gap than the previous
   version had. That gap is what separates a page that reads considered from one that
   reads templated — the old page ran every heading at the same 36px, so nothing told
   the eye what mattered. Hero runs to 82px, section heads to 46px, body sits at 15–16.

   Layout: a 1160px measure with a 720px variant for prose. The page alternates
   paper → pearl → ink so no two neighbouring bands share a surface. Two sections —
   the argument and the close — get double the vertical room; everything else stays
   dense, because "dense, not airy" is a standing rule and the premium comes from
   scale and surface, not from spacing everything out.

   Surface: one grain overlay, two floating objects (the hero mockup and the contact
   card). Everything else is a hairline, per the kit. Shadows are earned, not applied.
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-feature-settings: "cv05" 1, "ss03" 1;
}

/* The mark's own gradient, 3px, above everything. The only place the violet/red
   pair is allowed — it sits directly under the logo it is sampled from. */
body::before {
  content: "";
  position: fixed;
  inset: 0 0 auto 0;
  height: 3px;
  z-index: 300;
  background: linear-gradient(90deg, #6E65E0 0%, #B33C79 52%, #F81211 100%);
}

/* Grain. 3.5% of a static turbulence tile, fixed so it does not swim on scroll.
   This is the one thing that most separates a surface that reads premium from flat
   CSS colour, and it costs a single inline data URI — no request, no image file.
   Keep it under 5% or it stops being texture and starts being noise. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 400;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
:root[data-theme="dark"] body::after,
:root:not([data-theme="light"]) body::after { opacity: 0.05; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: var(--character-heading-weight);
  letter-spacing: var(--character-heading-tracking);
  line-height: var(--lh-tight);
  margin: 0;
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }

a {
  color: var(--signal);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-standard);
}
a:hover { color: var(--signal-deep); }

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

/* ==========================================================================
   layout primitives
   ========================================================================== */

.wrap {
  width: 100%;
  max-width: 1160px;
  margin: 0 auto;
  padding: 0 var(--s-5);
}
/* For sections that are mostly prose — a 1160px measure is unreadable at 16px. */
.wrap--narrow { max-width: 780px; }

.section { padding: var(--s-8) 0; position: relative; }
/* The header is sticky, so anything an anchor jumps to needs to clear it or the first
   line of the section lands underneath the bar. */
[id] { scroll-margin-top: 84px; }
.section--tight { padding: var(--s-7) 0; }
/* Only the two sections carrying the argument and the close get the extra room. */
.section--tall { padding: var(--s-9) 0; }

.section--pearl {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--pearl) 55%, var(--bg)) 0%,
    var(--pearl) 18%,
    var(--pearl) 82%,
    color-mix(in srgb, var(--pearl) 55%, var(--bg)) 100%);
}

/* The inverted bands. Dark in BOTH themes, which is why they re-declare the accent:
   a colour legible on paper is not legible on ink, and the light-theme orange is a
   deep brown that all but vanishes here. */
.section--ink {
  color: var(--paper-on-dark);
  --signal: #FF4438;
  --signal-deep: #FF5F52;
  --border: #35352F;
  --border-strong: #46463F;
  background:
    radial-gradient(58% 80% at 92% -14%,
      color-mix(in srgb, var(--signal) 11%, transparent) 0%, transparent 62%),
    linear-gradient(180deg, var(--ink-neutral-1) 0%, var(--ink-neutral-2) 100%);
}
:root[data-theme="dark"] .section--ink,
:root:not([data-theme="light"]) .section--ink {
  background:
    radial-gradient(58% 80% at 92% -14%,
      color-mix(in srgb, var(--signal) 9%, transparent) 0%, transparent 62%),
    linear-gradient(180deg, var(--ink-neutral-3) 0%, #141413 100%);
}
.section--ink h1, .section--ink h2, .section--ink h3 { color: var(--paper-on-dark); }
.section--ink p { color: #B7B7B1; }

/* A hairline between neighbours only when they share a surface. */
.section + .section { border-top: 1px solid var(--border); }
.section--pearl + .section, .section + .section--pearl,
.section--ink + .section,   .section + .section--ink { border-top: 0; }

/* ==========================================================================
   type
   ========================================================================== */

.eyebrow {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--tr-normal);
  color: var(--fg-muted);
  margin: 0 0 var(--s-4);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
/* A 14px accent tick before the label. Structural, not decorative — it is what
   makes a section opening read as an opening rather than as stray small text. */
.eyebrow::before {
  content: "";
  width: 14px; height: 2px;
  border-radius: var(--r-pill);
  background: var(--signal);
  flex: none;
}
.eyebrow--accent { color: var(--signal); }
/* Pinned against `.argue p` and `.contact p`, which are class+type and would otherwise
   set the size and colour of any eyebrow sitting inside them. */
.argue .eyebrow, .contact .eyebrow, .head .eyebrow {
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--fg-muted);
}
.argue .eyebrow--accent, .contact .eyebrow--accent, .head .eyebrow--accent { color: var(--signal); }
/* On ink the accent runs 3.7:1 at 12px, under the small-text floor. The lifted value
   clears it, and it is the same colour the kit already uses for accent-on-ink hover. */
.section--ink .eyebrow,
.section--ink .argue .eyebrow,
.section--ink .contact .eyebrow { color: #C9C7C1; }
.section--ink .eyebrow::before { background: var(--signal); }

.head { max-width: 34ch; margin-bottom: var(--s-6); }
.head--wide { max-width: 52ch; }
.head h1, .head h2 {
  font-size: clamp(32px, 4vw, 46px);
  line-height: 0.98;
  margin-bottom: var(--s-4);
}
.head p {
  font-size: var(--fs-md);
  line-height: var(--lh-relaxed);
  color: var(--fg-muted);
  max-width: 46ch;
}

.lede {
  font-size: 20px;
  line-height: 1.55;
  color: var(--fg-muted);
  max-width: 46ch;
}
.prose { max-width: 66ch; }
.prose p + p { margin-top: var(--s-4); }

/* The second line of a headline, in the accent. One per page — a second use spends it. */
/* ⚠️ This was display:block, which forced the accent word onto a line of its
   own and was the real cause of "AI will change how / everyone / works."
   Colour and line-breaking are different jobs: .accentline colours a word,
   .g decides where a line may break. Keeping them in one rule meant every
   heading's grouping was a side effect of which word happened to be accented. */
.accentline { color: var(--signal); }

/* ==========================================================================
   chrome — skip link, header, footer
   ========================================================================== */

.skip {
  position: absolute;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  left: -9999px;
  top: var(--s-3);
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-3);
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--fg);
  z-index: 500;
}
.skip:focus { left: var(--s-5); }

.site-header {
  position: sticky;
  top: 3px;
  z-index: 50;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: background var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard);
}
/* Solidifies once the page has moved. A header that changes state on scroll is a
   small thing that reads as considered; a permanently-bordered bar does not. */
.site-header.is-stuck {
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  border-bottom-color: var(--border);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  height: 66px;
}

/* Two asset variants, not one recoloured in CSS: the gradient ring must stay
   identical in both, and only the wordmark text swaps light/dark. */
.wordmark { display: inline-flex; align-items: center; padding: 8px 0; margin: -8px 0; }
.wordmark img { height: 30px; width: auto; display: block; }
.wordmark .logo-dark { display: none; }
:root[data-theme="dark"] .wordmark .logo-light { display: none; }
:root[data-theme="dark"] .wordmark .logo-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wordmark .logo-light { display: none; }
  :root:not([data-theme="light"]) .wordmark .logo-dark { display: block; }
}
:root[data-theme="light"] .wordmark .logo-light { display: block; }
:root[data-theme="light"] .wordmark .logo-dark { display: none; }

.nav { display: flex; align-items: center; gap: var(--s-5); }
.nav a {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--fg-muted);
  /* Padding, not size: the hit area reaches 44px while the text stays where it was. */
  padding: 13px 4px;
  margin: -13px -4px;
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--fg); }
.nav .btn { padding: 9px var(--s-4); min-height: 40px; }
/* `.nav a` is class+type and outranks `.btn--primary`, which painted the header
   button's label muted grey on the accent. Scoped back at equal weight. */
.nav .btn--primary, .nav .btn--primary:hover { color: var(--accent-fg); }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--r-3);
  color: var(--fg-muted);
  cursor: pointer;
  padding: 0;
  transition: border-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.theme-toggle:hover { border-color: var(--border-strong); color: var(--fg); }
.theme-toggle svg { width: 15px; height: 15px; }
:root[data-theme="dark"] .theme-toggle .i-moon,
:root:not([data-theme="light"]) .theme-toggle .i-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .i-sun,
:root:not([data-theme="light"]) .theme-toggle .i-sun { display: block; }
.theme-toggle .i-sun { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .i-moon { display: block; }
  :root:not([data-theme="dark"]) .theme-toggle .i-sun { display: none; }
}
:root[data-theme="light"] .theme-toggle .i-moon { display: block; }
:root[data-theme="light"] .theme-toggle .i-sun { display: none; }

.site-footer { border-top: 1px solid var(--border); padding: var(--s-6) 0; }
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  font-size: var(--fs-sm);
  /* --fg-subtle is 3.2:1 on paper, below the body-text floor. Muted clears it. */
  color: var(--fg-muted);
}
.site-footer nav { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); }
.site-footer a { color: var(--fg-muted); padding: 12px 0; margin: -12px 0; }
.site-footer a:hover { color: var(--fg); }

/* Footer brand — the one7x mark, on every page including the product pages,
   where the header carries the product's own identity instead. This is the path back to
   the parent brand. Reuses the header's light/dark swap, so no third asset is needed.
   Ported from the production branch, 2026-08-03. */
.footer-brand { display: flex; align-items: center; gap: var(--s-3); }
.footer-logo { display: inline-flex; align-items: center; padding: 8px 0; margin: -8px 0; }
.footer-logo img { height: 28px; width: auto; display: block; }
.footer-logo .logo-dark { display: none; }
:root[data-theme="dark"] .footer-logo .logo-light { display: none; }
:root[data-theme="dark"] .footer-logo .logo-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .footer-logo .logo-light { display: none; }
  :root:not([data-theme="light"]) .footer-logo .logo-dark { display: block; }
}
:root[data-theme="light"] .footer-logo .logo-light { display: block; }
:root[data-theme="light"] .footer-logo .logo-dark { display: none; }


/* ==========================================================================
   buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: var(--tr-normal);
  padding: 12px var(--s-5);
  min-height: 44px;
  border-radius: var(--r-3);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.btn--primary {
  color: var(--accent-fg);
  background-image: linear-gradient(180deg,
    var(--accent-bg) 0%,
    color-mix(in srgb, #000000 7%, var(--accent-bg)) 100%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.16),
              inset 0 1px 0 rgba(255, 255, 255, 0.10);
}
.btn--primary:hover {
  color: var(--accent-fg);
  transform: translateY(-1px);
  background-image: linear-gradient(180deg,
    var(--accent-bg) 0%, color-mix(in srgb, #000000 13%, var(--accent-bg)) 100%);
}
.btn--secondary {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--fg);
}
.btn--secondary:hover { border-color: var(--fg-muted); color: var(--fg); }
.section--ink .btn--secondary { color: var(--paper-on-dark); }

.quietlink {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--fg-muted);
  align-self: center;
  padding: 12px 0;
  margin: -12px 0;
}
.quietlink:hover { color: var(--signal); }

/* ==========================================================================
   icons and badges
   ========================================================================== */

.ico {
  width: 20px; height: 20px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  flex: none;
  border-radius: var(--r-4);
  background: var(--signal-tint);
  color: var(--signal);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--signal) 14%, transparent);
}
.badge--quiet {
  background: transparent;
  border: 1px solid var(--border);
  box-shadow: none;
  color: var(--fg-subtle);
  width: 34px; height: 34px;
  border-radius: var(--r-3);
}
.badge--quiet .ico { width: 16px; height: 16px; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-xs);
  font-weight: 600;
  padding: 4px 11px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  color: var(--fg-muted);
  background: var(--bg);
  white-space: nowrap;
}
.pill::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: var(--r-pill);
  background: var(--fg-subtle);
}
.pill--building { color: var(--warn-text); border-color: color-mix(in srgb, var(--warn-text) 34%, var(--border)); }
.pill--building::before { background: var(--warn-text); }
.pill--live { color: var(--ok-text); border-color: color-mix(in srgb, var(--ok-text) 34%, var(--border)); }
.pill--live::before { background: var(--ok-text); }
.pill--closed { color: var(--fg-muted); }

/* ==========================================================================
   card base — one definition, everything inherits it
   ========================================================================== */

.card {
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  background: var(--bg-elev);
  /* A 1px inner highlight along the top edge. Almost invisible on its own, and the
     reason a bordered card reads as an object rather than as an outlined rectangle. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  transition: transform var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
:root[data-theme="dark"] .card,
:root:not([data-theme="light"]) .card { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045); }
.card:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
:root[data-theme="dark"] .card:hover,
:root:not([data-theme="light"]) .card:hover {
  box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.section--ink .card {
  background: rgba(255, 255, 255, 0.035);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.section--ink .card:hover { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09); }

/* ==========================================================================
   hero
   ========================================================================== */

.hero {
  position: relative;
  isolation: isolate;
  padding: var(--s-9) 0 var(--s-8);
  /* Contains the bloom, which is deliberately wider than the column and would
     otherwise push the document ~29px sideways at 360px. Contained here rather than
     clipped at the root, which would hide the next real overflow instead of showing it. */
  overflow: hidden;
}
.hero::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: -22%;
  right: -6%;
  width: min(820px, 66vw);
  aspect-ratio: 1;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--signal) 8%, transparent) 0%,
    color-mix(in srgb, var(--signal) 2%, transparent) 42%,
    transparent 66%);
}
:root[data-theme="dark"] .eyebrow--accent,
:root:not([data-theme="light"]) .eyebrow--accent { color: var(--signal); }

:root[data-theme="dark"] .hero::before,
:root:not([data-theme="light"]) .hero::before {
  background: radial-gradient(circle,
    color-mix(in srgb, var(--signal) 13%, transparent) 0%,
    color-mix(in srgb, var(--signal) 4%, transparent) 42%,
    transparent 66%);
}

.hero__grid {
  display: grid;
  grid-template-columns: 1.14fr 0.86fr;
  gap: var(--s-7);
  align-items: center;
}
.hero h1 {
  font-size: clamp(40px, 4.9vw, 62px);
  line-height: 0.95;
  letter-spacing: -0.05em;
  margin-bottom: var(--s-5);
}
.hero .lede { max-width: 44ch; }
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  margin-top: var(--s-6);
  align-items: center;
}
.hero__points {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
  color: var(--fg-muted);
}
.hero__points span { display: inline-flex; align-items: center; gap: var(--s-2); }
.hero__points .ico { width: 15px; height: 15px; color: var(--signal); }

/* ==========================================================================
   product mockup — one of the two objects on the site allowed to float
   ========================================================================== */

.mock {
  margin: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  overflow: hidden;
  position: relative;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--signal) 4%, var(--bg-elev)) 0%, var(--bg-elev) 32%);
  box-shadow:
    0 2px 4px rgba(24, 24, 27, 0.04),
    0 12px 24px -8px rgba(24, 24, 27, 0.10),
    0 40px 72px -24px rgba(24, 24, 27, 0.20);
}
:root[data-theme="dark"] .mock,
:root:not([data-theme="light"]) .mock {
  box-shadow: 0 32px 64px -20px rgba(0, 0, 0, 0.7);
}
.mock__bar {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 11px var(--s-5);
  border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--pearl) 65%, var(--bg)) 0%, var(--pearl) 100%);
}
:root[data-theme="dark"] .mock__bar,
:root:not([data-theme="light"]) .mock__bar {
  background: linear-gradient(180deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0.02) 100%);
}
.mock__dot { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--fog); flex: none; }
.mock__title { margin-left: var(--s-2); font-size: var(--fs-xs); font-weight: 600; color: var(--fg-muted); }
.mock__body { padding: var(--s-5) var(--s-5) 28px; display: grid; gap: 18px; }
.mock__tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-3); }
.mock__tile {
  border: 1px solid var(--border);
  border-radius: var(--r-4);
  padding: var(--s-3) var(--s-4);
  background: var(--bg);
}
.mock__num {
  font-family: var(--font-display);
  font-weight: var(--character-heading-weight);
  letter-spacing: var(--tr-display);
  font-size: var(--fs-xl);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.mock__lbl { font-size: var(--fs-xs); color: var(--fg-muted); margin-top: 6px; }
.mock__rows { display: grid; }
.mock__row {
  display: grid;
  /* A fixed status column, or each pill's own width shifts the progress column and
     the bars come out visibly ragged row to row. */
  grid-template-columns: minmax(0, 1fr) 74px 118px;
  align-items: center;
  gap: var(--s-3);
  padding: 11px 0;
  font-size: var(--fs-sm);
}
.mock__row + .mock__row { border-top: 1px solid var(--border); }
.mock__row > :last-child { justify-self: start; }
.mock__rowhead { font-size: var(--fs-xs); font-weight: 600; color: var(--fg-muted); padding-bottom: 6px; }
/* Inline spans ignore height and overflow, which is why an earlier version of this
   rendered as a grey hairline with no accent fill at all. */
.mock__bar-track {
  display: block; width: 100%; height: 5px;
  border-radius: var(--r-pill);
  background: var(--mist);
  overflow: hidden;
}
.mock__bar-fill {
  display: block; height: 100%;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--signal-deep), var(--signal));
}
.mock__tag {
  font-size: var(--fs-xs);
  font-weight: 600;
  padding: 3px 9px;
  border-radius: var(--r-pill);
  white-space: nowrap;
  border: 1px solid var(--border);
  color: var(--fg-muted);
  background: var(--bg);
}
.mock__tag--done { color: var(--ok-text); border-color: color-mix(in srgb, var(--ok-text) 34%, var(--border)); }
.mock__tag--auto {
  color: var(--accent-fg);
  border-color: transparent;
  background: var(--accent-bg);
}
.mock__tag--wait { color: var(--warn-text); border-color: color-mix(in srgb, var(--warn-text) 34%, var(--border)); }
.mock__note {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  border-top: 1px solid var(--border);
  color: var(--signal-deep);
  font-size: var(--fs-xs);
  font-weight: 600;
  padding: 11px var(--s-5);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--signal) 12%, transparent) 0%,
    color-mix(in srgb, var(--signal) 4%, transparent) 100%);
}
.mock__note .ico { width: 14px; height: 14px; }
.figcap {
  margin-top: var(--s-3);
  font-size: var(--fs-xs);
  color: var(--slate-700);
  text-align: right;
}
:root[data-theme="dark"] .figcap,
:root:not([data-theme="light"]) .figcap { color: var(--fg-muted); }

/* ==========================================================================
   rule cards — the self-identification and negation lists
   ========================================================================== */

.rules { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-4); }
.rules li {
  display: flex;
  gap: var(--s-4);
  align-items: center;
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  background: var(--bg-elev);
  padding: var(--s-5) 28px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  transition: transform var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
:root[data-theme="dark"] .rules li,
:root:not([data-theme="light"]) .rules li { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045); }
.rules li:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
:root[data-theme="dark"] .rules li:hover,
:root:not([data-theme="light"]) .rules li:hover { box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255, 255, 255, 0.06); }
.rules .rt { font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--fg-muted); }
.rules b { display: block; color: var(--fg); font-weight: 600; font-size: var(--fs-base); margin-bottom: 4px; }
.section--ink .rules li { background: rgba(255, 255, 255, 0.035); }
.section--ink .rules b { color: var(--paper-on-dark); }

/* ==========================================================================
   the argument band
   ========================================================================== */

.argue { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-8); align-items: center; }
.argue h2 {
  font-size: clamp(34px, 4.4vw, 52px);
  line-height: 0.96;
  margin-bottom: var(--s-5);
}
.argue p { font-size: var(--fs-md); line-height: var(--lh-relaxed); max-width: 44ch; }

.thennow { display: grid; gap: var(--s-4); }
.thennow div {
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  padding: 26px 30px;
  background: rgba(255, 255, 255, 0.035);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.thennow b {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  margin-bottom: var(--s-3);
  color: #A2A29C;
}
.thennow b::before {
  content: "";
  width: 12px; height: 2px;
  border-radius: var(--r-pill);
  background: currentColor;
}
.thennow span { font-size: var(--fs-md); line-height: var(--lh-snug); color: var(--paper-on-dark); }
.thennow .now {
  border-color: color-mix(in srgb, var(--signal) 50%, transparent);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--signal) 11%, transparent) 0%,
    color-mix(in srgb, var(--signal) 3%, transparent) 100%);
}
.thennow .now b { color: #D6D3CE; }
.thennow .now b::before { background: var(--signal); }

/* ==========================================================================
   how it works — a real sequence, so it gets numbers and a connector
   ========================================================================== */

.howstrip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-5);
  counter-reset: st;
  position: relative;
}
.howstrip li {
  counter-increment: st;
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  background: var(--bg-elev);
  padding: 28px 30px;
  position: relative;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  transition: transform var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
:root[data-theme="dark"] .howstrip li,
:root:not([data-theme="light"]) .howstrip li { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045); }
.howstrip li:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
.howstrip li::before {
  content: counter(st);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border-radius: var(--r-pill);
  color: var(--accent-fg);
  font-size: var(--fs-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--s-4);
  background: linear-gradient(160deg,
    color-mix(in srgb, #FFFFFF 18%, var(--accent-bg)) 0%,
    color-mix(in srgb, #000000 14%, var(--accent-bg)) 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
/* The connector between steps. Drawn only where there IS a next step, and only
   where the three sit on one row — it would be nonsense stacked. */
@media (min-width: 901px) {
  .howstrip li:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 44px;
    right: calc(var(--s-5) * -1);
    width: var(--s-5);
    height: 1px;
    background: var(--border-strong);
  }
}
.howstrip b {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--character-heading-weight);
  letter-spacing: var(--tr-tight);
  line-height: var(--lh-snug);
  margin-bottom: var(--s-3);
}
.howstrip span { font-size: var(--fs-sm); line-height: var(--lh-relaxed); color: var(--fg-muted); }

/* A second route to the form, placed where a reader is most likely to be convinced.
   The only form on the page sits after seven sections, and on a phone the header
   button is hidden, so without this a decided visitor has to scroll to the end. */
.midcta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
  margin-top: var(--s-6);
}
.midcta span { font-size: var(--fs-sm); color: var(--fg-muted); }

/* A standalone inline link acting as a navigation step still needs a tappable box. */
.inline-next { display: inline-flex; align-items: center; min-height: 44px; font-size: var(--fs-sm); font-weight: 600; }

/* ==========================================================================
   the work list
   ========================================================================== */

.exp {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  overflow: hidden;
  background: var(--bg-elev);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
:root[data-theme="dark"] .exp,
:root:not([data-theme="light"]) .exp { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045); }
.exp__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: var(--s-4) var(--s-5);
  align-items: center;
  padding: 30px 32px;
  color: inherit;
  transition: background var(--duration-fast) var(--ease-standard);
}
a.exp__item:hover { background: var(--pearl); color: inherit; }
a.exp__item:hover .exp__name { color: var(--signal); }
.exp__item + .exp__item, .exp li + li { border-top: 1px solid var(--border); }
.exp__name {
  font-family: var(--font-display);
  font-weight: var(--character-heading-weight);
  letter-spacing: var(--tr-tight);
  font-size: var(--fs-xl);
  line-height: var(--lh-snug);
  color: var(--fg);
  transition: color var(--duration-fast) var(--ease-standard);
}
.exp__desc {
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--fg-muted);
  margin-top: var(--s-2);
  max-width: 62ch;
}
.exp__go { font-size: var(--fs-sm); font-weight: 600; color: var(--signal); white-space: nowrap; }
:root[data-theme="dark"] .exp__go,
:root:not([data-theme="light"]) .exp__go { color: var(--signal); }
.exp__note {
  padding: var(--s-5) 32px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--fg-muted);
  background: var(--pearl);
}

/* ==========================================================================
   about page — steps grid and the founder row
   ========================================================================== */

.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-6) var(--s-5); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
/* Four promises in a three-column grid left the fourth alone on its own row, which
   reads unfinished. Four across fills the band and keeps the section dense. */
.grid--4 { grid-template-columns: repeat(4, 1fr); }
.step { display: flex; flex-direction: column; gap: var(--s-3); }
.step h3 { font-size: var(--fs-lg); letter-spacing: var(--tr-tight); line-height: var(--lh-snug); }
.step p { font-size: var(--fs-sm); line-height: var(--lh-relaxed); color: var(--fg-muted); }

.mono {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 58px; height: 58px;
  flex: none;
  border-radius: var(--r-4);
  background: var(--ink-deep);
  color: var(--paper);
  font-family: var(--font-display);
  font-weight: var(--character-heading-weight);
  letter-spacing: var(--tr-display);
  font-size: var(--fs-md);
}
/* ⚠️ --signal, not --accent-bg, put white 18px/800 text on the LIFTED red and
   measured 3.36:1 against a 4.5:1 floor — 18px just misses the 18.66px
   large-text threshold, so the 3:1 allowance does not apply. This is the trap
   this repo already documents: a red light enough to READ as text on ink is
   too light to put white text ON. --accent-bg is the fill value in both
   themes and measures 5.10:1. Pre-existing; found 2026-08-25 by sampling. */
:root[data-theme="dark"] .mono,
:root:not([data-theme="light"]) .mono { background: var(--accent-bg); }
.founder {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--s-5);
  align-items: center;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  padding: 28px 32px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
:root[data-theme="dark"] .founder,
:root:not([data-theme="light"]) .founder { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045); }
.founder h3 { font-size: var(--fs-lg); letter-spacing: var(--tr-tight); margin-bottom: var(--s-3); }
.founder p { font-size: var(--fs-sm); line-height: var(--lh-relaxed); color: var(--fg-muted); max-width: 64ch; }
.founder p + p { margin-top: var(--s-3); }

/* ==========================================================================
   contact — the second floating object, on ink
   ========================================================================== */

.contact { text-align: center; }
.contact h2 {
  font-size: clamp(32px, 4.2vw, 50px);
  line-height: 0.98;
  margin-bottom: var(--s-4);
}
.contact > .wrap > p {
  font-size: var(--fs-md);
  max-width: 46ch;
  margin: 0 auto var(--s-7);
  line-height: var(--lh-relaxed);
}
.contact .eyebrow { justify-content: center; }

.ask {
  max-width: 640px;
  margin: 0 auto;
  text-align: left;
  display: grid;
  gap: var(--s-4);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  padding: 30px 32px;
  box-shadow: 0 24px 56px -20px rgba(0, 0, 0, 0.45);
}
.section--ink .ask { background: rgba(255, 255, 255, 0.05); }
.ask__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }
/* Off-screen rather than display:none, which some bots skip. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.field { display: grid; gap: 7px; }
.field > span { font-size: var(--fs-xs); font-weight: 600; color: var(--fg-muted); }
.section--ink .field > span { color: #C6C6C0; }
.field > span b { color: var(--signal); font-weight: 600; }
.field input, .field textarea {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-3);
  padding: 12px var(--s-4);
  min-height: 44px;
  width: 100%;
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}
.section--ink .field input, .section--ink .field textarea {
  background: rgba(0, 0, 0, 0.25);
  color: var(--paper-on-dark);
  border-color: #43434E;
}
.field textarea { resize: vertical; line-height: var(--lh-relaxed); }
.field input::placeholder, .field textarea::placeholder { color: var(--fg-muted); }
.section--ink .field input::placeholder, .section--ink .field textarea::placeholder { color: #8E8E88; }
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--signal) 38%, transparent);
}
/* Keyboard focus gets a real outline on top of the glow, and it has to be legible on
   ink as well as on paper — hence the light ring inside the inverted band. */
.field input:focus-visible, .field textarea:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
}
.section--ink :focus-visible { outline-color: var(--paper-on-dark); }
/* Only flag invalid fields after a submit attempt — validating as someone types
   scolds them mid-sentence. */
.ask.submitted :invalid {
  border-color: var(--danger);
  border-width: 2px;
  background-image: none;
}
.ask__foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-4); }
.ask__status { font-size: var(--fs-sm); font-weight: 600; margin: 0; }
.ask__status:focus { outline: none; }
.ask__status:focus-visible { outline: 2px solid var(--signal); outline-offset: 4px; }
.ask__status a { text-decoration: underline; }
/* Once it has sent, the fields have done their job — leaving four empty boxes on screen
   reads as though nothing happened. */
.ask.sent .ask__row, .ask.sent .field, .ask.sent .ask__foot { display: none; }
.ask.sent { text-align: center; }
.ask__status[data-state="ok"] { color: var(--ok); }
.ask__status[data-state="err"] { color: var(--danger); }
.section--ink .ask__status[data-state="ok"] { color: #4FD198; }
.section--ink .ask__status[data-state="err"] { color: #FF8A8A; }

/* ==========================================================================
   404
   ========================================================================== */
.notfound {
  min-height: 58vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--s-5);
}
.notfound h1 { font-size: clamp(44px, 7vw, var(--fs-4xl)); }
.notfound .lede { margin: 0 auto; }

/* ==========================================================================
   motion
   ========================================================================== */

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.enter { animation: fadeUp 0.62s var(--ease-standard) backwards; }
.enter-2 { animation-delay: 0.04s; }
.enter-3 { animation-delay: 0.11s; }
.enter-4 { animation-delay: 0.18s; }
.enter-5 { animation-delay: 0.25s; }
.enter-6 { animation-delay: 0.32s; }

/* The class that hides an element is added by script, never baked into markup, so a
   visitor with JS off sees everything instead of a page stuck at zero opacity. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .62s var(--ease-standard), transform .62s var(--ease-standard);
}
.reveal.in-view { opacity: 1; transform: none; }

/* ==========================================================================
   responsive
   ========================================================================== */

@media (max-width: 1000px) {
  .argue { grid-template-columns: 1fr; gap: var(--s-6); }
  .steps, .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
  .hero { padding: var(--s-8) 0; }
  .hero__grid { grid-template-columns: 1fr; gap: var(--s-7); }
  .hero .lede { max-width: 52ch; }
  .howstrip { grid-template-columns: 1fr; }
  .founder { grid-template-columns: auto minmax(0, 1fr); }
  .founder > .btn { grid-column: 2; justify-self: start; }
}
@media (max-width: 700px) {
  .rules { grid-template-columns: 1fr; }
  .steps, .grid--3, .grid--4 { grid-template-columns: 1fr; gap: var(--s-5); }
  /* Badge beside the title, body under both — a plain flex row squeezes the heading
     into a narrow column and it wraps one word per line. */
  .step {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr);
    column-gap: var(--s-4);
    row-gap: var(--s-2);
    align-items: center;
  }
  .step .badge { grid-area: 1 / 1; }
  .step h3     { grid-area: 1 / 2; }
  .step p      { grid-area: 2 / 2; }
}
/* Below this the CTA genuinely crowds the logo, and the mid-page button is now one
   scroll away either side of it. */
@media (max-width: 430px) {
  .nav .btn--header { display: none; }
}
@media (max-width: 640px) {
  .wrap { padding: 0 var(--s-4); }
  .section { padding: var(--s-7) 0; }
  .section--tall { padding: var(--s-8) 0; }
  .hero { padding: var(--s-7) 0; }
  .head { margin-bottom: var(--s-6); }
  .lede { font-size: var(--fs-md); }
  .ask__row { grid-template-columns: 1fr; }
  .ask, .exp__item, .founder, .exp__note, .rules li, .howstrip li, .thennow div { padding: var(--s-5); }
  .exp__item { grid-template-columns: 1fr; }
  .exp__item > .pill, .exp__item > .exp__go { justify-self: start; }
  .mock__body { padding: var(--s-4) var(--s-4) var(--s-5); }
  .mock__tiles { gap: var(--s-2); }
  .mock__tile { padding: var(--s-3); }
  .mock__row { grid-template-columns: minmax(0, 1fr) auto; }
  .mock__row .mock__bar-track { display: none; }
  .mock__rowhead > span:nth-child(2) { display: none; }
  .figcap { text-align: left; }
  .nav { gap: var(--s-4); }
  .nav a { font-size: var(--fs-xs); }
  .nav a { padding: 13px 2px; margin: -13px -2px; }
}

/* ==========================================================================
   2026-08-25 — the positioning rebuild.

   Direction is recorded in solo-projects-os at
   projects/one7x-website-2026-08/design-direction.md. The short version: the
   reference sites Dipender named (Coda, Asana, x.ai) are not more SPACED than
   this kit — x.ai's section padding is 64px and so is ours. They are EMPTIER.
   So everything below removes elements rather than adding air, which is what
   keeps "dense, not airy" intact while getting the same quality.

   Every value here resolves to a kit token. No new colour, radius or shadow.
   ========================================================================== */

/* --- hero: the idea is the hero, so there is no visual beside it ---------- */
/* Coda and x.ai both put nothing in the hero. With the mockup gone the copy
   is alone and can take the scale the kit already has and the old two-column
   layout could not afford. */
.hero--thesis { padding: var(--s-10) 0 var(--s-9); }

.hero--thesis h1 {
  font-size: clamp(44px, 7.4vw, 92px);
  line-height: 0.94;
  letter-spacing: var(--tr-display);
  max-width: 15ch;
}
.hero--thesis .lede {
  max-width: 58ch;
  font-size: var(--fs-md);
}

/* --- the territory: the one graphic on the page --------------------------- */
/* x.ai's "sparse SVG only" move, done in CSS: a hairline progression, no
   fills, no shadow. Five items reading left to right on desktop, stacking to
   a single column on a phone. The rule between them is the only ornament. */
.layers {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  border-top: 1px solid var(--border);
}
.layer {
  padding: var(--s-5) var(--s-5) var(--s-6) 0;
  border-right: 1px solid var(--border);
  display: grid;
  align-content: start;
  gap: var(--s-2);
}
.layer:last-child { border-right: 0; }
.layer:not(:first-child) { padding-left: var(--s-5); }

/* The numeral is data, so it is the one place --font-mono is correct here.
   Labels elsewhere stay sans, per the kit. */
.layer__n {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--signal);
  letter-spacing: var(--tr-normal);
}
.layer b {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--character-heading-weight);
  letter-spacing: var(--tr-tight);
  line-height: var(--lh-snug);
}
.layer span:not(.layer__n) {
  font-size: var(--fs-sm);
  color: var(--fg-muted);
  line-height: var(--lh-normal);
}
.layers__note {
  margin-top: var(--s-6);
  max-width: 62ch;
  color: var(--fg-muted);
  font-size: var(--fs-sm);
}

/* --- how one7x operates: three plain columns, hairline-separated ---------- */
.modes {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-6);
}
.modes li {
  display: grid;
  align-content: start;
  gap: var(--s-2);
  padding-top: var(--s-4);
  border-top: 2px solid var(--signal);
}
.modes b {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--character-heading-weight);
  letter-spacing: var(--tr-tight);
}
.modes span {
  font-size: var(--fs-sm);
  color: var(--fg-muted);
  line-height: var(--lh-normal);
}

/* --- the promise that replaced the kill dates ---------------------------- */
.worknote {
  margin-top: var(--s-6);
  padding-left: var(--s-4);
  border-left: 2px solid var(--signal);
  max-width: 56ch;
  font-size: var(--fs-sm);
  color: var(--fg-muted);
}

/* --- contact is no longer an inverted band ------------------------------- */
/* The point of view now holds the page's single dark section. Two dark bands
   halve the emphasis of each, which is the opposite of "colour as
   punctuation". The card's shadow drops to the kit's own --shadow-3: the old
   value was tuned to float on ink and is far too heavy on paper. */
.contact .ask { box-shadow: var(--shadow-3); }

@media (max-width: 900px) {
  .layers { grid-template-columns: repeat(2, 1fr); }
  .layer:nth-child(2n) { border-right: 0; }
  .layer { border-bottom: 1px solid var(--border); }
  .modes { grid-template-columns: 1fr; gap: var(--s-5); }
}
@media (max-width: 560px) {
  .layers { grid-template-columns: 1fr; }
  .layer { border-right: 0; padding-left: 0 !important; }
  .layer:last-child { border-bottom: 0; }
  .hero--thesis { padding: var(--s-8) 0 var(--s-7); }
}

/* The point-of-view band is now a single column — the then/now comparison that used to
   sit beside it was old-positioning content and is gone. Without this the copy stays
   stuck in column 1 of a 1fr 1fr grid: measured at 334px of text inside a 780px
   container, with 422px of dead space to its right.

   The container keeps the page's normal width and alignment; only the text carries a
   measure, so this band lines up with every other section rather than floating. */
.section--ink .argue { grid-template-columns: 1fr; }
.section--ink .argue h2 { max-width: 16ch; }
.section--ink .argue p { max-width: 56ch; }
.section--ink .argue p + p { margin-top: var(--s-4); }

/* One of the five layer names wraps to two lines and the other four do not, which
   dropped its description 26px below the rest — measured, not eyeballed. Reserving two
   lines for every title puts all five descriptions back on one baseline. Two lines is
   the real ceiling here: the longest name is "Living knowledge". */
.layer b { min-height: calc(2 * var(--lh-snug) * var(--fs-lg)); }
@media (max-width: 560px) { .layer b { min-height: 0; } }

/* ==========================================================================
   2026-08-25, second pass — ease the severity one step.

   Four reference sites now: Coda, Asana, Lemon Yellow and x.ai. Three of the
   four are light, warm and approachable; only x.ai is austere, and the first
   pass leaned on x.ai. Dipender's call: soften a step, keep the structure
   and the accent.

   Measured from lemonyellow.design's own stylesheet rather than from
   impression: its display sizes top out at 64px (not 92), its weights run
   300-800 with 400/500/600 carrying most of the page, and 9999px pills are
   its most-used radius. Its accent (#FAE100) is a single saturated mark on a
   near-white ground — the same "colour as punctuation" rule already applied
   here, so nothing changes on colour.

   Every value below is an existing kit token or a value between two of them.
   No new colour, radius or shadow enters the system.
   ========================================================================== */

/* Display drops from 92px to 72px, which is the range the three warm
   references actually occupy, and tracking eases from -0.045em to -0.03em.
   At 92px/-0.045em the headline read as a statement; at 72px/-0.03em it
   reads as a sentence, which is what a thesis should do. */
.hero--thesis h1 {
  font-size: clamp(40px, 5.6vw, 72px);
  line-height: 0.98;
  letter-spacing: -0.03em;
  max-width: 17ch;
}

/* The same easing on section headings, or the hero softens and the rest of
   the page stays sharp — which reads as an accident rather than a choice. */
.head h1, .head h2,
.argue h2,
.contact h2 { letter-spacing: -0.028em; }

/* Roundness moves up exactly one step within the existing token set:
   buttons r-3 (8px) -> r-4 (12px), and the pill stays where it already was
   on status chips. Nothing new is introduced. */
.btn { border-radius: var(--r-4); }

/* Cards were already r-5 (16px), the kit's largest non-pill radius, so they
   stay. Softening them further would need a token that does not exist. */

/* ==========================================================================
   2026-08-25 — the world-view rebuild.

   The page now leads with a claim someone could disagree with, rather than a
   category. Reasoning lives in solo-projects-os at
   knowledge/workspace/world-view.md and operating-model.md.

   This pass also answers "seems a static page, no micro animations, no
   imagery". The answer is NOT decoration: the one animated thing on the page
   is the workflow diagram, because that is the one idea a picture explains
   faster than a sentence. Everything else gets hover and focus response only.
   ========================================================================== */

/* --- the argument band: the turn gets its own weight --------------------
   ⚠️ Do NOT set colour here with --fg-inverse. The inverted band is dark in
   BOTH themes, but --fg-inverse flips to dark ink under a dark theme — which
   rendered this line at rgb(46,46,51) on rgb(27,28,25), about 1.4:1, and
   invisible. Caught by sampling the rendered pixel; the stylesheet looked
   correct. .section--ink already owns the text colour for this band, so the
   right move is to inherit it rather than restate it. */
.argue__turn {
  margin-top: var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid rgba(255,255,255,0.14);
  font-size: var(--fs-md) !important;
  max-width: 48ch;
}

/* --- the use cases ------------------------------------------------------ */
.flows { list-style: none; display: grid; gap: var(--s-4); }
.flow {
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  padding: 26px 30px;
  background: var(--bg-elev);
  transition: border-color var(--duration-base) var(--ease-standard);
}
.flow:hover { border-color: var(--border-strong); }
.flow__head {
  display: flex; align-items: center; gap: var(--s-4);
  flex-wrap: wrap;
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--border);
}
.flow__path { display: inline-flex; align-items: center; gap: var(--s-3); }
.flow__path b {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: var(--character-heading-weight);
  letter-spacing: -0.028em;
}
/* The arrow is drawn rather than typed, so it keeps its weight at every size. */
.flow__arrow {
  width: 26px; height: 2px;
  background: var(--signal);
  position: relative;
  flex: none;
}
.flow__arrow::after {
  content: ""; position: absolute; right: 0; top: 50%;
  width: 7px; height: 7px;
  border-top: 2px solid var(--signal);
  border-right: 2px solid var(--signal);
  transform: translateY(-50%) rotate(45deg);
}
.flow__body { display: grid; grid-template-columns: 128px 1fr; gap: var(--s-3) var(--s-5); margin: 0; }
.flow__body dt {
  font-size: var(--fs-xs); font-weight: 600; color: var(--fg-muted);
  padding-top: 2px;
}
.flow__body dd { margin: 0; font-size: var(--fs-sm); line-height: var(--lh-normal); }
.flow__link {
  display: inline-block; margin-top: var(--s-4);
  font-size: var(--fs-sm); font-weight: 600; color: var(--signal);
}
/* No .pill--live override here. It already exists above, in the semantic
   green, and redefining it in the accent broke the standing rule that status
   pills never borrow the brand colour — red now means "one7x", so a red pill
   next to a real status reads as "broken". The label on the idea-to-release
   row is a plain .pill instead: it is a fact about the row, not a status, and
   a green "live" pill there would also imply a shipped product. */

/* --- the worked example: the one animated thing on the page ------------- */
.track { margin: 0; position: relative; }
.track__line {
  position: absolute; left: 0; right: 0; top: 7px; height: 2px;
  background: var(--border);
  overflow: hidden;
}
/* Draws once when the figure scrolls into view. Pure transform/width on a
   single element — no layout thrash, and it settles rather than looping. */
.track__fill {
  display: block; height: 100%; width: 0;
  background: var(--signal);
  transition: width 1400ms var(--ease-standard);
}
.track.is-drawn .track__fill { width: 100%; }
.track__steps {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--s-5);
}
.track__steps li { display: grid; align-content: start; gap: var(--s-2); padding-right: var(--s-4); }
.track__dot {
  width: 16px; height: 16px; border-radius: var(--r-pill);
  background: var(--bg); border: 2px solid var(--border);
  transition: border-color 320ms var(--ease-standard), transform 320ms var(--ease-standard);
}
.track.is-drawn .track__dot { border-color: var(--signal); }
.track__steps li:nth-child(1) .track__dot { transition-delay: 120ms; }
.track__steps li:nth-child(2) .track__dot { transition-delay: 340ms; }
.track__steps li:nth-child(3) .track__dot { transition-delay: 560ms; }
.track__steps li:nth-child(4) .track__dot { transition-delay: 780ms; }
.track__steps li:nth-child(5) .track__dot { transition-delay: 1000ms; }
.track__steps li:nth-child(6) .track__dot { transition-delay: 1220ms; }
.track__steps b {
  font-family: var(--font-display);
  font-size: var(--fs-lg); font-weight: var(--character-heading-weight);
  letter-spacing: -0.028em; margin-top: var(--s-2);
}
.track__steps span:not(.track__dot) {
  font-size: var(--fs-sm); color: var(--fg-muted); line-height: var(--lh-normal);
}
.track figcaption {
  margin-top: var(--s-6); padding-left: var(--s-4);
  border-left: 2px solid var(--signal);
  font-size: var(--fs-sm); color: var(--fg-muted); max-width: 52ch;
}

/* --- ways in: the four depths ------------------------------------------ */
.depths { list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-5); }
.depths li {
  display: grid; align-content: start; gap: var(--s-2);
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
  transition: border-color var(--duration-base) var(--ease-standard);
}
.depths li:hover { border-top-color: var(--signal); }
.depths__n {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--signal); letter-spacing: var(--tr-normal);
}
.depths b {
  font-family: var(--font-display); font-size: var(--fs-lg);
  font-weight: var(--character-heading-weight); letter-spacing: -0.028em;
}
.depths span:not(.depths__n) { font-size: var(--fs-sm); color: var(--fg-muted); line-height: var(--lh-normal); }

/* --- responsive --------------------------------------------------------- */
@media (max-width: 1000px) {
  .track__steps { grid-template-columns: repeat(3, 1fr); row-gap: var(--s-6); }
  .track__line { display: none; }
  .depths { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .track__steps { grid-template-columns: 1fr; }
  .depths { grid-template-columns: 1fr; }
  .flow__body { grid-template-columns: 1fr; gap: var(--s-1) 0; }
  .flow__body dd { margin-bottom: var(--s-3); }
  .flow { padding: 22px 22px; }
}

/* Motion is a preference, not a default. Everything above still reads
   correctly with it off — the line is simply already drawn. */
@media (prefers-reduced-motion: reduce) {
  .track__fill { transition: none; width: 100%; }
  .track__dot { transition: none; border-color: var(--signal); }
}

/* ==========================================================================
   2026-08-25 — the centred hero and the animated field.

   The world view is now a belief about people: AI should change how everyone
   works. The reader wants AI and is uncertain, so the hero is centred and
   bold, the hesitation is named before anything is pitched, and the four ways
   in move up the page because for this reader the ladder IS the reassurance.

   ⚠️ The field behind the hero is a LIGHT SOURCE, not a fill. The standing
   rule: sample the rendered pixel, and max(R,G,B) − min(R,G,B) under 7 reads
   as grey while 25 or more reads as a colour. These orbs peak around 9% and
   drift slowly enough that no frame is brighter than a static bloom would be.
   Verified by sampling, not by eye.
   ========================================================================== */

.hero--center { position: relative; isolation: isolate; overflow: hidden; text-align: center;
  padding: var(--s-10) 0 var(--s-9); }
.hero--center::before { content: none; }   /* the static bloom is replaced by the field */

.hero__field { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.hero__orb {
  position: absolute; display: block; border-radius: 50%;
  filter: blur(46px);
  will-change: transform;
}
.hero__orb--1 {
  width: 46vw; height: 46vw; max-width: 640px; max-height: 640px;
  top: -14%; left: 6%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--signal) 9%, transparent) 0%,
    transparent 70%);
  animation: orbdrift-a 26s ease-in-out infinite alternate;
}
.hero__orb--2 {
  width: 38vw; height: 38vw; max-width: 520px; max-height: 520px;
  top: 6%; right: 2%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--signal) 7%, transparent) 0%,
    transparent 70%);
  animation: orbdrift-b 34s ease-in-out infinite alternate;
}
/* The third is neutral, not accent — three accent blooms would tip the band
   from "lit" to "tinted", which is the failure this rule exists to prevent. */
.hero__orb--3 {
  width: 30vw; height: 30vw; max-width: 420px; max-height: 420px;
  bottom: -18%; left: 34%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--fg) 6%, transparent) 0%,
    transparent 70%);
  animation: orbdrift-c 30s ease-in-out infinite alternate;
}
/* A faint grid, drifting one cell over the full cycle. It is what makes the
   motion read as "system" rather than as a lava lamp. */
.hero__grid {
  position: absolute; inset: -40px;
  display: block;
  background-image:
    linear-gradient(to right, color-mix(in srgb, var(--fg) 4%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in srgb, var(--fg) 4%, transparent) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 40%, #000 0%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 40%, #000 0%, transparent 75%);
  animation: griddrift 40s linear infinite;
}

@keyframes orbdrift-a { from { transform: translate3d(0,0,0)      scale(1);    }
                        to   { transform: translate3d(6%,7%,0)    scale(1.12); } }
@keyframes orbdrift-b { from { transform: translate3d(0,0,0)      scale(1.08); }
                        to   { transform: translate3d(-7%,5%,0)   scale(1);    } }
@keyframes orbdrift-c { from { transform: translate3d(0,0,0)      scale(1);    }
                        to   { transform: translate3d(5%,-6%,0)   scale(1.15); } }
@keyframes griddrift  { from { transform: translate3d(0,0,0);     }
                        to   { transform: translate3d(64px,64px,0); } }

.hero__mid { max-width: 900px; }
.hero--center h1 {
  font-size: clamp(40px, 6.4vw, 82px);
  line-height: 0.98;
  letter-spacing: -0.032em;
  margin: 0 auto var(--s-5);
  max-width: 16ch;
  text-wrap: balance;
}
.hero--center .lede { max-width: 54ch; margin-left: auto; margin-right: auto; }
.hero--center .eyebrow { justify-content: center; }
.hero--center .hero__actions { justify-content: center; }

/* Centred section heads, for the sections that now sit under a centred hero. */
.head--center { max-width: 46ch; margin-left: auto; margin-right: auto; text-align: center; }
.head--center .eyebrow { justify-content: center; }

/* --- the hesitation ----------------------------------------------------- */
.worries { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-5); }
.worries li {
  display: grid; align-content: start; gap: var(--s-3);
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  padding: 26px 28px;
  background: var(--bg-elev);
  transition: border-color var(--duration-base) var(--ease-standard);
}
.worries li:hover { border-color: var(--border-strong); }
.worries__ico {
  width: 38px; height: 38px; display: grid; place-items: center;
  border-radius: var(--r-4);
  background: var(--signal-tint);
}
.worries__ico svg { width: 19px; height: 19px; fill: none; stroke: var(--signal);
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.worries b {
  font-family: var(--font-display); font-size: var(--fs-md);
  font-weight: var(--character-heading-weight); letter-spacing: -0.024em;
  line-height: var(--lh-snug);
}
.worries span:not(.worries__ico) { font-size: var(--fs-sm); color: var(--fg-muted); line-height: var(--lh-normal); }

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

/* Motion is a preference. With it off the field is still there and still
   lit — it simply stops moving, so nothing about the composition changes. */
@media (prefers-reduced-motion: reduce) {
  .hero__orb, .hero__grid { animation: none !important; }
}

/* ==========================================================================
   2026-08-25 — the philosophy, and the three routes.

   The philosophy is a barrier -> condition transformation, drawn as one, so
   the reasoning is what you see rather than four principles in boxes. The
   "today" side is deliberately muted and the condition side carries the
   weight: that contrast IS the content.
   ========================================================================== */

/* The philosophy, rebuilt 2026-08-25 — "the four things section is too
   cluttered". It was four rows of six elements each: a numeral, a "Today"
   label, the struck barrier, an arrow, the condition and a detail line. The
   numeral and the label were chrome on top of the content and the detail line
   repeated the condition in longer words.

   What is left is the mechanism and nothing else: the barrier, struck through,
   and what replaces it. Two elements, four cells, read in seconds. */
.truths {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  overflow: hidden;
}
.truths li {
  display: grid;
  align-content: start;
  gap: var(--s-3);
  padding: var(--s-7) var(--s-6);
  background: var(--bg);
  background-image: var(--sheen-wash);
  transition: background-color var(--duration-base) var(--ease-standard);
}
.truths li:hover { background-color: var(--bg-elev); }
.truths__was {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--fg-muted);
}
.truths b {
  font-family: var(--font-display);
  font-size: clamp(20px, 2vw, 26px);
  font-weight: var(--character-heading-weight);
  letter-spacing: -0.028em;
  line-height: var(--lh-snug);
  text-wrap: balance;
}

/* --- the three routes --------------------------------------------------- */
.routes { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
.route {
  border: 1px solid var(--border);
  border-radius: var(--r-5);
  background: var(--bg-elev);
  transition: border-color var(--duration-base) var(--ease-standard),
              transform var(--duration-base) var(--ease-standard);
}
.route > a, .route > div {
  display: grid; align-content: start; gap: var(--s-3);
  padding: 28px 30px;
  height: 100%;
  color: inherit;
}
.route:not(.route--soon):hover { border-color: var(--signal); transform: translateY(-2px); }
.route:not(.route--soon):focus-within { border-color: var(--signal); }
.route__ico {
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: var(--r-4);
  background: var(--signal-tint);
}
.route__ico svg { width: 20px; height: 20px; fill: none; stroke: var(--signal);
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.route b {
  font-family: var(--font-display); font-size: var(--fs-xl);
  font-weight: var(--character-heading-weight); letter-spacing: -0.03em;
}
.route__what { font-size: var(--fs-sm); color: var(--fg-muted); line-height: var(--lh-normal); }
.route__go {
  margin-top: auto; padding-top: var(--s-2);
  font-size: var(--fs-sm); font-weight: 600; color: var(--signal);
}
/* Not built yet: present, honest, and visibly not a link. */
.route--soon { opacity: .78; }
.route--soon .route__ico { background: var(--mist); }
.route--soon .route__ico svg { stroke: var(--fg-subtle); }
.route--soon .route__go { color: var(--fg-subtle); font-weight: 500; }

@media (max-width: 1000px) {
  .routes { grid-template-columns: 1fr; }
  .truths { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Two contrast fixes, both found by sampling and both introduced 2026-08-25.

   1. --fg-subtle is a token for MARKS, not for text. Used on the philosophy's
      "today" column and its numerals it measured 3.20:1 on paper and 3.75:1
      on ink, against a 4.5:1 floor. --fg-muted is the text-weight neutral and
      clears it. The barrier column still reads as quiet — the strike-through
      and the weight carry that, which is the right way to do it anyway.

   2. ⚠️ The accent clears 4.5:1 as text on --paper and NOT on --pearl.
      Measured: #C93A28 on #F1F0EC is 4.43-4.47:1, just under. The repo's own
      accent note records 5.01:1 "as text on paper" and nobody checked the
      second surface. --signal-deep is the darker step and clears it on both.
      This applies to every accent text on a pearl band, not just the numerals
      that exposed it.
   -------------------------------------------------------------------------- */
.route--soon .route__go { color: var(--fg-muted); }

.section--pearl .depths__n,
.section--pearl .route__go,
.section--pearl .flow__link,
.section--pearl .inline-next { color: var(--signal-deep); }

/* Adding Build to the nav pushed the header 30px past the viewport at 360px —
   on every page, caught by the overflow assertion rather than by looking.
   Below 420px the two service routes drop out of the header: they are both
   reachable from the routes section, the footer, and the CTA, so nothing
   becomes unreachable. Work and About stay because they have no other
   in-page entry point on a phone. */
@media (max-width: 420px) {
  .nav a[href="/consulting"],
  .nav a[href="/build"] { display: none; }
}

/* Two of the three routes have no destination yet. They are NOT labelled
   "coming soon": a marketing surface does not publish its own roadmap state,
   and advertising incompleteness costs more than it explains. All three read
   as equal descriptions of how one7x works; only the one you can act on today
   carries a link and an arrow. */
.route--flat { opacity: 1; }
.route--flat:hover { border-color: var(--border); transform: none; }

/* ==========================================================================
   2026-08-25 — surface craft: depth, sheen, and a little life.

   ⚠️ THIS DELIBERATELY CHANGES TWO RULES IN THIS REPO'S OWN CLAUDE.md, on
   Dipender's instruction: "it has to be very polished, premium & clean yet
   modern (with sheens, or certain looping animations, etc — some liveliness
   has to be instilled in the site)."

   The rules being changed, so nobody reverts this as a regression:
     - "hairline first, shadow last. A card is bordered, not floated."
     - "two floating objects. Adding a third is how a page starts to look
        generic."
   Both were right for the page as it was and are now superseded. CLAUDE.md
   is updated in the same commit.

   What the audit found and this fixes: of 160 elements in <main>, four had
   any shadow and eight had a gradient — six of which were the hero orbs. The
   type scale was already strong (ten sizes, four weights). The surfaces were
   flat fills in uniform 1px boxes, which is what "not premium" actually was.

   No new colour enters the system. Every value below is a token, or white and
   black at low alpha — the same technique already used for the inverted band.
   ========================================================================== */

:root {
  /* Light falls from above. One highlight value, used on every raised
     surface, is what makes a set of cards read as one material. */
  --sheen-top: rgba(255,255,255,0.72);
  --sheen-wash: linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 42%);
  --lift: 0 1px 2px rgba(24,24,27,0.04), 0 8px 24px -12px rgba(24,24,27,0.14);
  --lift-hover: 0 2px 4px rgba(24,24,27,0.05), 0 18px 40px -16px rgba(24,24,27,0.20);
}
:root[data-theme="dark"], :root:not([data-theme="light"]) {
  --sheen-top: rgba(255,255,255,0.10);
  --sheen-wash: linear-gradient(180deg, rgba(255,255,255,0.055) 0%, rgba(255,255,255,0) 42%);
  --lift: 0 1px 2px rgba(0,0,0,0.4), 0 8px 24px -12px rgba(0,0,0,0.5);
  --lift-hover: 0 2px 4px rgba(0,0,0,0.45), 0 18px 40px -16px rgba(0,0,0,0.6);
}

/* --- raised surfaces: a top light, a soft ground, and a real hover ------- */
.route, .flow, .worries li, .exp__item, .ask {
  position: relative;
  background-image: var(--sheen-wash);
  box-shadow: var(--lift);
  transition: box-shadow var(--duration-slow) var(--ease-standard),
              transform var(--duration-slow) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard);
}
/* The 1px top edge lighter than the sides. This is the single detail that
   most separates a "card" from a "box with a border". */
.route::before, .flow::before, .worries li::before, .exp__item::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit;
  border-top: 1px solid var(--sheen-top);
  mask-image: linear-gradient(180deg, #000 0%, transparent 55%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 55%);
}
.route:not(.route--flat):hover, .flow:hover, .exp__item:hover {
  transform: translateY(-3px);
  box-shadow: var(--lift-hover);
  border-color: color-mix(in srgb, var(--signal) 42%, var(--border));
}
.worries li:hover { transform: translateY(-2px); box-shadow: var(--lift-hover); }

/* --- the primary button: gloss, a sheen that sweeps, and a press --------- */
.btn--primary {
  position: relative;
  overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.10), 0 1px 2px rgba(24,24,27,0.12),
              0 6px 16px -12px color-mix(in srgb, var(--accent-bg) 40%, transparent);
  transition: box-shadow var(--duration-base) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard);
}
.btn--primary::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 42%;
  left: -60%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.30), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
}
.btn--primary:hover::after { animation: sheen 720ms var(--ease-standard) 1; }
.btn--primary:hover {
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.14), 0 2px 4px rgba(24,24,27,0.14),
              0 10px 24px -12px color-mix(in srgb, var(--accent-bg) 50%, transparent);
}
.btn--primary:active { transform: translateY(1px); }
@keyframes sheen { from { left: -60%; } to { left: 120%; } }

/* --- a top-edge highlight where one surface meets another --------------- */
.section--pearl, .section--ink { position: relative; }
.section--pearl::before, .section--ink::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--signal) 26%, transparent) 20%,
    color-mix(in srgb, var(--signal) 26%, transparent) 80%, transparent);
  opacity: .5;
}

/* --- the one looping thing: a pulse travelling the track ----------------
   It only runs once the track has drawn, so the draw reads first and the loop
   becomes ambient afterwards rather than competing with it. */
.track.is-drawn .track__line::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 90px;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--signal) 85%, transparent), transparent);
  animation: trackpulse 4.2s var(--ease-standard) 1.6s infinite;
}
@keyframes trackpulse {
  0%   { left: -90px; opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}

/* The status dot breathes. It is the only thing on the page that says
   "this is live right now", and one small looping signal is enough. */
.pill--building::before { animation: breathe 2.8s var(--ease-standard) infinite; }
@keyframes breathe { 0%,100% { opacity: 1; } 50% { opacity: .45; } }

/* --- the route icons get the same material treatment ------------------- */
.route__ico, .worries__ico {
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), 0 1px 2px rgba(24,24,27,0.05);
  background-image: var(--sheen-wash);
}
:root[data-theme="dark"] .route__ico, :root[data-theme="dark"] .worries__ico,
:root:not([data-theme="light"]) .route__ico, :root:not([data-theme="light"]) .worries__ico {
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.09);
}

/* Every loop stops. The composition is unchanged with motion off — surfaces
   keep their depth, the sheen simply never sweeps and the pulse never runs. */
@media (prefers-reduced-motion: reduce) {
  /* display:none does not clear animation-name, and the hover rules above use
     :not() so they outrank a bare .route:hover. Both were caught by asserting
     the behaviour rather than by reading this block back. */
  .btn--primary::after { display: none; animation: none !important; }
  .track.is-drawn .track__line::after { display: none; animation: none !important; }
  .pill--building::before { animation: none !important; }
  .route:not(.route--flat):hover,
  .flow:hover,
  .exp__item:hover,
  .worries li:hover { transform: none !important; }
}

/* ==========================================================================
   Copy fit — 2026-08-25.

   Dipender: be aware of how the copy sits — the word limit for where it is
   placed, and which words have to stay together so there are no hanging words.

   Measured before fixing: 12 orphaned lines and 4 slot overruns across three
   pages and four widths. None of them are visible in the markup; they only
   exist once the text is rendered, and they move as the width changes.

   Two properties do most of the work, and they are not interchangeable:
     text-wrap: balance — evens the line lengths. Right for headings and short
       labels, where the shape of the block is part of the design.
     text-wrap: pretty  — spends its effort on the LAST line only, which is
       exactly the orphan case. Right for body copy, where balancing long text
       would make the measure ragged.
   ========================================================================== */

h1, h2, h3,
.route b,
.track__steps b,
.flow__path,
.eyebrow { text-wrap: balance; }

p, dd, figcaption,
.route__what,
.track__steps span,
.depths span,
.worries span:not(.worries__ico) { text-wrap: pretty; }

/* The track columns are narrow by design — six across — so a four-word caption
   lands as 3+1 without help. Balance splits it 2+2 instead. */
.track__steps span:not(.track__dot) { text-wrap: balance; }

/* ==========================================================================
   2026-08-25 — restoring the loops.

   Removing the worked example and the what's-being-built section also removed
   the track pulse and the breathing status dot, which were two of the three
   things that moved. What was left was the hero field alone, which is why the
   page went quiet again. These put the life back WITHOUT adding a fourth
   thing to read.

   Rule kept: motion is ambient, never demanding. Nothing bounces, nothing
   slides in, nothing repeats faster than every four seconds. All of it stops
   under prefers-reduced-motion.
   ========================================================================== */

/* --- 1. a light travels the top edge of each route card, in turn ---------
   The three cards share one cycle, staggered, so at any moment roughly one is
   lit. This is the detail that reads as "engineered" rather than decorated. */
.route { overflow: hidden; }
.route::after {
  content: ""; position: absolute; top: 0; left: 0; height: 1px; width: 38%;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--signal) 90%, transparent), transparent);
  opacity: 0;
  animation: edgesweep 9s var(--ease-standard) infinite;
  pointer-events: none;
}
.routes li:nth-child(1) .route::after, .route:nth-child(1)::after { animation-delay: 0s; }
.route:nth-child(2)::after { animation-delay: 3s; }
.route:nth-child(3)::after { animation-delay: 6s; }
@keyframes edgesweep {
  0%   { left: -38%; opacity: 0; }
  6%   { opacity: 1; }
  26%  { opacity: 1; }
  32%  { left: 100%; opacity: 0; }
  100% { left: 100%; opacity: 0; }
}

/* --- 2. the accent tick on every eyebrow breathes ------------------------
   Smallest possible signal, repeated across the page, so the whole thing has
   a pulse rather than one lively component and five still ones. */
.eyebrow::before { animation: tickbreathe 3.6s var(--ease-standard) infinite; }
@keyframes tickbreathe {
  0%, 100% { opacity: 1;   transform: scaleX(1); }
  50%      { opacity: .55; transform: scaleX(.72); }
}

/* --- 3. RETIRED 2026-08-25: a slow sheen used to cross the whole hero band.
   The word sheen on "everyone" is the same gesture done better — it lands on
   the word carrying the argument instead of washing the band behind it — and
   two sheens in one area compete rather than add. Seven looping things remain
   on the first screen, which is more than the shared skill recommends and is
   what Dipender asked for; this one was the redundant one, not the excess. */

/* --- 4. the four hesitations: the quote rule draws itself in -------------
   Not a loop — it happens once, when the grid arrives. But it is the moment
   that makes the section read as a reply rather than as a table. */
.truths__was {
  text-decoration: none;
  position: relative;
  display: inline-block;
}
/* Was a strike-through drawn across this line. Removed 2026-08-25: the line
   is now the visitor's own words, and striking out someone's sentence tells
   them they are wrong. The reveal beat survives as a rule growing down beside
   the quote — same timing, opposite manners. */
.truths__say::before { transform: scaleY(0); transform-origin: top;
  transition: transform 700ms var(--ease-standard); }
.truths.in-view .truths__say::before { transform: scaleY(1); }
.truths li:nth-child(1) .truths__say::before { transition-delay: 200ms; }
.truths li:nth-child(2) .truths__say::before { transition-delay: 380ms; }
.truths li:nth-child(3) .truths__say::before { transition-delay: 560ms; }
.truths li:nth-child(4) .truths__say::before { transition-delay: 740ms; }

@media (prefers-reduced-motion: reduce) {
  .route::after,
  .eyebrow::before,
  .hero--center::after { animation: none !important; }
  .route::after, .hero--center::after { opacity: 0 !important; }
  /* The quote rule is content, not decoration — it stays, it just does not draw. */
  .truths__say::before { transition: none !important; transform: scaleY(1) !important; }
}

/* ==========================================================================
   2026-08-25 — mobile pass, and one bug that was on every screen.
   ========================================================================== */

/* ⚠️ The strike ran ~350px past its own text. `display: inline-block` looked
   like the fix and never applied: a grid item is BLOCKIFIED by spec, so
   inline-block computes to block and the element fills its column.
   justify-self is what shrinks a grid item to its content. Measured after:
   box width now matches text width. */
.truths__say { justify-self: start; }

/* --- phones -------------------------------------------------------------
   Everything below was sized for a 1440px canvas and never revisited. On a
   390px screen the hero opened with 128px of dead space above the eyebrow,
   each of the four cells spent 64px of a 326px width on horizontal padding,
   and the page ran 3,638px for four sections. */
@media (max-width: 640px) {
  .hero--center { padding: var(--s-7) 0 var(--s-8); }
  /* The 40px floor in the desktop clamp was what broke the grouping on a
     phone: "how everyone works." does not fit on one line at 40px until the
     viewport reaches 414px. Measured, not estimated — the largest size that
     still gives the two intended lines is 34px at 360, 37px at 390, 40px at
     414, which is ~9.4vw throughout. 9.3vw sits just inside all three. */
  .hero--center h1 { font-size: clamp(28px, 9.3vw, 46px); line-height: 1.04; }
  .hero--center .lede { font-size: var(--fs-base); }

  .section { padding: var(--s-7) 0; }
  .section--tight { padding: var(--s-6) 0; }
  .section--tall { padding: var(--s-8) 0; }

  .head--center, .head--wide { margin-bottom: var(--s-5); }

  /* Cells were 48px/32px. On a phone that is a fifth of the screen spent on
     air inside a box that is already separated by a border. */
  .truths li { padding: var(--s-5) var(--s-5); gap: var(--s-2); }
  .truths b { font-size: var(--fs-md); }

  .route > a, .route > div { padding: var(--s-5); gap: var(--s-2); }
  .route b { font-size: var(--fs-lg); }
  .route__ico { width: 34px; height: 34px; }
  .route__ico svg { width: 17px; height: 17px; }

  .ask { padding: var(--s-5); }
  .contact h2 { font-size: clamp(28px, 8vw, 36px); }
}

/* The nav is now Consulting + About + toggle, and the CTA already hides below
   430px — so there is room, and hiding Consulting was left over from when
   Build was in the header too. It is the only route with a destination, so
   hiding it was the worst possible choice of thing to drop. */
@media (max-width: 420px) {
  .nav a[href="/consulting"] { display: inline-flex; }
}

/* ==========================================================================
   Line grouping — 2026-08-25.

   Dipender: the heading has to be grouped as "AI will change" / "how everyone
   works", and the subheading needs the same width awareness on phone and
   laptop.

   Measured before: the headline broke as "AI will change how" / "everyone" /
   "works." at EVERY width — three lines with a one-word last line.

   .g marks a group the line may break BETWEEN but not inside.

   ⛔ Deliberately inline-block, NOT white-space: nowrap. nowrap is a promise
   the text will never wrap, and on a narrow phone that promise can only be
   kept by overflowing the viewport — which is exactly how this site shipped a
   sideways-scrolling page once before. inline-block keeps a group together
   while it fits and lets it wrap normally when it genuinely cannot.
   ========================================================================== */
.g { display: inline-block; }


/* --- #3 · the looping sheen on "everyone" -------------------------------
   FIRST ATTEMPT WAS WRONG AND IS WORTH RECORDING: a second copy of the word
   painted from `content: attr(data-sheen)` in an absolutely-positioned
   ::after. The duplicate rendered at a different width and baseline from the
   real glyphs, so what you got was a ghost word offset up and to the left —
   obvious in a screenshot, invisible in the CSS. Any technique that paints
   the word twice has to align two independently-laid-out text runs to the
   pixel, which is not something to rely on.

   This paints the word ONCE. Two background layers clipped to the glyphs:
   a solid base in the accent, and a light band travelling across it. There
   is no second copy to misalign, and nothing extra for a screen reader.

   The band is a lighter red, not white: on near-white paper a white band
   erases the word for the length of the sweep.

   ⚠️ THE TIMING WAS WRONG FOR A WHOLE ROUND AND THE CSS LOOKED FINE.
   Measured: the highlight was on the word for 45ms of every 5400ms — a 0.8%
   duty cycle — because the band was 20% of a background 300% as wide as the
   word, travelling 840% of the word's width in 1.7 seconds. Dipender's report
   was exactly right: too fast to see, then a long gap.

   The numbers below are derived, not chosen. Band widened to 56% of a 190%
   background, sweep slowed and stretched to 70% of the cycle:
     highlight visible   1852ms   (was 45ms)
     gap between passes  2048ms   (was 5355ms)
     brightness lift     19.4 pts (was 11.4)
   /tmp/sheentime.mjs drives the animation frame by frame and reads the word's
   mean glyph brightness, so "how long is it actually visible" is a number
   rather than an impression.

   The white mix stays at 24%: operations/sheen-worst.mjs scans every frame of
   the cycle in both themes and the tightest is 3.36:1 against the paper, over
   the 3:1 large-text floor. At 55% it fails at 1.97:1 — negative-tested.      */
.accentline { color: var(--signal); }

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .accentline[data-sheen] {
    background-image:
      linear-gradient(100deg,
        transparent 22%,
        color-mix(in srgb, #FFFFFF 24%, var(--signal)) 50%,
        transparent 78%),
      linear-gradient(var(--signal), var(--signal));
    background-size: 190% 100%, 100% 100%;
    background-position: 150% 0, 0 0;
    background-repeat: no-repeat;
    -webkit-background-clip: text; background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    animation: wordsheen 3.9s linear 1.2s infinite;
  }
}
@keyframes wordsheen {
  0%   { background-position: 150% 0, 0 0; }
  70%  { background-position: -60% 0, 0 0; }
  100% { background-position: -60% 0, 0 0; }
}

/* Clipping to text means the fill comes from the background, and a forced
   colours mode throws backgrounds away — which would leave the word invisible
   rather than merely unstyled. Put the colour back on the property that
   survives. */
@media (forced-colors: active) {
  .accentline[data-sheen] {
    background-image: none; color: var(--signal); animation: none;
    -webkit-text-fill-color: var(--signal);
    -webkit-background-clip: border-box; background-clip: border-box;
  }
}

/* --- #6 · the barrier marks ---------------------------------------------
   Bare line marks, not icons in tinted squares: the route cards already use
   that treatment and the audit flagged repeating it as template-looking.
   22px, accent stroke, sitting above the struck line.                       */
/* One row: rule, mark, the visitor's own words. Rendered as three stacked
   blocks the mark read as floating above an unrelated line. */
.truths__say {
  display: flex; align-items: center; gap: 9px;
  padding-left: 13px; position: relative;
}
.truths__say::before {
  content: ""; position: absolute; left: 0; top: 2px; bottom: 2px;
  width: 2px; border-radius: 2px;
  background: color-mix(in srgb, var(--signal) 45%, transparent);
}
.truths__ico { display: block; line-height: 0; flex: none; }
.truths__ico svg {
  width: 22px; height: 22px;
  fill: none; stroke: var(--signal); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: .85;
}
.truths li:hover .truths__ico svg { opacity: 1; }
@media (max-width: 640px) { .truths__ico svg { width: 20px; height: 20px; } }

@media (prefers-reduced-motion: reduce) {
  /* No sweep, and the word keeps its solid fill — the base layer already is
     the accent, so stopping the animation leaves it correct rather than blank. */
  .accentline[data-sheen] { animation: none !important; background-position: 150% 0, 0 0 !important; }
}

/* --- .keep: a phrase that must land on one line -------------------------
   From the shared `copy-setting` skill. inline-block rather than nowrap:
   nowrap promises the phrase will NEVER break, which on a narrow column can
   only be kept by overflowing the page — the exact bug that scrolled this
   site sideways on every phone in August. inline-block keeps the phrase
   together while it fits and lets it wrap rather than overflow when it
   cannot, so the failure mode is ugly instead of broken.                   */
.keep { display: inline-block; }

/* The founder name is bound with a non-breaking space, because a person's
   name splitting across two lines is worse than any wrap. Bound, it is 186px
   wide in a 156px column at 320px — so the binding needs a size step, not
   removing. Measured: it fits at 18px, /tmp/namefit.mjs. This is the trap the
   `copy-setting` skill names — a bound phrase is, at some narrower width,
   wider than its column — and the answer it gives is exactly this. */
@media (max-width: 360px) {
  .founder h3 { font-size: 18px; }
}

/* ==========================================================================
   THE AIRY SCALE — 2026-08-25
   ==========================================================================
   Opt-in per page via `class="airy"` on <body>. Scoped rather than global so
   the consulting page can carry it first and the rest of the site follows in
   a separate, reviewed change — Dipender: apply it here, and if it works,
   apply it throughout, because consistency matters.

   Every number below is derived from measuring a reference he named
   (parallelhq.com) against our own page at the same width, not chosen:

                        reference    ours (before)   here
     body                20 / 30      16 / 24        19 / 30
     hero lede           24 / 36      18 / 29.7      22 / 34
     hero heading        60, lh 1.30  46, lh 0.98    56, lh 1.10
     section heading     44, lh 1.30  46, lh 0.98    40, lh 1.15
     running measure     ~67 chars    ~58 chars      ~66 chars

   Headings keep OUR face and weight — Inter Tight 800. The reference sets its
   headings in a light serif and he said he likes their fonts except the
   headings, so only the metrics cross over, not the type.

   The line-height is the real defect being fixed. 46px text on a 45px line is
   cramped by any measure; nothing in the kit ever justified it beyond a rule
   Taru wrote and mis-attributed (see design-system/components.md).           */

body.airy {
  font-size: 19px;
  line-height: 1.58;
}

/* Headings breathe. Tracking stays tight — that part was always right. */
.airy h1, .airy h2, .airy h3, .airy h4 { line-height: 1.15; }
.airy .head h1, .airy .head h2 { font-size: clamp(30px, 3.4vw, 40px); line-height: 1.15; }
.airy .hero h1 { font-size: clamp(38px, 4.6vw, 56px); line-height: 1.10; }
.airy .hero--center h1 { font-size: clamp(38px, 4.6vw, 56px); line-height: 1.10; }

.airy .lede { font-size: 22px; line-height: 1.55; max-width: 52ch; }
.airy .hero .lede { max-width: 50ch; }

/* Running text gets a real measure. 66ch is the long-standing typographic
   answer and it happens to match what the reference renders. */
.airy .prose, .airy .head p { max-width: 66ch; }
.airy .head { max-width: 40ch; }
.airy .head--wide { max-width: 58ch; }

/* Page rhythm — the part the retired "dense" rule was suppressing. */
.airy .section { padding: 112px 0; }
.airy .section--tight { padding: 88px 0; }
.airy .section--tall { padding: 136px 0; }
.airy .head { margin-bottom: var(--s-7); }

@media (max-width: 820px) {
  .airy .section { padding: 72px 0; }
  .airy .section--tight { padding: 60px 0; }
  .airy .section--tall { padding: 88px 0; }
  .airy .lede { font-size: 20px; }
}
@media (max-width: 480px) {
  body.airy { font-size: 18px; }
  .airy .section { padding: 56px 0; }
  .airy .section--tight { padding: 48px 0; }
  .airy .section--tall { padding: 64px 0; }
  .airy .lede { font-size: 19px; line-height: 1.5; }
}

/* ==========================================================================
   CONSULTING — a second header, and the components the page needs
   ==========================================================================
   Dipender asked for a header tailored to consulting with a route back to
   one7x.com. He also pointed at parallelhq.com and said "look how simple it
   is, menu" — their whole site nav is five plain links and no mega menu. With
   zero sub-pages of our own, a mega menu would be navigation for pages that
   do not exist, so this is four links. It gets built the day there is
   somewhere for it to go.                                                    */

.lockup { display: inline-flex; align-items: center; gap: 12px; padding: 8px 0; margin: -8px 0; }
.lockup__mark { display: inline-flex; align-items: center; padding: 6px 0; margin: -6px 0; }
.lockup img { height: 28px; width: auto; display: block; }
.lockup .logo-dark { display: none; }
:root[data-theme="dark"] .lockup .logo-light { display: none; }
:root[data-theme="dark"] .lockup .logo-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .lockup .logo-light { display: none; }
  :root:not([data-theme="light"]) .lockup .logo-dark { display: block; }
}
:root[data-theme="light"] .lockup .logo-light { display: block; }
:root[data-theme="light"] .lockup .logo-dark { display: none; }
.lockup__rule { width: 1px; height: 20px; background: var(--border-strong); }
/* The section name sits INSIDE the lockup, which is what makes this read as a
   section of one7x rather than a different company. Sans, never mono. */
.lockup__section {
  font-family: var(--font-sans); font-size: 15px; font-weight: 600;
  color: var(--fg); letter-spacing: -0.01em;
  padding: 8px 2px; margin: -8px -2px;
}
.lockup__section:hover { color: var(--signal); }

/* --- hero ---------------------------------------------------------------- */
.hero--consult .head { margin-bottom: 0; }
.hero--consult .hero__actions { margin-top: var(--s-6); }

/* --- the five places it breaks ------------------------------------------- */
.breaks { display: grid; gap: var(--s-4); grid-template-columns: repeat(3, 1fr); }
@media (max-width: 880px)  { .breaks { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .breaks { grid-template-columns: 1fr; } }
/* At three columns the five cards land 3+2, which reads as an arrangement.
   At two they land 2+2+1, which does not — so the last card spans both. */
@media (max-width: 880px) and (min-width: 621px) {
  .breaks li:last-child { grid-column: 1 / -1; }
}
.breaks li {
  display: grid; align-content: start; gap: 10px;
  padding: 28px 30px;
  border: 1px solid var(--border); border-radius: var(--r-5);
  background: var(--bg-elev);
  transition: border-color var(--duration-base) var(--ease-standard),
              transform var(--duration-base) var(--ease-standard);
}
.breaks li:hover { border-color: color-mix(in srgb, var(--signal) 42%, var(--border)); transform: translateY(-2px); }
.breaks__ico { display: block; line-height: 0; }
.breaks__ico svg {
  width: 22px; height: 22px; fill: none; stroke: var(--signal);
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.breaks b { font-family: var(--font-display); font-size: 21px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em; line-height: 1.2; }
.breaks__what { color: var(--fg-muted); font-size: 17px; line-height: 1.55; }

/* --- who this is for ------------------------------------------------------ */
.fits { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-6); }
@media (max-width: 900px) { .fits { grid-template-columns: 1fr; } }
.fits li { display: grid; gap: 8px; padding-left: 14px; border-left: 2px solid
  color-mix(in srgb, var(--signal) 45%, transparent); }
.fits b { font-family: var(--font-display); font-size: 19px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em; }
.fits span { color: var(--fg-muted); font-size: 17px; line-height: 1.55; }

/* --- the baseline panel --------------------------------------------------
   Illustrative, and it says so in the rendered page AND in the accessibility
   tree. Every cell is an em-dash: there is no number here that anyone could
   mistake for a result somebody achieved.                                    */
.roi {
  border: 1px solid var(--border); border-radius: var(--r-5);
  background: var(--bg); overflow: hidden;
  box-shadow: var(--lift);
}
.roi__label {
  font-size: 12px; font-weight: 600; letter-spacing: 0.04em;
  color: var(--fg-muted); background: var(--bg-elev);
  padding: 12px 22px; border-bottom: 1px solid var(--border); margin: 0;
}
.roi__scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.roi__table { width: 100%; border-collapse: collapse; font-size: 16px; min-width: 460px; }
.roi__table th, .roi__table td { padding: 16px 22px; text-align: left; }
.roi__table thead th {
  font-family: var(--font-sans); font-size: 13px; font-weight: 600;
  color: var(--fg-muted); border-bottom: 1px solid var(--border); letter-spacing: 0;
}
.roi__table tbody th { font-weight: 500; color: var(--fg); }
.roi__table tbody td { color: var(--fg-muted); }
.roi__table tbody tr + tr th, .roi__table tbody tr + tr td { border-top: 1px solid var(--border); }
.roi__foot { border-top: 1px solid var(--border); }
.roi__note { padding: 16px 22px 20px; margin: 0; color: var(--fg-muted);
  font-size: 16px; line-height: 1.55;
  /* Was inheriting the panel's full width and running to 139 characters —
     roughly double a readable measure. The divider moved to .roi__foot: as
     the note's own border it got clipped to the note's measure and stopped
     two-thirds of the way across the panel. Measured, operations/measure.mjs. */
  max-width: 56ch; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- the three steps ------------------------------------------------------ */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
@media (max-width: 900px) { .steps { grid-template-columns: 1fr; } }
.steps li {
  display: grid; align-content: start; gap: 10px;
  padding: 30px 32px; border: 1px solid var(--border);
  border-radius: var(--r-5); background: var(--bg-elev);
}
.steps__n { font-family: var(--font-display); font-size: 13px; font-weight: 700;
  color: var(--signal); letter-spacing: 0.06em; }
.steps b { font-family: var(--font-display); font-size: 22px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em; line-height: 1.2; }
.steps__what { color: var(--fg-muted); font-size: 17px; line-height: 1.55; }
.steps__meta { font-size: 14px; color: var(--fg-muted); margin-top: 4px;
  padding-top: 12px; border-top: 1px solid var(--border); }
.steps__after { margin-top: var(--s-5); color: var(--fg-muted);
  font-size: 16px; max-width: 62ch; }

/* --- what this is not ----------------------------------------------------- */
.nots { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-5); }
@media (max-width: 1000px) { .nots { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .nots { grid-template-columns: 1fr; } }
.nots li { display: grid; gap: 6px; }
.nots b { font-family: var(--font-display); font-size: 18px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em; }
.nots span { color: var(--fg-muted); font-size: 16px; line-height: 1.5; }

/* --- questions -----------------------------------------------------------
   Native <details>. Full function with no JavaScript, which is the right
   default for the one component a visitor is most likely to open on a phone
   with a flaky connection.                                                   */
.faq { max-width: 74ch; border-top: 1px solid var(--border); }
.faq details { border-bottom: 1px solid var(--border); }
.faq summary {
  cursor: pointer; list-style: none; padding: 22px 40px 22px 0; position: relative;
  font-family: var(--font-display); font-size: 19px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; position: absolute; right: 6px; top: 50%; width: 11px; height: 11px;
  border-right: 2px solid var(--fg-muted); border-bottom: 2px solid var(--fg-muted);
  transform: translateY(-70%) rotate(45deg);
  transition: transform var(--duration-base) var(--ease-standard);
}
.faq details[open] summary::after { transform: translateY(-20%) rotate(225deg); }
.faq summary:hover { color: var(--signal); }
.faq details p { padding: 0 40px 24px 0; margin: 0; color: var(--fg-muted);
  font-size: 17px; line-height: 1.6;
  /* 66ch measured 83 real characters here: `ch` is the width of a zero in the
     element's own font, and a proportional face averages narrower than that.
     62ch renders at ~72, which is inside the readable band. */
  max-width: 62ch; }

/* ⚠️ --fg-subtle is NOT a text colour. Measured on this page: 3.20:1 in light
   and 3.64:1 in dark against the surfaces it sat on, under the 4.5:1 floor for
   normal-size text. It is for rules, dividers and disabled states. Secondary
   text is --fg-muted. Six elements here were wrong before the checker caught
   them, which is the argument for the checker. */

/* --- the form's own select ------------------------------------------------ */
.field select {
  font: inherit; font-size: 16px; color: var(--fg);
  background: var(--bg); border: 1px solid var(--border-strong);
  border-radius: var(--r-3); padding: 12px 14px; min-height: 46px; width: 100%;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--fg-muted) 50%),
                    linear-gradient(135deg, var(--fg-muted) 50%, transparent 50%);
  background-position: calc(100% - 19px) 21px, calc(100% - 13px) 21px;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
.field select:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

@media (max-width: 760px) {
  .lockup img { height: 24px; }
  .lockup__section { font-size: 14px; }
  .breaks li, .steps li { padding: 24px 24px; }
  .roi__table { font-size: 15px; }
  .roi__table th, .roi__table td { padding: 13px 16px; }
}
/* The header CTA is hidden below 430px site-wide. On a conversion page that is
   the wrong trade — shorten the label instead of removing the only route to
   the form from the top of the page. */
@media (max-width: 430px) {
  .site-header--consult .btn--header { display: inline-flex; padding: 8px 12px; font-size: 13px; }
  .site-header--consult .nav a.nav__jump { display: none; }
}

/* --- what gets built: five services, each with its capability list -------
   The capability list is what makes a small firm read as one with depth. It
   is also the part that must stay results-shaped: a technology list here
   would turn the page into the agency its own section denies.              */
.svc { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
@media (max-width: 1100px) { .svc { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px)  { .svc { grid-template-columns: 1fr; } }
/* Five cards in a three-column grid land 3+2, which reads as an arrangement.
   In two columns they land 2+2+1, which does not — so the last one spans. */
@media (max-width: 1100px) and (min-width: 721px) {
  .svc > li:last-child { grid-column: 1 / -1; }
  /* Spanning fixes the orphan and creates a second defect: the card is now
     twice as wide and its body ran to 107 characters. Cap the text, not the
     card — the card should still fill the row. */
  .svc > li:last-child .svc__what { max-width: 58ch; }
  .svc > li:last-child .svc__caps { grid-template-columns: 1fr 1fr; column-gap: var(--s-6); }
}

.svc > li {
  display: grid; align-content: start; gap: 12px;
  padding: 30px 32px;
  border: 1px solid var(--border); border-radius: var(--r-5);
  background: var(--bg-elev);
}
.svc > li > b {
  font-family: var(--font-display); font-size: 22px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em; line-height: 1.2;
}
.svc__what { color: var(--fg-muted); font-size: 17px; line-height: 1.55; }
.svc__caps { display: grid; gap: 7px; margin-top: 4px; padding-top: 16px;
  border-top: 1px solid var(--border); }
.svc__caps li {
  position: relative; padding-left: 18px;
  font-size: 15px; line-height: 1.45; color: var(--fg);
}
.svc__caps li::before {
  content: ""; position: absolute; left: 0; top: 8px;
  width: 6px; height: 6px; border-radius: 50%;
  background: color-mix(in srgb, var(--signal) 70%, transparent);
}

/* --- where this fits ------------------------------------------------------
   Operating shapes, never sectors. A sector row on a page with no clients is
   an experience claim; these are true today. The label carries the claim, so
   it is "Where this fits" and never "Industries".                          */
.fitrow { margin-top: var(--s-7); text-align: center; }
.fitrow__label {
  font-size: 13px; font-weight: 600; color: var(--fg-muted);
  margin: 0 0 var(--s-4); letter-spacing: 0.01em;
}
.fitrow ul { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.fitrow li {
  font-size: 15px; color: var(--fg); padding: 9px 16px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--bg-elev);
}

/* --- how it is built ----------------------------------------------------- */
.built {
  margin-top: var(--s-7); padding: 30px 32px;
  border: 1px solid var(--border); border-radius: var(--r-5);
  background: var(--bg);
}
.built__label {
  font-family: var(--font-display); font-size: 17px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em;
  margin: 0 0 var(--s-5);
}
.built ul { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-5); }
@media (max-width: 980px) { .built ul { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .built ul { grid-template-columns: 1fr; } }
.built li { display: grid; gap: 5px; }
.built b { font-size: 15px; font-weight: 600; }
.built span { font-size: 15px; line-height: 1.5; color: var(--fg-muted); }

/* --- eyebrows as labels, not sentences ----------------------------------
   Rendered both ways and compared: sentence case reads as a short sentence
   competing with the headline below it; uppercase and tracked out reads as a
   category marker and sits quietly. The kit bars uppercase MONO labels — it
   does not bar uppercase sans, and this is sans.

   Scoped to .airy so it travels with the type scale rather than changing the
   homepage, which is already approved. It goes site-wide with the roll-out.  */
.airy .eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-size: 12.5px;
  font-weight: 700;
}

/* --- the hero figure -----------------------------------------------------
   The promise as a shape: work handled climbing while the team stays flat.
   role="img" with one honest label and aria-hidden innards — no axis labels
   and no numbers, because a chart implies data and there is none.           */
.hero--consult .wrap { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: var(--s-8); align-items: center; }
.herofig { width: 100%; }
.herofig svg { width: 100%; height: auto; display: block; overflow: visible; }
.herofig__grid path { stroke: var(--border); stroke-width: 1; fill: none; }
.herofig__flat { stroke: var(--fg-subtle); stroke-width: 2; fill: none; stroke-dasharray: 5 5; }
.herofig__rise { stroke: url(#hf); stroke-width: 3; fill: none; stroke-linecap: round; }
.herofig__dot { fill: var(--signal); }
.herofig__t { font-family: var(--font-sans); font-size: 12px; font-weight: 600; fill: var(--fg-muted); }
.herofig__t--a { fill: var(--signal); }
@media (max-width: 900px) {
  .hero--consult .wrap { grid-template-columns: 1fr; gap: var(--s-6); }
  .herofig { max-width: 420px; }
}

/* --- icons: the same mark per category in both sections ------------------
   The five services ARE the five bottlenecks, so each category carries one
   mark throughout. That makes the icon the category's identity rather than
   decoration invented twice.                                                */
.svc__ico, .built__ico { display: block; line-height: 0; }
.svc__ico svg { width: 22px; height: 22px; }
.built__ico svg { width: 19px; height: 19px; }
.svc__ico svg, .built__ico svg {
  fill: none; stroke: var(--signal); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.built li { grid-template-columns: auto; }

/* --- the services: one entry, three behind it ---------------------------
   Set by Dipender 2026-08-26 after the market research: India first, but the
   fractional tier stays on the page because he circulates this URL to US/UK
   alumni, where that category is the fastest-growing one. So the page has two
   readers and the layout does the separating — the entry has its own weight,
   the ladder sits behind it as three equal cards.

   NOT four equal cards: the research is that a small buyer should skip
   strategy and ship one implementation, so a flat list would point the wrong
   reader at the wrong thing.                                                */
/* Head across the top, areas in a grid beneath it — NOT two columns.
   Shipped 2026-08-27 as 0.85fr/1.15fr with the head in column one and all five
   areas stacked in column two. Measured on the live site after Dipender said he
   could not see the services: the head column carried ~200px of content inside a
   908px row, so ~700px of the section was empty white, and the five areas ran
   down a narrow 569px gutter beside it. It read as a large blank box rather than
   as a list of what one7x does. Every check passed the whole time — each one
   measures a property (contrast, overflow, copy fit, duty cycle) and none asks
   whether the section reads as anything. */
.lead-svc {
  display: grid; grid-template-columns: 1fr; gap: var(--s-6);
  padding: 34px 36px; margin-bottom: var(--s-5);
  border: 1px solid var(--border); border-radius: var(--r-5);
  background: var(--bg-elev);
}
.lead-svc__head { display: grid; align-content: start; gap: 12px; max-width: 62ch; }
.lead-svc__tag {
  justify-self: start;
  font-size: 12px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--signal);
  padding: 5px 11px; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--signal) 45%, transparent);
}
.lead-svc__head h3 {
  font-family: var(--font-display); font-size: 26px;
  font-weight: var(--character-heading-weight); letter-spacing: -0.02em; line-height: 1.2;
  margin: 0;
}
.lead-svc__head p { color: var(--fg-muted); font-size: 17px; line-height: 1.55; margin: 0; }
.lead-svc__areas {
  display: grid; gap: var(--s-6) var(--s-6); align-content: start;
  grid-template-columns: repeat(3, 1fr);
}
.lead-svc__areas li { display: grid; gap: 4px; }
.lead-svc__areas b { font-size: 16px; font-weight: 600; }
.lead-svc__areas .fl__what { font-size: 15px; line-height: 1.5; color: var(--fg-muted); }

/* --- the drawn flows ------------------------------------------------------
   One per area. The movement IS the content — an enquiry travelling to an
   answer, a document chased and returned — so these animate rather than sit
   still. They are aria-hidden: the label and sentence beside each one already
   say what it is, and a drawing that repeats the text is noise in the
   accessibility tree rather than help.

   ⛔ PAUSED BY DEFAULT. site.js adds .is-live when the block scrolls into view
   and removes it on the way out. The page already runs five continuous loops;
   five more running forever would double that on a phone for diagrams nobody
   is looking at.                                                            */
.fl { display: block; width: 100%; max-width: 200px; line-height: 0; }
.fl svg { width: 100%; height: auto; display: block; overflow: visible; }
.fl [class^="fl__"], .fl [class*=" fl__"] {
  fill: none; stroke-linecap: round; stroke-linejoin: round;
}
.fl__rail  { stroke: var(--border-strong); stroke-width: 1.5; }
.fl__node,
.fl__box,
.fl__stage { stroke: var(--border-strong); stroke-width: 1.5; }
.fl__dot,
.fl__marker { fill: var(--signal); stroke: none; }
.fl__fill,
.fl__check,
.fl__tick  { stroke: var(--signal); stroke-width: 2; }
.fl__bar   { fill: color-mix(in srgb, var(--signal) 22%, transparent); stroke: var(--signal); stroke-width: 1.5; }

/* Every animated part is declared paused; .is-live is the only thing that
   starts it. Declaring them running and pausing later means one full cycle
   plays before the observer has said anything. */
.fl__dot, .fl__check, .fl__fill, .fl__marker, .fl__bar, .fl__tick {
  animation-play-state: paused;
  animation-iteration-count: infinite;
  animation-timing-function: var(--ease-standard);
}
.is-live .fl__dot, .is-live .fl__check, .is-live .fl__fill,
.is-live .fl__marker, .is-live .fl__bar, .is-live .fl__tick {
  animation-play-state: running;
}

/* 1 · sales — an enquiry arrives, is answered, and a chase follows behind it */
.fl__dot--a { animation-name: fl-travel; animation-duration: 4.4s; }
.fl__dot--b { animation-name: fl-travel; animation-duration: 4.4s; animation-delay: 1.1s; }
.fl--sales .fl__tick { animation-name: fl-blip; animation-duration: 4.4s; animation-delay: 1.5s; }
@keyframes fl-travel {
  0%   { transform: translateX(0);    opacity: 0; }
  8%   { opacity: 1; }
  46%  { transform: translateX(52px); opacity: 1; }
  54%  { transform: translateX(52px); opacity: 1; }
  92%  { transform: translateX(104px); opacity: 1; }
  100% { transform: translateX(104px); opacity: 0; }
}
@keyframes fl-blip { 0%,26% { opacity: 0; } 34%,70% { opacity: 1; } 78%,100% { opacity: 0; } }

/* 2 · onboarding — the rows tick themselves off, in order */
.fl__check { opacity: 0; animation-name: fl-tick; animation-duration: 4.8s; }
.fl__check--1 { animation-delay: 0.2s; }
.fl__check--2 { animation-delay: 1.0s; }
.fl__check--3 { animation-delay: 1.8s; }
@keyframes fl-tick { 0% { opacity: 0; } 10%,82% { opacity: 1; } 94%,100% { opacity: 0; } }

/* 3 · delivery — the marker advances and the track fills behind it */
.fl__fill {
  stroke-dasharray: 92; stroke-dashoffset: 92;
  animation-name: fl-fill; animation-duration: 5.2s;
}
.fl__marker { animation-name: fl-advance; animation-duration: 5.2s; }
@keyframes fl-fill    { 0%,6% { stroke-dashoffset: 92; } 74%,88% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 92; } }
@keyframes fl-advance {
  0%,6% { transform: translateX(0); } 26% { transform: translateX(30.7px); }
  48% { transform: translateX(61.3px); } 74%,88% { transform: translateX(92px); }
  100% { transform: translateX(0); }
}

/* 4 · queries — most resolve below; the exception routes up to you */
.fl__dot--down { offset-path: path("M12 32c14 0 16 16 30 16h22"); animation-name: fl-path; animation-duration: 4.6s; }
.fl__dot--up   { offset-path: path("M12 32c14 0 16 -16 30 -16h22"); animation-name: fl-path; animation-duration: 4.6s; animation-delay: 2.3s; }
.fl--queries .fl__dot { cx: 0; cy: 0; }
@keyframes fl-path {
  0%   { offset-distance: 0%;   opacity: 0; }
  10%  { opacity: 1; }
  76%  { offset-distance: 100%; opacity: 1; }
  100% { offset-distance: 100%; opacity: 0; }
}

/* 5 · reporting — the bars grow out of the work line */
.fl__bar { transform-origin: 50% 50px; animation-name: fl-grow; animation-duration: 5s; }
.fl__bar--1 { animation-delay: 0.1s; }
.fl__bar--2 { animation-delay: 0.4s; }
.fl__bar--3 { animation-delay: 0.7s; }
.fl__bar--4 { animation-delay: 1.0s; }
@keyframes fl-grow { 0% { transform: scaleY(0); } 22%,84% { transform: scaleY(1); } 100% { transform: scaleY(0); } }

/* ⛔ Reduced motion rests on the FINISHED state, never a frozen mid-frame:
   every box ticked, the track full, the bars up, the dots delivered. A
   half-drawn diagram is a rendering bug to anyone who reads it. */
@media (prefers-reduced-motion: reduce) {
  .fl__dot, .fl__check, .fl__fill, .fl__marker, .fl__bar, .fl__tick,
  .is-live .fl__dot, .is-live .fl__check, .is-live .fl__fill,
  .is-live .fl__marker, .is-live .fl__bar, .is-live .fl__tick {
    animation: none !important;
  }
  .fl__check, .fl__tick { opacity: 1 !important; }
  .fl__fill   { stroke-dashoffset: 0 !important; }
  .fl__marker { transform: translateX(92px) !important; }
  .fl__bar    { transform: none !important; }
  .fl__dot--a { transform: translateX(104px) !important; opacity: 1 !important; }
  .fl__dot--b { opacity: 0 !important; }
  .fl--queries .fl__dot { offset-distance: 100% !important; opacity: 1 !important; }
}
@media (max-width: 1100px) { .lead-svc__areas { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 900px) {
  .lead-svc { padding: 28px 26px; }
}
@media (max-width: 620px)  { .lead-svc__areas { grid-template-columns: 1fr; } }

/* The ladder — three equal cards, so none of them outranks another. */
.svc--three { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1100px) { .svc--three { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px)  { .svc--three { grid-template-columns: 1fr; } }
/* The five-card rules above span the last child at two columns; with three
   cards that is exactly right too — 2+1 with the last one full width. */

/* --- the booking embed ---------------------------------------------------
   TidyCal injects its own iframe into .tidycal-embed. It arrives with no
   height of its own until the script runs, so the container reserves space:
   without it the section collapses and everything below jumps once the
   calendar lands.                                                           */
.book { margin-top: var(--s-6); }
.book .tidycal-embed { min-height: 640px; }
.book iframe {
  display: block; width: 100%; border: 0;
  border-radius: var(--r-5); background: var(--bg-elev);
}
@media (max-width: 720px) { .book .tidycal-embed { min-height: 820px; } }

/* --- writing instead -----------------------------------------------------
   One click behind the booking. It must not compete with the calendar, and it
   must not look like the page is hiding something.                          */
.writein { margin-top: var(--s-7); }
.writein > summary {
  cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 16px; color: var(--fg-muted);
  padding: 10px 0; border-bottom: 1px solid var(--border);
}
.writein > summary::-webkit-details-marker { display: none; }
.writein > summary::after {
  content: ""; width: 7px; height: 7px; margin-left: 2px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px); transition: transform .18s ease;
}
.writein[open] > summary::after { transform: rotate(225deg) translateY(-2px); }
.writein > summary:hover { color: var(--fg); }
.writein .ask { margin-top: var(--s-5); }
