/* Indrix marketing site.

   One theme, deliberately. Following the OS setting meant designing two pages and
   polishing neither, and a light page reads as clinical software rather than a developer
   tool. Emphasis comes from scale and a wash at the foot of the page, not a second palette.

   No JavaScript anywhere on this site. That is a security property, not a preference:
   indrix.app is the passkey rp-id, so the apex origin sits inside the credential scope
   and ships Content-Security-Policy: script-src 'none'. Every motion here is CSS.
   See docs/technical-decisions.md for the rp-id reasoning. */

:root {
  --paper:     #FCFCFA;
  --surface:   #FFFFFF;
  --ink:       #0C1A15;
  --ink-mid:   #40514A;
  /* 4.86:1 on paper. The previous value was 4.39:1 — under AA, and used at 11–15px for
     timestamps, the time rail and footer links, which is where it matters most. */
  --ink-soft:  #64736C;
  /* Translucent ink, not a fixed grey. An opaque hairline is only the right colour over
     plain paper — cross a green wash and it reads as a dirty line laid on top. Mixing
     with transparent lets every rule on the site pick up whatever is behind it. */
  --line:      color-mix(in srgb, #0C1A15 10%, transparent);
  --line-soft: color-mix(in srgb, #0C1A15 6%, transparent);
  --brand:     #047857;
  --brand-lit: #10B981;

  --wrap: 72rem;
  --gutter: 1.5rem;
  --measure: 34rem;

  /* Vertical rhythm. Sections breathe; everything inside them is tight. */
  --gap-section: clamp(6rem, 3rem + 9vw, 11rem);

  --text-xs:   0.8125rem;
  --text-sm:   0.9375rem;
  --text:      1.0625rem;
  --text-lead: clamp(1.1875rem, 1.08rem + 0.5vw, 1.4375rem);
  --text-h3:   1.1875rem;
  --text-h2:   clamp(2rem, 1.35rem + 2.6vw, 3.25rem);
  --text-h1:   clamp(2.5rem, 1.5rem + 4vw, 4.5rem);

  --radius: 0.75rem;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/geist-variable.woff2') format('woff2-variations');
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* `clip`, not `hidden`. Two reasons: overflow set on the body propagates to the viewport
     while the root is visible, so `overflow-x: hidden` there clips nothing — and `hidden`
     makes a scroll container, which is what silently breaks `position: sticky` on the
     header. `clip` contains the drifting glow without either problem. */
  overflow-x: clip;
}

/* The wash belongs to the page rather than to a section box, so it never stops at a hard
   edge — and it sits only at the foot. Nothing sticky may pass over it: a translucent bar
   cannot blend into a gradient, because blurring a smooth gradient returns the same
   gradient and leaves only the bar's own tint on top. */
body {
  position: relative;
  margin: 0;
  background-color: var(--paper);
  color: var(--ink);
  font-family: 'Geist', system-ui, -apple-system, sans-serif;
  font-size: var(--text);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Needed as well as the rule on the root: the root's own overflow is handed to the
     viewport and the root then behaves as visible, so it never clips its own children.
     The body is the containing block for the glow, so this is the one that catches it. */
  overflow-x: clip;
}

/* The drifting glow at the foot of the page.

   Its own element, not a background-image, so the drift can be a `transform` the
   compositor handles — animating the gradient's own position repaints a full-width band
   every frame. It sits flush to the bottom, because an absolutely positioned box still
   counts toward scrollable overflow and a negative `bottom` would add that much blank
   page under the footer; `transform-origin` is on that same edge so scaling grows it
   upward rather than back into a gap. `closest-side` at the default centre puts the
   falloff on every edge, so the glow reaches zero before it runs out of element. */
body::after {
  content: "";
  position: absolute;
  z-index: -1;
  bottom: 0;
  left: 50%;
  width: 110rem;
  height: 46rem;
  margin-left: -55rem;
  transform-origin: 50% 100%;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--brand-lit) 30%, transparent), transparent);
  pointer-events: none;
}

/* No glow on pages that are read rather than scanned. On the home page it is the closing
   note under a lot of whitespace; behind a privacy policy it is tint over text somebody is
   reading carefully, and a plain paper document reads as more trustworthy than a tinted
   one. The 404 keeps it — nothing is being read there. */
body.reading::after { content: none; }

