/* ============================================================
   Midnight — Posh Pixel AI
   See design-system-v1.md.

   Chosen by eye on 14 September 2026 from three directions, over a
   warm-cream setting of the same page. What it is trying to be, in
   the order it was asked for: luxurious, trustworthy, avant-garde,
   and evidently the work of people who are good at this.

   The idea it is built on is one tension, held on every page: the
   couture face next to the machine face. Bodoni Moda is a Didone —
   hairline thins against heavy stems, cut for fashion plates and
   title pages, and it needs darkness to show what it is. IBM Plex is
   a family drawn for a technology company, and every functional word
   on the site is set in it. A consultancy that puts AI into other
   people's businesses should look like both of those things at once,
   and nothing else on the page competes with that idea.

   The colour comes from the Client Desk — the tool Cata already works
   in every day. Its dark theme is a near-black that leans very slightly
   violet, with a rose accent, and this takes both: #131216 for the
   ground, #E38DA0 for the one accent. The site and the tool are then
   one family rather than two things by the same person.

   This was chosen over a blue-cast ground with a pale blue accent, and
   that is a real trade: blue reads as trust, and it was the only colour
   instruction on this project. It is written down here rather than
   quietly lost. What replaced it is cohesion, and a warmer page.

   The accent is used where the Client Desk uses it: links, focus, the
   small mono uppercase labels, and the rule beside a quotation. Never a
   heading, never a background, never a mood.

   The site this one was forked from had no shared stylesheet: its homepage carried the
   whole thing inline and a Python script copied it into every other
   page. That worked for four pages and would not survive twelve.
   This is a real stylesheet, linked once.
   ============================================================ */

:root{
  /* Near-black leaning very slightly violet, from the Client Desk. Not a
     neutral grey and not a blue — on a screen that difference is barely
     nameable and entirely visible. */
  --paper:#131216;
  --paper-raised:#1B1A20;

  /* Warm ivory type on a cool ground. The warm-on-cool tension is the
     whole reason this reads as expensive rather than as dark mode. */
  --ink:#F1EEE9;
  --ink-2:#BBB6C2;        /* deliberately cooler than --ink */
  --muted:#8B8598;        /* 5.3:1 on --paper. Do not lighten the ground. */

  --rule:#272430;
  --rule-strong:#3D3947;

  /* The Client Desk's rose. One accent, used only where something is live
     or where a label is doing structural work. A saturated colour here
     would read as every other technology site. */
  --accent:#E38DA0;
  --accent-soft:#C97288;
  --accent-wash:#2B1C22;

  --ink-hover:#FFFFFF;
  --tint:rgba(241,238,233,.08);

  /* form states only. never marketing copy. Taken from the Client Desk's
     dark theme so a green in the dashboard and a green on the site are
     the same green. */
  --good:#7AC5A0;
  --warn:#DCAE55;
  --bad:#E68B7D;

  /* The couture face and the machine face. There is no third idea. */
  --display:"Bodoni Moda",Didot,"Bodoni MT",Georgia,"Times New Roman",serif;
  --body:"IBM Plex Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  --measure:66ch;
  --shell:1140px;
  --gutter:clamp(20px,4vw,44px);
  --section:clamp(56px,9vw,120px);
}

*{box-sizing:border-box}

/* The `hidden` attribute means hidden, everywhere.
   Found live on 14 September: the quiz's Back button was hidden on question
   one with el.hidden = true, and showed anyway. `hidden` is only a default of
   `display:none`, and .btn sets `display:inline-flex`, which beats it on
   specificity. Any element with an explicit display has the same problem, so
   this is one rule rather than a fix in one place. */
[hidden]{display:none!important}

html{-webkit-text-size-adjust:100%}

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--body);
  font-size:17px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
}

img{max-width:100%;height:auto;display:block}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001ms!important;transition-duration:.001ms!important}
}

