/* ==========================================================================
   Roanlight company site
   Direction: "the light that's on after hours." Roan is a warm reddish-brown,
   so the page is a sunrise: it opens in the blue hour on a real 11:02 PM
   patient conversation and arrives at full daylight by the footer, scrubbed
   against the scrollbar the whole way. That is the product argument with no
   copy attached. The assistant is the light that is on while the practice is
   dark, and the booking is on the front desk's screen before anyone opens the
   door. Colour is otherwise spent in exactly three places (the closed-status
   dot, the assistant's reply marks, the demo link). No cards-with-icons, no
   logo wall, and the only gradients on the page are the sky.
   ========================================================================== */

/* ---------- fonts: self-hosted, no external requests ---------- */
/* Display face: Source Serif 4, variable on optical size and weight.
 *
 * It replaces Fraunces. Fraunces was chosen to fix Bricolage Grotesque's
 * legibility problems (condensed 88-95% width at 800 weight, -0.035em
 * tracking), and disabling its WONK/SOFT axes did remove the curled
 * swash alternates on j/g/y, but the typeface's default letterforms still
 * read as ornamented at heading size for this reader. Source Serif 4 is a
 * plain, rational transitional serif designed for screen legibility, with
 * no swash axis to fight: straight terminals, no decorative curl anywhere
 * in its default glyph set.
 *
 * Optical size is left on `auto` so the browser feeds font-size into the opsz
 * axis: fine, high-contrast strokes in the headlines, sturdier ones in the
 * 1.1rem subheads. That is the whole reason to ship a variable face here. */
@font-face {
  font-family: "Source Serif 4";
  src: url("/fonts/SourceSerif4-normal.woff2") format("woff2-variations");
  font-weight: 400 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Public Sans";
  src: url("/fonts/PublicSans-normal.woff2") format("woff2-variations");
  font-weight: 300 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "DM Mono";
  src: url("/fonts/DMMono-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "DM Mono";
  src: url("/fonts/DMMono-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}

/* ---------- tokens ---------- */
:root {
  --ink:        #12161C;   /* night. Near-black with blue in it, never #000 */
  --ink-2:      #1A202A;   /* raised surface on ink */
  --ink-3:      #232B36;   /* hairlines on ink */
  --paper:      #FBFAF7;   /* day. Warm white */
  --paper-2:    #F4F2ED;   /* recessed surface on paper */
  --graphite:   #1E242C;   /* body text on paper */
  --muted:      #4E5561;   /* secondary text on the sky. Measured, not picked:
                              it has to clear 4.5:1 against the warmest, darkest
                              point of the ramp that any body copy is ever on
                              screen for (#E7C9A4, 0.61 luminance), not just
                              against the white the ramp ends on. */
  --muted-ink:  #AEB6C1;   /* secondary text on the night half of the ramp.
                              Lighter than it looks like it needs to be, and
                              that is the point: it is what lets the sky lift
                              for the whole pinned hero instead of holding one
                              colour. It clears 4.5:1 up to 0.064 luminance,
                              where #98A1AE broke at 0.039. */
  --rule:       #E4E0D8;   /* hairline on paper */
  --light:      #E9A13B;   /* roanlight, the lamp. On ink only. */
  --light-deep: #6E430C;   /* the same idea, contrast-safe on the whole light
                              half of the ramp rather than only on paper */

  /* The static hero, used only when motion is off. The scrolled sunrise does
     not read its stops from here: those live in RAMP in site.js, because they
     are interpolated numerically and the contrast budget between them is
     arithmetic rather than a palette choice. */
  --night-3:    #2B2440;   /* brown-violet, the sky just before the sun */

  --display: "Source Serif 4", "Iowan Old Style", Georgia, "Times New Roman", serif;
  --body:    "Public Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono:    "DM Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --shell: 1160px;
  --gut: 24px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ---------- reset ---------- *
 * NOTE: deliberately NO `overflow-x: hidden` on html or body. It silently
 * disables `position: sticky` on the masthead. Overflow is contained on the
 * individual wide children instead (see .thread, .week-strip).            */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--graphite);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.62;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg { max-width: 100%; height: auto; display: block; }
h1, h2, h3 { margin: 0; font-weight: 400; }
p { margin: 0 0 1.05em; }
p:last-child { margin-bottom: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; }

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

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

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--paper); color: var(--graphite);
  padding: 12px 18px; font-weight: 600;
}
.skip:focus { left: 12px; top: 12px; }

/* ---------- shell ---------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding-inline: var(--gut);
}

/* ---------- shared type ---------- */
.eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--light);
  margin: 0 0 20px;
}
.eyebrow-dark { color: var(--light-deep); }

.h2 {
  font-family: var(--display);
  font-size: clamp(1.9rem, 3.7vw, 2.95rem);
  line-height: 1.14;
  letter-spacing: -0.008em;
  font-weight: 700;
  color: var(--graphite);
  max-width: 20ch;
  margin: 0 0 28px;
  text-wrap: balance;
}
.h2-light { color: var(--paper); }
.h2-sm { font-size: clamp(1.45rem, 2.4vw, 1.9rem); max-width: 24ch; }

