/* Celestro site. Tokens live in tokens.css.
   Order: base, shared pieces, navigation, then sections in page order, then motion. */

/* ---------- Base ---------- */

* { box-sizing: border-box; }
/* Scrollbar: a thin stone thumb on the page colour, in both themes. */
/* Scroll padding keeps anchors clear of the navigation, and focused controls clear of the bars at the bottom edge. */
html { scroll-padding-block: var(--nav-height) var(--space-9); scrollbar-width: thin; scrollbar-color: var(--rule) var(--bg); -webkit-tap-highlight-color: transparent; }
/* Older Safari has no scrollbar-color, so draw the same thing the WebKit way. */
@supports not (scrollbar-color: auto) {
  ::-webkit-scrollbar { width: 10px; height: 10px; background: var(--bg); }
  ::-webkit-scrollbar-thumb { border: 3px solid var(--bg); border-radius: 5px; background: var(--rule); }
}
a, button, label, input, textarea { touch-action: manipulation; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: var(--text-body)/1.6 var(--font-body);
  -webkit-font-smoothing: antialiased;
}
::selection { background: var(--ink); color: var(--bg); }
:focus-visible { outline: 2px solid var(--ember); outline-offset: 4px; }
h1, h2, h3 {
  margin: 0;
  font: 500 1em/1.08 var(--font-display);
  letter-spacing: -.005em;
  color: var(--ink);
  text-wrap: balance;
}
h1 { font-size: var(--text-h1); line-height: 1.02; }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
img, svg { display: block; }
[hidden] { display: none !important; }

.visually-hidden, .skip-link:not(:focus) {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.skip-link { position: fixed; top: var(--space-3); left: var(--space-3); z-index: 20; padding: var(--space-2) var(--space-4); background: var(--ink); color: var(--bg); }

/* ---------- Shared pieces ---------- */

.wrap { max-width: var(--page); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-top: var(--section); }

/* Light and dark cuts of the logo are separate brand files; show the one that fits. */
.logo-dark, [data-theme="dark"] .logo-light { display: none; }
[data-theme="dark"] .logo-dark { display: block; }

.link { position: relative; color: var(--ink); }
.link::after {
  content: "";
  position: absolute;
  inset: auto 0 -2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
}
.link:hover::after, .link:focus-visible::after, .link[aria-current]::after { transform: scaleX(1); }
.link-ruled { display: inline-block; padding-bottom: 2px; border-bottom: 1px solid var(--rule); font-weight: 500; }
.link-ruled::after { bottom: -1px; }

.button {
  display: inline-block;
  padding: 14px 26px;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  /* The page colour rises through the button on hover. */
  background: linear-gradient(var(--bg), var(--bg)) no-repeat bottom / 100% 0, var(--ink);
  color: var(--bg);
  font: 500 1rem/1.4 var(--font-body);
  text-align: center;
  cursor: pointer;
}
.button:hover { background-size: 100% 100%; color: var(--ink); }
.button:active { transform: translateY(1px); }
.button-small { padding: 9px 18px; }
/* While an enquiry is on its way the button keeps its focus, so it is marked as busy rather than disabled. */
.button[aria-disabled="true"] { background-size: 100% 0; color: var(--bg); opacity: .6; cursor: progress; }

/* Every section opens with a hairline and a heading; the paragraph sits beside it on wide screens. */
.section-head::before {
  content: "";
  display: block;
  height: 1px;
  margin-bottom: var(--space-6);
  background: var(--rule);
  transform-origin: left;
}
.section-head p { max-width: 31em; margin-top: var(--space-5); }
.section-head p + p { margin-top: var(--space-4); }
.split { display: grid; gap: var(--space-8); }
/* Narrative passages (the pause, About) sit on a slightly deeper paper than the informational ones. */
.narrative { background: var(--surface); }
/* On these rules an ember point rides the leading edge as the rule draws, then leaves. Motion only. */
.rule-point { position: relative; }
.rule-point::after {
  content: "";
  position: absolute;
  inset: -4px 0 auto;
  height: 9px;
  background: radial-gradient(circle 4.5px at calc(100% - 4.5px) 50%, var(--ember) 96%, transparent);
  opacity: 0;
  pointer-events: none;
}

/* Keyword accent: wrap a word or phrase in <span class="keyword-accent">. Ember passes contrast
   only as large text (24px and up), so use it in headings and the About statements, not body copy.
   With motion on, the word arrives in the heading's own colour and then settles into Ember, once. */
.keyword-accent { color: var(--ember); }

/* ---------- Navigation ---------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
  border-bottom: 1px solid transparent;
  font-size: 1rem;
}
.nav.is-scrolled { border-bottom-color: var(--rule-soft); }
.nav-inner { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-4); min-height: var(--nav-height); }
.nav-logo { margin-right: auto; padding-block: var(--space-3); }
.nav-logo img { width: 112px; height: auto; }
.nav-links { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-6); }
.nav-links .link { padding-block: var(--space-3); }
.nav-links .link::after { bottom: 10px; }

.theme-toggle {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--ink);
  cursor: pointer;
}
.theme-toggle:hover { background: var(--surface); }
/* The half-disc turns over and the star sets. */
[data-theme="dark"] .theme-toggle svg { transform: rotate(180deg); }

.menu-button {
  display: none;
  min-width: 3.5em;
  padding: var(--space-3) 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: 500 1rem/1.4 var(--font-body);
  text-align: right;
  cursor: pointer;
}

@media (width < 960px) {
  .js .menu-button { display: block; }
  .js .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    max-height: calc(100dvh - var(--nav-height));
    padding: var(--space-2) var(--gutter) var(--space-6);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--bg);
    border-bottom: 1px solid var(--rule);
  }
  .js .nav.is-open .nav-links { display: flex; }
  .js .nav-links .link { padding-block: var(--space-4); border-bottom: 1px solid var(--rule-soft); font: 500 1.75rem/1.2 var(--font-display); }
  .js .nav-links .link::after { display: none; }
  .js .nav-links .button { margin-top: var(--space-6); padding: 14px 26px; }
}