/* ---------- shell ---------- */
.shell{
  max-width:var(--shell);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
.section{padding-block:var(--section)}
.section + .section{border-top:1px solid var(--rule)}
.measure{max-width:var(--measure)}

/* ---------- type ---------- */
/* Bodoni Moda is variable on an optical-size axis, and the axis is the
   point of using it: the same drawing at 96 has hairlines a title needs
   and at 11 has the weight a subheading needs to survive. A single
   weight — 400 — across every heading on the site. Bolding a Didone
   thickens the stems and closes the counters, which is how an expensive
   face comes out looking like a cheap one. */
h1,h2,h3{
  font-family:var(--display);
  font-weight:400;
  letter-spacing:-.012em;
  text-wrap:balance;
  margin:0;
}
h1{font-size:clamp(36px,6.4vw,62px);line-height:1.04;
  font-variation-settings:"opsz" 96}
h2{font-size:clamp(28px,4.2vw,42px);line-height:1.1;
  font-variation-settings:"opsz" 48}
h3{font-size:20px;line-height:1.32;letter-spacing:-.002em;
  font-variation-settings:"opsz" 14}

p{margin:0}
.stack > * + *{margin-top:var(--gap,18px)}

.lede{font-size:clamp(17px,1.7vw,20px);line-height:1.55;color:var(--ink-2)}
.small{font-size:15px;line-height:1.55}
.muted{color:var(--muted)}

.label{
  font-family:var(--mono);
  font-size:11px;
  font-weight:500;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--muted);
  margin:0;
}

.num{font-variant-numeric:tabular-nums}

/* The accent exists for links and focus and nothing else. Underlines stay:
   a link that is only a colour is a link somebody misses. */
a{color:var(--accent);text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;text-decoration-color:var(--rule-strong)}
a:hover{text-decoration-color:var(--accent);text-decoration-thickness:1px}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;gap:10px;
  min-height:48px;padding:13px 26px;border-radius:2px;
  font-family:var(--body);font-weight:600;font-size:15.5px;
  text-decoration:none;cursor:pointer;
  background:var(--ink);color:var(--paper);border:1px solid var(--ink);
}
.btn:hover{background:var(--ink-hover);border-color:var(--ink-hover);text-decoration:none}
.btn small{font-weight:400;opacity:.75;font-size:13.5px}
.btn--quiet{background:transparent;color:var(--ink)}
.btn--quiet:hover{background:var(--tint)}

/* ---------- the pieces this site actually repeats ---------- */

/* A signature gate. A pull quote, not a callout box. */
.gate{border-left:3px solid var(--ink);padding:4px 0 4px 18px}
.gate p{font-size:19px;line-height:1.45}
.gate .label{margin-top:9px}

/* A priced thing. No badge, no card. */
.offer{display:flex;flex-direction:column;gap:6px}
.offer .name{font-family:var(--display);font-weight:400;font-size:22px;
  letter-spacing:-.01em}
.offer .price{font-family:var(--display);font-weight:400;font-size:34px;
  font-variant-numeric:tabular-nums;letter-spacing:-.015em}
.offer .terms{font-size:15px;color:var(--muted)}