.lede {
  font-size: clamp(1.05rem, 1.5vw, 1.2rem);
  line-height: 1.55;
  color: var(--muted-ink);
  max-width: 60ch;
  margin: 0 0 56px;
}
/* --muted-ink is tuned for the night half of the ramp (see #schedule, which
   sits on a fixed dark surface). #trained sits on the day half instead, so it
   needs the day-appropriate muted tone or the paragraph reads washed out. */
#trained .lede { color: var(--muted); }

/* ---------- buttons ---------- *
 * Buttons are ink or paper. The accent is never a button fill; it is
 * reserved for the three places that carry meaning.                       */
.btn {
  display: inline-flex; align-items: center; gap: 0.5em;
  font-family: var(--body);
  font-size: 0.95rem; font-weight: 600;
  letter-spacing: -0.005em;
  padding: 14px 22px;
  border-radius: 4px;
  text-decoration: none;
  min-height: 46px;
  cursor: pointer;
  transition: background-color 160ms var(--ease), color 160ms var(--ease),
              border-color 160ms var(--ease), transform 160ms var(--ease);
}
.btn .ext { font-size: 0.85em; opacity: 0.65; }
.btn-light { background: var(--paper); color: var(--ink); }
.btn-light:hover { background: #fff; transform: translateY(-1px); }
.btn-ink { background: var(--ink); color: var(--paper); }
.btn-ink:hover { background: #000; transform: translateY(-1px); }
.btn-big { padding: 17px 30px; font-size: 1.02rem; }
.btn-quiet {
  background: transparent; color: var(--paper);
  border: 1px solid var(--ink-3);
  padding: 10px 16px; min-height: 40px; font-size: 0.88rem;
}
.btn-quiet:hover { border-color: var(--muted-ink); background: var(--ink-2); }

.linkline {
  font-size: 0.95rem; font-weight: 600; text-decoration: none;
  color: var(--paper);
  border-bottom: 1px solid var(--ink-3);
  padding-bottom: 3px;
  transition: border-color 160ms var(--ease);
}
.linkline:hover { border-color: var(--light); }

/* ==========================================================================
   MASTHEAD. Sticky, and depends on no ancestor having overflow-x clipped.
   ========================================================================== */
.masthead {
  position: sticky; top: 0; z-index: 100;
  background: rgba(18, 22, 28, 0.86);
  backdrop-filter: saturate(1.4) blur(12px);
  -webkit-backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 200ms var(--ease), background-color 200ms var(--ease);
}
.masthead.is-stuck { border-bottom-color: var(--ink-3); background: rgba(18, 22, 28, 0.95); }
.masthead-in {
  display: flex; align-items: center; gap: 28px;
  height: 66px;
}
.wordmark {
  display: inline-flex; align-items: center; gap: 9px;
  text-decoration: none; color: var(--paper);
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.14rem; letter-spacing: -0.005em;
  margin-right: auto;
}
/* The lamp: the mark is literally the light the company is named for. */
.wordmark-lamp {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--light);
  box-shadow: 0 0 0 4px rgba(233, 161, 59, 0.16), 0 0 14px rgba(233, 161, 59, 0.5);
  flex: none;
}
.masthead-nav { display: flex; gap: 26px; }
.masthead-nav a {
  text-decoration: none; color: var(--muted-ink);
  font-size: 0.9rem; font-weight: 500;
  transition: color 160ms var(--ease);
}
.masthead-nav a:hover { color: var(--paper); }

/* ==========================================================================
   HERO
   ========================================================================== */
.hero {
  background-color: var(--night-3);
  color: var(--paper);
  padding: clamp(56px, 8vw, 104px) 0 clamp(64px, 9vw, 116px);
  /* The static sunrise: the bloom low in the frame, amber climbing out of it,
     and the lamp top-left. Deliberately the *finished* state of the scrubbed
     version, so a visitor with reduced motion, or no JS at all, gets the whole
     picture rather than a half-built gradient, and never a black screen. */
  background-image:
    radial-gradient(120% 62% at 50% 104%,
      rgba(255, 238, 205, 0.50) 0%,
      rgba(255, 203, 120, 0.32) 17%,
      rgba(240, 150, 70, 0.16) 36%,
      rgba(180, 96, 80, 0.05) 60%,
      transparent 76%),
    linear-gradient(to top,
      rgba(255, 176, 92, 0.20) 0%,
      rgba(226, 132, 60, 0.13) 22%,
      rgba(150, 80, 74, 0.07) 48%,
      rgba(90, 60, 90, 0.03) 74%,
      transparent 100%),
    radial-gradient(78ch 44ch at 8% -12%, rgba(233, 161, 59, 0.09), transparent 62%);
}
.hero-in {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  gap: clamp(36px, 5vw, 68px);
  align-items: start;
}
.hero-h1 {
  font-family: var(--display);
  font-size: clamp(2rem, 3.55vw, 3.05rem);
  line-height: 1.12;
  letter-spacing: -0.012em;
  font-weight: 700;
  color: var(--paper);
  margin: 0 0 26px;
}
/* The second clause is the argument, so it is the one thing that is lit. */
.hero-h1 em {
  font-style: normal;
  color: var(--light);
}
.hero-sub {
  font-size: clamp(1.05rem, 1.45vw, 1.19rem);
  line-height: 1.55;
  color: var(--muted-ink);
  max-width: 46ch;
  margin: 0 0 32px;
}
.hero-actions {
  display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
  margin-bottom: 28px;
}
.hero-note {
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--muted-ink);
  max-width: 42ch;
  padding-top: 22px;
  border-top: 1px solid var(--ink-3);
}

