/* ---- where this file came from --------------------------------------------
   The Vivifye visual system, copied whole from vivifye.com's repo (main at
   0573099, public/site.css) on 12 September 2026, when vivifye.com became a
   one-page parent-company site and this site took over the look. From that
   date this is the full copy; the one in the vivifye repo is the trimmed one,
   cut down to its single page. Changes to the system are made here. */

/* ---- the typefaces, self-hosted ----------------------------------------
   These lived on fonts.googleapis.com and cost 891ms of render-blocking on a
   phone: a DNS+TLS round-trip to fetch 1 KB of CSS, then a second origin for
   the woff2s. Same files (latin + latin-ext subsets, variable 400-700), now
   served from /fonts with the page, declared here so they cost zero extra
   requests before first paint. */
/* latin-ext */
@font-face {
  font-family: 'Ibarra Real Nova';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/ibarra-real-nova-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Ibarra Real Nova';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/ibarra-real-nova-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Ibarra Real Nova';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/ibarra-real-nova-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Ibarra Real Nova';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/ibarra-real-nova-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/inter-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/inter-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/inter-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/inter-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* site.css — Vivifye brand system, v2.1 (the re-spark, Harper-faithful).
   Two colors, two typefaces, one grain.
   Ink #181815 · Paper #fbf9f4 · Ibarra Real Nova (display) · Inter (everything else). */

:root{
  --ink:#181815;
  --paper:#fbf9f4;
  --tx:#f4f2ea;        /* text on ink */
  --tx2:#a3a094;       /* secondary on ink */
  --ln:#2d2c26;        /* hairline on ink */
  --ptx:#181815;       /* text on paper */
  --ptx2:#5d5a4f;      /* secondary on paper */
  --pln:#e3e0d3;       /* hairline on paper */
  /* The green from the mug in the photographs, Pantone 15-0343.
     It is a state colour, never decoration: progress, selection, focus,
     and the moment a link answers you. --gr is for ink backgrounds
     (7.1:1), --gr-p is the darker version paper needs (4.9:1). */
  --gr:#88b04b;
  --gr-p:#5a7731;
  /* The other state colour, and the one that was missing: every form on the
     site writes class="fmsg err" when a send fails or a field is wrong, and
     nothing anywhere defined either class, so "that didn't send" rendered as
     ordinary grey body copy. Same pairing as the green — --rd for ink
     backgrounds (6.7:1), --rd-p for paper (7.2:1). */
  --rd:#d98b76;
  --rd-p:#8c3b2e;
  --ease:cubic-bezier(.22,.68,.14,1);

  /* ---- the type scale ----------------------------------------------------
     Ten steps, and everything on the site is one of them. It used to be
     thirty-two: 17, 16.5 and 16 were all doing the job of body copy in
     different places, 12 and 12.5 were both the label size, 44/46/47 were all
     the section heading. Half a pixel does no work — it only means two blocks
     that should feel identical don't quite.

     d = display (Ibarra, tight leading) · s = editorial serif, running
     b = body (Inter) · u = uppercase UI labels

     s2 and b1 look like a near-duplicate and are not: a serif needs to be set
     a little larger than a sans to read the same size beside it. That line was
     already here on 31 August 2026 and got overridden anyway; see the x-height
     note below for what it cost.

     ---- rebuilt 31 August 2026, because the hierarchy had collapsed --------
     The scale was measured rather than eyeballed, and on a 375px phone it
     came out like this: h3 20.5, .il 18.5, .hand 18, body 17. A heading three
     and a half pixels larger than the paragraph under it is not a heading.
     Four of the ten steps were also flat pixel values, so they never moved:
     the scale opened up on a desktop and shut again on a phone, which is
     backwards from what a phone needs. Every step is fluid now, so the
     distance between two steps is the same relationship at 375px as at 1440px.

     ---- corrected the same day, because the serif went too far down --------
     The first pass also pushed the running serif BELOW the body, reasoning
     that an aside should be smaller than what it comments on. In px that is
     true. In what the eye actually reads it was wrong, and badly:

       font          size    x-height
       Inter body    17.3px    9.4px
       Ibarra .hand  19.0px    8.2px   <- a label, reading smaller than body
       Ibarra .il    17.5px    7.6px   <- a call to action, 19% under body

     Ibarra Real Nova has an x-height of 0.43 em against Inter's 0.543, so it
     has to be set about 26% LARGER than the sans to read the same size beside
     it. Set to the same number it reads a size smaller; set below it, in
     italic, on a dark ground, it stops being comfortably legible at all. The
     comment at the top of this block said exactly this and the first pass
     overrode it anyway.

     ---- up 25%, then back 10%, both on 31 August 2026 --------------------
     Two instructions, one after the other, so the multiplier these steps
     carry is 1.25 x 0.9 = 1.125. They are written out at their arrived-at
     values rather than as a chain, because the next person needs the number
     the browser gets, not the history of how it got there. d1-d4, s1, s2
     and u1 carry it. Three
     things are held out of it. b2 is the body copy the instruction exempted,
     and b1 and b3 are the same face at the sizes either side of it. The two
     wordmarks are exempt as well, by a second instruction: they had just been
     set by eye in the commit before this one and were already where they
     should be, so they keep their own steps and no longer move with the
     ladder at all. b3 in particular could not take it — hints and captions at
     14.5 x 1.25 is 18.1, which would set the small print LARGER than the body
     it qualifies, and that is the inversion this whole block exists to stop.
     If the captions should grow too, they need the body to move first.

     So the governing rule is x-height parity, not px parity. s2 is set to sit
     level with the body it links out of; s1 a step above it, because it labels
     rather than runs; and h3 far enough above both to lead a card. Those three
     were what looked almost illegible, and they were measured before and
     after rather than eyeballed twice. */
  /* The maxima are the desktop sizes, and they are set against the column the
     type actually lives in [about 560px in a two-up band], not against the
     window. At 90px an h1 fitted six characters to a line and ran to four,
     which is a poster, not a page. The minima are the phone and are unchanged.
     The ramp settles at its desktop size around 1000px rather than growing
     with the monitor, because past that width the column stops growing too. */
  --fs-d1:clamp(48.5px,6.98vw,68px);   /* h1 */
  --fs-d2:clamp(36px,4.84vw,46px);     /* h2, band headings, pull quotes */
  --fs-d3:clamp(28px,3.38vw,33px);     /* mid display: numerals, the footer mark */
  --fs-d4:clamp(29.5px,3.04vw,32px);   /* h3 */
  --fs-s1:clamp(23.5px,2.08vw,24.5px); /* the hand-written aside, statements */
  --fs-s2:clamp(22.5px,1.92vw,23px);   /* inline serif links, FAQ questions */
  --fs-b1:clamp(17.5px,1.45vw,19px);   /* lead paragraph */
  --fs-b2:clamp(17px,1.2vw,17.5px);    /* body */
  --fs-b3:clamp(14px,1.05vw,14.5px);   /* hints, captions, small print */
  --fs-u1:clamp(13.5px,1.07vw,14px);   /* buttons, eyebrows, footer nav */
  /* grain.webp is a 448px WebP of the real grain tile — 70K against the 574K
     PNG it replaced, invisible at 3.5% alpha, on every page load.
     Until that file exists the browser simply skips the layer and paints the
     generated one underneath, so nothing breaks either way. */
  --grain:url("/img/grain.webp"), url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/><feComponentTransfer><feFuncA type='linear' slope='0.055'/></feComponentTransfer></filter><rect width='240' height='240' filter='url(%23n)'/></svg>");
}
*{margin:0;padding:0;box-sizing:border-box}
.hpot{position:absolute!important;left:-9999px!important;width:1px;height:1px;opacity:0;pointer-events:none}
/* Read aloud, never drawn: the words that turn "02" into "item 2 of 5". */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
:focus-visible{outline:2px solid var(--gr);outline-offset:3px}
.lt :focus-visible{outline-color:var(--gr-p)}
html{scroll-behavior:smooth}
body{background:var(--ink);color:var(--tx);font-family:'Inter',system-ui,sans-serif;font-size:var(--fs-b2);line-height:1.52;-webkit-font-smoothing:antialiased;overflow-x:hidden}
img{display:block;max-width:100%}
p{color:var(--tx2)}

/* ---- form status lines ----
   Every form writes into a <p class="fmsg">, and swaps in "err" when something
   went wrong. Both live here so a failure never again looks like a footnote. */
.fmsg{font-size:var(--fs-b3);line-height:1.5;color:var(--tx2);min-height:1.55em}
.fmsg.err{color:var(--rd)}
.lt .fmsg{color:var(--ptx2)}
.lt .fmsg.err{color:var(--rd-p)}

/* Nothing here ever set a colour on a bare <a>, so every link is coloured only
   by its own class and an unclassed one falls back to browser blue on a dark
   ink page. That is what happened to the referral line on /about, and the
   reason inline prose links are written as style="color:inherit" all over the
   site. Inheriting by default makes that the behaviour rather than the habit;
   every link component below still sets its own colour explicitly. */
a{color:inherit}
.w{max-width:1150px;margin:0 auto;padding:0 30px}
.wide{max-width:1440px;margin:0 auto;padding:0 30px}

/* ---- type ---- */
h1,h2,h3{font-family:'Ibarra Real Nova',Georgia,serif;font-weight:400;line-height:1.1}
h1{line-height:1.0}
h2{line-height:1.05}
h1 em,h2 em,h3 em{font-style:italic}

/* ---- Ibarra optical tracking ----------------------------------------------
   Ibarra Real Nova is drawn generously wide, so every size is set negative and
   the tracking tightens as the type grows. Italics run slightly looser than the
   roman at the same size, because the slanted forms collide sooner. Set once
   here so nothing has to remember it. */
h1{letter-spacing:-.028em}
h2{letter-spacing:-.021em}
h3{letter-spacing:-.015em}
h1 em{letter-spacing:-.02em}
h2 em{letter-spacing:-.015em}
h3 em{letter-spacing:-.011em}
/* running-size serif: the hand-written asides and inline links */
.hand,.il,.top a,.wb .who,.note .sig,.parx .cap .line em{letter-spacing:-.011em}
.parx .cap .line{letter-spacing:-.022em}
/* the very large numerals, which need the most closing up of anything */
.phase .big{letter-spacing:-.038em}
.inv .amt,.wb .amt,.bnd .amt,.five .card .n,.steps .n,.jour .yrs{letter-spacing:-.026em}
/* ---- where the green actually lands ----
   The palette calls this the state colour and then only ever spent it on
   :hover, :focus and ::selection, so a page nobody had touched yet showed the
   brand colour exactly once: a 2px scroll rule at the very top. These are the
   three places it now sits at rest — the label above every section, the
   numerals that count something, and the dash that opens a list item. Small,
   repeated, structural. It should read as a system, not as decoration. */
.five .card .n,.steps .n,.jour .yrs,.phase .big{color:var(--gr)}
.lt .five .card .n,.lt .steps .n,.lt .jour .yrs,.lt .phase .big{color:var(--gr-p)}
/* the FAQ plus sign is a glyph, not a word */
.faq summary:after{letter-spacing:0}
h1{font-size:var(--fs-d1)}
h2{font-size:var(--fs-d2)}
h3{font-size:var(--fs-d4)}
em{font-family:'Ibarra Real Nova',Georgia,serif;font-style:italic}
/* An <em> inside sans body copy swaps Inter for Ibarra at the SAME px, and
   Ibarra's smaller x-height means the emphasised words then read a size down
   from the sentence they are emphasising. The nudge is optical, not a step on
   the scale: full parity would be 1.26 and would shout. Headings are already
   Ibarra on both sides of the swap, so they are exempt. */
p em,li em,.sub em,dd em,figcaption em{font-size:1.07em}

/* ---- the leading curve ----
   h1 1.0 → h2 1.05 → h3 1.1 is a considered curve, and it used to stop there:
   everything below 20px fell through to the body's leading, which is right for
   Inter and much too airy for Ibarra, where the lines stop reading as one
   block. So the curve continues down through the running serif, and only the
   sans body sits at the loose end of it.

   Tightened 31 August 2026, along with the scale above. Body went 1.62 → 1.52
   and stops there on purpose: 1.5 is the floor WCAG 1.4.12 expects text to
   survive being set to, so going under it to buy density would be trading a
   real accessibility guarantee for a look. Everything above the body is
   display type at a size where loose leading reads as drift, not air. */
.hand{line-height:1.34}
.il,.top a{line-height:1.3}
.sub{line-height:1.45}
/* An uppercase label at .106em tracking has no descender stack to clear, so
   the body's leading only showed up as a gap when it wrapped to two lines. */
.lb{line-height:1.32}

/* ---- the eyebrow ----
   [ like this ]. It sits above almost every heading on the site and its gap to
   that heading was set inline, page by page, at 14px, 16px, 20px and 24px. One
   value, declared once; it is the same relationship every time it appears. */
.lb{font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:0.106em;text-transform:uppercase;color:var(--gr)}
.lb:not(.inline){display:block;margin-bottom:18px}
.hero .lb{margin-bottom:26px}   /* the one above a page title carries more air */
.lt .lb{color:var(--gr-p)}
.sub{color:var(--tx2);font-size:var(--fs-b1);max-width:54ch}
.lt .sub{color:var(--ptx2)}
.hand{font-family:'Ibarra Real Nova',Georgia,serif;font-style:italic;font-size:var(--fs-s1);text-transform:none}

/* ---- bands ---- */
.lt{background-color:var(--paper);background-image:var(--grain);color:var(--ptx)}
.lt p{color:var(--ptx2)}
.lt h1,.lt h2,.lt h3{color:var(--ptx)}
/* ---- band rhythm ----
   Every band on every page carried the same padding top and bottom, so two
   neighbours always put 220px of nothing between their content and a page like
   /tools read as one undifferentiated column seven thousand pixels long. Pace
   it instead: a band that changes ground colour gets the full measure, because
   the colour change is the pause; a band that continues on the same ground
   closes up, because it is still the same thought. */
section{padding:var(--band) 0}
:root{--band:clamp(64px,8vw,88px);--band-tight:clamp(44px,5.2vw,60px)}
/* same ground as the band above it → keep going, don't restate the pause */
.lt + .lt,
section:not(.lt) + section:not(.lt){padding-top:var(--band-tight)}
/* a divider rule is itself the pause, so the band after one starts close */
.dv + section{padding-top:var(--band-tight)}
.shead{margin-bottom:44px}


/* ---- nav ---- */
nav.bar{position:sticky;top:0;z-index:60;background:rgba(24,24,21,.94);backdrop-filter:blur(8px);border-bottom:1px solid var(--ln)}
/* The bar is sticky and measures about 86px, so anything jumped to landed
   underneath it — the skip link's own target included, which is the one jump
   that has to work. 104px is that plus room to breathe. On [id] rather than a
   list of anchors, because scroll-margin costs nothing until something is
   actually scrolled to, and a list would go stale the next time one is added. */
[id]{scroll-margin-top:104px}
.nv{display:flex;justify-content:space-between;align-items:center;padding:20px 30px;max-width:1440px;margin:0 auto}
/* the wordmark, set rather than drawn. Caps in a serif need a little air to
   read as a mark instead of a cramped word, so these are the only positive
   tracking values left on Ibarra anywhere, and they are small. */
/* The wordmark used to ride on s2. It is set in caps, so it has no x-height
   problem to solve and the parity correction that s2 exists for does not
   apply to it; following s2 up would have sized it by an argument that is not
   about it. It carries its own step instead, and that step was raised on
   31 August 2026 because the name was the quietest thing in a bar that also
   holds a filled button. Caps at 24px against a 12.5px uppercase nav reads as
   the mark rather than another link. */
.lg{display:inline-flex;align-items:center;font-family:'Ibarra Real Nova',Georgia,serif;font-size:clamp(21px,1.7vw,24px);letter-spacing:.055em;color:var(--tx);text-decoration:none;transition:color .3s var(--ease)}
.lg:hover{color:var(--gr)}
/* This used to drop the mark to body size on a narrow phone, back when the
   base was 18.5px and the bar was tight. The base is larger now and the drop
   would undo the whole change on the screens most people are on, so it steps
   down rather than collapsing. */
@media(max-width:520px){.lg{font-size:20px}}
@media(max-width:820px){.lg{padding:9px 0}}
.nvr{display:flex;align-items:center;gap:clamp(16px,2.5vw,30px)}
.ncta{font-family:'Inter',sans-serif;font-weight:600;font-size:var(--fs-u1);letter-spacing:.09em;text-transform:uppercase;color:var(--tx);text-decoration:none;border:1px solid var(--ln);padding:9px 16px;transition:border-color .3s var(--ease),color .3s var(--ease);white-space:nowrap}
.ncta:hover{border-color:var(--gr);color:var(--gr)}
/* The persistent CTA used to be display:none below 560px, which is the phone —
   the device most of the traffic arrives on — so every page lost its standing
   call to action exactly where it mattered most. It stays now; the label is
   what shrinks. */
.ncta .cta-s{display:none}
@media(max-width:560px){
  .ncta{padding:9px 12px;letter-spacing:.06em}
  .ncta .cta-l{display:none}
  .ncta .cta-s{display:inline}
}
@media(max-width:360px){.ncta{padding:8px 10px}}

/* Nine destinations behind a hamburger at every width, 1440px included, is a
   phone pattern wearing a desktop. Four of them come out on a wide screen —
   the check (that's the button), the tools, work with me, about — and the
   overlay keeps the rest. */
.nvl{display:none}
.nvl a{font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.09em;text-transform:uppercase;color:var(--tx2);text-decoration:none;padding:9px 0;transition:color .3s var(--ease)}
.nvl a:hover,.nvl a[aria-current="page"]{color:var(--gr)}
@media(min-width:900px){.nvl{display:flex;align-items:center;gap:clamp(18px,2.2vw,30px)}}

/* skip link — first focusable thing on the page, invisible until it is used */
.skip{position:absolute;left:-9999px;top:0;z-index:300;background:var(--ink);color:var(--tx);border:1px solid var(--gr);
  font-family:'Inter',sans-serif;font-weight:600;font-size:var(--fs-u1);letter-spacing:.09em;text-transform:uppercase;padding:12px 18px;text-decoration:none}
.skip:focus{left:12px;top:12px}
/* The burger is the only way to navigate on a phone and it was a 28x35 target,
   smaller than anything else on the page. Same 44x44 as the close button it
   opens — the bars are unchanged, the box around them is not. */
.bgr{display:flex;flex-direction:column;justify-content:center;align-items:center;
  width:44px;height:44px;margin-right:-10px;background:none;border:none;cursor:pointer;color:var(--tx);padding:0}
.bgr span{display:block;width:24px;height:1px;background:currentColor;margin:7px 0}
.bgr:hover{color:var(--tx2)}

/* ---- full-screen menu overlay ---- */
.mmenu{position:fixed;inset:0;z-index:200;background:var(--ink);color:var(--tx);transform:translateX(100%);visibility:hidden;transition:transform .55s var(--ease),visibility 0s .55s}
.mmenu.open{transform:none;visibility:visible;transition:transform .55s var(--ease),visibility 0s 0s}
.mmenu-in{display:flex;height:100%}
.mmenu-photo{flex:0 0 44%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:26px;padding:64px 48px;border-right:1px solid var(--ln)}
/* Two crops, everywhere: 4/5 upright and 16/10 wide. The site had six —
   4/5, 3/4, 2/3, 4/3, 16/10 and 1/1 — and on the home page the two photographs
   either side of one paragraph were 3/4 and 4/5, which reads as an accident
   rather than a pair. The square below is the single deliberate exception: it
   is an inset on top of an upright, and it needs to be a different shape. */
.mmenu-photo img{width:min(340px,100%);aspect-ratio:4/5;object-fit:cover}
.mmenu-ig{font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:0.114em;text-transform:uppercase;color:var(--tx2);text-decoration:none}
.mmenu-ig:hover{color:var(--gr)}
/* Every link in the full-screen menu was a 22px-tall target with dead space
   between them. The distance between one word and the next is unchanged —
   the gap simply moved inside the link, where it can be tapped. */
.mmenu-links{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;padding:72px 28px;overflow-y:auto}
.mmenu-links a{display:block;padding:clamp(11px,1.7vh,15px) 18px;font-family:'Inter',sans-serif;font-weight:500;font-size:clamp(13.5px,1.51vw,17px);letter-spacing:0.129em;text-transform:uppercase;color:var(--tx);text-decoration:none}
.mmenu-links a:hover{color:var(--gr)}
.mmenu-lg{position:absolute;top:26px;left:28px;z-index:2;display:inline-flex;align-items:center;padding:6px 0;font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-d4);letter-spacing:.05em;color:var(--tx);text-decoration:none;transition:color .3s var(--ease)}
.mmenu-lg:hover{color:var(--gr)}
@media(max-width:720px){.mmenu-lg{left:22px}}
.mmenu-x{position:absolute;top:26px;right:28px;width:44px;height:44px;background:none;border:none;cursor:pointer;z-index:2;padding:0}
.mmenu-x span{position:absolute;top:50%;left:8px;right:8px;height:1px;background:var(--tx)}
.mmenu-x span:first-child{transform:rotate(45deg)}
.mmenu-x span:last-child{transform:rotate(-45deg)}
.mmenu-x:hover span{background:var(--tx2)}
@media(max-width:720px){.mmenu-photo{display:none}}
@media(prefers-reduced-motion:reduce){.mmenu{transition:none}}

