/* ==========================================================================
   bezier.studio
   One stylesheet. No framework, no build step, no preprocessor.

   Contents
     1.  Fonts
     2.  Tokens
     3.  Reset and base
     4.  Type scale
     5.  Layout
     6.  Header
     7.  Hero and the curve
     8.  Section furniture
     9.  Buttons and links
     10. Work
     11. Standards, process, pricing
     12. Accordion
     13. Forms
     14. Footer
     15. Motion and reduced motion
   ========================================================================== */

/* -------------------------------------------------------------- 1. Fonts -- */
/* Self hosted, latin subset, 68KB for all three files. Self hosted rather than
   pulled from a font service, so no third party is handed a visitor's IP
   address merely to render text. The one third party on the site is Plausible,
   which is cookieless. */
@font-face {
  font-family: 'Space Grotesk';
  src: url('../fonts/space-grotesk.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Space Mono';
  src: url('../fonts/space-mono-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Space Mono';
  src: url('../fonts/space-mono-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ------------------------------------------------------------- 2. Tokens -- */
:root {
  /* Ink and bone. Bone is deliberately NOT pure white: #FFF on a dark scheme
     glares, and in print it reads cheap. */
  --ink:        #15171C;
  --ink-raised: #1B1E25;
  --ink-line:   #262A33;
  --bone:       #F4F1EA;
  --bone-line:  #DEDACF;

  /* Every one of these is COMPUTED from the hex values below, not estimated.
     An earlier version of this comment claimed 5.4:1 for muted-ink; the real
     figure for the colour it described was 4.53:1, scraping past AA by 0.03.
     Documenting an unverified number is worse than documenting none, because
     nobody rechecks a figure that is already written down.
       bone on ink ............ 15.90:1
       muted on ink ............  7.07:1
       accent on ink ...........  6.69:1
       accent-2 on ink ......... 10.22:1
       ink on bone ............ 15.90:1
       accent-ink on bone ......  5.77:1
       muted-ink on bone .......  5.66:1
     There are TWO accents on purpose. A blue bright enough to read on ink is
     far too pale on bone, and the reverse. Do not merge them. */
  --muted:      #9AA3B5;
  /* Lifted from #5B84FF after measuring the real rendered pixels. On flat ink
     the old value was a comfortable 5.28:1, but over the brightest point of the
     cyan bloom it fell to 4.54:1: still passing AA, with almost no margin left.
     This measures 5.75:1 there and 6.69:1 on flat ink. */
  --accent:     #769AFF;
  --accent-ink: #1B4DE4;
  --muted-ink:  #5C5F67;

  /* Second tone. It is NOT a decorative gradient partner: it means one
     specific thing, "control handle", the way a vector editor distinguishes a
     handle from an anchor point. Cyan appears on grips and at the far end of
     the hero curve, nowhere else. 10.2:1 on ink, so it has room to spare. */
  --accent-2:   #3FD8D3;

  --focus:      #FFD24A;

  /* Fluid scale, ratio about 1.22, no font-size media query anywhere. */
  --t-xs:   clamp(0.72rem, 0.70rem + 0.10vw, 0.78rem);
  --t-sm:   clamp(0.84rem, 0.81rem + 0.14vw, 0.94rem);
  --t-base: clamp(1.00rem, 0.96rem + 0.20vw, 1.14rem);
  --t-md:   clamp(1.16rem, 1.08rem + 0.38vw, 1.42rem);
  --t-lg:   clamp(1.42rem, 1.26rem + 0.76vw, 2.02rem);
  --t-xl:   clamp(1.86rem, 1.52rem + 1.62vw, 3.00rem);
  --t-2xl:  clamp(2.32rem, 1.70rem + 2.96vw, 4.40rem);
  --t-3xl:  clamp(2.90rem, 1.80rem + 5.20vw, 6.60rem);

  --gutter:  clamp(1.25rem, 0.6rem + 3vw, 4rem);
  --measure: 68ch;
  --wide:    1280px;
  --radius:  3px;

  --dur:  520ms;
  --dur-s: 260ms;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ----------------------------------------------------- 3. Reset and base -- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Two tone ground. Two very large, very soft blooms: brand blue at the top
   right where the hero curve climbs, cyan at the bottom left. Fixed rather
   than scrolling, so it behaves like a light source on the page rather than a
   texture moving past.

   Kept deliberately weak. The cyan bloom sits exactly where the body copy is,
   and it is the only thing here that costs contrast: muted body text over it
   measures 6.0:1 against 7.1:1 on flat ink. Still well clear of AA's 4.5, but
   that is the number that moves if anyone strengthens these, so do not.

   It is on a fixed pseudo element behind everything, so light sections simply
   paint over it and need no exception. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(1100px 620px at 84% 2%,  rgba(91, 132, 255, 0.17), transparent 60%),
    radial-gradient(900px  520px at 6%  98%, rgba(63, 216, 211, 0.10), transparent 62%);
}

body {
  background: var(--ink);
  color: var(--bone);
  font-family: 'Space Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: var(--t-base);
  font-weight: 400;
  line-height: 1.62;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; height: auto; }
ul, ol { padding: 0; list-style: none; }

::selection { background: var(--accent); color: #fff; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip {
  position: absolute; left: 50%; top: 0; transform: translate(-50%, -140%);
  background: var(--bone); color: var(--ink);
  padding: 0.7rem 1.2rem; z-index: 999; font-weight: 500;
  transition: transform var(--dur-s) var(--ease);
}
.skip:focus { transform: translate(-50%, 0); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* -------------------------------------------------------- 4. Type scale -- */
h1, h2, h3, h4 {
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.035em;
  text-wrap: balance;
}
h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-lg); line-height: 1.16; letter-spacing: -0.028em; }
h4 { font-size: var(--t-md); line-height: 1.24; letter-spacing: -0.02em; }

p, li { max-width: var(--measure); }
p + p { margin-top: 1.05em; }

.lede {
  font-size: var(--t-md);
  line-height: 1.5;
  color: var(--bone);
  letter-spacing: -0.014em;
}
.muted { color: var(--muted); }
.section--light .muted { color: var(--muted-ink); }

/* The mono face carries every label, number and piece of metadata. Keeping
   one voice for "machine" text and another for "human" text is most of what
   makes the page feel engineered rather than decorated. */
.mono {
  font-family: 'Space Mono', ui-monospace, 'Cascadia Mono', Menlo, monospace;
  font-size: var(--t-xs);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  font-weight: 400;
}
.tnum { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- 5. Layout -- */
.wrap {
  width: min(100% - (var(--gutter) * 2), var(--wide));
  margin-inline: auto;
}
.section { padding-block: clamp(4rem, 2.4rem + 7vw, 9.5rem); }
.section--tight { padding-block: clamp(3rem, 2rem + 4vw, 5.5rem); }

.section--light {
  background: var(--bone);
  color: var(--ink);
}
.section--raised { background: var(--ink-raised); }

.rule { height: 1px; background: var(--ink-line); border: 0; }
.section--light .rule { background: var(--bone-line); }

.grid { display: grid; gap: clamp(1.5rem, 1rem + 2vw, 3rem); }
.cols-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.cols-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }

/* Editorial split: a narrow label column and a wide content column. Collapses
   to one column below 62rem. */
.split { display: grid; gap: clamp(1.5rem, 1rem + 3vw, 4rem); }
@media (min-width: 62rem) {
  .split { grid-template-columns: minmax(15rem, 25rem) 1fr; }
  .split--wide { grid-template-columns: minmax(14rem, 22rem) 1fr; }
}
/* A heading in the narrow label column must NOT use the full display size.
   At --t-2xl it sets at about 70px inside a 320px column, which wrapped
   "No brief. No deposit. No risk." onto four ragged lines nearly 300px tall.
   The column is the constraint, so the type has to answer to it. */
.split > :first-child h2 { font-size: var(--t-xl); }

/* ------------------------------------------------------------- 6. Header -- */
.header {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--ink) 82%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--ink-line);
}
@supports not (backdrop-filter: blur(1px)) { .header { background: var(--ink); } }

.header__in {
  display: flex; align-items: center; gap: 1.1rem;
  padding-block: 0.95rem;
}

.brand {
  display: inline-flex; align-items: baseline; gap: 0;
  text-decoration: none; color: var(--bone);
  font-size: 1.32rem; font-weight: 500; letter-spacing: -0.045em;
  line-height: 1; margin-right: auto; flex: none;
}
.brand__sub { color: var(--muted); font-weight: 400; }

/* The dotless i, with the tittle replaced by a real anchor point. This is the
   whole identity: it is set in live text, so it stays selectable, scales with
   the type and needs no image. */
.dotless { position: relative; display: inline-block; }
.dotless::after {
  content: ''; position: absolute;
  left: 50%; transform: translateX(-50%);
  bottom: 0.74em;
  /* Proportional em sizing alone fails at small type: at the 21px header size
     0.03em is a 0.63px border, which browsers render as a grey smudge or drop
     entirely, and the anchor point (the entire identity) disappears. The floors
     keep it a real, visible square at every size the wordmark is ever set at,
     while the em values still let it scale properly at display sizes. */
  width:  max(4px, 0.13em);
  height: max(4px, 0.13em);
  border: max(1px, 0.032em) solid var(--accent);
  background: transparent;
}
.section--light .dotless::after { border-color: var(--accent-ink); }

.nav { display: none; }
@media (min-width: 54rem) {
  .nav { display: flex; align-items: center; gap: clamp(1.2rem, 2vw, 2.2rem); }
}
.nav a {
  color: var(--muted); text-decoration: none; font-size: var(--t-sm);
  letter-spacing: -0.01em; transition: color var(--dur-s) var(--ease);
}
.nav a:hover, .nav a:focus-visible { color: var(--bone); }
.nav a[aria-current='page'] { color: var(--bone); }

.burger {
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: none; border: 1px solid var(--ink-line); color: var(--bone);
  padding: 0.5rem 0.8rem; border-radius: var(--radius); cursor: pointer;
  font: inherit; font-size: var(--t-xs);
}
@media (min-width: 54rem) { .burger { display: none; } }

/* Below the nav breakpoint the brand, the CTA and the burger together are
   wider than a 360px viewport, and the burger was pushed out to 397px causing
   the whole page to scroll sideways. The CTA is therefore dropped from the bar
   and repeated inside the drawer instead, where it has room to breathe. */
@media (max-width: 53.999rem) {
  .header__in > .btn { display: none; }
}

.drawer {
  display: none; border-top: 1px solid var(--ink-line);
  padding-block: 0.5rem 1.2rem;
}
.drawer.is-open { display: block; }
@media (min-width: 54rem) { .drawer, .drawer.is-open { display: none; } }
.drawer a {
  display: block; padding: 0.72rem 0; color: var(--bone);
  text-decoration: none; border-bottom: 1px solid var(--ink-line);
}
.drawer .btn { margin-top: 1rem; width: 100%; border-bottom: 0; }

/* ------------------------------------------------- 7. Hero and the curve -- */
.hero {
  position: relative;
  padding-block: clamp(3.5rem, 2rem + 9vw, 9rem) clamp(3rem, 2rem + 5vw, 7rem);
  overflow: hidden;
}
.hero__in { position: relative; z-index: 2; }
.hero h1 { max-width: 16ch; }
.hero .lede { margin-top: clamp(1.4rem, 1rem + 1.4vw, 2.2rem); max-width: 46ch; }
.hero__actions {
  display: flex; flex-wrap: wrap; gap: 0.85rem;
  margin-top: clamp(1.8rem, 1.2rem + 2vw, 3rem);
}

/* The signature. A cubic Bezier drawn across the hero with its control
   handles shown, exactly as a vector editor would display it. It draws itself
   once on load and then holds. Decorative, so aria-hidden. */
.curve {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%;
  pointer-events: none;
  opacity: 0.9;
}
.curve__path {
  fill: none; stroke: url(#curveGrad); stroke-width: 1.5;
  stroke-linecap: round;
  opacity: 0.65;
}
/* If the gradient reference ever fails to resolve, fall back to a flat stroke
   rather than an invisible path. */
@supports not (fill: url(#x)) { .curve__path { stroke: var(--accent); } }
.curve__arm { stroke: var(--muted); stroke-width: 1; opacity: 0.32; }
.curve__node { fill: var(--ink); stroke: var(--accent); stroke-width: 1.5; }
/* Grips are handles, so they take the second tone. The square anchors stay
   blue. That one rule is the whole colour system: blue is a point you have
   placed, cyan is a handle you would drag. */
.curve__grip { fill: var(--accent-2); opacity: 0.85; }

/* -------------------------------------------------- 8. Section furniture -- */
/* Every section is labelled with a mono eyebrow preceded by an anchor square.
   It is the identity used as punctuation. */
.label {
  display: flex; align-items: center; gap: 0.6rem;
  color: var(--muted); margin-bottom: clamp(1rem, 0.6rem + 1.4vw, 2rem);
}
.label::before {
  content: ''; flex: none;
  width: 7px; height: 7px;
  border: 1.5px solid var(--accent);
}
.section--light .label { color: var(--muted-ink); }
.section--light .label::before { border-color: var(--accent-ink); }

.section-head { margin-bottom: clamp(2rem, 1.2rem + 3vw, 4rem); }
.section-head p { margin-top: 1.1rem; color: var(--muted); max-width: 54ch; }
.section--light .section-head p { color: var(--muted-ink); }

/* ------------------------------------------------- 9. Buttons and links --- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.55rem;
  padding: 0.86rem 1.5rem;
  font: inherit; font-size: var(--t-sm); font-weight: 500; letter-spacing: -0.01em;
  border-radius: var(--radius); border: 1px solid transparent;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background var(--dur-s) var(--ease), color var(--dur-s) var(--ease),
              border-color var(--dur-s) var(--ease), transform var(--dur-s) var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn--primary { background: var(--bone); color: var(--ink); }
.btn--primary:hover, .btn--primary:focus-visible { background: #fff; }

.btn--ghost { border-color: var(--ink-line); color: var(--bone); }
.btn--ghost:hover, .btn--ghost:focus-visible { border-color: var(--muted); }

.section--light .btn--primary { background: var(--ink); color: var(--bone); }
.section--light .btn--primary:hover { background: #000; }
.section--light .btn--ghost { border-color: var(--bone-line); color: var(--ink); }
.section--light .btn--ghost:hover { border-color: var(--muted-ink); }

/* Prose links. The :not(.btn) is load bearing: without it this rule outranks
   .btn on specificity and repaints every button label. That exact bug shipped
   on an earlier site and was only caught in audit. */
.section--light a:not(.btn):not(.brand):not(.work__link) { color: var(--accent-ink); }
a:not(.btn):not(.brand):not(.nav a) { color: var(--accent); }

.tlink {
  color: var(--bone); text-decoration: none;
  border-bottom: 1px solid var(--ink-line);
  padding-bottom: 2px;
  transition: border-color var(--dur-s) var(--ease);
}
.tlink:hover, .tlink:focus-visible { border-color: var(--accent); }

/* --------------------------------------------------------------- 10. Work - */
.work { display: grid; gap: clamp(2.5rem, 1.5rem + 5vw, 6rem); }

.work__item { display: grid; gap: clamp(1.2rem, 0.8rem + 2vw, 2.4rem); }
@media (min-width: 60rem) {
  .work__item { grid-template-columns: 1fr 1fr; align-items: center; }
  .work__item:nth-child(even) .work__media { order: 2; }
}

.work__media {
  position: relative;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--ink-raised);
}
.section--light .work__media { border-color: var(--bone-line); }
.work__media img {
  width: 100%; aspect-ratio: 3 / 2; object-fit: cover; object-position: top center;
  transition: transform 900ms var(--ease);
}
.work__item:hover .work__media img { transform: scale(1.02); }

.work__meta { display: flex; gap: 1.1rem; align-items: baseline; flex-wrap: wrap; }
.work__no { color: var(--accent); }
.section--light .work__no { color: var(--accent-ink); }
.work__title {
  font-size: var(--t-lg); line-height: 1.16; letter-spacing: -0.028em;
  font-weight: 500; margin-block: 0.5rem 0.9rem;
}
.work__body p { color: var(--muted); }
.section--light .work__body p { color: var(--muted-ink); }

.tags { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: 1.2rem; }
.tag {
  border: 1px solid var(--ink-line); border-radius: 100px;
  padding: 0.28rem 0.7rem; color: var(--muted);
  font-family: 'Space Mono', monospace; font-size: var(--t-xs);
  letter-spacing: 0.04em;
}
.section--light .tag { border-color: var(--bone-line); color: var(--muted-ink); }

/* ------------------------------------- 11. Standards, process, pricing ---- */
.std { border-top: 1px solid var(--ink-line); padding-top: 1.4rem; }
.section--light .std { border-top-color: var(--bone-line); }
.std h3 { font-size: var(--t-md); margin-bottom: 0.6rem; }
.std p { color: var(--muted); font-size: var(--t-sm); }
.section--light .std p { color: var(--muted-ink); }

.steps { counter-reset: step; display: grid; gap: 0; }
.step {
  display: grid; gap: 0.4rem 1.6rem;
  padding-block: clamp(1.4rem, 1rem + 1.4vw, 2.2rem);
  border-top: 1px solid var(--ink-line);
}
.section--light .step { border-top-color: var(--bone-line); }
@media (min-width: 48rem) { .step { grid-template-columns: 5rem 1fr; } }
.step__no { color: var(--accent); }
.section--light .step__no { color: var(--accent-ink); }
.step p { color: var(--muted); margin-top: 0.4rem; }
.section--light .step p { color: var(--muted-ink); }

.price {
  border: 1px solid var(--ink-line); border-radius: var(--radius);
  padding: clamp(1.4rem, 1rem + 1.6vw, 2.4rem);
  display: flex; flex-direction: column; gap: 0.9rem;
  background: var(--ink-raised);
}
.section--light .price { border-color: var(--bone-line); background: transparent; }
.price__fig { font-size: var(--t-xl); letter-spacing: -0.04em; line-height: 1; }
.price ul { display: grid; gap: 0.55rem; margin-top: 0.4rem; }
.price li {
  position: relative; padding-left: 1.3rem;
  font-size: var(--t-sm); color: var(--muted);
}
.section--light .price li { color: var(--muted-ink); }
.price li::before {
  content: ''; position: absolute; left: 0; top: 0.62em;
  width: 6px; height: 6px; border: 1.5px solid var(--accent);
}
.section--light .price li::before { border-color: var(--accent-ink); }

/* ---------------------------------------------------------- 12. Accordion - */
.faq { border-top: 1px solid var(--ink-line); }
.section--light .faq { border-top-color: var(--bone-line); }
.faq__item { border-bottom: 1px solid var(--ink-line); }
.section--light .faq__item { border-bottom-color: var(--bone-line); }

.faq__q {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; text-align: left;
  background: none; border: 0; color: inherit; cursor: pointer;
  font: inherit; font-size: var(--t-md); font-weight: 500; letter-spacing: -0.02em;
  padding-block: clamp(1.05rem, 0.8rem + 0.9vw, 1.6rem);
}
.faq__sign { flex: none; width: 12px; height: 12px; position: relative; }
.faq__sign::before, .faq__sign::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  background: var(--accent);
}
.section--light .faq__sign::before, .section--light .faq__sign::after { background: var(--accent-ink); }
.faq__sign::before { width: 100%; height: 1.5px; }
.faq__sign::after  { width: 1.5px; height: 100%; transition: transform var(--dur-s) var(--ease); }
.faq__q[aria-expanded='true'] .faq__sign::after { transform: scaleY(0); }

/* Default OPEN, and JS adds .is-collapsed. With JS off the answers stay
   readable instead of being permanently hidden by CSS. */
.faq__a { display: grid; grid-template-rows: 1fr; transition: grid-template-rows var(--dur) var(--ease); }
.faq__a.is-collapsed { grid-template-rows: 0fr; }
.faq__a > div { overflow: hidden; }
.faq__a p { color: var(--muted); padding-bottom: 1.5rem; }
.section--light .faq__a p { color: var(--muted-ink); }

/* -------------------------------------------------------------- 13. Forms - */
.field { display: grid; gap: 0.45rem; margin-bottom: 1.15rem; }
.field label { font-size: var(--t-sm); color: var(--muted); }
.section--light .field label { color: var(--muted-ink); }
.field input, .field textarea, .field select {
  font: inherit; font-size: var(--t-base);
  background: var(--ink-raised); color: var(--bone);
  border: 1px solid var(--ink-line); border-radius: var(--radius);
  padding: 0.8rem 0.9rem; width: 100%;
  transition: border-color var(--dur-s) var(--ease);
}
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--accent); }
.field textarea { min-height: 9rem; resize: vertical; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.note {
  border-left: 2px solid var(--accent); padding: 0.2rem 0 0.2rem 1rem;
  color: var(--muted); font-size: var(--t-sm);
}
.section--light .note { border-left-color: var(--accent-ink); color: var(--muted-ink); }

/* ------------------------------------------------------------- 14. Footer - */
.footer { border-top: 1px solid var(--ink-line); padding-block: clamp(3rem, 2rem + 3vw, 5rem); }
.footer__grid { display: grid; gap: 2.4rem; }
@media (min-width: 52rem) { .footer__grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.footer__h { font-size: var(--t-sm); color: var(--muted); font-weight: 400; margin-bottom: 0.9rem; letter-spacing: 0; }
.footer ul { display: grid; gap: 0.5rem; }
.footer a { color: var(--bone); text-decoration: none; font-size: var(--t-sm); }
.footer a:hover { color: var(--accent); }
.footer__base {
  display: flex; flex-wrap: wrap; gap: 0.8rem 1.6rem; justify-content: space-between;
  margin-top: clamp(2rem, 1.4rem + 2vw, 3.5rem);
  padding-top: 1.5rem; border-top: 1px solid var(--ink-line);
  color: var(--muted);
}

/* ------------------------------------ 15. Motion and reduced motion ------- */
.reveal {
  opacity: 0; transform: translateY(14px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.reveal.is-visible { opacity: 1; transform: none; }

/* No JS: nothing may be left hidden. This rule is why the site is readable
   with scripting disabled. */
.no-js .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
}

/* ------------------------------------------------------- 16. Placeholders - */
/* Anything the owner still has to confirm is marked, so it cannot ship by
   accident. Find them all with:  grep -rn 'class="tbc"' *.html build-pages.mjs */
.tbc {
  border-bottom: 1px dashed var(--focus);
  padding-bottom: 1px;
}

/* ------------------------------------------------- 17. Pricing, two ways -- */
/* Each tier carries two prices: hosted with a monthly, or one off. Stacking
   them rather than tabbing between them means a prospect can compare without
   clicking, and it works with JavaScript off. */
.price__opts {
  display: grid; gap: 0.85rem;
  padding-bottom: 0.3rem;
}
.price__opt > .mono { display: block; margin-bottom: 0.25rem; }
.price__fig--sm { font-size: var(--t-lg); }
.price__per {
  display: block; margin-top: 0.3rem;
  font-size: var(--t-sm); letter-spacing: 0;
  color: var(--muted); font-weight: 400;
}
.section--light .price__per { color: var(--muted-ink); }

/* Ticked and crossed lists. The marker is the anchor square again, hollow for
   included and filled for excluded, so the two read differently at a glance
   without relying on colour alone. */
.ticks { display: grid; gap: 0.7rem; }
.ticks li {
  position: relative; padding-left: 1.5rem;
  font-size: var(--t-sm); color: var(--muted); max-width: 42ch;
}
.section--light .ticks li { color: var(--muted-ink); }
.ticks li::before {
  content: ''; position: absolute; left: 0; top: 0.55em;
  width: 7px; height: 7px;
  border: 1.5px solid var(--accent);
}
.section--light .ticks li::before { border-color: var(--accent-ink); }
.ticks--no li::before {
  background: currentColor; border-color: currentColor; opacity: 0.45;
}
.ticks strong { color: var(--bone); font-weight: 500; }
.section--light .ticks strong { color: var(--ink); }
