/* ==========================================================================
   Sergio Sousa — brand system v2
   Implements SERGIO SOUSA - Brand Identity Guidelines v2.0 (5 Aug 2026).
   Two grounds, two accents, split by surface. Brass on dark, oxblood on light,
   never both on one surface.
   ========================================================================== */

:root {
  /* Grounds */
  --nigredo:    #0F1012;
  --nigredo-1:  #161719;
  --nigredo-2:  #1E1F22;
  --bone:       #EDEAE4;

  /* Text */
  --chalk:      #F5F3EF;
  --ash:        #8A8A8F;
  --ink:        #14151A;

  /* Accents. One per surface. */
  --brass:      #B8893B;
  --gold:       #C9A227;
  --oxblood:    #7B2D26;

  /* Faces */
  --display: 'Cormorant Garamond', 'Times New Roman', serif;
  --sans:    'Jost', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono:    'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* Spacing rhythm. One scale, used everywhere. */
  --s1: 0.5rem;  --s2: 0.75rem; --s3: 1rem;   --s4: 1.5rem;
  --s5: 2rem;    --s6: 3rem;    --s7: 4.5rem; --s8: 7rem;   --s9: 10rem;

  --measure: 65ch;
  --gutter: clamp(1.5rem, 5vw, 4rem);
  --maxw: 1180px;

  /* Surface-scoped, reassigned by .on-bone */
  --ground: var(--nigredo);
  --fg:     var(--bone);
  --fg-hi:  var(--chalk);
  --fg-mut: var(--ash);
  --accent: var(--brass);
  --rule:   rgba(237, 234, 228, 0.14);
}

.on-bone {
  --ground: var(--bone);
  --fg:     var(--ink);
  --fg-hi:  var(--ink);
  --fg-mut: #5C5C60;          /* tinted from the ground, not neutral gray */
  --accent: var(--oxblood);
  --rule:   rgba(20, 21, 26, 0.16);
}

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

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--nigredo);
  color: var(--bone);
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* --- Grain. Present and visible, per the imagery spec. Digital-clean reads
       as stock. Resolution-independent, so it stays fine at any size. ------ */
body::after {
  content: '';
  position: fixed;
  inset: -50%;
  pointer-events: none;
  z-index: 60;
  opacity: 0.16;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ==========================================================================
   The ceiling
   A hairline you are always reading underneath. It holds low while the
   argument is being made, then lifts and leaves the top of the frame
   entirely. This is the one authored motion on the site; everything else
   holds still.
   ========================================================================== */

.ceiling {
  position: fixed;
  left: 0; right: 0;
  top: 0;
  height: 1px;
  z-index: 40;
  pointer-events: none;
  /* translate3d, not `top`: animating `top` forces layout on every scroll frame. */
  transform: translate3d(0, var(--ceiling-y, 80vh), 0);
  will-change: transform, opacity;
  /* Reads as a horizon rather than a strikethrough where it crosses copy.
     Holds 0.3 in the margins and thins to 0.1 across the reading column: at
     the previous 0.5/0.2 the line announced itself, and the device works
     better as something you half-notice and then miss once it is gone. */
  background: linear-gradient(90deg,
    rgba(184, 137, 59, 0) 0%,
    rgba(184, 137, 59, 0.30) 7%,
    rgba(184, 137, 59, 0.10) 20%,
    rgba(184, 137, 59, 0.10) 64%,
    rgba(184, 137, 59, 0.30) 80%,
    rgba(184, 137, 59, 0) 100%);
  /* Two independent reasons to disappear share one channel: the exit fade as
     the line clears the top of the frame, and the bone guard below. `site.js`
     drives the variable; the guard overrides it as an own-element
     declaration, which beats the inherited value from :root. */
  opacity: var(--ceiling-o, 1);
  /* Short: a slow fade lets brass and bone coexist at the boundary. */
  transition: opacity 140ms linear;
}
/* The label is a legend, and a legend needs somewhere to stand.

   It used to sit at `left: var(--gutter)` — the window's edge — which is also
   where the reading column starts at any width below roughly 1300px, so the
   two occupied the same x and the word printed straight through the copy. The
   760px breakpoint meant to prevent this was set for phones and missed the
   whole 760–1300 band, which is most laptops.

   It now tracks the *column* rather than the window: it parks just left of the
   1180px content box, in the outer margin, falling back to the gutter when
   that margin runs out. And it only renders once the margin is wide enough to
   hold it. A hairline with no legend still works; a legend printed through a
   sentence does not. */
.ceiling::before {
  content: 'CEILING';
  position: absolute;
  left: max(var(--gutter), calc((100% - var(--maxw)) / 2 - 5rem));
  top: -1.7em;
  font-family: var(--mono);
  font-size: 0.56rem;
  letter-spacing: 0.3em;
  /* Solid brass: 6.05:1 on nigredo. At 0.5 alpha this composited to 2.39:1,
     so the label gets quieter by size and tracking, never by alpha. The scroll
     fade is a separate channel. */
  color: var(--brass);
  /* Names the device once at the top of the page, then gets out of the way.
     `site.js` drives this; without it the label simply stays put. */
  opacity: var(--ceiling-label-o, 1);
}

/* Brass may not touch bone. Rather than recolour the rule mid-scroll, the
   device stands down while a light band occupies its line. `site.js` toggles
   this; without JS the rule simply never moves and never reaches the band. */
.ceiling.over-bone { --ceiling-o: 0; }

/* One-shot rise for pages with no scroll range to drive it. */
@media (prefers-reduced-motion: no-preference) {
  .ceiling.one-shot { transition: transform 1400ms cubic-bezier(0.16, 1, 0.3, 1), opacity 520ms ease; }
}
/* The rule still tracks the scroll under this preference, so it keeps its
   compositing hint; only the timed fades go. See the note in `site.js`. */
@media (prefers-reduced-motion: reduce) {
  .ceiling { transition: none; }
}
/* The rule survives at every width; its label does not. The threshold is the
   width at which an outer margin exists that can actually hold the word:
   `.wrap` caps at 1180px and centres, so the margin is (100vw - 1180) / 2, and
   the label needs roughly 90px of it. 1400px is that, with room to spare.
   Below this the label would sit on the reading column, which is what the old
   760px breakpoint failed to catch. */
@media (max-width: 1400px) {
  .ceiling::before { display: none; }
}

/* ==========================================================================
   Type
   ========================================================================== */

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 300;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
  color: var(--fg-hi);
}