/* ---- form fields ----
   One underlined field, used by the contact form, the review request and the
   room's wait list. It was three implementations: .cform, .rform and .wl each
   defined their own label and their own control, byte for byte the same except
   for a label margin of 30px, 28px and 6px. A change to the focus colour, the
   font size or the touch target had to be made in three places and had already
   drifted in three. The fit quiz is a fourth variant and stays where it is:
   its fields are built by script and answer a different shape of question.

   Layout stays with the page, because that is the part that genuinely differs:
   .cform has a two-up row, .wl is a flex row with a button on the end. Only
   the field itself moved here. */
.fields label{display:block;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.084em;text-transform:uppercase;color:var(--ptx2);margin:28px 0 4px}
.fields input,.fields textarea,.fields select{width:100%;background:none;border:none;border-bottom:1px solid var(--pln);color:var(--ptx);font:inherit;font-size:var(--fs-s2);padding:8px 2px 12px;outline:none;resize:vertical}
.fields input:focus,.fields textarea:focus,.fields select:focus{border-bottom-color:var(--ptx2)}

/* ---- steps ----
   A numbered run of three, used by /room for its six sessions and by /review
   for its three stages. It was two components with two names, .rsteps and
   .hsteps, defined separately on their own pages and identical apart from a
   text-align and two margins that differed by 2px. Two pixels do no work; they
   only mean two blocks that should feel the same do not quite, and that a
   change to one is a change somebody has to remember to make twice. The
   contrast pass had already had to name both in one rule, which was the tell.

   .steps is the component. .steps.mid is the centred variant /review wants. */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(24px,3vw,44px)}