/* ---------- Hero ---------- */

.hero { display: flex; min-height: calc(100svh - var(--nav-height)); padding-block: clamp(40px, 8vh, 104px) var(--space-7); }
.hero .wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  /* A paper-coloured halo, as in print: the loop behind breaks cleanly around the type. */
  text-shadow: 5px 0 var(--bg), -5px 0 var(--bg), 0 5px var(--bg), 0 -5px var(--bg), 4px 4px var(--bg), -4px 4px var(--bg), 4px -4px var(--bg), -4px -4px var(--bg);
}
.hero .button { text-shadow: none; }
.hero h1 { max-width: 15.5em; }
.hero-sub { max-width: 34em; margin-top: var(--space-6); }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6); margin-top: var(--space-6); }

/* The trace: one enquiry moving through the system. Vertical on small screens, a timeline on wide ones. */
.trace { width: 100%; margin: auto 0 0; padding-top: clamp(56px, 9vh, 104px); }
.trace figcaption { max-width: 36em; margin-bottom: var(--space-5); font-size: var(--text-small); color: var(--muted); }
.trace li {
  /* The step's place in the sequence, which times its arrival when motion is on. */
  --i: 0;
  display: grid;
  grid-template-columns: 9px 1fr;
  column-gap: var(--space-4);
  align-content: start;
  padding-bottom: var(--space-5);
  font-size: 1rem;
  line-height: 1.45;
}
.trace li:last-child { padding-bottom: 0; }
.trace li:nth-child(2) { --i: 1; }
.trace li:nth-child(3) { --i: 2; }
.trace li:nth-child(4) { --i: 3; }
.trace li:nth-child(5) { --i: 4; }
.trace li:nth-child(6) { --i: 5; }
.trace li > * { grid-column: 2; }
.trace li::before {
  content: "";
  grid-area: 1 / 1;
  width: 9px;
  height: 9px;
  margin-top: .45em;
  border-radius: 50%;
  background: var(--ink);
}
.trace li:last-child::before { background: var(--ember); }
.trace li::after {
  content: "";
  grid-area: 1 / 1 / span 3;
  justify-self: center;
  width: 1px;
  margin-block: calc(.45em + 15px) calc(-1 * var(--space-5) - .45em + 6px);
  background: var(--rule);
  transform-origin: top;
}
.trace li:last-child::after { display: none; }
.trace-time { font-weight: 500; font-variant-numeric: tabular-nums; color: var(--ink); }
.trace-service { font-size: var(--text-small); color: var(--muted); }