@media (prefers-reduced-motion: no-preference) {
  body::after {
    animation: drift 42s ease-in-out infinite alternate;
  }
  /* Sideways and upward only — any downward travel reintroduces the gap below the footer. */
  @keyframes drift {
    from { transform: translate3d(-5%, 0, 0) scale(1); }
    to   { transform: translate3d(5%, -4%, 0) scale(1.08); }
  }
}

/* Display type is set large and light rather than large and bold: at 5rem, weight 500
   carries plenty of presence and bold reads as shouting. */
h1, h2, h3 { margin: 0; text-wrap: balance; }

h1 {
  font-size: var(--text-h1);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.038em;
}

h2 {
  font-size: var(--text-h2);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.03em;
}

h3 {
  font-size: var(--text-h3);
  font-weight: 550;
  line-height: 1.3;
  letter-spacing: -0.014em;
}

p { margin: 0; text-wrap: pretty; }

a { color: inherit; text-underline-offset: 0.22em; text-decoration-thickness: 1px; }

img, svg { display: block; max-width: 100%; }

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

.skip {
  position: absolute;
  left: var(--gutter);
  top: -4rem;
  z-index: 20;
  padding: 0.5rem 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.skip:focus { top: 0.75rem; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ================================================================== masthead */

.masthead {
  position: sticky;
  top: 0;
  z-index: 10;
  /* Solid paper, no backdrop-filter. Frosting only reads as glass over content with
     texture, and the wash now sits at the foot, so there is nothing behind this bar but
     paper. Keep it that way: a backdrop-filter on a sticky element whose background also
     animates makes Chrome re-rasterize the filter every scroll frame. */
  background-color: var(--paper);
  border-bottom: 1px solid transparent;
}

/* Only the hairline changes on scroll, and it fades rather than steps: a `steps(1)` switch
   flips the instant you cross the threshold, so nudging the wheel there toggles it on and
   off. The background is deliberately left out of this — it already matches the page. */
@supports (animation-timeline: scroll()) {
  .masthead {
    animation: masthead-rule linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 5rem;
  }
  @keyframes masthead-rule {
    from { border-bottom-color: color-mix(in srgb, var(--ink) 0%, transparent); }
    to   { border-bottom-color: color-mix(in srgb, var(--ink) 9%, transparent); }
  }
}

.masthead-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: 4.25rem;
}

/* Both marks are inlined in the HTML rather than loaded, so the lockup cannot break on a
   file failing to resolve. They are never set beside each other: the mark is a bracketed
   I, so next to the wordmark it reads IINDRIX. Wordmark in the header, mark in the footer
   and in every square slot — favicon, app icon, social. */
.brand { display: inline-flex; text-decoration: none; color: var(--ink); }
.brand .wordmark { height: 1rem; width: auto; }
.brand .mark { width: 1.75rem; height: 1.75rem; }

.masthead nav {
  display: flex;
  align-items: center;
  gap: 2rem;
  font-size: var(--text-sm);
}

.masthead nav a.nav-page {
  color: var(--ink-mid);
  text-decoration: none;
  transition: color 150ms ease;
}
.masthead nav a.nav-page:hover { color: var(--ink); }

@media (max-width: 40rem) {
  .masthead nav { gap: 1.25rem; }
  .masthead nav a.nav-page { display: none; }
}

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

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.875rem;
  padding: 0 1.4rem;
  border: 0;
  border-radius: 999px;
  background: var(--brand);
  color: #FFFFFF;
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: -0.006em;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 180ms ease, transform 180ms var(--ease);
}
.button:hover { background: #036148; }
.button:active { transform: scale(0.985); }

.button-small { min-height: 2.25rem; padding: 0 1.05rem; }

/* Text links are brand green and underlined at rest — both kinds. A link that only looks
   like a link on hover is invisible to anyone not already pointing at it, and to every
   touch user, who has no hover at all. */
.link,
.doc-body a {
  color: var(--brand);
  text-decoration-thickness: 1px;
  transition: background-color 180ms ease, color 180ms ease;
}

/* A standalone link gets a pill on hover, sharing the button's radius so the two read as
   siblings rather than as unrelated controls.

   The padding is cancelled by an equal negative margin, so the margin box stays exactly
   the size it was and the pill grows into space that was already reserved — a hover that
   nudges its neighbours is the one thing worse than no hover at all. */
.link {
  display: inline-flex;
  align-items: center;
  font-size: var(--text-sm);
  font-weight: 500;
  padding: 0.5rem 0.85rem;
  margin: -0.5rem -0.85rem;
  border-radius: 999px;
}

/* Standing next to a button, the pill takes the button's height so the pair sits as one
   row of controls rather than two sizes of the same shape. Only the block padding is
   dropped — the inline padding and its cancelling margin still hold the layout still. */
.hero-actions .link {
  min-height: 2.875rem;
  padding-block: 0;
  margin-block: 0;
}

/* The underline hands off to the pill: at rest the underline says "link", on hover the
   shape does, and the two never have to say it at once. Touch users, who get no hover,
   keep the underline permanently. */
.link:hover {
  background-color: color-mix(in srgb, var(--brand) 9%, transparent);
  color: #036148;
  text-decoration: none;
}

/* Inline links stay inline: a pill around a link mid-sentence breaks the line rhythm. */
.doc-body a:hover { color: #036148; }

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

.hero {
  position: relative;
  padding-block: clamp(4.5rem, 2rem + 9vw, 9rem) clamp(2.5rem, 1rem + 4vw, 4rem);
  text-align: center;
}

.hero-head { max-width: 20ch; margin-inline: auto; }

.h-line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}

.hero-lead {
  max-width: 38rem;
  margin: 1.75rem auto 0;
  font-size: var(--text-lead);
  line-height: 1.48;
  letter-spacing: -0.012em;
  color: var(--ink-mid);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1rem 1.75rem;
  margin-top: 2.25rem;
}

/* ========================================================== schedule surface */

.stage { padding-bottom: 0.5rem; }

.surface {
  --row: 1.9rem;          /* one half-hour */
  --rail: 3.5rem;
  border: 1px solid var(--line);
  border-radius: 1rem;
  background: var(--surface);
  overflow: hidden;
  box-shadow:
    0 1px 2px rgb(12 26 21 / 0.04),
    0 8px 20px -10px rgb(12 26 21 / 0.10),
    0 32px 60px -30px rgb(12 26 21 / 0.16);
}

.surface-head {
  padding: 0.95rem 1.25rem 0.8rem;
  border-bottom: 1px solid var(--line-soft);
  font-size: var(--text-xs);
}

.surface-day {
  display: block;
  margin-bottom: 0.85rem;
  font-weight: 550;
  letter-spacing: -0.01em;
}

/* Column labels sit on the same grid as the day below them, offset by the slot's own
   padding so a provider's name lines up with their appointments. */
.surface-cols {
  display: grid;
  grid-template-columns: var(--rail) repeat(3, minmax(0, 1fr));
}

/* No boxes and no header row rules: the names float over the columns rather than
   sitting in cells. The day's load under each one is the one genuinely useful thing
   the app's own calendar has that this did not. */
.surface-col {
  display: grid;
  gap: 0.15rem;
  padding-left: 0.55rem;
  min-width: 0;
}

.surface-col b {
  font-weight: 550;
  letter-spacing: -0.008em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.surface-col small {
  font-size: 0.6875rem;
  color: var(--ink-soft);
}

.surface-body {
  display: grid;
  grid-template-columns: var(--rail) repeat(3, minmax(0, 1fr));
  padding: 0.6rem 1.25rem 1.25rem;
  background: #FCFCFB;
}

.rail {
  display: grid;
  grid-auto-rows: calc(var(--row) * 2);
  font-size: 0.6875rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.rail span { transform: translateY(-0.45em); }

/* Appointments are keyed to the provider, the way a practice reads its own day:
   green for the dentists' chairs, slate for hygiene. */
.col { --accent: var(--brand); }
.surface-body > .col:nth-child(4) { --accent: #64748B; }

.col {
  position: relative;
  height: calc(10 * var(--row));
  padding-inline: 0.25rem;
  border-left: 1px solid var(--line-soft);
  background: repeating-linear-gradient(
    to bottom,
    var(--line-soft) 0 1px,
    transparent 1px calc(var(--row) * 2)
  );
}

/* --start and --span are in half-hours, set from data attributes so the markup stays
   free of inline style (the CSP forbids it). */
.slot {
  position: absolute;
  top: calc(var(--start) * var(--row) + 0.15rem);
  left: 0.25rem;
  right: 0.25rem;
  height: calc(var(--span) * var(--row) - 0.3rem);
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.45rem 0.6rem;
  border-radius: 0.4rem;
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(12 26 21 / 0.05);
  font-size: 0.78125rem;
  line-height: 1.32;
  overflow: hidden;
}

[data-start="0"] { --start: 0; }
[data-start="1"] { --start: 1; }
[data-start="2"] { --start: 2; }
[data-start="4"] { --start: 4; }
[data-start="5"] { --start: 5; }
[data-start="7"] { --start: 7; }
[data-start="8"] { --start: 8; }
[data-span="2"]  { --span: 2; }
[data-span="3"]  { --span: 3; }

.slot b { font-weight: 550; letter-spacing: -0.008em; }
.slot > span { color: var(--ink-soft); }

.slot-open {
  border: 1px dashed var(--line);
  background: transparent;
  color: var(--ink-soft);
  justify-content: center;
  align-items: center;
  font-size: var(--text-xs);
}

/* `.chip` is placement only; the state always comes from a modifier. Leaving one state as
   the bare default makes the other look like the exception. */
.chip {
  margin-top: auto;
  font-style: normal;
  font-size: 0.6875rem;
}
.chip-ok   { color: var(--brand);    font-weight: 500; }
.chip-wait { color: var(--ink-soft); font-weight: 400; }

/* A three-chair day needs width. On a phone it keeps one chair rather than squeezing
   three into columns too narrow to read. */
/* Three chairs, then two, then one — going straight to one leaves a tablet with a single
   wide column that reads as an empty page rather than a day. `nth-child`, not
   `nth-of-type`: the rail and the header spacer are the same element type as the columns,
   so `nth-of-type` counts them and hides the wrong ones. */
@media (max-width: 52rem) {
  .surface-cols,
  .surface-body { grid-template-columns: var(--rail) repeat(2, minmax(0, 1fr)); }
  .surface-cols > span:nth-child(n + 4),
  .surface-body > .col:nth-child(n + 4) { display: none; }
}

@media (max-width: 34rem) {
  .surface-cols,
  .surface-body { grid-template-columns: var(--rail) minmax(0, 1fr); }
  .surface-cols > span:nth-child(n + 3),
  .surface-body > .col:nth-child(n + 3) { display: none; }
}

/* ================================================================== sections */

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

/* Anchor links land under the sticky bar otherwise. */
.section, .band { scroll-margin-top: 4.25rem; }

.section-head { max-width: 22ch; }

/* A claim on the left, a piece of the product on the right, divided by a hairline
   that draws itself as the row arrives. No cards: cards would flatten the difference
   between the statement and the evidence. */
.row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: 2rem 4rem;
  align-items: start;
  margin-top: 3.5rem;
  padding-top: 2.25rem;
  view-timeline: --row block;
}

.rule {
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 1px;
  background: var(--line);
  transform-origin: left center;
}

.row-text p {
  max-width: 30rem;
  margin-top: 0.6rem;
  color: var(--ink-mid);
}

@media (max-width: 52rem) {
  .row { grid-template-columns: 1fr; gap: 1.75rem; }
}

/* -------------------------------------------------- product evidence panels */

.proof {
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--surface);
  overflow: hidden;
  font-size: var(--text-sm);
  box-shadow: 0 1px 2px rgb(12 26 21 / 0.03), 0 10px 24px -16px rgb(12 26 21 / 0.14);
}

.proof-head {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--line-soft);
  font-size: var(--text-xs);
}
.proof-head span:first-child { font-weight: 550; }
.proof-head span:last-child { color: var(--ink-soft); }

.proof-rows { margin: 0; padding: 0.35rem 1rem 0.75rem; }
.proof-rows > div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.42rem;
}
.proof-rows > div + div { border-top: 1px solid var(--line-soft); }
.proof-rows dt { color: var(--ink-soft); }
.proof-rows dd { margin: 0; font-variant-numeric: tabular-nums; }