.steps.mid{text-align:center}
.steps .n{font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-d2);line-height:1;display:block;margin-bottom:10px}
.steps h3{margin-bottom:8px}
.steps p{font-size:var(--fs-b2)}
@media(max-width:860px){.steps{grid-template-columns:1fr}}

/* ---- proof ----
   Nothing on this site is verified by anybody but Vivian: no testimonial, no
   named client, no case study, no outcome number, against prices from $290 to
   $9,500. Everything else here is built to earn trust honestly — the prices
   are on the page, the refund is unconditional, the free tier is real — which
   makes the absence louder rather than quieter.

   Filling this in needs other people, so the component is here and the three
   slots are marked in the markup [home above the trio, /work-with-me above the
   pricing, /tools beside a buy button]. One line of attributed evidence beats
   another paragraph of prose. If an NDA stops you naming someone, "a two-person
   design studio, Denver" plus a number still counts; "clients love it" does
   not, and neither does an unattributed quote. */
.proof{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--ln);border:1px solid var(--ln);max-width:1040px;margin:0 auto}
.proof>figure{background:var(--ink);margin:0;padding:clamp(24px,3vw,34px);display:flex;flex-direction:column;gap:14px}
.proof blockquote{margin:0;font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-s2);line-height:1.45;color:var(--tx)}
.proof .num{font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-d3);line-height:1;color:var(--gr)}
.proof figcaption{margin-top:auto;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.09em;text-transform:uppercase;color:var(--tx2)}
.lt .proof,.lt .proof>figure{border-color:var(--pln)}
.lt .proof{background:var(--pln)}
.lt .proof>figure{background:var(--paper)}
.lt .proof blockquote{color:var(--ptx)}
.lt .proof figcaption{color:var(--ptx2)}
.lt .proof .num{color:var(--gr-p)}
/* one on its own, for the slot beside a buy button */
.proof.one{grid-template-columns:1fr;max-width:560px;margin:0}
@media(max-width:880px){.proof{grid-template-columns:1fr}}

/* ---- the ladder ----
   The business model is a clean price ladder where every rung credits into
   the next, and that is the most persuasive fact about the offer. It existed
   only as sentences scattered over five pages; nowhere did the ladder look
   like a ladder. This is that picture.

   Deliberately not an SVG: the rungs have to reflow on a phone, stay
   selectable, print, and be read out in order by a screen reader. The rising
   bar behind each row is a pseudo-element sized off --i, so a rung can be
   added by writing one <li>. */
.ladder{--rungs:6;max-width:860px;margin:0 auto}
.ladder ol{list-style:none;margin:0;padding:0;display:grid;gap:1px;background:var(--ln);border:1px solid var(--ln)}
.ladder li{position:relative;background:var(--ink);display:grid;
  grid-template-columns:minmax(112px,.62fr) minmax(0,.9fr) minmax(0,1.5fr);
  align-items:baseline;gap:4px 24px;padding:16px 20px}
/* the rung itself: a bar that grows one step per row */
.ladder li:before{content:"";position:absolute;inset:0 auto 0 0;pointer-events:none;
  width:calc((var(--i) + 1) / var(--rungs) * 100%);background:var(--gr);opacity:.11}

/* ---- the ladder builds itself as you climb it --------------------------
   The bars were already here and already said the right thing: each rung is
   a step longer than the one under it, so the shape of the section is the
   shape of the offer. It just said it all at once, to a reader who had not
   arrived yet.

   Now each bar draws as its own rung crosses into view. Because the rungs
   arrive in order, the ladder assembles from the bottom up at exactly the
   rate the reader is scrolling, and "every rung pays for the next one" is
   something they watch happen instead of something they are told.

   This is the one place on the site that gets a scripted moment, and that is
   the point: sixteen things all rising 42px is not emphasis, it is weather.
   One thing that behaves differently is the thing you remember.

   Scroll-driven rather than triggered, so it tracks the thumb on iOS instead
   of firing once and finishing off-screen. Same mechanism the progress rule
   already uses. If the range never resolves the bars hold at scaleX(0) and
   the ladder reads as it always did minus its tint, which is decoration, not
   content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .ladder li:before{
      transform-origin:left center;
      animation:rungDraw linear both;
      animation-timeline:view();
      animation-range:entry 12% entry 72%;
    }
    @keyframes rungDraw{from{transform:scaleX(0)}to{transform:scaleX(1)}}
  }
}
.ladder li>*{position:relative}
.ladder .pr{font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-s2);color:var(--tx);white-space:nowrap}
.ladder .nm{font-family:'Inter',sans-serif;font-weight:600;font-size:var(--fs-u1);letter-spacing:.09em;text-transform:uppercase;color:var(--tx)}
.ladder .nt{font-size:var(--fs-b3);color:var(--tx2)}
.ladder .nm a{color:inherit;text-decoration:none;padding-bottom:2px;border-bottom:1px solid transparent;transition:border-color .3s var(--ease),color .3s var(--ease)}
.ladder .nm a:hover{color:var(--gr);border-bottom-color:currentColor}
.lt .ladder .nm a:hover{color:var(--gr-p)}
.ladder .cred{font-size:var(--fs-b3);color:var(--tx2);text-align:center;margin:18px auto 0;max-width:64ch}
/* on a light band the rows take the paper palette */
.lt .ladder ol,.lt .ladder li{border-color:var(--pln);background:var(--paper)}
.lt .ladder ol{background:var(--pln)}
.lt .ladder .pr,.lt .ladder .nm{color:var(--ptx)}
.lt .ladder .nt,.lt .ladder .cred{color:var(--ptx2)}
@media(max-width:720px){
  .ladder li{grid-template-columns:1fr;gap:2px;padding:14px 16px}
}

/* ---- arrows ---- */
.arw{display:inline-block;flex:none;align-self:center;overflow:visible}
.arw line,.arw polyline{fill:none;stroke:currentColor;stroke-width:1;stroke-linecap:square}
.arw.r{width:62px;height:9px}
.arw.l{width:62px;height:9px;transform:scaleX(-1)}
.arw.u{width:9px;height:58px}

/* ---- links & buttons ---- */
.il{font-family:'Ibarra Real Nova',Georgia,serif;font-style:italic;font-size:var(--fs-s2);color:inherit;text-decoration:none;display:inline-flex;align-items:baseline;gap:14px;white-space:nowrap;text-transform:none}
.il span{background-image:linear-gradient(currentColor,currentColor);background-repeat:no-repeat;background-size:0 1px;background-position:0 100%;transition:background-size .45s var(--ease);padding-bottom:3px}
.il:hover span{background-size:100% 1px}
.il .arw{transition:transform .5s var(--ease)}
.il:hover .arw.r{transform:translateX(7px)}
/* Buttons answer the way the divider bars do: a deliberate wipe, one axis,
   no chasing the cursor. On ink the paper button wipes to ink-with-paper-border;
   on paper the inverse. */