@media (min-width: 960px) {
  .trace-steps { display: grid; grid-template-columns: repeat(5, 1fr); }
  /* The home page's six stages share the row; a shorter timeline keeps the five-column measure. */
  .trace-steps:has(li:nth-child(6)) { grid-template-columns: repeat(6, 1fr); }
  .trace li { grid-template: auto 9px auto auto / 1fr; row-gap: var(--space-3); padding: 0 var(--space-6) 0 0; }
  .trace li > * { grid-column: 1; }
  .trace li::before { grid-area: 2 / 1; margin-top: 0; }
  .trace li::after {
    grid-area: 2 / 1;
    justify-self: stretch;
    align-self: center;
    width: auto;
    height: 1px;
    margin: 0 calc(6px - var(--space-6)) 0 15px;
    transform-origin: left;
  }
  .trace-service { margin-top: calc(-1 * var(--space-2)); }
}

/* ---------- Services ---------- */

.tier { margin-top: var(--space-8); }
.tier-label { padding-bottom: var(--space-4); font-size: 1rem; color: var(--muted); }
.tier-label strong { font-weight: 500; color: var(--ink); }
/* The one strong line in the section: what customers see sits above it, what runs the business below. */
.tier-behind { position: relative; padding-top: var(--space-5); border-top: 1px solid var(--ink); }
.tier-behind::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ember);
}
.service { display: grid; gap: var(--space-4); padding-block: var(--space-6); border-top: 1px solid var(--rule-soft); }
.service h3 { font-size: clamp(2rem, 3.4vw, 3rem); }
.service-lead { max-width: 22em; font: 500 var(--text-lead)/1.3 var(--font-display); color: var(--ink); }
.service-detail li + li { margin-top: var(--space-1); }
.service-detail .link { margin-top: var(--space-5); }

/* ---------- Outcomes (the band) ---------- */

.band {
  --bg: var(--band-bg);
  --ink: var(--band-ink);
  --text: var(--band-text);
  --muted: var(--band-muted);
  --rule: var(--band-rule);
  --rule-soft: var(--band-rule-soft);
  margin-top: var(--section);
  padding-bottom: var(--section);
  background: var(--bg);
  color: var(--text);
}
.band h2 { max-width: 11em; }
.changes { margin-top: var(--space-8); }
.changes-head { display: none; }
.changes li { display: grid; gap: var(--space-2); padding-block: var(--space-5); border-top: 1px solid var(--rule-soft); }
.before { color: var(--muted); }
.after { max-width: 26em; font: 500 var(--text-lead)/1.3 var(--font-display); color: var(--ink); }
.changes-note { max-width: 36em; padding-top: var(--space-5); border-top: 1px solid var(--rule-soft); font-size: var(--text-small); color: var(--muted); }

/* ---------- Approach ---------- */

.steps-wrap {
  --row-gap: var(--space-7);
  --numeral: 2.75rem;
  --loop: 22px;
  /* Rows are equal height, so the fifth numeral sits four fifths of the way down. */
  --travel: calc((100% + var(--row-gap)) * 4 / 5);
  position: relative;
}
/* The line ends in the logo's loop: the last stage leads back to the first. */
.steps-wrap::after {
  content: "";
  position: absolute;
  left: calc(.5px - var(--loop) / 2);
  top: calc(var(--numeral) / 2 + var(--travel));
  width: var(--loop);
  height: var(--loop);
  border: 1px solid var(--rule);
  border-radius: 50%;
}
.steps {
  position: relative;
  display: grid;
  grid-auto-rows: 1fr;
  row-gap: var(--row-gap);
  padding-left: var(--space-6);
  counter-reset: step;
}
.steps::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--numeral) / 2);
  width: 1px;
  height: var(--travel);
  background: var(--rule);
}
.steps::after {
  content: "";
  position: absolute;
  z-index: 1;
  left: -4px;
  top: calc(var(--numeral) / 2 + var(--travel) - 4px);
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ember);
}
.steps li { display: grid; grid-template-columns: calc(var(--numeral) * 1.1) 1fr; align-items: baseline; counter-increment: step; }
.steps li::before { content: counter(step); font: 500 var(--numeral)/1 var(--font-display); font-variant-numeric: lining-nums; color: var(--ink); }
.steps h3 { line-height: 1; }
.steps p { max-width: 30em; margin-top: var(--space-3); }
.steps .step-gives { margin-top: var(--space-2); font-size: 1rem; color: var(--muted); }