.proof-list { list-style: none; margin: 0; padding: 0.35rem 1rem 0.75rem; }
.proof-list li {
  display: flex;
  gap: 0.85rem;
  padding-block: 0.42rem;
  color: var(--ink-mid);
}
.proof-list li + li { border-top: 1px solid var(--line-soft); }
.proof-list b {
  min-width: 1.6rem;
  color: var(--ink);
  font-weight: 550;
  font-variant-numeric: tabular-nums;
}

.trail {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: 0;
  padding: 0.9rem 1rem 1rem;
}
.trail li {
  position: relative;
  display: grid;
  gap: 0.15rem;
  padding-top: 1.15rem;
  font-size: var(--text-xs);
}
/* The track and its node, drawn once per step so the line runs edge to edge. */
.trail li::before {
  content: "";
  position: absolute;
  top: 0.28rem;
  left: 0;
  right: 0;
  height: 1px;
  background: color-mix(in srgb, var(--brand) 35%, transparent);
}
.trail li::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 999px;
  background: var(--brand);
}
/* The track runs from the first node to the last, so the final cell draws none. */
.trail li:last-child::before { display: none; }
.trail b { font-weight: 550; }
.trail span { color: var(--ink-soft); }

/* ================================================================= switching */

/* One continuous light field, no inverted block: a light sticky header passing over a dark
   section is always a grey slab, since the bar cannot be light over paper and dark over ink
   at once. Switching earns its weight from scale and a wash that fades on every side. */