.btn{position:relative;overflow:hidden;isolation:isolate;display:inline-block;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:0.091em;text-transform:uppercase;background:var(--paper);color:var(--ink);border:1px solid var(--paper);padding:16px 34px;text-decoration:none;cursor:pointer;transition:color .32s var(--ease),border-color .32s var(--ease)}
.btn:before{content:"";position:absolute;inset:0;z-index:-1;background:var(--ink);transform:scaleX(0);transform-origin:0 50%;transition:transform .45s var(--ease)}
.btn:hover{color:var(--paper)}
.btn:hover:before{transform:scaleX(1)}
.lt .btn{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.lt .btn:before{background:var(--paper)}
.lt .btn:hover{color:var(--ink)}
.btn:hover{transform:translateY(-2px)}
.btn.ghost{background:none;color:var(--tx);border-color:var(--ln)}
.btn.ghost:hover{border-color:var(--gr)}
.lt .btn{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.lt .btn.ghost{background:none;color:var(--ptx);border-color:var(--pln)}
.btn.wide{display:block;width:100%;text-align:center;padding:18px 34px}

/* ---- divider bar with arrows ---- */
.dv{position:relative;overflow:hidden;display:flex;align-items:center;justify-content:space-between;gap:26px;padding:26px 30px;border-top:1px solid var(--ln);border-bottom:1px solid var(--ln);font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:0.106em;text-transform:uppercase;color:var(--tx2);text-decoration:none;isolation:isolate}
/* a:hover wipes the paper across, inverts the text, and throws both arrows outward */
.dv:before{content:"";position:absolute;inset:0;z-index:-1;background:var(--paper);transform:scaleX(0);transform-origin:0 50%;transition:transform .62s var(--ease)}
.dv>*{position:relative;transition:color .4s var(--ease),transform .62s var(--ease)}
.dv .lbl{display:inline-block;transition:letter-spacing .62s var(--ease),color .4s var(--ease)}
a.dv:hover:before{transform:scaleX(1)}
a.dv:hover{color:var(--ink)}

a.dv:hover .lbl{letter-spacing:.16em}
a.dv:hover .arw.l{transform:scaleX(-1) translateX(14px)}
a.dv:hover .arw.r{transform:translateX(14px)}
/* the same wipe on any full-width button */
.btn.wide{position:relative;overflow:hidden;isolation:isolate}
.btn.wide:before{content:"";position:absolute;inset:0;z-index:-1;background:var(--ink);transform:scaleX(0);transform-origin:0 50%;transition:transform .55s var(--ease)}
.lt .btn.wide:before{background:var(--paper)}
.btn.wide:hover{color:var(--paper);transform:none}
.lt .btn.wide:hover{color:var(--ink)}
.btn.wide:hover:before{transform:scaleX(1)}

/* ---- "really good at" strip: three text columns, hairline dividers ---- */
.good{border-top:1px solid var(--ln)}
/* The label belongs to the heading, so it centres with it. It was left at the
   section's left edge while the h2 was centred, which read as a broken grid
   rather than a style: two elements of one unit starting at two different x. */
.good .lb{text-align:center}
.good h2{text-align:center;margin-bottom:clamp(36px,4.5vw,60px)}
.good .cols{display:grid;grid-template-columns:repeat(3,1fr)}
/* These three columns were centred at about 34 characters a line, which is the
   one setting that suits neither: centred type wants either a deliberate short
   rag or a wide measure, and 34ch delivers a ragged block with no axis. Ranged
   left, each column gets a spine its italic heading shares. */
.good .cols>div{padding:0 clamp(24px,3vw,48px);border-right:1px solid var(--ln);text-align:left}
.good .cols>div:last-child{border-right:none}
/* Centred copy in a column this narrow strands the last word on its own line —
   "it." sitting alone under four full lines. `pretty` costs nothing and is what
   stops that; the columns are short enough now that it rarely has to work. */
.good .cols p{font-size:var(--fs-b2);text-wrap:pretty}
.good .cols .hand{display:block;margin-bottom:12px;color:var(--tx)}
@media(max-width:860px){
  .good .cols{grid-template-columns:minmax(0,1fr);gap:34px}
  .good .cols>div{border-right:none}
  /* ---- the one pinned moment on a phone -------------------------------
     Stacked, the three columns run 669px against an 812px screen, so the
     heading has most of a screenful of travel to hold while "your hours",
     "your hands" and "your name" pass under it one at a time. That is the
     whole argument of the section, held in place while its three pieces
     go by, and it is the sentence worth still having on screen when you
     reach the third one.

     A card stack was the other option and was measured out of it: 669px is
     not enough scroll for three cards to stack over each other and read as
     deliberate rather than broken.

     Sticky rather than anything scripted on purpose. It is the one scroll
     effect iOS runs on the compositor, so it keeps up with a momentum
     scroll, which is exactly where the JS effects on this site do not.

     14px, not below the nav: the bar tucks itself away on scroll down, which
     is the direction this pin happens in, so the heading gets the top of the
     screen. Scrolling back up the bar returns over it, which is the moment
     you are leaving the section anyway. The ink background is what lets the
     columns pass underneath instead of through. */
  .good h2{
    position:sticky;top:14px;z-index:2;
    background:var(--ink);
    padding:16px 0 18px;
    margin-bottom:24px;
  }
  /* The reveal translates this heading 42px down and sticky then resolves
     against the moved box, so the pin landed 42px lower than the 14px asked
     for. A pinned element is the thing everything else is measured against;
     it should not also be sliding. It keeps the fade and gives up the travel. */
  .mjs .good h2[data-rv],.mjs .good h2[data-rv].rv-in{transform:none}
}

/* ---- three offer cards, Harper's dark trio ---- */
.trio{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(14px,1.6vw,22px)}
.trio>div{border:1px solid var(--ln);padding:clamp(34px,3.8vw,52px) clamp(24px,2.6vw,38px);text-align:center;display:flex;flex-direction:column;gap:14px}
.trio h3{margin-bottom:2px}
.trio .pr{font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:0.084em;text-transform:uppercase;color:var(--gr)}
.trio p{font-size:var(--fs-b2);flex:1}
.trio .il,.trio .btn{align-self:center}
.lt .trio>div{border-color:var(--pln)}
@media(max-width:880px){.trio{grid-template-columns:minmax(0,1fr)}}

/* ---- scroll band: one wide photo that drifts as the page moves ---- */
.parx{position:relative;overflow:hidden;height:clamp(380px,64vh,680px);border-top:1px solid var(--ln);border-bottom:1px solid var(--ln)}
.parx img{position:absolute;left:0;right:0;top:-12%;width:100%;height:124%;object-fit:cover;will-change:transform}
/* the caption owns its corner: a real scrim panel, not a whisper on a photo */
.parx .cap{position:absolute;left:0;bottom:0;right:0;padding:clamp(80px,10vw,140px) clamp(24px,5vw,60px) clamp(30px,4vw,52px);background:linear-gradient(to top,rgba(20,20,17,.88) 0%,rgba(20,20,17,.55) 45%,rgba(20,20,17,0) 100%);color:var(--tx)}
.parx .cap .lb{color:var(--tx);opacity:.85}
.parx .cap .line{font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-d2);line-height:1.12;max-width:22ch;text-shadow:0 1px 24px rgba(0,0,0,.35)}
.parx .cap .line em{font-style:italic}
@media(prefers-reduced-motion:reduce){
  .parx img{transform:none!important;top:0;height:100%}
}

/* ---- freebie-style band (dark, centered, big) ---- */
.fre .w2{max-width:1140px;margin:0 auto;padding:0 30px;display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr);gap:clamp(36px,6vw,90px);align-items:start}
.fre h2{font-size:var(--fs-d2);margin-top:14px}
/* :not([class]) rather than a bare `.fre p`. A plain paragraph in this band is
   prose and wants the lead treatment; anything carrying a class is a component
   that brings its own. The bare selector had already caught two of them: the
   form's .fmsg status line rendered at lead size, and the .lb eyebrow above
   the forwardable line was handed max-width:52ch, which made a centred label
   sit 256px left of the card it labels. */
.fre p:not([class]){margin-bottom:30px;font-size:var(--fs-b2);max-width:52ch}
.fre form{display:flex;align-items:center;gap:18px;border-bottom:1px solid var(--tx2);padding-bottom:13px;max-width:520px;transition:border-color .35s var(--ease)}
.fre form:focus-within{border-color:var(--gr)}
.fre form input[type=email]{flex:1;background:none;border:none;color:var(--tx);font:inherit;font-size:var(--fs-b2);padding:6px 2px;outline:none}
.fre form button{display:inline-flex;align-items:center;gap:10px;background:none;border:none;color:var(--tx);font-family:'Inter',sans-serif;font-weight:600;font-size:var(--fs-u1);letter-spacing:.084em;text-transform:uppercase;cursor:pointer;white-space:nowrap;padding:12px 0;min-height:44px;transition:color .3s var(--ease)}
.fre form button .arw{width:30px}
.fre form button:hover{color:var(--gr)}
.fre .fmsg{margin-top:16px}
@media(max-width:820px){.fre .w2{grid-template-columns:minmax(0,1fr)}}

/* ---- hi-i'm band with overlapping photos ---- */
.hi{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(30px,5vw,80px);align-items:center}

/* ---- the desktop moment ------------------------------------------------
   Measured at 1440x900 before choosing it: every section on this page is
   shorter than the viewport except two, so the desktop layout is a run of
   screen-sized panels and pinning is mostly the wrong instrument for it. The
   phone's pinned heading works because everything stacks into one tall
   column; there is no such column here.

   This band is the exception. The photographs run 808px against 490px of
   text, so there are 318px where one side has somewhere to go and the other
   does not. Her introduction holds still while the pictures of her move past
   it, which is the right way round for the one section on the page whose job
   is who she is rather than what she sells.

   Not the trio cards, which was the other candidate: all three carry
   data-tilt, and motion.js writes an inline transform on mousemove. A CSS
   animation on transform outranks an inline style, so animating those cards
   would have silently killed a working desktop interaction to add a worse
   one.

   align-self, not a change to the band's align-items, so the photographs stay
   where they were and only the text column moves to the top of the run it is
   about to hold through. */
