/* Peptara Labs Longevity, design system.
   Palette is the store's own, counted from its live stylesheets 2026-09-08, with the
   ground inverted. There is no second accent hue: gold carries every state.
   Spacing is an 8px scale and nothing outside it is permitted anywhere in this file. */

:root{
  /* ground */
  --bg:#0B0D10;
  --surface:#14171B;
  --raised:#1B1F24;
  --line:#23282E;
  --line-soft:#191D22;

  /* type */
  --ink:#FAF8F5;        /* the store's own off-white */
  --body:#A8A8A3;       /* the store's own warm grey */
  --muted:#75787C;

  /* the one accent */
  --gold:#C9A96E;
  --gold-deep:#B8944F;
  --cream:#EFE7D6;

  /* 8px scale, the only permitted values */
  --s1:8px; --s2:16px; --s3:24px; --s4:32px; --s5:48px;
  --s6:64px; --s7:96px; --s8:128px; --s9:192px;

  --serif:"Fraunces",Georgia,"Times New Roman",serif;
  --sans:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --measure:68ch;
  --ease:cubic-bezier(.22,.61,.36,1);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;background:var(--bg);color:var(--body);
  font:400 17px/1.6 var(--sans);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow-x:hidden;
}

/* the helix sits behind everything and never intercepts a pointer */
#helix{position:fixed;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;display:block}
.page{position:relative;z-index:1}

/* ---------- type ---------- */
h1,h2,h3,h4{font-family:var(--serif);color:var(--ink);font-weight:600;margin:0;
  font-variation-settings:"SOFT" 0,"WONK" 0;letter-spacing:-.015em}
h1{font-size:clamp(40px,7vw,86px);line-height:1.02;letter-spacing:-.03em}
h2{font-size:clamp(30px,4.4vw,52px);line-height:1.08;letter-spacing:-.022em}
h3{font-size:clamp(21px,2.2vw,28px);line-height:1.2}
h4{font-size:19px;line-height:1.3}
p{margin:0 0 var(--s3);max-width:var(--measure)}
a{color:var(--ink);text-underline-offset:3px;text-decoration-color:rgba(201,169,110,.45)}
a:hover{text-decoration-color:var(--gold)}
strong{color:var(--ink);font-weight:600}
.lede{font-size:clamp(18px,2vw,21px);line-height:1.55;color:var(--body);max-width:56ch}
.eyebrow{font:600 11px/1 var(--sans);letter-spacing:.18em;text-transform:uppercase;
  color:var(--gold);display:block;margin-bottom:var(--s3)}
.dim{color:var(--muted)}
.serif-num{font-family:var(--serif);font-variant-numeric:tabular-nums}

/* ---------- layout ---------- */
.wrap{width:min(1200px,calc(100% - var(--s5)));margin-inline:auto}
.narrow{width:min(760px,calc(100% - var(--s5)));margin-inline:auto}
section{padding-block:var(--s8)}
@media (max-width:900px){section{padding-block:var(--s7)}}
@media (max-width:600px){
  section{padding-block:var(--s6)}
  .wrap,.narrow{width:calc(100% - var(--s4))}
}
.rule{height:1px;background:var(--line);border:0;margin:0}

/* ---------- header ---------- */
.top{position:sticky;top:0;z-index:20;background:rgba(11,13,16,.82);
  backdrop-filter:saturate(1.4) blur(14px);border-bottom:1px solid var(--line-soft)}
.top .wrap{display:flex;align-items:center;justify-content:space-between;
  min-height:72px;gap:var(--s4)}
.brand{font-family:var(--serif);font-size:19px;color:var(--ink);text-decoration:none;
  letter-spacing:-.01em;white-space:nowrap}
.brand em{font-style:normal;color:var(--gold)}
.nav{display:flex;gap:var(--s4);align-items:center}
.nav a{font-size:14px;color:var(--body);text-decoration:none;transition:color .2s var(--ease)}
/* .nav a is more specific than .btn-primary, so without this the gold button inherits the
   nav's warm grey and the label all but vanishes against it. Verified in the browser. */