/* ---------- Work ---------- */

.project { display: grid; gap: var(--space-7); margin-top: var(--space-8); }
.project-figure { position: relative; margin: 0; }
/* The screenshots are of the client's live site, shown as they are in both themes. */
/* A narrow mat around the capture, like a mounted print. */
.cover { overflow: hidden; padding: clamp(6px, .9vw, 12px); border: 1px solid var(--rule-soft); background: var(--surface); }
.cover img, .cover-phone { width: 100%; height: auto; }
.cover-phone { display: none; }
.project-figure figcaption { margin-top: var(--space-3); font-size: var(--text-small); color: var(--muted); }
.project h3 { font-size: var(--text-h2); }
.project-lead { max-width: 22em; font: 500 var(--text-lead)/1.3 var(--font-display); color: var(--ink); }
.project-facts { margin: var(--space-6) 0; }
.project-facts div { padding-block: var(--space-4); border-top: 1px solid var(--rule-soft); }
.project-facts dt { font-size: var(--text-small); color: var(--muted); }
.project-facts dd { margin: var(--space-1) 0 0; }

/* ---------- The pause ---------- */

.pause { margin-top: var(--section); padding-block: var(--section) 0; overflow-x: clip; }
.pause p { display: flex; flex-direction: column; gap: var(--space-5); font: 500 clamp(1.75rem, 3.4vw, 3rem)/1.15 var(--font-display); color: var(--ink); }
/* The line runs from where the sentence ends to the edge of the page; the point sits at its start. */
.pause-line { position: relative; height: 1px; margin-right: calc((100% - 100vw) / 2); }
.pause-line::before { content: ""; position: absolute; inset: 0; background: var(--rule); transform-origin: left; }
.pause-line::after { content: ""; position: absolute; left: -4.5px; top: -4px; width: 9px; height: 9px; border-radius: 50%; background: var(--ember); }

/* ---------- About ---------- */

#about { padding-bottom: var(--section); }

.beliefs li { padding-block: var(--space-6); border-bottom: 1px solid var(--rule-soft); font: 500 clamp(1.625rem, 2.8vw, 2.5rem)/1.2 var(--font-display); color: var(--ink); text-wrap: balance; }
.beliefs li:first-child { padding-top: 0; }
.beliefs p { max-width: 31em; margin-top: var(--space-6); }

/* ---------- Contact ---------- */

.contact-form { display: grid; gap: var(--space-6); max-width: 36rem; }
.field { min-width: 0; margin: 0; padding: 0; border: 0; }
.field > label, .field legend { display: block; padding: 0; font-size: 1rem; font-weight: 500; color: var(--ink); }
.field > label span { font-weight: 400; color: var(--muted); }
.field input:not([type="checkbox"]), .field textarea {
  display: block;
  width: 100%;
  padding: var(--space-2) 0 var(--space-3);
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  background: none;
  color: var(--ink);
  caret-color: var(--ember);
  font: inherit;
  resize: vertical;
}
.field input:not([type="checkbox"]):focus-visible, .field textarea:focus-visible { outline: 0; border-bottom-color: var(--ink); box-shadow: 0 1px 0 var(--ink); }
.choices { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-6); margin-top: var(--space-3); }
.choices label { display: flex; align-items: center; gap: var(--space-3); padding-block: var(--space-2); cursor: pointer; }
.choices input { width: 18px; height: 18px; margin: 0; accent-color: var(--ink); }
.form-send { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-5); }
.form-send p, .form-note { font-size: var(--text-small); color: var(--muted); }
/* Room is kept for the spam check, so the button does not jump when it arrives. */
.cf-turnstile { min-height: 65px; }
/* What went wrong, in words: beside the button for the whole form, under a field for that field. */
.form-send .form-status, .field-error { font-weight: 500; color: var(--ink); }
.field-error { margin-top: var(--space-2); font-size: var(--text-small); }
.field [aria-invalid] { border-bottom-color: var(--ink); }