/* Measures live on the type itself, where `ch` resolves against the display
   face. `min(100%, …)` is what stops a long line overflowing on small screens. */
.t-hero  { font-size: clamp(2.5rem, 1.5rem + 4.6vw, 5.5rem); letter-spacing: -0.025em; max-width: min(100%, 16ch); }
.t-h1    { font-size: clamp(2rem, 1.4rem + 2.6vw, 2.9rem);  letter-spacing: -0.01em; max-width: min(100%, 22ch); }
.t-h2    { font-size: clamp(1.55rem, 1.25rem + 1.5vw, 1.95rem); font-weight: 400; letter-spacing: 0; max-width: min(100%, 26ch); }

.eyebrow {
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(0.6rem, 0.56rem + 0.16vw, 0.68rem);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--fg-mut);
}

.lede {
  font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.3rem);
  line-height: 1.6;
  color: var(--fg);
  max-width: min(100%, 42ch);
}

p { max-width: min(100%, var(--measure)); }
p + p { margin-top: var(--s4); }

.data {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  line-height: 1.65;
  color: var(--fg-mut);
}

strong { font-weight: 500; color: var(--fg-hi); }

/* ==========================================================================
   Layout
   ========================================================================== */

/* width:100% matters: as a child of a column flex container, margin-inline:auto
   would otherwise shrink-wrap the box instead of stretching it. */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }

/* A narrow reading column that keeps the page's single left edge. Constraining
   with max-width would re-centre the box and give the page two left margins;
   padding the right side instead holds the column flush with the hero.

   The `min(100%, var(--maxw))` is load-bearing and a bare `100%` here is a bug.
   A percentage in `padding` resolves against the CONTAINING BLOCK, not the
   element, and this element's containing block is the full-width `section`, not
   the 1180px column. With a bare `100%` the right padding tracked the viewport:
   at 1920px it computed to 1120px against a 1180px box and collapsed the
   reading column to zero, breaking every paragraph to one word per line. The
   damage scaled with viewport width, so it was invisible at 1440 and fatal
   above ~1855. Clamping to `--maxw` first makes the subtraction happen against
   the box the column actually lives in. */