/* ---------- the transcript (signature element) ---------- */
.scope {
  margin: 0;
  background: var(--ink-2);
  border: 1px solid var(--ink-3);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 30px 70px -32px rgba(0, 0, 0, 0.85);
}
.scope-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 13px 17px;
  border-bottom: 1px solid var(--ink-3);
  background: rgba(0, 0, 0, 0.22);
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.06em;
  color: var(--muted-ink);
  text-transform: uppercase;
}
.scope-status { display: inline-flex; align-items: center; gap: 8px; }
/* Accent use #1: the closed light. */
.dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--light);
  box-shadow: 0 0 0 3px rgba(233, 161, 59, 0.15);
  flex: none;
  animation: pulse 3.2s var(--ease) infinite;
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(233, 161, 59, 0.15); }
  50%      { box-shadow: 0 0 0 6px rgba(233, 161, 59, 0.04); }
}
.scope-clock { color: #6E7784; }

/* Static default: the whole conversation, no clipping, no inner scrollbar.
   The scroll-driven build turns this into a fixed-height window (below). */
.thread-window { position: relative; }
.thread {
  padding: 20px 17px 6px;
  display: flex; flex-direction: column; gap: 14px;
}
/* The date separator a messaging app puts above the first message of a
   session. It is here because it is true, this happened on a Tuesday
   night after hours, and because a chat panel without one looks like a
   mockup rather than a transcript. */
.thread-mark {
  display: flex; align-items: center; gap: 12px;
  align-self: stretch;
  margin: 2px 0 4px;
  font-family: var(--mono);
  font-size: 0.66rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: #6E7784;
}
.thread-mark::before, .thread-mark::after {
  content: ""; flex: 1; height: 1px; background: var(--ink-3);
}
.turn { display: flex; flex-direction: column; max-width: 88%; }
.turn-them { align-self: flex-start; align-items: flex-start; }
.turn-us   { align-self: flex-end;   align-items: flex-end; }
.bubble {
  margin: 0;
  padding: 11px 14px;
  border-radius: 12px;
  font-size: 0.925rem;
  line-height: 1.5;
}
.turn-them .bubble {
  background: #2A323D;
  color: #DCE1E8;
  border-bottom-left-radius: 4px;
}
.turn-us .bubble {
  background: var(--paper);
  color: var(--ink);
  border-bottom-right-radius: 4px;
}
.stamp {
  margin: 6px 2px 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: #838C99;
  display: inline-flex; align-items: center; gap: 6px;
}
/* Accent use #2: marks the turns the assistant answered. */
.stamp-mark {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--light); flex: none;
}
/* The panel spends five turns getting here, so the arrival gets its own
   weight: more air above and below it than any other row in the transcript,
   and a warm amber wash instead of the plain black scrim the message rows
   sit on, so BOOKED reads as the one thing in the panel that is actually
   settled rather than as another line of conversation. */
.scope-foot {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  padding: 26px 18px;
  margin-top: 16px;
  border-top: 1px solid var(--ink-3);
  background: linear-gradient(180deg, rgba(233, 161, 59, 0.14), rgba(233, 161, 59, 0.05));
  font-size: 0.82rem;
  color: var(--muted-ink);
}
/* Accent use #4: BOOKED, spent larger and more deliberately than the dot or
   the stamp marks, because this is the one moment the whole panel is built
   to reach. Still text, not a filled badge or a button; the site's rule that
   the accent is never a fill holds here too. */
.scope-foot-key {
  font-family: var(--mono);
  font-size: 1.4rem; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--light);
}
.scope-foot-val { min-width: 0; }

/* NOTE ON HIDDEN STATES
 * Nothing on this page is hidden by CSS. Every "before" state for a scrubbed
 * or revealed element is written by JS, and only after JS has confirmed that
 * motion is wanted. That is the whole reason the reduced-motion and no-JS
 * renderings are guaranteed complete rather than blank: there is no rule here
 * that could strand an element at opacity 0.
 */

/* ==========================================================================
   BANDS
   ========================================================================== */
.band { padding: clamp(64px, 8.5vw, 118px) 0; }
.band-tight { padding: clamp(52px, 6.5vw, 92px) 0; }
.band-paper { background: var(--paper); }
.band-paper + .band-paper { padding-top: 0; }
.band-ink {
  background: var(--ink);
  color: var(--muted-ink);
}
.band-ink .h2 { color: var(--paper); }