/* ---------- Footer ---------- */

.footer { margin-top: var(--section); padding-block: var(--space-9) var(--space-7); border-top: 1px solid var(--rule-soft); font-size: 1rem; line-height: 1.5; }
.footer-inner { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-7) var(--space-6); }
.footer-brand, .footer-legal { grid-column: 1 / -1; }
.footer-brand img { width: clamp(200px, 30vw, 360px); height: auto; }
.footer-brand p { max-width: 15em; margin-top: var(--space-6); font: 500 1.5rem/1.3 var(--font-display); color: var(--ink); }
.footer h2 { margin-bottom: var(--space-3); font: 500 1rem/1.5 var(--font-body); letter-spacing: 0; }
.footer li { padding-block: var(--space-1); }
.footer-legal { font-size: var(--text-small); color: var(--muted); }

/* ---------- Text pages (privacy, terms) ---------- */

.legal { padding-top: clamp(40px, 8vh, 104px); }
.legal-head p { max-width: 34em; margin-top: var(--space-5); }
.legal-head .legal-date { font-size: var(--text-small); color: var(--muted); }
/* Each part opens with a hairline, as the home page's sections do; on wide screens its heading sits beside the text. */
.legal section { display: grid; gap: var(--space-5); margin-top: var(--space-8); padding-top: var(--space-6); border-top: 1px solid var(--rule-soft); }
.legal h2 { font-size: var(--text-h3); }
.legal section :is(p, ul, dl) { max-width: 38em; }
.legal section :is(p, ul, dl) + :is(p, ul, dl) { margin-top: var(--space-4); }
.legal ul { padding-left: 1.2em; list-style: disc; }
.legal li + li { margin-top: var(--space-2); }
.legal dl { margin-bottom: 0; }
/* A run-in label, as on the service tiers. */
.legal strong { font-weight: 500; color: var(--ink); }

/* ---------- At the bottom edge ----------
   The privacy notice, and on small screens a bar that keeps "Start a conversation" in reach.
   Never both at once: the notice is there while the page's opening heading is on screen; the bar
   appears further down, between the hero's own button and the contact section (see main.js), and not
   on a phone held sideways, where there is no height to spare. */

.notice, .cta-bar {
  position: fixed;
  inset: auto 0 0;
  z-index: 9;
  padding-block: var(--space-3) calc(var(--space-3) + env(safe-area-inset-bottom));
  background: var(--bg);
  border-top: 1px solid var(--rule-soft);
}
.notice-inner { display: flex; align-items: center; gap: var(--space-4); font-size: var(--text-small); line-height: 1.5; }
.notice p { flex: 1; }
.nav.is-scrolled ~ .notice { visibility: hidden; transform: translateY(100%); }
.cta-bar { display: none; padding-inline: var(--gutter); }
@media (width < 960px) and (height >= 500px) {
  .js .cta-bar { display: block; visibility: hidden; transform: translateY(100%); }
  .js .cta-bar.is-shown { visibility: visible; transform: none; }
  /* The open menu already holds the same button. */
  .js .nav.is-open ~ .cta-bar { display: none; }
  .cta-bar .button { display: block; }
}

/* ---------- Wider screens ---------- */