.wrap.narrow { padding-right: max(var(--gutter), calc(min(100%, var(--maxw)) - 46rem - var(--gutter))); }

section { position: relative; }
.band { padding-block: var(--s8); }
.band-tight { padding-block: var(--s7); }

.band > * + * { margin-top: var(--s5); }
.band .t-h2 + p, .band .t-h2 + .stack { margin-top: var(--s4); }

/* More space above a heading than below it. */
.stack > * + * { margin-top: var(--s4); }
.stack > * + .t-h2 { margin-top: var(--s7); }

.on-bone { background: var(--bone); color: var(--ink); }
.on-bone h1, .on-bone h2, .on-bone h3 { color: var(--ink); }
/* Optical correction, not a palette change: a 300 weight that reads solid as
   bone-on-nigredo goes thin as ink-on-bone. Same colour, one step of weight. */
.on-bone p, .on-bone li { font-weight: 400; }

/* Hairline rules that separate bands. */
.band + .band { border-top: 1px solid var(--rule); }

/* ==========================================================================
   Buttons. Flat, hairline border or solid brass fill. No gradients, no
   shadows, no shimmer.
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-weight: 400;
  font-size: 0.76rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 1.05rem 2.1rem;
  border: 1px solid var(--accent);
  border-radius: 0;
  cursor: pointer;
  transition: background-color 480ms cubic-bezier(0.22, 1, 0.36, 1),
              color 480ms cubic-bezier(0.22, 1, 0.36, 1),
              border-color 480ms cubic-bezier(0.22, 1, 0.36, 1);
}

.btn-solid { background: var(--brass); color: var(--nigredo); border-color: var(--brass); }
.btn-solid:hover, .btn-solid:focus-visible { background: var(--gold); border-color: var(--gold); }

.btn-line { background: transparent; color: var(--fg); border-color: var(--rule); }
.btn-line:hover, .btn-line:focus-visible { border-color: var(--accent); color: var(--fg-hi); }

.on-bone .btn-solid { background: var(--oxblood); color: var(--bone); border-color: var(--oxblood); }
.on-bone .btn-solid:hover, .on-bone .btn-solid:focus-visible { background: #8E362E; border-color: #8E362E; }

:where(a, button, input, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.link-quiet {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  transition: border-color 400ms ease;
}
.link-quiet:hover, .link-quiet:focus-visible { border-color: var(--accent); }

/* ==========================================================================
   Masthead / wordmark
   ========================================================================== */

.masthead {
  position: relative;
  z-index: 20;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  padding-block: var(--s5) 0;
  flex-wrap: wrap;
}

.wordmark {
  font-family: var(--sans);
  font-weight: 200;
  font-size: clamp(0.82rem, 0.76rem + 0.3vw, 1rem);
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--fg-hi);
  text-decoration: none;
  white-space: nowrap;
}

.masthead .eyebrow { max-width: 26ch; text-align: right; line-height: 1.7; }
/* The brand tagline is a signal, not wayfinding, and tracked caps do not wrap
   gracefully at phone widths, so it goes. `.eyebrow-qualify` does not: it is
   the line that qualifies the buyer, and mobile is the real usage scene. */
@media (max-width: 760px) {
  .masthead .eyebrow:not(.eyebrow-qualify) { display: none; }
  .masthead .eyebrow-qualify { text-align: left; max-width: 100%; }
  .masthead { gap: var(--s2); }
}

/* The one piece of pure ornament the brand allows: rule, dot, rule. */
.divider {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: var(--s2);
}
.divider::before, .divider::after {
  content: ''; height: 1px; width: 2.2rem; background: var(--accent); opacity: 0.75;
}
.divider i { width: 2px; height: 2px; background: var(--accent); border-radius: 50%; }

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  isolation: isolate;
  overflow: hidden;
}

/* The plate sits low so the dark ridges occupy the lower frame and the pale sky
   stays out of it. Darkened at source so the ground stays nigredo. */
.hero-plate {
  position: absolute;
  inset: 0;
  z-index: -2;
  background-image: url('ridge-hero.jpg');
  background-size: cover;
  background-position: 50% 100%;
  filter: brightness(0.62);
  opacity: 0.9;
}

/* Scrim protecting type: opaque across the upper frame where the headline sits,
   opening in the lower third so the ridgeline actually reads. */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg,
      var(--nigredo) 0%,
      rgba(15,16,18,0.94) 30%,
      rgba(15,16,18,0.66) 62%,
      rgba(15,16,18,0.42) 84%,
      rgba(15,16,18,0.86) 100%);
}