/* ---------- the gap ---------- */
.gap-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}
.gap-copy { max-width: 54ch; color: var(--muted); font-size: 1.03rem; }
.gap-kicker {
  color: var(--graphite);
  font-weight: 500;
  padding-top: 18px;
  border-top: 1px solid var(--rule);
}

.week { margin: 0; }
.week-head {
  font-family: var(--mono);
  font-size: 0.72rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 14px;
}
.week-strip {
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 12px;
  background: var(--paper-2);
  overflow: hidden;
}
.week-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; }
.week-day { display: grid; grid-template-rows: repeat(24, 1fr); gap: 2px; }
.week-hour {
  height: 6px;
  border-radius: 1px;
  background: #DDD8CF;          /* closed */
}
.week-hour.is-open { background: var(--graphite); }
.week-legend {
  display: flex; gap: 20px; margin-top: 12px;
  font-family: var(--mono); font-size: 0.68rem; color: var(--muted);
}
.wl { display: inline-flex; align-items: center; gap: 7px; }
.sw { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.sw-open { background: var(--graphite); }
.sw-shut { background: #DDD8CF; }

/* ---------- what it does ---------- */
.does {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--rule);
}
.does li {
  padding: 30px 34px 32px 0;
  border-bottom: 1px solid var(--rule);
}
.does li:nth-child(odd) { border-right: 1px solid var(--rule); padding-right: 34px; }
.does li:nth-child(even) { padding-left: 34px; }
.does h3 {
  font-family: var(--display);
  font-size: 1.16rem; font-weight: 700;
  letter-spacing: -0.004em;
  line-height: 1.28;
  color: var(--graphite);
  margin: 0 0 10px;
  max-width: 26ch;
}
.does p { color: var(--muted); font-size: 0.97rem; max-width: 44ch; }

/* ---------- channels (where it answers) ---------- */
.channels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--rule);
  list-style: none;
  margin: 0; padding: 0;
}
.channels li {
  padding: 24px 34px 26px 0;
  border-bottom: 1px solid var(--rule);
}
.channels li:nth-child(odd) { border-right: 1px solid var(--rule); padding-right: 34px; }
.channels li:nth-child(even) { padding-left: 34px; }
.channels h3 {
  font-family: var(--display);
  font-size: 1.16rem; font-weight: 700;
  letter-spacing: -0.004em; line-height: 1.28;
  color: var(--graphite); margin: 0 0 8px;
}
.channels p { color: var(--muted); font-size: 0.93rem; max-width: 40ch; margin: 0; }
.channels-note { color: var(--muted); font-size: 0.88rem; margin: 20px 0 0; }

@media (max-width: 720px) {
  .channels { grid-template-columns: minmax(0, 1fr); }
  .channels li,
  .channels li:nth-child(odd),
  .channels li:nth-child(even) {
    border-right: 0; padding-left: 0; padding-right: 0;
  }
}

/* ---------- limits ---------- */
.limits {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}
.limits-copy { color: var(--muted); max-width: 50ch; }
.limits-list {
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 26px 28px;
}
.limits-head {
  font-family: var(--mono);
  font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--light-deep);
  margin: 0 0 16px;
}
.limits-list ul { display: flex; flex-direction: column; }
.limits-list li {
  padding: 11px 0 11px 22px;
  border-bottom: 1px solid var(--rule);
  font-size: 0.96rem;
  color: var(--graphite);
  position: relative;
}
.limits-list li::before {
  content: ""; position: absolute; left: 0; top: 1.08em;
  width: 11px; height: 1px; background: var(--muted);
}
.limits-list li:last-child { border-bottom: 0; }
.limits-foot {
  margin: 16px 0 0; font-size: 0.88rem; color: var(--muted); line-height: 1.55;
}

/* ---------- ledger (read / never touch) ---------- */
.ledger {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 4vw, 56px);
  padding-bottom: 44px;
  margin-bottom: 44px;
  border-bottom: 1px solid var(--ink-3);
}
.ledger-head {
  font-family: var(--mono);
  font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase;
  margin: 0 0 16px; padding-bottom: 12px;
  border-bottom: 1px solid var(--ink-3);
}
.ledger-head-yes { color: var(--light); }
.ledger-head-no  { color: #7C8592; }
.ledger-col li {
  padding: 10px 0;
  font-size: 0.97rem;
  color: #C4CBD4;
  line-height: 1.5;
}
.ledger-col:last-child li { color: #8B94A0; }

.notes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 3.5vw, 46px);
}
.note-solo { margin-top: 8px; max-width: 60ch; }
.note-key {
  font-family: var(--mono);
  font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--paper);
  margin: 0 0 10px;
}
.note p:not(.note-key) { font-size: 0.93rem; line-height: 1.6; color: #8B94A0; }

/* ---------- demo ---------- */
.demo-in { max-width: 860px; }
.demo-h2 { max-width: 22ch; }
.demo-url {
  color: var(--light-deep);
  text-decoration: none;
  border-bottom: 2px solid var(--light);   /* accent use #3 */
  white-space: nowrap;
  transition: background-color 160ms var(--ease);
}
.demo-url:hover { background: rgba(233, 161, 59, 0.16); }
.demo-sub {
  font-size: 1.06rem; color: var(--muted); max-width: 58ch; margin-bottom: 34px;
}
.tryit {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: 24px 0 26px;
  margin-bottom: 34px;
}
.tryit-head {
  font-family: var(--mono);
  font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 16px;
}
.tryit-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 9px 34px;
}
.tryit-list li { font-size: 0.99rem; color: var(--graphite); }
.tryit-list li:last-child { color: var(--muted); font-style: italic; }