.band {
  position: relative;
  margin-top: var(--gap-section);
  padding-block: clamp(5rem, 3rem + 7vw, 8.5rem);
  isolation: isolate;
}

.band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(70% 90% at 20% 35%,
      color-mix(in srgb, var(--brand-lit) 11%, transparent), transparent 70%),
    radial-gradient(60% 80% at 88% 85%,
      color-mix(in srgb, var(--brand-lit) 8%, transparent), transparent 72%);
  /* The wash has to reach zero before the section does. A radial that is still even
     slightly opaque where the section box ends gets cut off square, and that shows up as
     a hard line straight across the page. This fades it out well inside its own edges. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 20%, #000 80%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 20%, #000 80%, transparent 100%);
  pointer-events: none;
}

/* The largest heading on the page after the hero: this is the objection that decides
   the sale, so it gets the one typographic moment. */
.band-head {
  max-width: 16ch;
  font-size: clamp(2.25rem, 1.4rem + 3.2vw, 3.875rem);
}

.band-lead {
  max-width: 40rem;
  margin-top: 1.5rem;
  font-size: var(--text-lead);
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--ink-mid);
}

.steps {
  counter-reset: step;
  list-style: none;
  margin: 4rem 0 0;
  padding: 0;
  display: grid;
  gap: 2.5rem 3rem;
}