/* Rows that wrap and stack. Not cards. */
.row{display:grid;gap:clamp(24px,4vw,44px)}
.row--2{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.row--3{grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}

/* The one place a surface lifts: a form, a dialog. */
.raised{background:var(--paper-raised);border:1px solid var(--rule);
  border-radius:3px;padding:clamp(20px,3vw,30px)}

/* ---------- header and footer ---------- */
.masthead{border-bottom:1px solid var(--rule)}
/* One row: logo left, menu right. It took three goes to get here and the
   reason is worth keeping, because it was misdiagnosed twice.

   Measured in Chromium at 1280px: the content box is 1052px (1140 minus the
   44px gutters — an earlier note here used the border box and was wrong by
   88px), and the column gap is 28px. So the menu has 1052 − 28 − the logo.

   The blocker was never the logo. With the quiz button reading "Not sure
   where to start? Take the 5-minute quiz" the menu needed 958px, and 353px of
   that was the one label. No logo size fits beside 958px — at the 66px that
   was left the lockup would be 17px tall — which is why this was called
   impossible twice. Cata shortened the label to "Take the quiz" on 16
   September 2026: the menu came to 731px against 1052 of content box. She then
   replaced the menu outright — seven links, Home through Contact, plus both
   buttons — which took it to 843px against 826px of room. Seventeen over.
   Closing the item gap from 22px to 16px gets it to 795px, 31px spare.

   That margin is the whole design. Add a menu item, lengthen a label, or make
   the logo taller, and the menu drops onto a second row with no warning —
   it still works, it just stops being what she asked for. The full sentence
   still runs under the headline on the home page, which is where it was
   doing the persuading anyway.

   34px above and 26px below: she asked for air over the logo, and a single
   row wants an asymmetric pad — equal padding reads as though the header is
   sitting on the content. Header 114px, down from 168px.

   Centre rather than baseline because an image has no baseline worth aligning
   to. flex-wrap stays: below about 900px the menu still needs to wrap, and
   that is correct, not a fallback. */.masthead-in{display:flex;flex-wrap:wrap;gap:20px 28px;align-items:center;
  justify-content:space-between;padding-block:34px 26px}
/* A Didone at 22px wants air, not tightening. The negative tracking that
   suited Fraunces here closed the counters and made the wordmark look
   condensed. */
/* The type rules stay even though the wordmark is an image now. They are what
   renders if the image ever fails: the alt text comes back as Bodoni at 22px,
   which is exactly what was here before, rather than as browser default serif
   halfway down the masthead. */
.wordmark{font-family:var(--display);font-weight:400;font-size:22px;
  font-variation-settings:"opsz" 14;letter-spacing:.01em;text-decoration:none;
  display:block;line-height:0}
/* 50px. Cata's number, and the fifth one she has set — 60, 44, 30, 40, then
   this. Sizing the header by eye took a slider and several
   passes, which is what it takes; the file rebuilds from one constant.
   The lockup is 3.961:1, so 50px tall is 198px wide — five sixths of the
   238px it started at. Its width is what the one-row header is budgeted
   against, so making it taller costs the menu its place on the top right.
   The tagline band is 19.1% of the height: 9.6px, descenders included.
   The slogan is also the footer descriptor and the <title> on every page, at
   a size that is legible regardless.
   The image file is cut to suit: DISPLAY_H in brand/logo/header-copy.py must
   match the height below, or the header ships a picture built for a size it
   is not shown at. */
.wordmark img{display:block;height:50px;width:auto}
/* 16px between items, not 22. Seven links plus two buttons came to 843px
   against 826px of room — seventeen pixels over, which is the kind of miss
   that silently drops the menu onto a second row. Six pixels off each of the
   eight gaps buys 48 and leaves 31 spare. Tried and rejected: shrinking the
   type (12px spare, and 15px is already the floor for a menu), and trimming
   the quiz button's padding (works, but it is already tighter than the solid
   button beside it). */
.nav{display:flex;flex-wrap:wrap;gap:8px 16px;align-items:center;
  font-size:15.5px}
.nav a{text-decoration:none}
.nav a:hover{text-decoration:underline;text-underline-offset:3px}

.foot{border-top:1px solid var(--rule);padding-block:34px 56px;
  font-size:14.5px;color:var(--muted)}
.foot a{color:var(--muted)}
.foot .descriptor{font-family:var(--mono);font-size:11px;letter-spacing:.14em;
  text-transform:uppercase}

/* ---------- forms ---------- */
label{display:block;font-size:14px;font-weight:500;margin-bottom:6px}
input,select,textarea{
  font:inherit;font-size:16px;width:100%;
  background:var(--paper-raised);color:var(--ink);
  border:1px solid var(--rule-strong);border-radius:2px;
  padding:12px 13px;min-height:48px;
}
textarea{min-height:120px;resize:vertical}

/* ---------- tables of figures ---------- */
.table-wrap{overflow-x:auto}
table{border-collapse:collapse;width:100%;font-size:15.5px}
th,td{text-align:left;padding:11px 14px;border-bottom:1px solid var(--rule);
  vertical-align:top}
thead th{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);font-weight:500;
  border-bottom:1px solid var(--rule-strong)}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}