@media (min-width: 760px) {
  .nav-logo img { width: 132px; }
  .steps-wrap { --row-gap: var(--space-8); --numeral: 3.5rem; --loop: 28px; }
  .steps { padding-left: var(--space-7); }
  /* The phone capture overlaps the lower right corner of the desktop one. */
  .cover-phone { display: block; position: absolute; right: var(--space-6); bottom: calc(-1 * var(--space-5)); width: 19%; border: 1px solid var(--rule); }
  .project-figure figcaption { padding-right: 30%; }
  .pause p { flex-direction: row; align-items: center; gap: var(--space-6); }
  .pause-line { flex: 1; }
  .footer-inner { grid-template-columns: 5fr repeat(3, 2fr); }
  .footer-brand { grid-column: auto; }
}
@media (min-width: 960px) {
  .wrap:not(.split) > .section-head { display: grid; grid-template-columns: 7fr 5fr; column-gap: var(--space-8); align-items: end; }
  .wrap:not(.split) > .section-head::before { grid-column: 1 / -1; }
  .wrap:not(.split) > .section-head p { margin-top: 0; padding-bottom: .35em; }
  .split { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: start; }
  .legal section { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--space-8); }
  .project { grid-template-columns: 8fr 4fr; gap: var(--space-7) var(--space-8); align-items: start; }
  .project > h3 { grid-column: 1 / -1; font-size: var(--text-h1); line-height: 1.02; }
  /* The capture stays in view while the facts beside it are read. */
  .project-figure { position: sticky; top: calc(var(--nav-height) + var(--space-6)); }
  .split > :last-child { margin-top: calc(var(--space-6) + 1px); }
  .split .section-head { position: sticky; top: calc(var(--nav-height) + var(--space-6)); }
  /* About is not pinned. Its heading runs on one line across both columns and the statements start below it. */
  #about .section-head { position: relative; top: 0; }
  #about h2 { width: max-content; max-width: calc(min(100vw, var(--page)) - 2 * var(--gutter)); }
  #about .split > :last-child { margin-top: calc(var(--space-6) + var(--text-h2) * 1.08 + var(--space-7)); }
  .changes-head { display: grid; padding-bottom: var(--space-3); font-size: var(--text-small); color: var(--muted); }
  .changes-head, .changes li { grid-template-columns: 4fr 8fr; column-gap: var(--space-7); align-items: baseline; }
}

/* ---------- Orbit: the logo's loop, opening the page and closing it ---------- */

.hero, .contact { position: relative; overflow: hidden; overflow: clip; }
.hero .wrap, .contact .wrap { position: relative; z-index: 0; }
/* Zero-height marker just under the hero buttons; the loop is placed from it and sits behind the text. */
.orbit-anchor { position: relative; width: 100%; height: 0; z-index: -1; }
.orbit {
  position: absolute;
  left: calc(var(--cx) - var(--d) / 2);
  top: calc(var(--cy) - var(--d) / 2);
  width: var(--d);
  height: var(--d);
  border: 1px solid var(--rule-soft);
  border-radius: 50%;
  pointer-events: none;
}
/* The star is placed from the centre: turn to the angle, then step out to the rim. */
.orbit-star {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px;
  border-radius: 50%;
  background: var(--ember);
  transform: rotate(var(--angle)) translateY(calc(var(--d) / -2));
}
/* Hero: the star starts part-way round and climbs to the top of the loop as the page is scrolled. */
.hero .orbit { --d: 280px; --cx: calc(100% + 50px); --cy: -40px; --angle: calc(-90deg + var(--p, 0) * 90deg); }
/* Contact: a wide horizon over the last section. The star travels along it and stops at the top,
   where it sits in the logo. */
.contact .orbit {
  --d: 250vw;
  --cx: 50%;
  --cy: calc(var(--section) * .25 + var(--d) / 2);
  --angle: calc((1 - clamp(0, (var(--p, 1) - .08) / .3, 1)) * -20deg);
}
.contact h2 { font-size: var(--text-h1); line-height: 1.02; }

/* ---------- Services diagram (wide screens only) ---------- */

.system { display: none; }
.system-line, .system-ring { fill: none; stroke: var(--rule); vector-effect: non-scaling-stroke; }
.system-node { fill: var(--bg); stroke: var(--rule); vector-effect: non-scaling-stroke; }
.system-star { fill: var(--ember); transform: rotate(calc(var(--active, 0) * 90deg)); }
.system text { fill: var(--muted); font: 12px var(--font-body); }
.system text.is-active { fill: var(--ink); font-weight: 500; }
.system .system-tier { font-size: 10px; }

@media (min-width: 960px) {
  .hero .orbit { --d: min(80svh, 780px); --cx: 92%; --cy: -130px; --angle: calc(-100deg + var(--p, 0) * 100deg); }

  .services-aside { position: sticky; top: calc(var(--nav-height) + var(--space-6)); }
  .services-aside .section-head { position: static; }
  .system { display: block; width: min(100%, 62svh); margin-top: var(--space-6); }
  .services-list .tier:first-child { margin-top: 0; }
  .services-list .service { grid-template-columns: 1fr 1fr; gap: var(--space-4) var(--space-7); padding-block: var(--space-7); }
  .services-list .service h3 { grid-column: 1 / -1; font-size: var(--text-h2); }
  /* The diagram carries the line between front and behind here. */
  .services-list .tier-behind { border-top-color: var(--rule-soft); }
  .services-list .tier-behind::before { display: none; }
}