.hero-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: var(--s8) var(--s7);
}

.hero-inner { max-width: 100%; }
.hero .lede { margin-top: var(--s5); }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s6); }

/* Monochrome and barely there. The asset is filled bone, not brass: at this
   opacity a brass tint reads as a colour cast on the photograph rather than as
   geometry, and the diagram is structure, not an accent.

   It is sized to sit whole in the frame. The diagram is nearly square, so
   anything past roughly 100vh clips its bhupura arms against the top and bottom
   edges, and a cut-off mandala reads as a cropping accident rather than as
   scale. `min(92vh, 84vw)` keeps the outer frame intact at any aspect.

   That it can sit dead centre behind the headline at all is a function of the
   opacity, not independent of it: at the 13% it originally shipped at, this
   size would compete with the type outright. */
.yantra {
  position: absolute;
  z-index: -1;
  top: 50%; left: 50%;
  translate: -50% -50%;
  width: min(92vh, 84vw);
  height: auto;
  opacity: 0.025;
  pointer-events: none;
}
@media (max-width: 900px) {
  .yantra { width: min(78vh, 116vw); opacity: 0.022; }
}

/* A hero band that is not a full viewport: same landscape treatment as .hero,
   used where the page opens straight into an offer rather than a statement.
   The brief specifies landscape behind the hero on every website surface. */
.hero-band { position: relative; isolation: isolate; overflow: hidden; }
.hero-band::before {
  content: '';
  position: absolute; inset: 0; z-index: -2;
  background-image: url('ridge-plate.jpg');
  background-size: cover; background-position: 50% 78%;
  filter: brightness(0.7);
  opacity: 0.95;
}
.hero-band::after {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg,
    rgba(15,16,18,0.96) 0%,
    rgba(15,16,18,0.72) 42%,
    rgba(15,16,18,0.4) 76%,
    rgba(15,16,18,0.88) 100%);
}

/* ==========================================================================
   Pull line
   ========================================================================== */

/* One turn in a long passage, lifted out of the reading column at plate scale
   so the wall of body copy breaks. Sized to `.plate-line` on purpose: the two
   moments on the page that state the thesis speak at the same volume.

   No quote marks, no rule, no italic. The size change is the entire device and
   anything hung off it would read as decoration. `--fg-hi` rather than a fixed
   colour so it survives on `.on-bone` as well as the dark ground. */
.pull {
  font-family: var(--display);
  font-weight: 300;
  /* Tops out at the plate's 3.6rem so the two thesis moments match, but on a
     shallower slope than `.plate-line`: this one sits inside the 46rem reading
     column instead of the full bleed, so it has to reach a size where the
     longest authored line still holds on one line at 320px. */
  font-size: clamp(1.4rem, 0.8rem + 3.3vw, 3.6rem);
  line-height: 1.08;
  letter-spacing: -0.022em;
  color: var(--fg-hi);
  /* Wide enough that the longest authored line holds on one line inside the
     46rem reading column. Tighten this and the lines re-wrap, which turns three
     statements into five ragged ones and kills the device. */
  max-width: min(100%, 24ch);
}
/* The breaks are authored, not wrapped, so each line is its own block and the
   line gap can be set independently of the leading inside a line. */
.pull > span { display: block; }
.pull > span + span { margin-top: 0.3em; }
/* Air on both sides. Without it the size change alone reads as a typo. */
.stack > * + .pull,
.stack > .pull + * { margin-top: var(--s7); }
/* The weight correction `.on-bone` applies to body copy would thicken this into
   a different face; it is display type, not a paragraph. */
.on-bone p.pull { font-weight: 300; }

/* ==========================================================================
   Section plate
   ========================================================================== */

/* A full-bleed breath in the scroll that carries one line. An image band with
   nothing in it encodes nothing, which the brief bans outright. */
.plate {
  position: relative;
  min-height: 46vh;
  display: grid;
  align-content: end;
  isolation: isolate;
  overflow: hidden;
  padding: var(--s8) 0 var(--s7);
}
/* The tagline is the loudest sentence on the page after the hero, so it is set
   at hero scale, one step down.

   It used to carry `.wrap` on the paragraph itself, which was a class collision
   rather than a layout: `.plate-line`'s `max-width: 20ch` overrode `.wrap`'s
   1180px, and `.wrap`'s `margin-inline: auto` then centred that 20ch box in the
   full-bleed section. The line ended up floating in the middle of the band
   instead of starting at the page's one left edge, which is exactly how it
   read. The measure now lives on the type inside a real `.wrap`. */