.sr{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap}

/* ─── the buy area ─────────────────────────────────────────────────────────
   These class names are not chosen here. functions/_lib/course.js writes the
   tier cards at request time and emits them, so renaming one silently empties
   the buy area on a page that still passes every test. Change both or neither. */

.cs-grid{display:grid;gap:clamp(20px,3vw,32px);
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr));margin-top:34px}
.cs-card{background:var(--paper-raised);border:1px solid var(--rule);
  border-radius:3px;padding:clamp(22px,3vw,30px);display:flex;flex-direction:column}
.cs-top{display:flex;justify-content:space-between;align-items:baseline;
  gap:14px;padding-bottom:14px;border-bottom:1px solid var(--rule);margin-bottom:18px}
.cs-kind{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted)}
.cs-price{font-family:var(--display);font-weight:400;font-variation-settings:"opsz" 40;font-size:36px;
  line-height:1;font-variant-numeric:tabular-nums;white-space:nowrap}
.cs-card h3{margin:0}
.cs-card p{margin-top:8px;color:var(--ink-2)}
.cs-card ul{color:var(--ink-2)}
.cs-meta{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);margin-top:auto;padding-top:20px}
.cs-instalment{display:block;width:100%;margin-top:10px;padding:10px 14px;
  font:inherit;font-size:14.5px;text-align:center;cursor:pointer;
  background:transparent;color:var(--ink-2);
  border:1px solid var(--rule-strong);border-radius:2px}
.cs-instalment:hover{background:var(--tint)}
.cs-instalment small{display:block;font-size:12.5px;color:var(--muted);margin-top:2px}

/* The checkout drawer. Stripe's own iframe mounts inside it. */
.pay{position:fixed;inset:0;z-index:50;display:flex;align-items:flex-start;
  justify-content:center;background:rgba(3,5,9,.78);overflow:auto;padding:24px 16px}
.pay-in{background:var(--paper);border:1px solid var(--rule);border-radius:3px;
  width:100%;max-width:620px;padding:22px}
.pay-top{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  padding-bottom:14px;border-bottom:1px solid var(--rule)}
.pay-x{background:none;border:0;font:inherit;font-size:22px;line-height:1;
  cursor:pointer;color:var(--muted);padding:0 4px}
.pay-msg{margin-top:16px;font-size:15px;color:var(--ink-2)}

/* Weeks. A numbered list that reads as a roadmap rather than as a list. */
.weeks{display:grid;gap:clamp(26px,3.5vw,38px);margin-top:34px}
.week{display:grid;grid-template-columns:auto 1fr;gap:clamp(16px,3vw,30px);
  align-items:start}
.week-n{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);padding-top:6px;white-space:nowrap}
@media (max-width:560px){ .week{grid-template-columns:1fr;gap:10px}
  .week-n{padding-top:0} }

/* ─── the book page ────────────────────────────────────────────────────────
   Restraint is the expensive-looking part. The money here goes into space,
   into one very large serif, and into hairlines — not into more elements. */

/* The title, set like a title page rather than like a headline. Bodoni Moda
   at the top of its optical-size axis, where the thins go to hairlines, with
   the parenthetical dropped to its own line in italic the way it sits on the
   cover. This is the one place on the site where the type is allowed to be
   the whole idea. */