@media(min-width:861px){
  .hi>:not(.stack){
    align-self:start;
    position:sticky;
    top:clamp(96px,14vh,150px);
  }
}
.hi .stack{position:relative;margin-bottom:8%}
.hi .stack .m img{aspect-ratio:4/5;object-fit:cover;width:100%}
.hi .stack .o{position:absolute;right:-6%;bottom:-9%;width:42%;border:8px solid var(--paper)}
.hi .stack .o img{aspect-ratio:1/1;object-fit:cover;width:100%}
.hi h2{margin:14px 0 10px}
.hi .hand{display:block;margin-bottom:18px}
.hi p{margin-bottom:14px;max-width:50ch;font-size:var(--fs-b2)}
@media(max-width:880px){.hi{grid-template-columns:minmax(0,1fr)}.hi .stack .o{display:none}}

/* ---- faqs: hairline rows, a plus that turns into an x ---- */
.faq{border-top:1px solid var(--ln);max-width:880px;margin:0 auto}
.faq details{border-bottom:1px solid var(--ln)}
/* The questions were Inter 17px — byte-identical to body copy — so twenty-four
   of them across three pages read as UI labels on a site whose whole voice is
   the serif. They are questions somebody actually asks; set them in the voice
   that answers. The block is also pulled in from the full column: at 1150px a
   row put its plus seven hundred pixels from the question it belongs to. */
.faq summary{list-style:none;cursor:pointer;display:flex;justify-content:space-between;align-items:baseline;gap:22px;padding:22px 4px;font-family:'Ibarra Real Nova',Georgia,serif;font-weight:400;font-size:var(--fs-s2);letter-spacing:-.011em;color:var(--tx);transition:color .3s var(--ease)}

.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--gr)}
.faq summary:after{content:"+";font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-s1);line-height:1;color:var(--tx2);flex:none;transition:transform .35s var(--ease),color .35s var(--ease)}
.faq details[open] summary:after{transform:rotate(45deg);color:var(--gr)}
.faq .a,.faq details>p{padding:2px 4px 24px;color:var(--tx2);max-width:66ch;font-size:var(--fs-b2)}
.lt .faq,.lt .faq details{border-color:var(--pln)}
.lt .faq summary{color:var(--ptx)}
.lt .faq summary:hover{color:var(--gr-p)}
.lt .faq summary:after{color:var(--ptx2)}
.lt .faq details[open] summary:after{color:var(--gr-p)}
.lt .faq .a,.lt .faq details>p{color:var(--ptx2)}

footer{border-top:1px solid var(--ln);padding:44px 30px 30px}
/* The footer mark is the sign-off and the last thing on the page, so it is
   the one place the name gets to be large. It rode d3, a shared mid-display
   step also used by counting numerals; it carries its own step now so it can
   grow without dragging those with it. */
.flg{display:block;width:fit-content;margin:0 auto 8px;padding:4px 8px;font-family:'Ibarra Real Nova',Georgia,serif;font-size:clamp(31px,3.4vw,44px);letter-spacing:.04em;color:var(--tx);text-decoration:none;transition:color .3s var(--ease)}
/* footer lockup: the wordmark and the tagline are one unit, so the wordmark
   loses its own bottom margin and the tagline carries the gap instead. */
.ftag{display:block;width:fit-content;margin:0 auto 30px;text-align:center}
.flg:hover{color:var(--gr)}
.fnav{display:flex;flex-wrap:wrap;justify-content:center;gap:12px 34px;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:0.091em;text-transform:uppercase}
.fnav a{color:var(--tx2);text-decoration:none}
.fnav a:hover{color:var(--gr)}
/* back to the top sits centred, with the small print beneath it */
.fbot{display:flex;flex-direction:column;align-items:center;gap:22px;margin:40px auto 0;border-top:1px solid var(--ln);padding-top:32px;max-width:1440px}
.cp{font-family:'Inter',sans-serif;font-size:var(--fs-u1);letter-spacing:.08em;color:var(--tx2);text-align:center}
.cp a{display:inline-block;padding:3px 4px;color:inherit;text-decoration:none;transition:color .3s var(--ease)}
.cp a:hover{color:var(--gr)}
.top{display:flex;flex-direction:column;align-items:center;gap:8px}
.top a{font-family:'Ibarra Real Nova',Georgia,serif;font-style:italic;font-size:var(--fs-s2);color:var(--tx2);text-decoration:none}
.top a:hover{color:var(--gr)}
@media(max-width:700px){.fbot{flex-direction:column;gap:22px;align-items:center}}

/* ---- motion ---- */
#pgbar{position:fixed;top:0;left:0;height:2px;width:100%;background:var(--gr);transform-origin:0 50%;transform:scaleX(0);z-index:70;pointer-events:none}
@supports(animation-timeline:scroll()){#pgbar{animation:pg linear both;animation-timeline:scroll(root)}}
@keyframes pg{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes rise{from{opacity:0;transform:translateY(26px)}to{opacity:1;transform:none}}
html.js section{opacity:0;transform:translateY(26px);transition:opacity .9s var(--ease),transform .9s var(--ease)}
html.js section.vis{opacity:1;transform:none}
/* The reveal is a scroll effect, and print has no scroll: an IntersectionObserver
   never fires for anything below the fold, so printing or saving to PDF used to
   produce a page of blank sections. Print sees everything, revealed. */
@media print{
  html.js section,html.js section:not(.vis){opacity:1!important;transform:none!important;transition:none!important}
  #pgbar,nav.bar,.mmenu,.skip{display:none!important}
}
@media(prefers-reduced-motion:reduce){
  html.js section{opacity:1;transform:none;transition:none}
  #pgbar{display:none}
  .il span,.il .arw,.btn,.opt{transition:none}
  .btn:hover{transform:none}
  .dv:before,.dv>*,.dv .lbl,.btn.wide:before{transition:none}
  a.dv:hover:before,.btn.wide:hover:before{transform:none}
  a.dv:hover{color:var(--tx2)}
}


/* ============================ motion layer ============================
   Companion to /motion.js. Everything here is inert until the script adds
   its classes, so a browser with JS off sees a perfectly still, complete
   page, and reduced-motion gets the same courtesy from the script itself. */

/* split headlines: each word rises from under its baseline */
[data-split] .sw{display:inline-block}
.mjs .split-ready .sw{opacity:0;transform:translateY(1.1em) rotate(2.2deg);animation:wordup .82s var(--ease) both;animation-delay:calc(.09s + var(--i)*.055s)}
@keyframes wordup{to{opacity:1;transform:none}}

/* reveal on scroll, staggered among siblings */
/* 26px over .8s was a fade with a hint of drift, and on a phone it read as
   nothing at all. The travel is the part you notice, so it is the part that
   grew: far enough to register as movement, slow enough to still be happening
   while you look at it. */
.mjs [data-rv]{opacity:0;transform:translateY(42px);transition:opacity 1s var(--ease),transform 1s var(--ease);transition-delay:calc(var(--rvi,0)*.09s)}
.mjs [data-rv].rv-in{opacity:1;transform:none}

/* parallax images: the wrapper crops, the image drifts oversize inside it */
.plx{will-change:transform}

/* ---- scroll-driven parallax, where the browser has it -------------------
   motion.js adds .plx-css and measures --plx-travel; the drift itself is the
   browser's job from here. This exists because the JS version cannot work on
   an iPhone: iOS Safari withholds scroll events during a momentum scroll, so
   a rAF-driven parallax is frozen for exactly as long as the reader is
   watching it move, then jumps when they stop. A view() timeline is driven by
   the compositor and keeps moving with the thumb.

   If --plx-travel is somehow unset the transform is invalid at computed-value
   time and resolves to none, which is the still image we already had. There
   is no state here that can hide anything. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .plx-css{
      animation:plxDrift linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes plxDrift{
      from{transform:translate3d(0,calc(var(--plx-travel) * -1),0) scale(var(--plx-scale,1))}
      to{transform:translate3d(0,var(--plx-travel),0) scale(var(--plx-scale,1))}
    }
  }
}
.hero .hph,.split3 .s,.split3 .b,.hi .stack .m,.hi .stack .o{overflow:hidden}

/* tilt cards */
.tilt{transition:transform .45s var(--ease);transform-style:preserve-3d}
.tilt:hover{transition:transform .06s linear}


/* the nav tucks away on scroll down */
nav.bar{transition:transform .45s var(--ease)}
nav.bar.tuck{transform:translateY(-100%)}

@media(prefers-reduced-motion:reduce){
  .mjs [data-rv]{opacity:1;transform:none;transition:none}
  .mjs .split-ready .sw{opacity:1;transform:none;animation:none}
  nav.bar,nav.bar.tuck{transform:none;transition:none}
}

/* ---------------------------------------------------------------------------
   Mobile corrections, 12 August 2026.

   A bare `1fr` grid track has an automatic minimum of min-content, so any item
   that cannot shrink drags the whole track wider than the screen. Every
   desktop rule here already used minmax(0,...); the mobile overrides did not,
   which is what pushed the home page 41px wider than a 375px phone (86px at
   360px). The signup form was the item that could not shrink: input 201px +
   gap 18px + button 213px = 432px of min-content.
--------------------------------------------------------------------------- */
@media(max-width:520px){
  /* Let the email field own a line and the button sit under it. */
  .fre form{flex-wrap:wrap;gap:10px}
  .fre form input[type=email]{flex:1 1 100%;min-width:0}
  .fre form button{flex:0 0 auto}
  /* The rule that says "type here" lives on the form, which is right while the
     field and the button share a line and wrong the moment they stop: it ends
     up under the button and the email field is left with no edge at all. So on
     the wrapped layout the field carries its own. */
  .fre form{border-bottom:none;padding-bottom:0}
  .fre form input[type=email]{border-bottom:1px solid var(--tx2);padding-bottom:13px;transition:border-color .35s var(--ease)}
  .fre form:focus-within input[type=email]{border-color:var(--gr)}
}
/* ---------------------------------------------------------------------------
   The fit quiz on /work-with-me, 12 August 2026.

   The quiz markup uses .opts and .opt, which were only ever defined in
   app/tools.css. That file is loaded by the app; /work-with-me loads site.css
   alone. So all 34 option buttons rendered as raw browser defaults: Arial,
   #efefef, a 2px outset border, 86x20px, in the middle of a page set in Ibarra
   Real Nova on paper.

   Worse than the look: the click handler sets .sel and nothing was styled for
   it, so answering a question produced no visible response at all. On the page
   that routes people to a $2,000 half-day or a $9,500 rebuild.

   These match app/tools.css .ck .opt, using the paper-side tokens because
   every quiz group sits on --paper.
--------------------------------------------------------------------------- */
/* .q is the third class in this family that only existed in app/tools.css, and
   it was the one missed when .opts/.opt and the text fields were brought over.
   It wraps every question, and it carries the only separation between them, so
   without it all fifteen ran flush together: the hint under "What's the
   business, in one sentence?" sat against the [ 02 ] label with 0px between
   them, and the year buttons touched [ 02b ]. app/tools.css uses 22px in a
   narrow tool panel; this page gives each question a label line of its own, so
   it needs a little more air to read as fifteen questions rather than one wall. */
#qform .q{margin-bottom:30px}

/* The same omission runs through the rest of the question: .qk, .head and
   .hint had no rule on this page either, so all three inherited the paragraph
   size. The number, the question and the parenthetical read at an identical
   17px/400 — a flat wall of text where there should be a hierarchy of three.
   .qk is the bracketed micro-label every other section of the site sets in
   12px uppercase Inter (see .k above and .lb on the ink side); .head and .hint
   mirror `.q label` and `.hint` in app/tools.css. */
#qform .qk{
  font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.106em;
  text-transform:uppercase;color:var(--ptx2);display:block;margin-bottom:6px;
}
#qform .head{
  display:block;font-family:'Inter',sans-serif;font-weight:600;font-size:var(--fs-b2);
  line-height:1.45;color:var(--ptx);margin-bottom:8px;
}
#qform .hint{font-size:var(--fs-b3);line-height:1.55;color:var(--ptx2);margin-top:8px}