.nav a.btn-primary{color:#0B0D10}
.nav a.btn-primary:hover{color:#0B0D10}
.nav a:hover,.nav a[aria-current]{color:var(--ink)}
@media (max-width:760px){.nav a:not(.btn){display:none}}
/* At 375px the header row fits with EXACTLY zero slack: brand 204 (white-space:nowrap, so it
   cannot shrink) + gap 32 + button 107 = 343, against a wrap of 343. Measured in a real 375px
   viewport. Nothing is broken today, but a font fallback, a longer button label or a wider
   brand string overflows on the next edit, and because the brand cannot shrink the thing that
   gets clipped is the button, the one control the page exists to be pressed. This buys back
   about 50px of margin.

   Do not verify this with a headless screenshot on macOS: Chrome enforces a 500px minimum
   window, so --window-size=375 renders a 500px viewport and crops it to 375. That crop reads
   as an overflow that is not there, and it is what sent the first pass at this rule chasing a
   phantom. Measure in a real 375px viewport. */
@media (max-width:480px){
  .top .wrap{gap:var(--s2);min-height:64px}
  .brand{font-size:16px}
  .nav .btn{padding:var(--s1) var(--s2);font-size:14px}
}

/* ---------- buttons: gold appears once per viewport, here ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--s1);
  font:600 15px/1 var(--sans);padding:var(--s2) var(--s3);border-radius:2px;
  text-decoration:none;border:1px solid transparent;cursor:pointer;
  transition:background .2s var(--ease),border-color .2s var(--ease),color .2s var(--ease)}
.btn-primary{background:var(--gold);color:#0B0D10;border-color:var(--gold)}
.btn-primary:hover{background:var(--cream);border-color:var(--cream)}
.btn-primary:active{background:var(--gold-deep);border-color:var(--gold-deep)}
.btn-ghost{background:transparent;color:var(--ink);border-color:var(--line)}
.btn-ghost:hover{border-color:var(--gold);color:var(--ink)}
.btn-lg{padding:var(--s2) var(--s4);font-size:16px}
.btn:focus-visible,a:focus-visible,button:focus-visible,
input:focus-visible,select:focus-visible,textarea:focus-visible{
  outline:2px solid var(--gold);outline-offset:3px}

/* ---------- hero ---------- */
.hero{--strip-h:clamp(120px,17vw,245px);
  padding-block:var(--s6) 0;position:relative;overflow:hidden}
@media (max-width:900px){.hero{padding-block:var(--s5) 0}}
.hero h1{max-width:15ch}
.hero h1 .g{color:var(--gold);display:block}
.hero .sub{font-family:var(--serif);font-size:clamp(18px,2.1vw,24px);color:var(--body);
  margin-top:var(--s3);font-style:italic;letter-spacing:-.01em}
.hero .cta{display:flex;gap:var(--s2);margin-top:var(--s5);flex-wrap:wrap}
.hero .note{margin-top:var(--s4);font-size:13px;color:var(--muted);max-width:46ch}
.heroinner{position:relative;z-index:2;padding-bottom:var(--s5)}

/* ABSTRACT LAYER. Two soft gold fields and one hairline ring, all driven by the same
   scroll value the helix uses. They are decoration with no content, so they are
   aria-hidden in the markup and pointer-events:none here: nothing about the page's
   meaning or its tab order changes if a browser refuses to paint them. */
.heroabs{position:absolute;inset:-10% -10% 0 -10%;z-index:0;pointer-events:none;
  overflow:hidden;contain:paint}
.heroabs i{position:absolute;display:block;border-radius:50%;
  will-change:transform;transform:translate3d(0,0,0)}
.heroabs .f1{width:52vw;height:52vw;left:-8vw;top:-14vw;
  background:radial-gradient(circle at 50% 50%,rgba(201,169,110,.13),rgba(201,169,110,0) 62%)}
.heroabs .f2{width:44vw;height:44vw;right:-10vw;top:8vw;
  background:radial-gradient(circle at 50% 50%,rgba(239,231,214,.075),rgba(239,231,214,0) 64%)}
.heroabs .ring{width:38vw;height:38vw;left:56vw;top:2vw;border:1px solid rgba(201,169,110,.16);
  background:none}
/* ring2 is an ELLIPSE, not a circle. A rotating circle is a still image, so the rotation
   hero.js applies to it would cost frames and show nothing. */
.heroabs .ring2{width:30vw;height:21vw;left:60vw;top:9vw;border:1px solid rgba(201,169,110,.10);
  background:none}

/* THE FILMSTRIP. Ken's ask was one photorealistic frame holding every persona at once.
   Seven group renders failed on it: the model will not hold a dark ground, six mixed ages
   and six ethnicities in one image, and it returned bright skies, a purple studio and a row
   of nuns instead. A contact strip of the six is the same statement and is strictly better,
   because every face in it was individually approved.

   The flex-grow values ARE the aspect ratios, 2:1 -> 1.9, 1:1 -> 1.0, 2:3 -> .70. Every cell
   therefore crops by the same fraction of its width at any viewport, so the strip reads as
   one exposure rather than six differently-cropped stills. Do not "tidy" them to equal
   values: that is what makes the landscapes lose their subject. */
.heroband{position:relative;z-index:1;display:flex;gap:2px;
  /* THE HEIGHT SETS THE CROP, and the relationship is exact. Every cell is the same height h
     and a width proportional to its source aspect, so each cell's aspect is k times its
     source's, where k = strip width / (h * sum of the grow values) = 1430 / (h * 7.2) at a
     1440 viewport. k = 1 shows all six frames uncropped, at h = 199px. k < 1 crops every cell
     by the same fraction, which is what keeps the strip reading as one exposure. Raising this
     number crops harder, it does not show more picture.

     THE MIDDLE TERM IS WHY IT IS 17vw. Substituting h = 0.17 * vw makes k = 1/(0.17 * 7.2) =
     0.817 at EVERY viewport, so the strip trims the same 18 percent off a 1400px laptop and a
     760px tablet and never drifts. The 245px ceiling caps it on very wide screens; the 120px
     floor only binds below 706px, and the contact sheet takes over at 620 before that floor
     can crop hard. An earlier 165px floor bound at 970px and squeezed the tablet range to a
     34 percent trim, which is where the faces started going. */
  height:var(--strip-h);width:100%;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 22%,#000 100%);
          mask-image:linear-gradient(to bottom,transparent 0,#000 22%,#000 100%)}
.bandcell{margin:0;position:relative;overflow:hidden;flex:1 1 0;min-width:0;
  will-change:transform}
.bandcell[data-shape="wide"]{flex-grow:1.9}
.bandcell[data-shape="square"]{flex-grow:1}
.bandcell[data-shape="tall"]{flex-grow:.7}
/* The image is 118% of the cell and hung 9% above it. That 9% of headroom top and bottom is
   what the parallax translates into: moving the IMG inside a clipped cell can never expose an
   edge, whereas moving the CELL opens a gap in the strip. The cap in hero.js is 8%, one point
   inside the headroom. */
.bandcell img{position:absolute;left:0;top:-9%;width:100%;height:118%;display:block;
  object-fit:cover;object-position:center 34%;
  filter:saturate(.7) contrast(1.04) brightness(var(--g,1));
  will-change:transform}
.bandcell::after{content:"";position:absolute;inset:0;
  background:linear-gradient(to top,rgba(11,13,16,.55),rgba(11,13,16,.06) 55%)}
/* MOBILE: the strip becomes a contact SHEET, three across and two down.

   The single row does not survive a phone, and the arithmetic says why rather than the eye:
   the six cells want 7.2 times the strip height in width, so at 375px and a 150px height each
   cell is 45 to 84px wide and every face is a sliver. There is no height that fixes it, because
   shrinking the height shrinks the cells with it.

   Three columns at 375px gives 124px cells, and a 3:4 cell keeps a readable face from all three
   source shapes: an upright loses 11 percent of its height, a square 25 percent of its width,
   and the two landscapes crop hard but around the subject anchor, which is exactly what those
   anchors are for. All six people still appear together, which was the point. */
@media (max-width:620px){
  .heroband{display:grid;grid-template-columns:repeat(3,1fr);height:auto;gap:2px;
    -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 9%,#000 100%);
            mask-image:linear-gradient(to bottom,transparent 0,#000 9%,#000 100%)}
  .bandcell{flex:none;aspect-ratio:3/4}
  .bandcell img{top:-6%;height:112%}
  /* the cue anchors off --strip-h, which is meaningless once the strip is auto height, and a
     phone needs no invitation to scroll */
  .scrollcue{display:none}
}

/* the cue sits on the strip, so it is dark on light and needs no extra scrim */
/* The cue marks the boundary between the copy and the strip. Anchoring it to the bottom of
   the hero put it underneath the strip and off the fold, so it hangs off --strip-h instead
   and stays on the seam at every viewport. */
.scrollcue{position:absolute;left:50%;bottom:calc(var(--strip-h) + var(--s2));z-index:3;
  transform:translateX(-50%);
  width:1px;height:var(--s5);background:linear-gradient(to bottom,rgba(201,169,110,0),var(--gold));
  overflow:hidden}
.scrollcue span{position:absolute;inset:auto 0 0 0;height:40%;background:var(--gold);
  animation:cue 2.4s var(--ease) infinite}
@keyframes cue{0%{transform:translateY(-100%)}60%,100%{transform:translateY(100%)}}
@media (prefers-reduced-motion:reduce){.scrollcue span{animation:none}}

/* ---------- goal cards: the mosaic ----------
   Six identical 3:4 frames read as a catalogue. This is a four-column mosaic instead, two
   rows that each add to exactly four columns, with one landscape, two squares and two
   uprights per pass:

     row 1   [ shape 2:1, span 2 ][ energy 1:1 ][ recovery 1:1 ]
     row 2   [ skin 2:3 ][ sleep 2:1, span 2 ][ focus 2:3 ]

   THE ALIGNMENT, which is the whole reason this is a mosaic and not a mess. With column
   width c and gap g, a span-2 landscape is (2c+g)/2 = c + g/2 tall and a square is c tall,
   so the landscape overhangs a square by HALF A GAP, twelve pixels, at every viewport. That
   is why the ratios are 2:1 and 1:1 and not something rounder: any other pair drifts apart
   as the page widens. align-items:end flushes the bottoms, so the six captions sit on one
   line and the twelve pixels land at the top where nothing reads them.

   In row 2 the landscape is the SHORT one, 1.09c against 1.5c, so bottom-flushing it would
   look dropped. align-self:center floats it between the two uprights. One rule covers both
   rows: in row 1 the landscape is the tallest item, so centring it is a no-op.

   The images are generated at these exact ratios by gen_mosaic.py rather than cropped to
   them. Cropping a 3:4 portrait to 2:1 keeps a third of the frame and takes the head with
   it. If the ratios here ever change, regenerate; do not crop. */
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s3);align-items:end}
.card[data-shape="wide"]{grid-column:span 2;align-self:center}
.card[data-shape="wide"] .ph{aspect-ratio:2/1}
.card[data-shape="square"] .ph{aspect-ratio:1/1}
.card[data-shape="tall"] .ph{aspect-ratio:2/3}
@media (max-width:1000px){
  /* two columns: the span-2 landscape would strand a hole beside an upright, so every card
     drops to one column and keeps its own ratio. The stagger is the point. */
  .cards{grid-template-columns:repeat(2,1fr)}
  .card[data-shape="wide"]{grid-column:span 1;align-self:end}
  .card[data-shape="wide"] .ph{aspect-ratio:4/3}
}
@media (max-width:620px){
  .cards{grid-template-columns:1fr;gap:var(--s2)}
  .card[data-shape="tall"] .ph{aspect-ratio:3/4}
}
.card{position:relative;display:block;text-decoration:none;overflow:hidden;
  border:1px solid var(--line);background:var(--surface);border-radius:2px;
  transition:border-color .3s var(--ease),transform .45s var(--ease)}
.card:hover{border-color:var(--gold)}
/* height:auto is LOAD BEARING, not tidiness. The img carries width and height attributes so
   the browser can reserve the box before the bytes arrive, and those attributes are
   presentational hints: they set CSS height. With width:100% and an inherited height, BOTH
   axes are specified and aspect-ratio is ignored outright, which rendered every card at the
   intrinsic pixel height of its source: a 2:1 landscape came out 1026px tall in a 588px
   column. Removing the attributes instead would fix the ratio and reintroduce layout shift.
   Keep the attributes, and let height:auto hand the second axis back to aspect-ratio. */
.card .ph{width:100%;height:auto;display:block;object-fit:cover;object-position:center 30%;
  filter:saturate(.86) contrast(1.03) brightness(var(--g,1));
  transition:filter .5s var(--ease),transform .7s var(--ease);transform:scale(1)}
.card:hover .ph{filter:saturate(1) contrast(1.05) brightness(calc(var(--g,1) * 1.06));
  transform:scale(1.035)}

/* SUBJECT ANCHORS. object-fit:cover crops around the centre of the frame, and none of these
   six subjects is at the centre of theirs: the two landscapes deliberately place the person
   to one side with negative space beside them, which is what makes them landscapes. A single
   shared object-position therefore crops the head off two frames and the subject entirely out
   of a third. These pairs are the subject's own position in each source, read off the file.
   They apply to the card AND the strip cell, because both crop the same picture. Re-read them
   whenever an image is regenerated. */
.card[data-goal="shape"] .ph,.bandcell[data-goal="shape"] img{object-position:64% 24%}
.card[data-goal="energy"] .ph,.bandcell[data-goal="energy"] img{object-position:52% 26%}
.card[data-goal="recovery"] .ph,.bandcell[data-goal="recovery"] img{object-position:48% 22%}
.card[data-goal="skin"] .ph,.bandcell[data-goal="skin"] img{object-position:62% 34%}
.card[data-goal="sleep"] .ph,.bandcell[data-goal="sleep"] img{object-position:12% 40%}
.card[data-goal="focus"] .ph,.bandcell[data-goal="focus"] img{object-position:42% 22%}
/* GRADE PASS, measured by grade.py, never guessed. The six mosaic frames span 3.64x in mean
   luminance over a centre crop (skin 41, sleep 150), which reads as six stock photographs
   rather than one commissioned set. These multipliers pull the set 62 percent of the way to
   a common mean and land the spread at 2.00x, close enough to read as one grade while the two
   deliberately moody frames stay moody. skin is CLAMPED at the 1.42 ceiling: the clamp is
   there so that a frame needing more than this is regenerated rather than rescued in CSS.
   Re-run `python3 grade.py` and paste its output here whenever an image changes. */
.card[data-goal="shape"] .ph,.bandcell[data-goal="shape"] img{--g:1.14}
.card[data-goal="energy"] .ph,.bandcell[data-goal="energy"] img{--g:1.18}
.card[data-goal="recovery"] .ph,.bandcell[data-goal="recovery"] img{--g:0.92}
.card[data-goal="skin"] .ph,.bandcell[data-goal="skin"] img{--g:1.42}
.card[data-goal="sleep"] .ph,.bandcell[data-goal="sleep"] img{--g:0.78}
.card[data-goal="focus"] .ph,.bandcell[data-goal="focus"] img{--g:1.21}
/* THE CARD SAYS WHAT IT IS. Goal name first and largest, the promise as subtext under it,
   then the action named outright. The earlier order, a tiny gold eyebrow above a big serif
   promise, was better looking and worse: nothing on it told the reader that clicking opened a
   questionnaire. The scrim is deeper than before because there are now three lines to keep
   legible over a photograph, including over the bright upper half of the sleep frame. */
.card .meta{position:absolute;inset:auto 0 0 0;padding:var(--s3);
  background:linear-gradient(to top,rgba(11,13,16,.96) 12%,rgba(11,13,16,.82) 48%,rgba(11,13,16,0))}
.card .goal{font-family:var(--serif);font-size:clamp(23px,2.1vw,28px);color:var(--ink);
  line-height:1.05;letter-spacing:-.02em;display:block}
.card .line{font-size:14px;line-height:1.4;color:var(--body);display:block;
  margin-top:var(--s1);max-width:30ch}
.card .go{display:inline-flex;align-items:center;gap:var(--s1);margin-top:var(--s2);
  font:600 11px/1 var(--sans);letter-spacing:.16em;text-transform:uppercase;color:var(--gold)}
/* The arrow is drawn, not typed: a literal character here would be read out in the middle of
   the link's accessible name. Only its WIDTH animates, not the flex gap: the gap is on the
   8px scale the whole file is held to, and an off-scale hover value fails the gate. */
.card .go::after{content:"";width:14px;height:1px;background:var(--gold);
  transition:width .3s var(--ease)}
.card:hover .go::after{width:26px}
.card .hair{position:absolute;left:var(--s3);right:var(--s3);bottom:0;height:1px;
  background:var(--gold);transform:scaleX(0);transform-origin:left;
  transition:transform .45s var(--ease)}
.card:hover .hair{transform:scaleX(1)}

/* ---------- steps ---------- */
.steps{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s5);counter-reset:st}
@media (max-width:900px){.steps{grid-template-columns:repeat(2,1fr);gap:var(--s4)}}
@media (max-width:560px){.steps{grid-template-columns:1fr}}
.step{counter-increment:st;padding-top:var(--s3);border-top:1px solid var(--line)}
.step::before{content:counter(st,decimal-leading-zero);font-family:var(--serif);
  font-size:13px;color:var(--gold);display:block;margin-bottom:var(--s2);letter-spacing:.06em}
.step h4{margin-bottom:var(--s1)}
.step p{font-size:15px;margin:0;color:var(--body)}

/* ---------- generic panel + list ---------- */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:2px;padding:var(--s5)}
@media (max-width:600px){.panel{padding:var(--s4)}}
.two{display:grid;grid-template-columns:1fr 1fr;gap:var(--s6)}
@media (max-width:860px){.two{grid-template-columns:1fr;gap:var(--s5)}}
.promise{list-style:none;margin:0;padding:0}
.promise li{padding:var(--s3) 0;border-bottom:1px solid var(--line-soft)}
.promise li:last-child{border-bottom:0}
.promise b{display:block;font-family:var(--serif);font-size:19px;color:var(--ink);
  margin-bottom:var(--s1);font-weight:600}
.promise span{font-size:15px}

/* ---------- faq ---------- */
details.faq{border-bottom:1px solid var(--line-soft)}
details.faq summary{cursor:pointer;list-style:none;padding:var(--s3) 0;
  font-family:var(--serif);font-size:19px;color:var(--ink);display:flex;
  justify-content:space-between;gap:var(--s3);align-items:baseline}
details.faq summary::-webkit-details-marker{display:none}
details.faq summary::after{content:"+";color:var(--gold);font-family:var(--sans);
  font-size:18px;flex:0 0 auto}
details.faq[open] summary::after{content:"\2212"}
details.faq .a{padding:0 0 var(--s3);font-size:16px;max-width:var(--measure)}

/* ---------- footer ---------- */
.foot{border-top:1px solid var(--line);padding-block:var(--s6);margin-top:var(--s7);
  background:#090B0D}
.foot .cols{display:grid;grid-template-columns:2fr 1fr 1fr;gap:var(--s5)}
@media (max-width:800px){.foot .cols{grid-template-columns:1fr;gap:var(--s4)}}
.foot h5{font:600 11px/1 var(--sans);letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);margin:0 0 var(--s3)}
.foot ul{list-style:none;margin:0;padding:0}
.foot li{margin-bottom:var(--s2)}
.foot a{font-size:14px;color:var(--body);text-decoration:none}
.foot a:hover{color:var(--ink)}
.foot .legal{margin-top:var(--s5);padding-top:var(--s4);border-top:1px solid var(--line-soft);
  font-size:13px;color:var(--muted);max-width:82ch}
.foot .legal p{font-size:13px;margin-bottom:var(--s2);max-width:none}
.parent{color:var(--body)}
.parent b{color:var(--ink);font-weight:600}

/* ---------- disclaimer block ---------- */
.disc{border-left:2px solid var(--gold-deep);padding:var(--s2) 0 var(--s2) var(--s3);
  font-size:14px;color:var(--body);max-width:78ch;background:transparent}
.disc b{color:var(--cream)}

/* ---------- reveal on scroll ---------- */
/* Scoped to html.js, which an inline script in the head sets. With JavaScript off the class
   is never added, these rules never match, and the page renders in full rather than blank.
   Hiding content by default and relying on a script to reveal it is a single point of failure
   for the whole site; this makes the failure mode "no animation" instead of "no page". */
.js .rev{opacity:0;transform:translateY(14px);
  transition:opacity .7s var(--ease),transform .7s var(--ease)}
.js .rev.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.js .rev{opacity:1;transform:none;transition:none}}

/* ---------- forms, used by the questionnaire ---------- */
label{display:block;font-size:14px;color:var(--body);margin-bottom:var(--s1)}
input[type=text],input[type=number],select,textarea{
  width:100%;background:var(--raised);border:1px solid var(--line);color:var(--ink);
  border-radius:2px;padding:var(--s2);font:400 16px/1.4 var(--sans)}
textarea{min-height:96px;resize:vertical}
input::placeholder,textarea::placeholder{color:#5C6066}

/* ---------- questionnaire ---------- */
.qhead{display:flex;gap:var(--s3);align-items:center;margin-bottom:var(--s5)}
.ring{flex:0 0 64px}
.qhead h2{font-size:clamp(24px,3vw,34px);margin-top:var(--s1)}
.qmeta{font-size:13px;margin:var(--s1) 0 0}
.qform{display:flex;flex-direction:column;gap:var(--s5)}
fieldset.q{border:0;padding:0;margin:0;transition:opacity .3s var(--ease)}
fieldset.q legend{font-family:var(--serif);font-size:20px;color:var(--ink);
  line-height:1.25;padding:0;margin-bottom:var(--s2)}
fieldset.q.miss legend{color:var(--gold)}
.qhelp{font-size:14px;color:var(--muted);margin:0 0 var(--s2);max-width:56ch}
.opts{display:flex;flex-direction:column;gap:var(--s1)}
.opt{display:flex;align-items:center;gap:var(--s2);padding:var(--s2);cursor:pointer;
  border:1px solid var(--line);border-radius:2px;background:var(--surface);
  font-size:15px;color:var(--body);transition:border-color .2s var(--ease),color .2s var(--ease)}
.opt:hover{border-color:#39414A;color:var(--ink)}
.opt.on{border-color:var(--gold);color:var(--ink)}
.opt input{accent-color:var(--gold);flex:0 0 auto;margin:0}
.qnav{display:flex;justify-content:space-between;align-items:center;gap:var(--s2);
  margin-top:var(--s6);padding-top:var(--s4);border-top:1px solid var(--line)}

/* ---------- the plan ---------- */
.plan{max-width:760px}
.planhead{padding-bottom:var(--s4);border-bottom:1px solid var(--line);margin-bottom:var(--s5)}
.quoted{font-family:var(--serif);font-size:19px;color:var(--body);margin-top:var(--s2)}
.quoted em{color:var(--cream);font-style:italic}
.pblock{margin-bottom:var(--s6)}
.pblock h3{margin-bottom:var(--s3)}
.phase h3{color:var(--gold)}
.plist{margin:0 0 var(--s3);padding-left:var(--s3)}
.plist li{margin-bottom:var(--s2);max-width:var(--measure)}
.plist.num{list-style:decimal}
.pmeta{margin:var(--s3) 0 0;padding:var(--s3);background:var(--surface);
  border:1px solid var(--line);border-radius:2px}
.pmeta dt{font:600 11px/1 var(--sans);letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold);margin-bottom:var(--s1)}
.pmeta dd{margin:0 0 var(--s3);font-size:15px}
.pmeta dd:last-child{margin-bottom:0}
.planactions{display:flex;gap:var(--s2);flex-wrap:wrap;margin-top:var(--s5)}

/* ---------- tools ---------- */
.tool{background:var(--surface);border:1px solid var(--line);border-radius:2px;
  padding:var(--s4);margin-bottom:var(--s4)}
.tool h3{margin-bottom:var(--s2)}
.tool .row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3);margin-bottom:var(--s3)}
@media (max-width:600px){.tool .row{grid-template-columns:1fr}}
.tool .out{font-family:var(--serif);font-size:26px;color:var(--gold);
  padding-top:var(--s3);border-top:1px solid var(--line);font-variant-numeric:tabular-nums}
.tool .note{font-size:13px;color:var(--muted);margin:var(--s2) 0 0}

/* ---------- print: the plan is the deliverable ---------- */
@media print{
  #helix,.top,.foot,.planactions,.qnav{display:none!important}
  body{background:#fff;color:#111}
  h1,h2,h3,h4,.quoted em{color:#000}
  .pmeta{background:#f6f4f1;border-color:#ddd}
  .plist li,.pmeta dd{color:#222}
  .pmeta dt{color:#8a6d2f}
  .phase h3{color:#8a6d2f}
  a{color:#000}
  section{padding-block:16px}
}