h1.display{
  font-size:clamp(38px,7.4vw,78px);
  line-height:1.02;
  letter-spacing:-.022em;
  font-variation-settings:"opsz" 96;
  max-width:16ch;
}
h1.display .sub{
  display:block;
  margin-top:.28em;
  font-size:clamp(17px,2.3vw,27px);
  line-height:1.2;
  letter-spacing:-.005em;
  font-weight:400;
  font-style:italic;
  color:var(--ink-2);
  font-variation-settings:"opsz" 11;
}

.row-cta{display:flex;flex-wrap:wrap;gap:12px 14px;align-items:center}

/* Appendices. A definition list, because that is what it is — and it gets a
   hairline per entry instead of a bullet, which is the difference between a
   list and a contents page. */
.deflist{margin:0}
.deflist dt{
  font-family:var(--display);font-weight:400;font-size:19px;line-height:1.3;
  font-variation-settings:"opsz" 14;
  padding-top:16px;border-top:1px solid var(--rule);
}
.deflist dt:first-child{padding-top:0;border-top:0}
.deflist dd{margin:6px 0 20px;color:var(--ink-2);font-size:15.5px;line-height:1.6}
.deflist dd:last-child{margin-bottom:0}

/* Cata speaking, set apart from the page's "we" by the typography as well as
   by the words. Nothing decorative: an indent, a rule, and italic serif. */
.note{
  margin:0;border-left:1px solid var(--rule-strong);
  padding:6px 0 6px clamp(20px,3vw,34px);
}
.note p{
  font-family:var(--display);font-weight:400;font-style:italic;
  font-size:clamp(18px,2vw,22px);line-height:1.5;color:var(--ink-2);
  font-variation-settings:"opsz" 16;
}
.note p + p{margin-top:16px}
.note footer{
  margin-top:20px;font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);
}
.note footer::before{content:"— "}

/* The last line before the buttons. One question, given room. */
.closing{
  margin-top:clamp(34px,5vw,54px);
  font-family:var(--display);font-weight:400;font-variation-settings:"opsz" 48;
  font-size:clamp(23px,3.2vw,34px);line-height:1.18;letter-spacing:-.01em;
}

/* ---------- the legal pages ---------- */
/* A policy is one document read top to bottom, not a marketing page where each
   block is its own idea with air around it. At the site's normal section
   rhythm the refunds page came to 3,700 pixels for about four hundred words,
   which is a lot of scrolling to find out whether you can have your money
   back. Halved. The hairlines stay: in a document they read as dividers,
   which is what they are. */
.legal .section{padding-block:clamp(24px,3vw,38px)}
.legal h2{font-size:clamp(21px,2.4vw,27px)}
.legal .deflist{margin-top:4px}

/* A slim bar above the masthead, holding the language toggle and nothing else.
   The toggle was in the menu first and missed by 47 pixels: seven links, two
   buttons and the toggle came to 873px against 826px of room, and the header
   fell back to two rows — undoing what Cata had asked for an hour earlier.
   Its own bar costs about thirty pixels and keeps the menu on one row. */
.topbar{border-bottom:1px solid var(--rule)}
.topbar-in{display:flex;justify-content:flex-end;align-items:center;
  padding-block:7px;min-height:30px}

/* The language toggle. Two letters, not a flag and not a dropdown.
   A flag says country when the thing being chosen is a language — Spanish is
   not Spain here, it is Colombia and Miami — and a dropdown hides the one fact
   the visitor wants, which is that the other language exists at all.
   The language you are in is plain text and the other one is the link, so
   there is nothing on screen to click that does nothing. It sits last in the
   nav and is the smallest thing in it: findable, not competing with the menu. */
.langsw{display:inline-flex;align-items:center;gap:0;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.1em}
.langsw > *{padding:3px 7px;line-height:1}
.langsw > *:first-child{border-right:1px solid var(--rule-strong)}
.langsw span[aria-current]{color:var(--ink)}
.langsw a{color:var(--muted);text-decoration:none}
.langsw a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
.langsw a:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* The page you are on, in the nav. Marked, not shouted. */
.nav a[aria-current="page"]{text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;color:var(--ink)}