.plate-line {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(2rem, 1.3rem + 3.1vw, 3.6rem);
  line-height: 1.08;
  letter-spacing: -0.022em;
  color: var(--chalk);
  max-width: min(100%, 15ch);
}
.plate::before {
  content: '';
  position: absolute; inset: 0; z-index: -2;
  background-image: url('ridge-plate.jpg');
  background-size: cover; background-position: 50% 42%;
  /* Mirrored so the ridge falls the opposite way to the hero, which uses the
     same plate. The element is a full-bleed pseudo, so the flip is invisible
     apart from the image itself. */
  transform: scaleX(-1);
  filter: brightness(0.72);
  opacity: 0.95;
}
/* Feathered into the ground at both edges so the plate reads as a breath in the
   scroll rather than a pasted-in rectangle. */
.plate::after {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg,
    var(--nigredo) 0%,
    rgba(15,16,18,0.34) 26%,
    rgba(15,16,18,0.30) 62%,
    var(--nigredo) 100%);
}

/* ==========================================================================
   Doors (the two offers). Deliberately unequal: this is a choice, not a grid
   of matching cards.
   ========================================================================== */

.doors { display: grid; gap: 1px; background: var(--rule); border-block: 1px solid var(--rule); }
@media (min-width: 860px) { .doors { grid-template-columns: 1fr 1.25fr; } }

.door {
  background: var(--nigredo);
  padding: var(--s6) var(--s5);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  transition: background-color 520ms cubic-bezier(0.22, 1, 0.36, 1);
}
.door:hover { background: var(--nigredo-1); }
.door-lead { background: var(--nigredo-1); }
.door-lead:hover { background: var(--nigredo-2); }
.door h3 { font-size: 1.65rem; }
.door p { color: var(--fg); max-width: min(100%, 40ch); margin-bottom: var(--s5); }
/* auto keeps the two actions on one baseline whatever the copy length does. */
.door .btn { align-self: flex-start; margin-top: auto; }

.price-tag { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.22em; color: var(--brass); }

/* ==========================================================================
   Offer panel (sales page)
   ========================================================================== */

.offer {
  border: 1px solid var(--rule);
  padding: var(--s6) var(--s5);
  background: var(--nigredo-1);
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}
.offer-price {
  font-family: var(--display);
  font-size: 4rem;
  line-height: 1;
  color: var(--chalk);
  font-weight: 300;
}
.offer ul { list-style: none; display: flex; flex-direction: column; gap: var(--s2); }
.offer li { display: grid; grid-template-columns: 1.1rem 1fr; gap: var(--s2); align-items: baseline; }
.offer li::before { content: ''; width: 5px; height: 1px; background: var(--brass); translate: 0 -0.35em; }
.offer .btn { width: 100%; }

.fine { font-family: var(--mono); font-size: 0.72rem; line-height: 1.7; color: var(--fg-mut); }

/* Prose, so it is set as prose. Mono is scoped to measurement, and these two
   lines are the strongest trust elements on their pages, not figure labels. */
.offer-refund,
.credit-line {
  font-family: var(--sans);
  font-weight: 300;
  font-size: 0.95rem;
  line-height: 1.65;
  color: var(--fg);
  max-width: min(100%, 46ch);
}
.offer-refund { border-top: 1px solid var(--rule); padding-top: var(--s3); }

.sales-grid { display: grid; gap: var(--s7); align-items: start; }
/* The offer comes FIRST in source, so on a phone the price and the booking
   action arrive right after the hero instead of ~2,600px down the page. Grid
   placement puts it back on the right at desktop widths. */
@media (min-width: 940px) {
  .sales-grid { grid-template-columns: 1fr 22.5rem; }
  .sales-main { grid-column: 1; grid-row: 1; }
  .offer { grid-column: 2; grid-row: 1; position: sticky; top: var(--s6); }
}

/* ==========================================================================
   Phases (what happens in the hour)
   ========================================================================== */