/* ---------- Motion ----------
   One system: slow, eased-out arrivals; words as the unit of typography; the star as the
   only thing that travels. Scroll-linked values come from --p, set in main.js.
   Everything here is off when the visitor asks for reduced motion ("motion" is never added). */

.w { display: inline-block; vertical-align: top; overflow: hidden; padding-block: .06em .18em; margin-block: -.06em -.18em; }
.w > span { display: inline-block; }
/* Headings wait for the script to split them, but never longer than three seconds. */
.motion :is(h1, main h2, .project h3, .beliefs li):not(.is-split) { visibility: hidden; animation: show-anyway 0s 3s forwards; }
@keyframes show-anyway { to { visibility: visible; } }

@media screen and (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
  .link::after { transition: transform var(--t-base) var(--ease-out); }
  .button { transition: background-size var(--t-base) var(--ease-out), color var(--t-quick) .08s, transform var(--t-quick); }
  .nav { transition: border-color var(--t-base); }
  .theme-toggle svg { transition: transform var(--t-slow) var(--ease-out); }
  .field input, .field textarea { transition: border-color var(--t-quick), box-shadow var(--t-quick); }
  /* The notice and the small-screen bar slide away below the edge, and only count as hidden once they have left. */
  .notice, .cta-bar { transition: transform var(--t-base) var(--ease-out), visibility 0s var(--t-base); }
  .nav:not(.is-scrolled) ~ .notice, .cta-bar.is-shown { transition-delay: 0s; }
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: .5s; }

  /* Headings arrive word by word from behind their own baseline. */
  .motion :is(h1, h2, .project h3) .w > span { transform: translateY(118%); transition: transform var(--t-slow) var(--ease-out) calc(var(--i) * 45ms); }
  .motion .is-in :is(h1, h2, h3) .w > span, .motion h2.is-in .w > span { transform: none; }
  .motion .w > .keyword-accent {
    color: inherit;
    transition: transform var(--t-slow) var(--ease-out) calc(var(--i) * 45ms), color .9s ease-out calc(var(--i) * 45ms + .65s);
  }
  .motion .is-in .w > .keyword-accent { color: var(--ember); }
  .motion .section-head::before { transform: scaleX(0); transition: transform 1.6s var(--ease-out); }
  .motion .section-head.is-in::before { transform: none; }
  /* Same duration and curve as the rule, so the point stays on its leading edge. */
  .motion .rule-point.is-in::after { animation: ride 2.4s linear both; }
  @keyframes ride {
    from { opacity: 1; transform: translateX(-100%); animation-timing-function: var(--ease-out); }
    66.7% { opacity: 1; transform: none; }
    to { opacity: 0; transform: none; }
  }
  .motion .pause-line::before { transform: scaleX(0); transition: transform 1.6s var(--ease-out) .3s; }
  .motion .pause-line::after { scale: 0; transition: scale .9s var(--ease-out); }
  .motion .pause.is-in .pause-line::before { transform: none; }
  .motion .pause.is-in .pause-line::after { scale: 1; }

  /* Opening scene: the loop appears, the headline rises, the star arrives, then a point of light walks the trace. */
  .hero .orbit { animation: appear 2.4s ease-out both; }
  .hero .orbit-star { animation: arrive .9s var(--ease-out) 1.5s both; }
  .hero-sub { animation: appear var(--t-slow) ease-out .7s both; }
  .hero-actions { animation: appear var(--t-slow) ease-out .9s both; }
  .trace figcaption { animation: appear var(--t-slow) ease-out 1.4s both; }
  .trace li { --at: calc(1.8s + var(--i) * .5s); }
  .trace li > * { animation: appear .7s ease-out var(--at) both; }
  .trace li::before { animation: ignite .8s var(--ease-out) var(--at) both; }
  .trace li::after { animation: draw-down .5s linear calc(var(--at) + .1s) both; }
  @keyframes arrive { from { scale: 0; } }
  @keyframes appear { from { opacity: 0; } }
  @keyframes ignite { from { transform: scale(0); background: var(--ember); } 45% { transform: scale(1.6); background: var(--ember); } }
  @keyframes draw-down { from { transform: scaleY(0); } }
  @keyframes draw-across { from { transform: scaleX(0); } }
  /* Leaving the hero: the headline lifts away a little faster than the rest. */
  .motion .hero h1 { transform: translateY(calc(var(--p, 0) * -56px)); }

  /* Services: the star moves to the service being read. */
  .system-star { transition: transform 1.2s var(--ease-in-out); }
  .system text { transition: fill var(--t-base); }

  /* Outcomes: each "after" slides in beside its "before". On wide screens the band also opens to full width. */
  .band { overflow-x: clip; }
  .motion .changes .after { transition: opacity var(--t-slow) ease-out .15s, transform var(--t-slow) var(--ease-out) .15s; }
  .motion .changes li:not(.is-in) .after { opacity: 0; transform: translateX(28px); }

  /* Approach: the star keeps pace with the reader and each stage wakes as it is reached. */
  .motion .steps::after { top: calc(var(--numeral) / 2 - 4px + min(var(--p, 1) * 100%, var(--travel))); }
  .motion .steps li::before { transform-origin: left 70%; transition: color var(--t-base), transform var(--t-slow) var(--ease-out); }
  .motion .steps h3 { transition: color var(--t-base); }
  .motion .steps li:not(.is-in)::before, .motion .steps li:not(.is-in) h3 { color: var(--rule); }
  /* The stage being read has the authority: its numeral steps forward, and the ones already passed go quiet. */
  .motion .steps li.is-current::before { transform: scale(1.16); }
  .motion .steps:has(.is-current) li.is-in:not(.is-current)::before, .motion .steps:has(.is-current) li.is-in:not(.is-current) h3 { color: var(--muted); }
  .motion .steps p { transition: opacity var(--t-slow) ease-out, transform var(--t-slow) var(--ease-out); }
  .motion .steps li:not(.is-in) p { opacity: 0; transform: translateY(12px); }

  /* Work: the capture is unmasked from the top as it comes into view; the phone follows a beat later. */
  .motion :is(.cover, .cover-phone) { transition: clip-path 1.3s var(--ease-out); }
  .motion .cover-phone { transition-delay: .35s; }
  .motion .project:not(.is-in) :is(.cover, .cover-phone) { clip-path: inset(0 0 100% 0); }

  /* About: slower. The statements ink in word by word at reading pace. */
  .motion .beliefs li { --ink-in: clamp(0, (var(--p, 1) - .1) / .28, 1); }
  .motion .beliefs .w > span { opacity: clamp(.5, var(--ink-in) * (var(--n) + 3) - var(--i), 1); }
}