/* ─── Midnight: the two moves that are not colour ──────────────────────────

   1. Section rules run the full width of the window rather than stopping at
      the text column. Blocks stacked inside a container read as a webpage;
      plates registered against a full-bleed line read as something printed.
      The rule lives on the section, the padding lives on .shell inside it,
      so nothing about the markup changes.

   2. Every functional word — labels, week numbers, file counts, prices —
      is set in the mono. Every spoken word is set in the Didone or in Plex
      Sans. A reader never has to be told which is which. */

.section{position:relative}
.section + .section{border-top:0}
.section + .section::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:var(--rule);
}

/* Type on a dark ground needs a hair less weight than on a light one, or it
   blooms. This is the only place the body face is touched. */
body{ -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale }

/* Numbers are money here. They line up. */
.cs-price,.week-n,.cs-meta,.label,table{font-variant-numeric:tabular-nums}

/* The select in the buy area is written by functions/_lib/course.js with an
   inline white background, which is correct on paper and a white slab on
   this ground. Overridden here rather than in the JS so the two stay in one
   place; see the note above .cs-grid. */
.cs-card select{
  background:var(--paper)!important;color:var(--ink);
  border:1px solid var(--rule-strong)!important;border-radius:2px!important;
}
.cs-card select:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* The checkout drawer sits on paper, not on midnight: Stripe's embedded form
   renders light and cannot be themed from here, so a dark shell around a
   white form looks like a bug rather than a decision. */