@media (min-width: 52rem) {
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 3.25rem 4rem; }
}

/* Numbered because it genuinely is a sequence — these four happen in this order. */
.steps li {
  counter-increment: step;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}

.steps li::before {
  content: counter(step);
  display: block;
  margin-bottom: 0.9rem;
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--brand);
}

.steps h3 { margin-bottom: 0.45rem; }
.steps p { color: var(--ink-mid); }

/* ================================================================== security */

.trio {
  display: grid;
  gap: 2.5rem 3rem;
  margin-top: 3.5rem;
}

@media (min-width: 52rem) {
  .trio { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.section-more { margin-top: 2.5rem; }

.trio > div { padding-top: 1.1rem; border-top: 1px solid var(--line); }
.trio h3 { margin-bottom: 0.5rem; }
.trio p { color: var(--ink-mid); font-size: var(--text-sm); line-height: 1.6; }

/* ==================================================================== closer */

/* No margin on top of the padding: the two together left a screen and a half of
   nothing between the last section and this one. */
.closer {
  position: relative;
  padding-block: clamp(6rem, 3.5rem + 7vw, 9.5rem);
  text-align: center;
  overflow: hidden;
}

.closer-head { max-width: 18ch; margin-inline: auto; }

.closer p {
  max-width: 34rem;
  margin: 1.25rem auto 0;
  font-size: var(--text-lead);
  line-height: 1.5;
  letter-spacing: -0.008em;
  color: var(--ink-mid);
}

/* ================================================================= documents */

/* The security, privacy and 404 pages. Left-aligned and narrow: these are read, not
   scanned, so they get a reading measure rather than the home page's centred display. */

/* The bottom value matters: with none, the last section's text runs almost straight into
   the footer, and these pages end on a paragraph rather than on a closing block. */
.doc { padding-block: clamp(3.5rem, 2rem + 5vw, 6rem) clamp(4.5rem, 3rem + 5vw, 8rem); }

.doc-head { max-width: 18ch; }

.doc-intro {
  max-width: 36rem;
  margin-top: 1.5rem;
  font-size: var(--text-lead);
  line-height: 1.5;
  letter-spacing: -0.008em;
  color: var(--ink-mid);
}

.doc-meta {
  margin-top: 1.5rem;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.doc-body { margin-top: clamp(3rem, 2rem + 3vw, 4.5rem); }

.doc-body section { padding-top: 2rem; border-top: 1px solid var(--line); }
.doc-body section + section { margin-top: 3rem; }

.doc-body h2 {
  font-size: 1.375rem;
  font-weight: 550;
  line-height: 1.3;
  letter-spacing: -0.014em;
  max-width: 28ch;
}

.doc-body p {
  max-width: 36rem;
  margin-top: 0.9rem;
  color: var(--ink-mid);
}

.doc-body ul {
  max-width: 36rem;
  margin: 0.9rem 0 0;
  padding-left: 1.1rem;
  color: var(--ink-mid);
}
.doc-body li { margin-top: 0.45rem; }

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

.lost {
  padding-block: clamp(6rem, 4rem + 8vw, 11rem);
  text-align: center;
}
.lost p {
  max-width: 32rem;
  margin: 1.25rem auto 0;
  font-size: var(--text-lead);
  color: var(--ink-mid);
}

/* ==================================================================== footer */

/* No rule above the footer: it cut a hard line straight across the green wash, which
   is the one place on the page the background is doing the separating. */
.colophon {
  padding-block: 2.5rem 3.5rem;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.colophon-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.75rem 2.5rem;
}

.colophon nav { display: flex; flex-wrap: wrap; gap: 1.5rem; }
.colophon a { text-decoration: none; transition: color 150ms ease; }
.colophon a:hover { color: var(--ink); }

.colophon-legal { display: flex; flex-wrap: wrap; gap: 1.5rem; }

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

/* One orchestrated arrival on load, then the page is still until you scroll it.
   Everything below is additive: with reduced motion, or without scroll-driven
   animation support, the page simply renders finished. */

@media (prefers-reduced-motion: no-preference) {

  .h-line > span {
    display: block;
    animation: line-up 1000ms var(--ease) backwards;
  }
  .h-line:nth-of-type(2) > span { animation-delay: 90ms; }

  @keyframes line-up {
    from { transform: translateY(105%); }
  }

  .hero-lead   { animation: lift 900ms var(--ease) 240ms backwards; }
  .hero-actions { animation: lift 900ms var(--ease) 340ms backwards; }
  .surface     { animation: lift 1100ms var(--ease) 420ms backwards; }

  @keyframes lift {
    from { opacity: 0; transform: translateY(14px); }
  }

  /* The day fills in after the surface lands — the appointments arriving is the
     one thing on this page worth animating for its own sake. */
  .slot { animation: slot-in 620ms var(--ease) backwards; }

  .surface-body > .col:nth-child(2) .slot { animation-delay: 760ms; }
  .surface-body > .col:nth-child(3) .slot { animation-delay: 840ms; }
  .surface-body > .col:nth-child(4) .slot { animation-delay: 920ms; }

  .surface-body > .col:nth-child(2) .slot:nth-child(2) { animation-delay: 830ms; }
  .surface-body > .col:nth-child(2) .slot:nth-child(3) { animation-delay: 900ms; }
  .surface-body > .col:nth-child(3) .slot:nth-child(2) { animation-delay: 910ms; }
  .surface-body > .col:nth-child(3) .slot:nth-child(3) { animation-delay: 980ms; }
  .surface-body > .col:nth-child(4) .slot:nth-child(2) { animation-delay: 990ms; }
  .surface-body > .col:nth-child(4) .slot:nth-child(3) { animation-delay: 1060ms; }
  .surface-body > .col:nth-child(4) .slot:nth-child(4) { animation-delay: 1130ms; }

  @keyframes slot-in {
    from { opacity: 0; transform: translateY(6px) scale(0.985); }
  }
}

/* Scroll-linked, and the only scroll motion on the page: each row's hairline draws
   itself as the row arrives. The text settles a few pixels rather than sliding in. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .rule {
      animation: draw-rule linear both;
      animation-timeline: --row;
      animation-range: entry 12% entry 78%;
    }
    @keyframes draw-rule {
      from { transform: scaleX(0); }
      to   { transform: scaleX(1); }
    }

    .row-text,
    .proof {
      animation: settle linear both;
      animation-timeline: --row;
      animation-range: entry 16% entry 72%;
    }
    .proof { animation-range: entry 22% entry 80%; }

    @keyframes settle {
      from { opacity: 0; transform: translateY(8px); }
      to   { opacity: 1; transform: none; }
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