@media screen and (prefers-reduced-motion: no-preference) and (min-width: 960px) {
  .motion .band { --open: clamp(0, var(--p, 1) / .2, 1); clip-path: inset(0 calc((1 - var(--open)) * var(--gutter))); }
  .trace li::after { animation-name: draw-across; }
  /* The service being read is at full strength; the others recede to the muted colour, which still passes contrast. */
  .motion .service { transition: border-color var(--t-base); }
  .motion .service :is(h3, .service-lead, li) { transition: color var(--t-base); }
  .motion .has-active .service:not(.is-active, :focus-within) :is(h3, .service-lead, li) { color: var(--muted); }
  /* The rule above the service being read darkens, as the star reaches it on the diagram. */
  .motion .has-active .service.is-active { border-top-color: var(--ink); }
}

@media (forced-colors: active) {
  .nav-logo img, .footer-brand img { background: #FAF8F5; forced-color-adjust: none; }
  /* The light cut sits on its own paper ground, so it is the one that stays legible. */
  .logo-light { display: block !important; }
  .logo-dark { display: none !important; }
  .field input:focus-visible, .field textarea:focus-visible { outline: 2px solid; }
  .trace li::before, .tier-behind::before, .steps::after, .orbit-star, .system-star { forced-color-adjust: none; }
}