/* ---------- steps ---------- */
.steps { display: grid; gap: 0; border-top: 1px solid var(--rule); }
.steps li {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  gap: 20px;
  padding: 26px 0 28px;
  border-bottom: 1px solid var(--rule);
}
.step-n {
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--muted);
  padding-top: 3px;
}
.steps h3 {
  font-family: var(--display);
  font-size: 1.14rem; font-weight: 700;
  letter-spacing: -0.004em;
  color: var(--graphite);
  margin: 0 0 8px;
  max-width: 46ch;
}
.steps p { color: var(--muted); font-size: 0.97rem; max-width: 62ch; }

/* ---------- two-up ---------- */
.two {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}
.two p { color: var(--muted); max-width: 48ch; font-size: 0.99rem; }

/* ==========================================================================
   FOOTER
   ========================================================================== */
.footer {
  background: var(--ink);
  color: var(--muted-ink);
  padding: clamp(60px, 8vw, 104px) 0 34px;
  background-image: radial-gradient(70ch 40ch at 92% 110%, rgba(233, 161, 59, 0.07), transparent 60%);
}
.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
  gap: clamp(32px, 5vw, 72px);
  padding-bottom: 52px;
  border-bottom: 1px solid var(--ink-3);
}
.footer-sub { color: var(--muted-ink); max-width: 46ch; margin-bottom: 26px; }
.mailto {
  font-family: var(--display);
  font-size: clamp(1.25rem, 2.6vw, 1.85rem);
  font-weight: 700;
  letter-spacing: -0.008em;
  color: var(--paper);
  text-decoration: none;
  border-bottom: 2px solid var(--ink-3);
  padding-bottom: 4px;
  display: inline-block;
  word-break: break-word;
  transition: border-color 160ms var(--ease);
}
.mailto:hover { border-color: var(--light); }
.footer-links { display: flex; flex-direction: column; align-items: flex-start; }
.footer-key {
  font-family: var(--mono);
  font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: #6E7784;
  margin: 0 0 12px;
}
.footer-key:not(:first-child) { margin-top: 26px; }
.footer-links a {
  text-decoration: none; color: var(--muted-ink);
  font-size: 0.95rem; padding: 5px 0;
  transition: color 160ms var(--ease);
}
.footer-links a:hover { color: var(--paper); }
.footer-links .ext { margin-left: 0.4em; font-size: 0.85em; opacity: 0.6; }
.footer-base {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  padding-top: 26px;
}
.wordmark-sm { font-size: 1rem; margin-right: 0; }
.footer-fine { font-size: 0.83rem; color: #626B77; }
.footer-fine-group { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

/* ==========================================================================
   MOTION
   --------------------------------------------------------------------------
   Everything below is gated behind `html.js-motion`, a class JS adds only
   after it has (a) loaded, (b) found GSAP, and (c) confirmed the visitor has
   not asked for reduced motion. Remove the class and the page above is the
   page you get: complete, legible, no hidden elements. The two rules in here
   that can leave an element invisible, `.turn` and `.rise`, are both inside
   `.js-motion` and both depend on a class JS adds.

   The system is four movements, and each one is the argument of its section
   rather than an effect applied to it:

     1. The transcript. The scrollbar stops being a way to reach the next
        screen and becomes the timeline of the 11:02 PM conversation. One
        message per threshold, revealed in place, and you arrive at BOOKED.
        Nothing translates: the panel holds every turn's final position from
        the first frame, so there is no layout shift and no reflow.
     2. Daybreak. The company is named for a light left on at night, so the
        page runs night to day on a single background surface whose colour is
        a function of total scroll position. Black at the top, white by the
        pricing section, no seam anywhere because there is no second
        background to hand off to.
     3. The week, drawn. 40 open hours land first; the 128 closed ones wash
        in after them, counted up as they fill. The arithmetic performs.
     4. Entrances. 14px and 400ms, once, never scrubbed. Deliberately dull,
        because attention spent here is attention not available for 1-3.
   ========================================================================== */

/* Lenis: it drives scroll itself, so the browser's own smooth behaviour and
   the auto page height have to get out of the way. */
/* Lenis owns anchor scrolling once motion is on, so the browser's native
   smooth behaviour has to be switched off, or the two interpolate the
   same jump at once and the page visibly fights itself. Lenis's own stylesheet
   only does this while a smooth scroll is in flight, which is too late. */
.js-motion { scroll-behavior: auto; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: clip; }

/* ---------- 2. the sunrise ---------- *
 * The company is called Roanlight; roan is a warm reddish-brown; a sunrise is
 * the name rendered as an animation. So the page's dark-to-light progression
 * is not a palette decision, it is the sun coming up, scrubbed against the
 * scrollbar. It also lands the product argument without a word of copy: the
 * hero is an 11:02 PM conversation, the assistant is the light that is on
 * while the practice is dark, and by the footer the practice has opened.
 *
 * ONE SURFACE. This is the whole structure. There is no "hero background" and
 * no separate "page background" that meet somewhere. There is a single fixed
 * sky behind the entire document, its colour is a continuous function of total
 * scroll progress, and every section that sits on it is transparent. The hero
 * is dark because it is at the top of the scroll, not because it is a dark
 * panel. By the pricing section the same surface is pure white and the content
 * is simply sitting on it. There is no seam to dissolve because there is no
 * second background.
 *
 * THE BACKGROUND CARRIES IT, NOT THE TEXT. An earlier version scrubbed the
 * headline from grey to amber at the same time, and that is the change the eye
 * tracked, so the whole thing registered as "the words light up" rather than
 * "the sky lights up." The headline accent is static now. Nothing in the
 * foreground competes.
 *
 * THE CONTRAST RULE, which the ramp is shaped around:
 *
 *   Light text only ever sits on genuinely dark sky. Dark text only ever sits
 *   on genuinely light sky. No body copy is on screen while the ramp is in the
 *   band where neither would clear 4.5:1.
 *
 * That band is narrow and known. Against #FBFAF7 light text needs the sky
 * under 0.17 relative luminance; against #1E242C dark text needs it over 0.27.
 * So the ramp is eased to cross 0.17 to 0.27 quickly, and it crosses it in the
 * scroll window between the hero copy finishing its exit and the gap section
 * revealing its first line. Before that window and after it the ramp is slow
 * and continuous the entire length of the page, which is what makes it read as
 * one dawn rather than as a switch. An earlier attempt crossfaded a fixed
 * layer straight from ink to paper at a constant rate and produced a frame
 * where the entire screen was featureless mid-grey with nothing legible on it.
 * This is what replaced it.
 */

/* --- the one sky --- */
.sky { display: none; }
.js-motion .sky {
  display: block;
  position: fixed; inset: 0; z-index: -2;
  overflow: hidden;
  pointer-events: none;
}
/* The ramp itself. JS writes backgroundColor from total scroll progress and
   nothing else touches it. Deep night at the top of the page, pure white by
   the pricing section, every value in between reached on the way up and on the
   way down alike, because it is a pure function of scroll position rather than
   a timeline with state. */
.js-motion .sky-base {
  position: absolute; inset: 0;
  background-color: #0B1220;
  will-change: background-color;
}

/* The light source, and the reason this reads as a sunrise instead of a fade.
   A soft radial whose centre sits exactly on the bottom edge of the viewport,
   scrubbed upward and outward across the page. Transform and opacity only, so
   it costs a composite and nothing else. It is faded back out as the sky
   approaches white, so the end of the ramp is white and not cream. */
.js-motion .sky-bloom {
  position: absolute;
  left: -30%; width: 160%;
  bottom: -58%; height: 116%;
  border-radius: 50%;
  transform-origin: 50% 50%;
  background: radial-gradient(closest-side circle at 50% 50%,
    rgba(255, 241, 214, 0.95) 0%,
    rgba(255, 206, 128, 0.66) 18%,
    rgba(243, 153, 73, 0.34) 36%,
    rgba(183, 99, 83, 0.12) 58%,
    rgba(183, 99, 83, 0.00) 76%);
  opacity: 0;
  will-change: opacity, transform;
}

/* The hero owns no background once motion is on. It is a transparent pane on
   the sky, which is the entire point of the restructure. */
.js-motion .hero {
  position: relative;
  background-color: transparent;
  background-image: none;
}
/* The lamp the company is named for, kept faint so it reads as a light left on
   in a window rather than as a second sun. */
.js-motion .hero::before {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: radial-gradient(78ch 44ch at 8% -12%, rgba(233, 161, 59, 0.09), transparent 62%);
}
.js-motion .hero-in { position: relative; z-index: 1; }

/* Everything else on the sky goes transparent too. The two sections that keep
   an opaque surface are the schedule band and the footer: those are indoors,
   and their copy is light on dark by design. */
.js-motion body,
.js-motion #gap,
.js-motion #does,
.js-motion #trained,
.js-motion #demo,
.js-motion #live { background-color: transparent; }

/* The masthead has to change sides with the sky under it, or it is a dark bar
   floating on a white page for the whole lower half. `is-day` is toggled by JS
   from what is actually under the bar. A class with a transition rather than a
   scrub, because re-blurring a backdrop-filter bar on every scroll frame is
   the most expensive thing this page could do. */
.masthead-nav a, .wordmark, .btn-quiet { transition: color 200ms var(--ease), border-color 200ms var(--ease), background-color 200ms var(--ease); }
.is-day .masthead { background: rgba(255, 255, 255, 0.82); }
.is-day .masthead.is-stuck { background: rgba(255, 255, 255, 0.94); border-bottom-color: var(--rule); }
.is-day .wordmark { color: var(--graphite); }
.is-day .masthead-nav a { color: var(--muted); }
.is-day .masthead-nav a:hover { color: var(--graphite); }
.is-day .btn-quiet { color: var(--graphite); border-color: var(--rule); }
.is-day .btn-quiet:hover { border-color: var(--muted); background: var(--paper-2); }

/* ---------- 1. the pinned hero ---------- */
/* Pinned, so it has to be exactly one screen and it has to clear the
   masthead. svh rather than vh: on iOS vh includes the collapsing toolbar,
   which makes a pinned panel jump the first time the toolbar hides. */
.js-motion .hero {
  min-height: 100svh;
  display: flex; align-items: center;
  padding-top: calc(66px + clamp(14px, 2.4vw, 40px));
  padding-bottom: clamp(22px, 3vw, 48px);
}
.js-motion .hero-in { width: 100%; align-items: center; }

/* A pinned hero has to fit in one screen or the visitor is trapped in a
   panel they cannot finish reading. These are the millimetres that buy it. */
.js-motion .hero-h1 { margin-bottom: 20px; }
.js-motion .hero-sub { margin-bottom: 24px; }
.js-motion .hero-actions { margin-bottom: 22px; }
.js-motion .hero-note { max-width: 56ch; padding-top: 14px; margin-bottom: 0; }

/* Short windows: the type steps down rather than the hero overflowing. The
   panel now reserves the full height of all six turns from the first frame,
   so this is load bearing rather than a nicety, and it starts at 900px of
   viewport height rather than 760px because that is where six reserved turns
   stop fitting. Below 900px wide the hero is not pinned, so this only applies
   where it is. */
@media (min-width: 900px) and (max-height: 920px) {
  .js-motion .hero { padding-top: calc(66px + clamp(10px, 1.6vw, 26px)); }
  .js-motion .hero-h1 { font-size: clamp(1.9rem, 2.95vw, 2.7rem); margin-bottom: 16px; }
  .js-motion .hero-sub { font-size: 1.02rem; margin-bottom: 20px; }
  .js-motion .hero-note { font-size: 0.82rem; }
  .js-motion .thread { padding: 12px 14px 3px; gap: 9px; }
  .js-motion .bubble { padding: 8px 12px; font-size: 0.845rem; line-height: 1.42; }
  .js-motion .stamp { margin-top: 3px; font-size: 0.65rem; }
  .js-motion .thread-mark { margin: 0 0 2px; font-size: 0.62rem; }
  .js-motion .scope-bar { padding: 10px 14px; }
  .js-motion .scope-foot { padding: 18px 14px; margin-top: 8px; font-size: 0.78rem; gap: 4px; }
  .js-motion .scope-foot-key { font-size: 1.1rem; }
}

/* THE TRANSCRIPT REVEAL.
 *
 * An earlier version made this a fixed-height window with the whole track
 * translated upward on every scroll frame, bottom-anchored, to mimic a real
 * chat panel. Every message moved on every frame, and it read as rough.
 *
 * Now nothing moves. All six turns are laid out in their final positions from
 * the start, at opacity 0, so the panel is full height before you scroll a
 * pixel and there is zero layout shift and zero reflow for the whole
 * sequence. Crossing a message's threshold adds `is-in` to that one message,
 * and a short CSS transition brings it up in place. Six opacity toggles for
 * the whole conversation.
 *
 * The 5px settle is not a slide. It is under a third of a line and it exists
 * so a message reads as having landed rather than blinked.
 *
 * These are the only rules in this file that can leave an element invisible,
 * and they are inside `.js-motion`, which JS adds only after it has confirmed
 * motion is wanted. Reduced motion and no-JS never see them.
 */
.js-motion .turn,
.js-motion [data-booked] {
  opacity: 0;
  transform: translateY(5px);
  transition: opacity 300ms var(--ease), transform 300ms var(--ease);
  will-change: opacity, transform;
}
.js-motion .turn.is-in,
.js-motion [data-booked].is-in {
  opacity: 1;
  transform: none;
}

/* The same mechanism, used for the gap section only. Every other entrance on
   the page fires once and stays, which is right, because they are far enough
   down that the sky under them is light in both directions. The gap section is
   the exception: it is the first dark-on-light copy on the page and it is on
   screen while the ramp is still crossing, so its entrance has to reverse when
   you scroll back up or it would sit as dark text on a dark sky on the way
   back to the top. Reversible, for the same reason the sky is. */
.js-motion .rise { opacity: 0; transform: translateY(16px); transition: opacity 460ms var(--ease), transform 460ms var(--ease); }
.js-motion .rise.is-in { opacity: 1; transform: none; }
/* The panel keeps its full height throughout, so it is worth tightening the
   transcript a little where it is pinned into a single screen. Type size is
   untouched below 900px, where the panel is not pinned. */
@media (min-width: 900px) {
  .js-motion .thread { padding: 15px 16px 4px; gap: 11px; }
  .js-motion .bubble { padding: 9px 13px; font-size: 0.885rem; line-height: 1.46; }
  .js-motion .stamp { margin-top: 4px; font-size: 0.68rem; }
  .js-motion .scope-foot { padding: 24px 18px; margin-top: 12px; }
}

/* The scroll affordance. Hidden unless the scrubbed hero is really running,
   because otherwise the sentence would be a lie. */
.scrollcue { display: none; }
.js-motion .scrollcue {
  display: flex; align-items: center; gap: 11px;
  margin: 18px 0 0;
  font-family: var(--mono);
  font-size: 0.68rem; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--muted-ink);
}
.scrollcue-rail {
  position: relative; display: block; flex: none;
  width: 34px; height: 1px; background: var(--ink-3);
}
.scrollcue-rail i {
  position: absolute; inset: 0;
  background: var(--muted-ink);
  transform-origin: 0 50%;
  animation: cue 2.6s var(--ease) infinite;
}
@keyframes cue {
  0%        { transform: scaleX(0);   opacity: 0; }
  30%       { transform: scaleX(1);   opacity: 1; }
  70%, 100% { transform: scaleX(1);   opacity: 0; }
}
/* Once you have scrolled, you know. Stop telling you. */
.js-motion.has-scrolled .scrollcue { opacity: 0; transition: opacity 300ms var(--ease); }

/* The clock is live during the pin, so it needs a stable width or the whole
   bar twitches every time the minute changes. */
.scope-clock { font-variant-numeric: tabular-nums; }

/* ---------- 3. the week ---------- */
.js-motion .week-hour { will-change: transform, opacity; }
.week-legend span[data-count-open],
.week-legend span[data-count-shut] { font-variant-numeric: tabular-nums; }

/* ---------- 4. the steps rail ---------- */
.steps { position: relative; }
.steps-rail { display: none; }
.js-motion .steps-rail {
  display: block;
  position: absolute; left: 0; top: 26px; bottom: 28px;
  width: 1px; background: var(--rule);
}
/* The rail takes the far left, so the step numbers move over to sit beside it
   rather than on top of it. */
.js-motion .step-n { padding-left: 15px; }
@media (max-width: 760px) {
  /* Not enough horizontal room for a rail and a number both. The number wins. */
  .js-motion .steps-rail { display: none; }
  .js-motion .step-n { padding-left: 0; }
}
.js-motion .steps-rail i {
  position: absolute; inset: 0;
  background: var(--light-deep);
  transform: scaleY(0); transform-origin: 50% 0;
}

/* ==========================================================================
   RESPONSIVE. Checked at 1440, 1024, 768, 375 and 320.
   ========================================================================== */
/* Below 900px wide the hero copy alone is taller than the viewport, and below
   800px tall the transcript panel with all five turns reserved does not fit
   under the masthead. Either way the hero goes back to ordinary flow and JS
   drops the pin, so the conversation reveals a message at a time as it comes
   up the screen instead. Same gesture, nothing clipped. */
@media (max-width: 899px), (max-height: 799px) {
  .js-motion .hero {
    min-height: 0; display: block;
    padding-top: clamp(56px, 8vw, 104px);
    padding-bottom: clamp(64px, 9vw, 116px);
  }
}

@media (max-width: 1000px) {
  .hero-in { grid-template-columns: minmax(0, 1fr); }
  .scope { max-width: 620px; }
  .gap-grid, .limits, .two { grid-template-columns: minmax(0, 1fr); }
  .notes { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .masthead-nav { display: none; }
}

@media (max-width: 760px) {
  .does { grid-template-columns: minmax(0, 1fr); }
  .does li,
  .does li:nth-child(odd),
  .does li:nth-child(even) {
    padding: 24px 0 26px;
    border-right: 0;
  }
  .ledger { grid-template-columns: minmax(0, 1fr); gap: 30px; }
  .tryit-list { grid-template-columns: minmax(0, 1fr); }
  .footer-top { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 560px) {
  :root { --gut: 18px; }
  body { font-size: 16px; }
  .masthead-in { height: 58px; gap: 12px; }
  .masthead-cta { padding: 9px 13px; font-size: 0.82rem; }
  .hero { padding-top: 40px; }
  .hero-h1 { font-size: clamp(2rem, 8.4vw, 2.6rem); letter-spacing: -0.008em; }
  .hero-actions { gap: 16px; }
  .hero-actions .btn { width: 100%; justify-content: center; }
  .h2 { max-width: none; }
  .turn { max-width: 94%; }
  .thread { padding: 16px 13px 4px; }
  .scope-bar { font-size: 0.63rem; padding: 11px 13px; }
  .steps li { grid-template-columns: 32px minmax(0, 1fr); gap: 12px; }
  .week-hour { height: 5px; }
  .limits-list { padding: 20px 18px; }
  .btn-big { width: 100%; justify-content: center; }
}

@media (max-width: 380px) {
  .week-days { gap: 3px; }
  .week-hour { height: 4px; }
  .scope-bar { flex-wrap: wrap; gap: 4px; }
}