.pay-in{background:#F7F5F1;color:#12151B;border-color:#D8D4CC}
.pay-in h3{color:#12151B;font-family:var(--body);font-weight:600;font-size:17px}
.pay-x{color:#5C6168}
.pay-msg{color:#33383D}

/* Where the accent is allowed to appear. This list is the whole of it:
   links and focus (set with the type rules above), the small mono labels
   that do structural work, and the rule beside Cata speaking. Adding a
   fourth place is how one accent becomes a colour scheme. */
.label,.week-n,.cs-kind{color:var(--accent)}
.note{border-left-color:var(--accent)}
.note p{color:var(--ink)}

/* The gate keeps its heavier rule; it is the loudest thing on the page and
   it should stay ivory rather than turn blue. */
.gate{border-left-color:var(--ink-2)}

/* ─── the Fit Finder ───────────────────────────────────────────────────────
   One question at a time, on a page that otherwise does nothing. The screen
   is deliberately almost empty: somebody answering twelve questions should
   never be able to see how many are left except in the one place that says so.

   Every control here is a real radio or checkbox with a real label, styled.
   Nothing is a div pretending to be a button, because a div cannot be reached
   with a keyboard or read out by a screen reader, and both of those matter
   more on this page than anywhere else on the site. */

.ff{padding-block:clamp(40px,7vw,88px);min-height:60vh}
.ff-step{animation:none}

.ff-head{margin-bottom:clamp(26px,4vw,40px)}
.ff-bar{height:2px;background:var(--rule);margin-top:12px;max-width:var(--measure)}
.ff-bar i{display:block;height:2px;background:var(--accent);
  transition:width .22s ease}
@media (prefers-reduced-motion:reduce){.ff-bar i{transition:none}}

.ff-q{border:0;padding:0;margin:0;min-width:0}
.ff-q legend{font-family:var(--display);font-weight:400;
  font-variation-settings:"opsz" 48;
  font-size:clamp(25px,3.6vw,38px);line-height:1.14;letter-spacing:-.012em;
  padding:0;margin-bottom:10px;text-wrap:balance}

.ff-options{display:grid;gap:10px;margin-top:26px;max-width:var(--measure)}
.ff-options--row{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}

/* The option itself. The input stays in the page and stays focusable — it is
   made invisible, not display:none, because display:none removes it from the
   tab order and from the accessibility tree. */
.ff-opt{display:flex;align-items:flex-start;gap:12px;
  padding:15px 18px;border:1px solid var(--rule-strong);border-radius:2px;
  background:transparent;cursor:pointer;font-size:16px;line-height:1.45}
.ff-opt:hover{background:var(--tint)}
.ff-opt.on{border-color:var(--accent);background:var(--accent-wash)}
.ff-opt input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.ff-opt span{flex:1;min-width:0}
/* The mark that says "this one". Drawn rather than relying on the native
   control, so it reads the same in both directions and at any zoom. */
.ff-opt span::before{content:"";display:inline-block;width:11px;height:11px;
  margin-right:12px;vertical-align:1px;border-radius:50%;
  border:1px solid var(--rule-strong)}
.ff-opt.on span::before{background:var(--accent);border-color:var(--accent);
  box-shadow:inset 0 0 0 2px var(--paper)}
.ff-opt:focus-within{outline:2px solid var(--accent);outline-offset:2px}

.ff-nav{display:flex;flex-wrap:wrap;gap:12px;align-items:center;
  margin-top:clamp(28px,4vw,44px)}
.ff-nav .btn{min-width:120px;justify-content:center}

.ff-err{color:var(--bad);font-size:15px;margin-top:14px;max-width:var(--measure)}

.ff-fields{display:grid;grid-template-columns:1fr 1fr;gap:16px;
  max-width:var(--measure)}
@media (max-width:600px){.ff-fields{grid-template-columns:1fr}}
.ff-fields .f{display:flex;flex-direction:column;gap:6px;min-width:0}
.ff-fields label{font-size:13px;font-weight:500;margin:0}

.ff-screen{border:0;padding:0;margin:0;min-width:0}
.ff-screen legend{font-family:var(--display);font-weight:400;
  font-variation-settings:"opsz" 14;font-size:20px;padding:0;margin-bottom:6px}

.ff-check{display:flex;align-items:flex-start;gap:11px;font-size:15px;
  line-height:1.5;font-weight:400;max-width:var(--measure);margin:0;cursor:pointer}
.ff-check input{margin-top:3px;width:auto;min-height:0;flex:none}

/* The honeypot. Off-screen rather than display:none: a bot that renders CSS
   can see display:none and skip the field, which is the whole point of it. */
.ff-pot{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* ─── the result ─── */
.ff-name{font-size:clamp(30px,5vw,50px);line-height:1.06;
  font-variation-settings:"opsz" 96}
.ff-price{font-family:var(--display);font-weight:400;
  font-variation-settings:"opsz" 40;font-size:28px;
  font-variant-numeric:tabular-nums;color:var(--ink-2)}
.ff-price-sm{font-family:var(--display);font-weight:400;font-size:.8em;
  color:var(--muted);font-variant-numeric:tabular-nums}
.ff-block{margin-top:clamp(30px,4.5vw,50px);padding-top:clamp(26px,3.5vw,38px);
  border-top:1px solid var(--rule)}
.ff-block h2{font-size:clamp(20px,2.4vw,26px);
  font-variation-settings:"opsz" 24;margin-bottom:14px}
.ff-list{margin:0;padding:0;list-style:none;max-width:var(--measure)}
.ff-list li{padding-left:22px;position:relative;line-height:1.6;color:var(--ink-2)}
.ff-list li + li{margin-top:9px}
.ff-list li::before{content:"\203A";position:absolute;left:0;color:var(--muted)}
.ff-aside{border-top:1px solid var(--rule-strong)}
.gate-note{border-left:3px solid var(--accent);padding:4px 0 4px 18px;
  font-size:17px;line-height:1.5}

/* The header call to action. Quiet on purpose — "Book a call" stays the
   primary, and two solid buttons in one masthead is two primaries, which is
   none. */
.ff-cta{font-size:14px;padding:11px 18px;min-height:42px}
@media (max-width:820px){.ff-cta{width:100%;justify-content:center}}