/* The four "how I work" statements are the ones you react to, and they were
   also indistinguishable from body copy. The site's editorial voice is Ibarra,
   so they get it, which also stops them reading as instructions. */
#qform .statement{
  font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-s1);
  line-height:1.5;color:var(--ptx);margin-bottom:14px;
}

.opts{display:flex;flex-wrap:wrap;gap:10px;margin-top:12px}
.opt{
  font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-b3);
  background:#fffdf8;border:1px solid var(--pln);color:var(--ptx2);
  padding:13px 18px;text-align:left;cursor:pointer;
  transition:border-color .25s var(--ease),color .25s var(--ease),box-shadow .25s var(--ease);
}
.opt:hover{border-color:var(--gr-p);color:var(--ptx)}
.opt.sel{border-color:var(--gr-p);color:var(--ptx);box-shadow:inset 3px 0 0 var(--gr-p)}
.opt:focus-visible{outline:2px solid var(--gr-p);outline-offset:2px}

/* Submitting with buttons unpicked used to say "scroll up for the ones that
   aren't highlighted" and then highlight nothing, on a form four screens tall.
   This is what "aren't highlighted" now means. */
/* #qform .head and #qform .statement below are id-scoped, so these have to be
   too or the colour never lands. */
#qform .q.needs .head,#qform .q.needs .statement,#qform .q.needs .qk{color:var(--rd-p)}
.q.needs .opts{box-shadow:inset 3px 0 0 var(--rd-p);padding-left:13px;margin-left:-16px}

/* The quiz's text fields had the same problem as its buttons: nothing in this
   stylesheet touched them, so all five rendered as browser defaults — 13.3px
   monospace and Arial in 178x33 and 143x20 boxes, with a 2px inset border.
   "What made you fill this out today?" expects a paragraph and offered a box
   narrower than the question. Modelled on .q in app/tools.css. */
#qform textarea,
#qform input[type=text],
#qform input[type=email]{
  width:100%;display:block;background:none;border:none;
  border-bottom:1px solid var(--pln);color:var(--ptx);
  font-family:'Inter',sans-serif;font-size:var(--fs-b2);line-height:1.55;
  padding:8px 2px 11px;margin-top:6px;outline:none;resize:vertical;
  transition:border-color .25s var(--ease);
}
#qform textarea{min-height:76px}
#qform textarea:focus,
#qform input[type=text]:focus,
#qform input[type=email]:focus{border-bottom-color:var(--gr-p)}
#qform textarea::placeholder,
#qform input::placeholder{color:var(--ptx2);opacity:.65}
@media(max-width:560px){
  /* Stacked and full width: a 4-option row wraps into an unreadable grid on a
     phone, and these were the smallest tap targets on the site at 20px. */
  .opts{flex-direction:column;gap:8px}
  .opt{width:100%}
}

@media(max-width:520px){
  /* An inline arrow link is inline-flex + nowrap, so its min-content is the
     full sentence plus the 62px arrow (352px here). That is wider than the
     content box on a 375px phone, so let it wrap on the smallest screens. */
  .il{white-space:normal;flex-wrap:wrap}
}
@media(max-width:700px){
  /* Footer links were 19-22px tall. Apple asks for 44, Android 48. */
  .fnav{gap:4px 28px}
  .fnav a{display:inline-block;padding:12px 6px;margin:0 -6px}
}
@media(max-width:560px){
  /* This used to lift the bracketed labels from 12.5px to 13.5px, because
     uppercase at .106em tracking is the hardest combination to read on a
     phone. u1 is 15px now, so the override had inverted: it was pulling the
     labels DOWN on exactly the screens it was written to help. Removed
     rather than rescaled, since the base already clears the bar it was
     reaching for. */
  /* Small standalone links (the "[ linkedin ]" style) were 15px tall. */
  .fbot a{display:inline-block;padding:12px 8px;margin:0 -8px}
}
/* .il is the arrow-link component — always a standalone action, never a word
   inside a sentence — so on anything touched it gets a thumb-sized box. The
   underline animates on the inner span, so the padding does not drag it down
   with it. 820px, not 560: a tablet is a touch device too. */
@media(max-width:820px){.il{padding:10px 0}}

/* ===================== vivianrobelo.com, on this system =====================
   Everything above is the system as vivifye.com had it on 12 September 2026.
   What follows is what this site needs that vivifye.com never did, written in
   the same tokens so nothing here is a second palette. */

/* On paper, the ink-side names mean their paper-side values. vivifye.com
   wrote every component twice, once plain and once under .lt, and that works
   for thirty components. The long-form pages here carry hundreds of inline
   rules written as var(--tx2) and var(--ln), and repeating each under .lt is
   how one gets missed and turns pale grey on paper. So inside a paper band the
   tokens flip, and a rule written once reads right on either ground. The .lt
   rules above still say the same thing explicitly, and still win. */
.lt{--tx:var(--ptx);--tx2:var(--ptx2);--ln:var(--pln);--gr:var(--gr-p);--rd:var(--rd-p)}

/* Pages here give their images width and height attributes, so the browser
   reserves the box before the file arrives. vivifye.com's did not, and its
   rules set a width and an aspect-ratio and left height alone, which with the
   attributes present means height="1800" wins: the About portrait became a
   550px-wide, 1,800px-tall strip inside a 4:5 frame that showed only the
   wall above her head. :where() keeps this at zero specificity, so any rule
   that does set a height (a hero, a parallax band) still gets it. */
:where(img[height]){height:auto}

/* The bar is injected by /_chrome.js at DOMContentLoaded, after the page has
   painted, so it used to push the whole of <main> down on arrival: Lighthouse
   measured a layout shift of 0.109 on the home page (12 Sep 2026). Its height
   is reserved here until it lands. Every term is fixed, not font metrics: the
   burger is 44px, the wordmark's box is its font size times the body's 1.52
   leading plus the 9px padding it takes at 820px and under, and the bar adds
   20px padding top and bottom and a 1px rule. */
body::before{content:"";display:block;height:85px}
@media(max-width:820px){body::before{height:calc(21px * 1.52 + 18px + 41px)}}
@media(max-width:520px){body::before{height:calc(20px * 1.52 + 18px + 41px)}}
body:has(> nav.bar)::before{display:none}

/* The menu is injected by /_chrome.js, which shows it by removing [hidden]
   and then adding .open a frame later, so the slide still runs. The six
   links sit in a nav of their own inside .mmenu-links, and the two quieter
   ones (privacy, Instagram) under them, because the portrait column that
   would otherwise hold Instagram is not drawn on a phone. */
.mmenu[hidden]{display:none}
.mmenu-links nav{display:flex;flex-direction:column;align-items:center}
.mmenu-links a[aria-current="page"]{color:var(--gr)}
.mmenu-util{margin-top:clamp(18px,3vh,30px);padding-top:clamp(12px,2vh,18px);border-top:1px solid var(--ln)}
.mmenu-links .mmenu-util a{font-size:var(--fs-b3);letter-spacing:.06em;text-transform:none;color:var(--tx2);padding:11px 14px}
.mmenu-links .mmenu-util a:hover{color:var(--gr)}

/* the footer's second row: the things looked for rather than browsed */
.fnav-u{margin-top:18px;gap:6px 26px}
.fnav-u a{text-transform:none;letter-spacing:.03em;font-weight:400}
footer .cp-lic{max-width:62ch;letter-spacing:.02em;line-height:1.5}

/* A row of actions: the one button and the quieter .il beside it. */
.hcta{display:flex;flex-wrap:wrap;gap:18px 28px;align-items:center}