.phases { display: flex; flex-direction: column; }
.phase { padding-block: var(--s5); border-top: 1px solid var(--rule); display: grid; gap: var(--s2); }
.phase:last-child { border-bottom: 1px solid var(--rule); }
@media (min-width: 720px) { .phase { grid-template-columns: 7rem 1fr; gap: var(--s5); } }
.phase-time { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.16em; color: var(--brass); padding-top: 0.4rem; }
.phase h3 { font-size: 1.3rem; }
.phase p { margin-top: var(--s2); color: var(--fg); }

/* ==========================================================================
   Quote
   ========================================================================== */

.quote { border-left: 1px solid var(--accent); padding-left: var(--s4); }
.quote blockquote { font-family: var(--display); font-size: clamp(1.35rem, 1.1rem + 1.1vw, 1.75rem); line-height: 1.35; color: var(--fg-hi); max-width: 32ch; }
.quote figcaption { margin-top: var(--s3); }

/* ==========================================================================
   Q&A
   ========================================================================== */

.qa { display: flex; flex-direction: column; }
.qa-item { padding-block: var(--s5); border-top: 1px solid var(--rule); }
.qa-item:last-child { border-bottom: 1px solid var(--rule); }
.qa-item h3 { font-family: var(--sans); font-weight: 400; font-size: 1.02rem; letter-spacing: 0; line-height: 1.5; color: var(--fg-hi); max-width: 48ch; }
.qa-item p { margin-top: var(--s3); }

/* ==========================================================================
   Form (lead magnet)
   ========================================================================== */

.form { display: flex; flex-direction: column; gap: var(--s3); max-width: 26rem; }
.form input {
  font-family: var(--sans);
  font-weight: 300;
  font-size: 1rem;
  color: var(--bone);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  padding: 0.85rem 0;
  transition: border-color 400ms ease;
}
.form input::placeholder { color: var(--ash); opacity: 1; }
/* The focus ring stays. A border-colour change alone is not a keyboard
   indicator, and the global :where() rule has zero specificity so it loses
   to anything declared here. */
.form input:focus-visible {
  outline: 2px solid var(--brass);
  outline-offset: 4px;
  border-bottom-color: var(--brass);
}
.form input:focus:not(:focus-visible) { outline: none; border-bottom-color: var(--brass); }
.form .btn { margin-top: var(--s3); }
.form button[disabled] { opacity: 0.55; cursor: not-allowed; }

.msg { font-family: var(--mono); font-size: 0.78rem; line-height: 1.6; padding: var(--s3) 0; }
.msg-ok { color: var(--gold); }
.msg-err { color: #D98A80; }

/* ==========================================================================
   Footer
   ========================================================================== */

.foot { padding-block: var(--s7) var(--s6); border-top: 1px solid var(--rule); }
/* `flex-end`, not `baseline`: an image has no text baseline, so baseline
   alignment resolves against its bottom margin edge and drags the copyright
   line with it. Bottom-aligning is stable and reads correctly either way. */
.foot-inner { display: flex; flex-wrap: wrap; gap: var(--s4); justify-content: space-between; align-items: flex-end; }

/* The one mark on the site, and the footer is the only place it fits. At the
   masthead's 16px cap height the crown collapses into noise; it needs about
   40px to hold its detail, and a 40px mark beside a 16px wordmark would make
   the mark primary on a brand whose name is the brand. Down here there is room
   and nothing to compete with, so it closes the page instead of opening it.
   The asset is tinted to bone at build time rather than shipped pure white,
   which would be brighter than every other element on the page. */
.foot-id { display: flex; align-items: center; gap: var(--s4); }
.foot-mark { width: auto; height: 44px; opacity: 0.88; flex: none; }
@media (max-width: 600px) {
  .foot-mark { height: 36px; }
  .foot-id { gap: var(--s3); }
}

/* ==========================================================================
   Entrance. Text arrives, it does not perform. Visible by default so a JS
   failure or reduced-motion never hides content.
   ========================================================================== */

/* The hidden state is gated on `html.js-rise`, which site.js sets on itself
   before paint. If that script never runs, never parses, or errors, every
   .rise block is simply visible. Content must never depend on JS to exist. */
@media (prefers-reduced-motion: no-preference) {
  .js-rise .rise {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 760ms cubic-bezier(0.16,1,0.3,1),
                transform 760ms cubic-bezier(0.16,1,0.3,1);
  }
  .js-rise .rise.in { opacity: 1; transform: none; }
}