/* ---- the page head ----
   Every page that is not the home page opens on ink with its eyebrow, its
   title and one paragraph, and the reading happens on the paper band below.
   vivifye.com opened each inner page on a photograph; these pages are
   documents, and the document is the thing to get to. */
.phead{padding:clamp(64px,9vw,128px) 0 clamp(48px,6vw,80px);border-bottom:1px solid var(--ln)}
.phead h1{max-width:18ch}
.phead .sub{margin-top:22px}
.phead .fine{margin-top:18px}
.phead .hcta{margin-top:32px}

/* ---- long-form reading ----
   Legal, the journal and the prose on the library pages. The measure is
   68 characters, the body is the lead size because this is the text people
   came for rather than text around a component, and the headings keep the
   display ladder so a section break is visible from a thumb's scroll away.
   A list item opens on a green dash: the dash counts something, which is
   what green is for at rest. */
.prose{max-width:68ch}
.prose>*+*{margin-top:1.05em}
.prose p,.prose li{font-size:var(--fs-b1);line-height:1.62;color:var(--tx2)}
.prose strong{font-weight:600;color:var(--tx)}
.prose h2{margin-top:clamp(56px,7vw,84px);color:var(--tx)}
.prose h3{margin-top:clamp(36px,4vw,48px);color:var(--tx)}
.prose>:first-child{margin-top:0}
.prose h2+*,.prose h3+*{margin-top:16px}
.prose ul,.prose ol{list-style:none}
.prose li{position:relative;padding-left:26px}
.prose li+li{margin-top:12px}
.prose ul>li:before{content:"–";position:absolute;left:0;color:var(--gr)}
.prose ol{counter-reset:item}
.prose ol>li{counter-increment:item}
.prose ol>li:before{content:counter(item);position:absolute;left:0;font-family:'Ibarra Real Nova',Georgia,serif;color:var(--gr)}
.prose blockquote{padding-left:22px;border-left:1px solid var(--gr);font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-s1);line-height:1.4;color:var(--tx)}
/* The one frame a photograph may have is the 1px hairline. It was the rule
   the paper design kept for every image on the site, and it is the one worth
   keeping: a photograph on paper with no edge reads as a hole in the page. */
.prose figure img,.prose>img{width:100%;border:1px solid var(--ln)}
.prose figcaption{margin-top:10px;font-size:var(--fs-b3);color:var(--tx2)}

/* An inline link inside prose. The Vivifye system has no such thing, because
   every link on vivifye.com was a component (.il, .btn, .dv); the pages here
   link out of running sentences. The underline is the affordance; green is
   the moment it answers. */
.tl{color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;text-decoration-color:currentColor;transition:color .3s var(--ease)}
.tl:hover{color:var(--gr)}
.lt .tl:hover{color:var(--gr-p)}

/* small print that is not a caption: a licence, a note under a form */
.fine{display:block;margin-top:16px;font-size:var(--fs-b3);line-height:1.55;color:var(--tx2)}
.lt .fine{color:var(--ptx2)}
.fine b{font-weight:600;color:var(--tx)}
.lt .fine b{color:var(--ptx)}

/* A choice of two, as buttons. Selection is one of the three things green is
   for, so the chosen one carries it. */
.pills{display:flex;flex-wrap:wrap;gap:10px;margin:4px 0 22px}
.pill{min-height:44px;padding:12px 18px;background:none;border:1px solid var(--ln);color:var(--tx2);cursor:pointer;
  font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.09em;text-transform:uppercase;
  transition:border-color .25s var(--ease),color .25s var(--ease),box-shadow .25s var(--ease)}
.pill:hover{color:var(--tx);border-color:var(--tx2)}
.pill[aria-pressed="true"]{color:var(--tx);border-color:var(--gr);box-shadow:inset 3px 0 0 var(--gr)}
.lt .pill{border-color:var(--pln);color:var(--ptx2)}
.lt .pill[aria-pressed="true"]{color:var(--ptx);border-color:var(--gr-p);box-shadow:inset 3px 0 0 var(--gr-p)}
@media(prefers-reduced-motion:reduce){.pill{transition:none}}

/* A narrower column for pages that are one thing to read, top to bottom. */
.w-read{max-width:860px}

/* ---- a journal post ----
   The title with the post's photograph beside it, in the hairline, and the
   post on paper below. The photograph used to hang over the corner of the
   headline as a tilted sticker, which is what put both headlines under it at
   four widths out of seven; beside it, in its own column, it cannot. */
.jhead .w{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,240px);gap:clamp(30px,5vw,64px);align-items:end}
.jhead h1{max-width:20ch}
.jshot{display:block;border:1px solid var(--ln)}
.jshot img{width:100%;aspect-ratio:1/1;object-fit:cover}
@media(max-width:760px){.jhead .w{grid-template-columns:minmax(0,1fr);gap:28px}.jshot{order:-1;width:clamp(120px,40vw,180px)}}
.prose .sig{margin-top:40px;font-size:var(--fs-d4);color:var(--tx)}

/* ---- where to go next ----
   Two plain cards at the foot of a post: a label, a title, a line, the link. */
.nextup{margin-top:clamp(56px,7vw,88px);padding-top:32px;border-top:1px solid var(--ln)}
.nextup-g{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(16px,2.4vw,28px)}
.nextup-g a{display:flex;flex-direction:column;gap:10px;padding:clamp(22px,2.6vw,32px);border:1px solid var(--ln);color:var(--tx);text-decoration:none;transition:border-color .3s var(--ease)}
.nextup-g a:hover{border-color:var(--gr)}
.nextup .k{font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.09em;text-transform:uppercase;color:var(--tx2)}
.nextup .ttl{font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-d4);line-height:1.1;letter-spacing:-.015em}
.nextup .dsc{color:var(--tx2)}
.nextup .il{margin-top:auto}
@media(max-width:640px){.nextup-g{grid-template-columns:minmax(0,1fr)}}

/* =============================== the library ===============================
   The seventeen /library pages are documents: the thing somebody came for,
   to copy from, tick off and come back to. Each opens on ink with its title
   and then the document itself on paper (the page wraps everything below its
   header in .lt.lib-body). Everything here is scoped to body.lib, because
   the library's vocabulary grew up in stylesheets of its own, and names like .note, .card and .split are too ordinary to let
   loose on the rest of the site.

   These rules were written once per page before, in fifteen near-identical
   <style> blocks, with a shared stylesheet loaded over the top of most of them. They
   are written once here instead, and the pages keep only what is theirs.

   Two surfaces on the paper: the page, and --card, a whiter stock for the
   thing you copy from. Commentary sits on the page; the product sits on the
   card. --paper2 is the tint a chapter band alternates to. */
:root{--card:#fffdf8;--paper2:#f3f0e6}
.lib-body{padding-bottom:clamp(56px,7vw,96px)}
.lib .wrap{max-width:880px;margin:0 auto;padding:0 30px}
.lib.lay-wide .wrap,.lib.lay-twocol .wrap,.lib.lay-split .wrap{max-width:1150px}
.lib .eyebrow{display:block;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.106em;text-transform:uppercase;color:var(--gr)}
.lib .lead{color:var(--tx2);font-size:var(--fs-b1);max-width:58ch}

/* the page head, in its three shapes */
.lib .vhead,.lib header.hero,.lib .phero{padding:clamp(56px,8vw,112px) 0 clamp(44px,5.5vw,72px);border-bottom:1px solid var(--ln)}
.lib .vhead h1,.lib header.hero h1,.lib .phero h1{max-width:18ch}
.lib .vhead .sub,.lib header.hero .sub,.lib .phero .sub,.lib .phero .lead{margin-top:20px;max-width:58ch}
.lib .own{margin-top:22px;max-width:70ch;font-size:var(--fs-b3);line-height:1.6;color:var(--tx2)}
.lib .own a{color:var(--tx)}
.lib .vbody{padding:clamp(48px,6vw,80px) 0 0}
.lib .hsplit{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.85fr);gap:56px;align-items:center}
/* The props beside a split page's title: three notes, lifted off the ink.
   Decoration, so they go below 900px where they would land above the h1. */
.lib .fhero{position:relative;min-height:340px}
.lib .fob{position:absolute;background:var(--card);color:var(--ink);border:1px solid var(--pln);box-shadow:0 18px 34px -24px rgba(0,0,0,.7)}
.lib .fob .ftag{display:block;font-family:'Inter',sans-serif;font-weight:500;font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--ptx2)}
@media(max-width:900px){.lib .fhero.hide-sm{display:none}}
@media(max-width:880px){.lib .hsplit{grid-template-columns:minmax(0,1fr);gap:32px}.lib .fhero{min-height:300px;order:-1;margin-top:8px}}

/* the label-left, content-right pages */
.lib .split{display:grid;grid-template-columns:minmax(0,34fr) minmax(0,66fr);gap:48px;padding-top:56px}
.lib .split .sh h2{position:sticky;top:104px;margin:0}
.lib .split .sc>:first-child{margin-top:0}
@media(max-width:880px){.lib .split{grid-template-columns:minmax(0,1fr);gap:0;padding-top:44px}.lib .split .sh h2{position:static;margin:0 0 14px}}

/* the document */
.lib .doc{max-width:none}
.lib .doc h2{margin:clamp(44px,5vw,60px) 0 14px}
.lib .doc h3{margin:32px 0 8px;font-size:var(--fs-s1);letter-spacing:-.011em}
.lib .doc p,.lib .doc li{font-size:var(--fs-b2);line-height:1.62;color:var(--tx2)}
.lib .doc ol,.lib .doc ul{padding-left:22px;margin:10px 0}
.lib .doc li{padding:5px 0}
.lib .doc li::marker{color:var(--gr)}
.lib .doc .say{display:block;margin:16px 0;padding:22px 24px;background:var(--card);border:1px solid var(--ln);border-left:3px solid var(--tx);color:var(--tx);font-size:var(--fs-b2);line-height:1.7}
.lib .doc .k{display:block;margin-bottom:8px;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.106em;text-transform:uppercase;color:var(--gr)}
.lib .doc table{width:100%;margin:16px 0;border-collapse:collapse;background:var(--card)}
.lib .doc th,.lib .doc td{padding:11px 13px;border:1px solid var(--ln);font-size:var(--fs-b3);line-height:1.55;color:var(--tx2);text-align:left;vertical-align:top}
.lib .doc th{font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.08em;text-transform:uppercase;color:var(--tx)}

/* a card of copy: the thing itself, on the whiter stock, with its number and
   its copy button along the top. The number counts, so it is green. */
.lib .vc{margin:0 0 18px;padding:clamp(20px,2.4vw,28px);background:var(--card);border:1px solid var(--ln)}
.lib .vc p,.lib .vc .tx{color:var(--tx);font-size:var(--fs-b2);line-height:1.68}
.lib .vc .ck{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);line-height:1.3;letter-spacing:.09em;text-transform:uppercase;color:var(--gr)}
.lib .vc .note{margin-top:12px;padding:12px 16px;background:var(--paper);border-left:2px solid var(--ln);font-size:var(--fs-b3);line-height:1.6;color:var(--tx2)}
.lib .vc .note strong{color:var(--tx);font-weight:600}
.lib .vc.lit{border-color:var(--gr);box-shadow:0 0 0 1px var(--gr)}
.lib .cp2{flex:none;padding:11px 16px;background:var(--ink);color:var(--paper);border:1px solid var(--ink);cursor:pointer;
  font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);line-height:1;letter-spacing:.09em;text-transform:uppercase;
  transition:background .25s var(--ease),color .25s var(--ease)}
.lib .cp2:hover{background:none;color:var(--tx)}

/* filters, and the jump list: choices, so the chosen one is green */
.lib .filters{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:30px}
.lib .filt,.lib .rndb{padding:11px 16px;background:var(--card);border:1px solid var(--ln);color:var(--tx2);cursor:pointer;
  font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);line-height:1;letter-spacing:.08em;text-transform:uppercase;
  transition:border-color .25s var(--ease),color .25s var(--ease),box-shadow .25s var(--ease)}
.lib .rndb{background:none;border-style:dashed}
.lib .filt:hover,.lib .rndb:hover{color:var(--tx);border-color:var(--tx2)}
.lib .filt.on{color:var(--tx);border-color:var(--gr);box-shadow:inset 3px 0 0 var(--gr)}
.lib .jmp{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px}
.lib .jmp a{display:inline-flex;align-items:center;min-height:44px;padding:10px 16px;border:1px solid var(--ln);color:var(--tx);text-decoration:none;
  font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);line-height:1.2;letter-spacing:.08em;text-transform:uppercase;
  transition:border-color .25s var(--ease),color .25s var(--ease)}
.lib .jmp a:hover{border-color:var(--gr);color:var(--gr)}
@media(pointer:coarse){.lib .cp2,.lib .filt,.lib .rndb{min-height:44px}}

/* ---- chapters ----
   A long document announces each chapter with a divider that breaks out of
   the reading column, and alternate chapters sit on the tinted paper, so a
   page of a hundred and twenty cards reads as zones rather than a wall. */
/* Out of the column by margin, not by transform: nav.js reveals every
   <section> by setting its transform to none, and a chapter band placed with
   translateX(-50%) landed 430px to the right the moment it was revealed. */
.lib .chap,.lib .chapzone{width:min(1150px,calc(100vw - 60px));margin-left:calc(50% - min(575px,50vw - 30px))}
.lib .chap{margin-top:clamp(48px,6vw,84px);padding:clamp(28px,3.6vw,44px) 0 clamp(16px,2.2vw,24px);border-top:1px solid var(--ln)}
.lib .chap .bw{max-width:880px;margin:0 auto;padding:0 30px}
.lib.lay-wide .chap .bw,.lib.lay-twocol .chap .bw,.lib.lay-split .chap .bw{max-width:1150px}
.lib .chapn{display:block;margin-bottom:12px;font-family:'Ibarra Real Nova',Georgia,serif;font-size:var(--fs-d3);line-height:1;letter-spacing:-.026em;color:var(--gr)}
.lib .chap h2{margin:0;max-width:24ch}
.lib .chap p{margin-top:12px;max-width:62ch;font-size:var(--fs-b1);color:var(--tx2)}
.lib section.chapzone{padding:clamp(22px,3vw,34px) 0 clamp(26px,3.4vw,44px)}
.lib .chapzone>.chapin{max-width:880px;margin:0 auto;padding:0 30px}
.lib.lay-wide .chapzone>.chapin,.lib.lay-twocol .chapzone>.chapin,.lib.lay-split .chapzone>.chapin{max-width:1150px}
.lib .chapzone.alt,.lib .chap:has(+ .chapzone.alt){background-color:var(--paper2)}
/* a filtered page must not leave a labelled band standing empty */
.lib .chapzone:has(.vc[data-cat]):not(:has(.vc[data-cat]:not([style*="none"]))){display:none}
.lib .chap:has(+ .chapzone:has(.vc[data-cat]):not(:has(.vc[data-cat]:not([style*="none"])))){display:none}
/* the sales page runs a sticky wireframe beside the copy, so nothing inside
   it may break the column; its bands bleed to the column edge instead */
.lib.lay-twocol .dlayout .chap,.lib.lay-twocol .dlayout .chapzone{width:auto;transform:none;margin-left:-22px;margin-right:-22px}
.lib.lay-twocol .dlayout .chap{padding-left:22px;padding-right:22px}
.lib.lay-twocol .dlayout .chap .bw{max-width:none;padding:0}
.lib.lay-twocol .dlayout .chapzone>.chapin{max-width:none;padding:0 22px}
.lib .vbody .cal,.lib .vbody .wide{width:min(1150px,calc(100vw - 60px));margin-left:calc(50% - min(575px,50vw - 30px))}
@media(max-width:900px){
  .lib .chap,.lib .chapzone,.lib .vbody .cal,.lib .vbody .wide{width:auto;margin-left:0;margin-right:0;transform:none}
  .lib .chapzone>.chapin{padding:0}
  .lib.lay-twocol .dlayout .chap,.lib.lay-twocol .dlayout .chapzone{margin-left:0;margin-right:0}
  .lib.lay-twocol .dlayout .chap{padding-left:0;padding-right:0}
  .lib.lay-twocol .dlayout .chapzone>.chapin{padding:0}
}

/* the notes worth stopping for, and the one instruction on each page */
.lib .callout{max-width:880px;margin:44px auto;padding:clamp(22px,3vw,34px);background:var(--card);border:1px solid var(--ln);border-left:3px solid var(--tx)}
.lib.lay-wide .callout,.lib.lay-twocol .callout,.lib.lay-split .callout{margin-left:0}
.lib .callout>.k,.lib .callout .ck,.lib .callout .rule:first-child{display:flex;margin-bottom:16px;padding:0;background:none;border:none;
  font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.106em;text-transform:uppercase;color:var(--gr)}
.lib .callout p{color:var(--tx);font-size:var(--fs-b2);line-height:1.7}
.lib .callout p+p{margin-top:14px}
.lib .callout.sendoff .note{margin-top:14px;padding:0;background:none;border:none;font-size:var(--fs-b3)}
.lib .donow{max-width:60ch;margin-top:clamp(34px,4vw,48px);padding:20px 24px;background:var(--card);border-left:3px solid var(--tx);font-size:var(--fs-b2);line-height:1.62;color:var(--tx)}
.lib .donow .k{display:block;margin-bottom:7px;font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.106em;text-transform:uppercase;color:var(--gr)}

/* the links to the next page, and the short footer a document closes on */
.lib .sib{margin-top:56px;padding-top:22px;border-top:1px solid var(--ln)}
.lib .sib p{font-size:var(--fs-b3);color:var(--tx2)}
.lib .sib a{color:var(--tx)}
.lib footer2{display:block;margin-top:clamp(40px,5vw,64px);padding:28px 0 0;border-top:1px solid var(--ln);font-size:var(--fs-b3);line-height:1.7;color:var(--tx2)}
/* .il is a component on this system, an italic link with a drawn arrow; the
   library's older markup writes it as a bare <a class="il">, which would
   otherwise have no underline and look like a caption. */
.lib a.il:not(:has(>span)){text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:5px}

/* the few older primitives two or three library pages still use */
.lib .card{display:flex;flex-direction:column;gap:12px;padding:28px;background:var(--card);border:1px solid var(--ln)}
.lib .lbl{display:inline-block;padding-bottom:10px;border-bottom:1px solid var(--tx);font-family:'Inter',sans-serif;font-weight:500;font-size:var(--fs-u1);letter-spacing:.106em;text-transform:uppercase;color:var(--tx)}
.lib .num{font-variant-numeric:tabular-nums}
.lib .grid{display:grid;gap:30px}
.lib .cap{max-width:560px}
.lib .cap form{display:flex;align-items:center;gap:18px;margin-top:18px;padding-bottom:12px;border-bottom:1px solid var(--tx2);transition:border-color .35s var(--ease)}
.lib .cap form:focus-within{border-color:var(--gr)}
.lib .cap input[type=email]{flex:1;min-width:0;padding:6px 2px;background:none;border:none;outline:none;color:var(--tx);font:inherit;font-size:var(--fs-b2)}
.lib .cap form button{min-height:44px;background:none;border:none;color:var(--tx);cursor:pointer;font-family:'Inter',sans-serif;font-weight:600;font-size:var(--fs-u1);letter-spacing:.084em;text-transform:uppercase}
.lib .cap .msg{min-height:1.5em;margin-top:12px;font-size:var(--fs-b3);color:var(--tx2)}
@media(max-width:520px){.lib .cap form{flex-wrap:wrap}.lib .cap input[type=email]{flex:1 1 100%}}
@media print{.lib nav.bar,.lib footer,.lib .sib,.lib .mmenu{display:none}.lib .lt{background:#fff}}
