/* franzfloyd.com: the one stylesheet. Every page, static or generated, links
   this file and carries no CSS of its own. test/site.test.mjs holds that.

   Authoring subset, which the tests read and enforce: font sizes in rem only
   (a rem length, or a clamp() whose bounds are rem), no nesting, no @import.

   Order is load-bearing. The shared rules come first, then each page's own,
   in the order they had when every page carried its own copy, so two rules
   of equal specificity still resolve the way they did. */

/* Fonts are self-hosted, subset to Latin. The page makes no third-party request. */
@font-face{font-family:'Karla';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/karla-400.woff2) format('woff2')}
@font-face{font-family:'Karla';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/karla-600.woff2) format('woff2')}
@font-face{font-family:'Karla';font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/karla-700.woff2) format('woff2')}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/jetbrains-mono-500.woff2) format('woff2')}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/jetbrains-mono-600.woff2) format('woff2')}
/* Metric-matched fallbacks, so the swap moves nothing. */
@font-face{font-family:'Karla Fallback';src:local('Arial'),local('Helvetica');size-adjust:97%;ascent-override:93%;descent-override:24%;line-gap-override:0%}
@font-face{font-family:'Mono Fallback';src:local('Menlo'),local('Consolas'),local('Liberation Mono');size-adjust:100%;ascent-override:100%;descent-override:28%;line-gap-override:0%}

/* ============================================================
   PALETTE
   One palette, declared twice: light, and dark by the device
   setting. There is no control and no other palette: a page follows
   the device (the rust, wine and harbor values are kept in the brand
   file). Every token is declared in both blocks, and
   test/site.test.mjs fails when one is not.

   --on-amber is the text on an amber fill: the same dark ink in both
   modes, because the page ink turns light in dark mode and fails on
   amber. --tint is a surface one step off the paper, for a band.
   --card is the surface of a field, a card and the scene. --band and
   --on-band are the close band's fixed pair, and --stage the lit
   corner of the rolodex's stage: the same in both modes, so a dark
   band stays a dark band.
   ============================================================ */
:root{
  --sans:'Karla','Karla Fallback',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
  --mono:'JetBrains Mono','Mono Fallback',ui-monospace,SFMono-Regular,Menlo,Consolas,'Liberation Mono',monospace;
  --gutter:clamp(1.25rem,5vw,3.5rem);
  --section:clamp(4rem,11vh,7.5rem);
}
:root{
  --paper:#FBFAF7; --ink:#14232B; --lake:#1B4A50; --amber:#B96F24;
  --ink-soft:rgba(20,35,43,.84); --hair:rgba(110,143,153,.42);
  --on-amber:#14232B; --tint:#F1EFE8; --card:#FFFFFF;
  --band:#14232B; --on-band:#FBFAF7; --stage:#23434D;
}
@media (prefers-color-scheme:dark){
  :root{
    --paper:#0F1B21; --ink:#EDEAE3; --lake:#9CBAC2; --amber:#D9903F;
    --ink-soft:rgba(237,234,227,.78); --hair:rgba(156,186,194,.36);
    --on-amber:#14232B; --tint:#16252D; --card:#0F1B21;
    --band:#14232B; --on-band:#FBFAF7; --stage:#23434D;
  }
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--sans);
  font-size:clamp(1rem,.97rem + .35vw,1.0625rem);
  line-height:1.62;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
h1,h2,h3{margin:0;font-weight:700;letter-spacing:-.02em;line-height:1.15}
p{margin:0;hanging-punctuation:first last}
img,svg{max-width:100%;display:block}

.wrap{max-width:1120px;margin-inline:auto;padding-inline:var(--gutter)}
main > section{padding-top:var(--section)}

a{color:inherit}
a:focus-visible,
:focus-visible{outline:2px solid var(--amber);outline-offset:3px;border-radius:1px}

/* visually hidden, and the skip link that appears on focus */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;margin:-1px}
.skip{position:absolute;left:var(--gutter);top:-4rem;z-index:30;background:var(--paper);color:var(--ink);padding:.5rem .75rem;font-family:var(--mono);font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;text-decoration:none;border:1px solid var(--ink)}
.skip:focus{top:1rem}

/* ---------- mono labels ---------- */
.eyebrow{
  font-family:var(--mono);
  font-weight:500;
  font-size:.8125rem;
  line-height:1.2;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--lake);
  margin:0;
}
.index{
  font-family:var(--mono);
  font-weight:500;
  font-size:.8125rem;
  letter-spacing:.16em;
  color:var(--lake);
}

/* ---------- section rule: label + hairline to the right edge ---------- */
.rule{display:flex;align-items:center;gap:1.25rem;margin-bottom:clamp(2rem,5vh,3.25rem)}
.rule .hair{flex:1 1 auto;height:1px;background:var(--hair)}
/* Section headings carry more weight than the other mono labels: they are
   signposts, and they were too quiet against the body copy. */
.rule h2.eyebrow{
  font-weight:600;
  font-size:clamp(.8125rem,.76rem + .22vw,.9375rem);
  letter-spacing:.2em;
  color:var(--ink);
}

/* ---------- the button ----------
   One component. Every button, and every link drawn as one, carries .btn
   and at most one variant: .btn--primary, the amber fill, at most one to a
   view; or .btn--quiet, the text button. No rule anywhere styles a button
   by where it sits. Two rules of equal weight once did, the later one won,
   and the primary action rendered at 11px with no amber.
   A container that needs another colour sets --btn-ink or --btn-bg and
   nothing else. Spacing around a button belongs to its container's layout.
   test/site.test.mjs holds all of this, and pins the primary. */
.btn{
  -webkit-appearance:none;appearance:none;
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  margin:0;padding:0;
  min-height:2.75rem;
  border:0;border-radius:0;
  background:var(--btn-bg,transparent);
  color:var(--btn-ink,var(--ink));
  font:inherit;line-height:1.2;
  text-align:center;text-decoration:none;
  cursor:pointer;
}
.btn:focus-visible{outline:2px solid var(--amber);outline-offset:3px}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.55;cursor:not-allowed}
/* The fill is amber and the text is the fixed dark ink, in both modes.
   19px at weight 700 is large text, which is what lets that pair pass. */
.btn--primary{
  padding:.95rem 1.25rem;
  border-radius:6px;
  background:var(--amber);
  color:var(--on-amber);
  font-size:1.1875rem;
  font-weight:700;
  min-height:2.75rem;
}
.btn--primary:hover{box-shadow:inset 0 0 0 2px var(--on-amber)}
/* The text sits on its amber rule at the foot of the box; the rest of the
   44px is tap height above it. */
.btn--quiet{
  align-items:flex-end;
  padding:.65rem 0;
  border-bottom:2px solid var(--amber);
  font-family:var(--mono);font-weight:600;font-size:.8125rem;letter-spacing:.14em;text-transform:uppercase;
  transition:border-bottom-width .12s ease;
}
.btn--quiet:hover{border-bottom-width:4px}
/* A round control with one glyph in it: the rolodex's previous and next. */
.btn--round{
  width:2.75rem;
  border:1.5px solid var(--ink);
  border-radius:50%;
  font-family:var(--mono);
}
.btn--round:hover{background:var(--ink);color:var(--paper)}

/* ---------- the bar ----------
   The first thing on every page, written by siteBar() in src/shell.js: the
   wordmark, the four page links, and the talk link. Under 860px the nav's
   links become items of the bar: the talk link stays top right, a zero-height
   item breaks the line, and the four page links follow on a second row at
   13px, visible, with no script. The footer carries them too. */
.bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;column-gap:1rem;padding-block:.4rem}
.bar a{display:inline-flex;align-items:center;min-height:2.75rem;text-decoration:none}
.bar .mark{font-size:1.25rem}
.bar nav{display:flex;gap:1.5rem;font-size:.9375rem;color:var(--ink-soft)}
.bar nav a:hover,
.bar nav [aria-current="page"]{color:var(--ink)}
.bar .talk{color:var(--ink);font-weight:600;text-decoration-line:underline;text-decoration-color:var(--amber);text-decoration-thickness:2px;text-underline-offset:.4em}
@media (max-width:859.98px){
  .bar nav{display:contents}
  .bar .talk{order:1;margin-left:auto}
  .bar::after{content:"";order:2;flex:1 0 100%;height:0}
  .bar .wide{order:3;min-height:2.25rem;font-size:.8125rem;color:var(--ink-soft)}
}

/* The wordmark, as the bar sets it. */
.mark{
  font-family:var(--mono);
  font-weight:500;
  letter-spacing:-.03em;
  line-height:1;
  color:var(--ink);
  white-space:nowrap;
}
.mark .slot{color:var(--amber);font-weight:600}

/* A row of mono links: the sections of /work, the ways on from the 404. */
.index{
  display:flex;flex-wrap:wrap;gap:.5rem 1.75rem;
  margin-top:clamp(1.5rem,4vh,2.25rem);
  font-family:var(--mono);font-weight:500;font-size:.8125rem;
  letter-spacing:.18em;text-transform:uppercase;color:var(--lake);
}
.index a{text-decoration:none;padding-block:.35rem;border-bottom:1px solid transparent}
.index a:hover{border-bottom-color:var(--lake)}
h2[id]{scroll-margin-top:2rem}
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}

/* ============================================================
   The home page, at the sizes of the mockup Brian approved on
   13 October 2026. Top to bottom: the hero (headline, who it is
   for, the trade form) beside the rolodex, what you get, who builds
   it, the terms, and the one dark band that closes the page.
   ============================================================ */
.home{line-height:1.5}
.home p{text-wrap:pretty}
.home h2,
.hero h1{line-height:1.08;text-wrap:balance}
.hero{padding-block:clamp(1.5rem,5vh,4rem) clamp(2.5rem,7vh,5rem);display:grid;gap:2.25rem}
.hero > *{min-width:0}
.hero h1{font-size:clamp(2.25rem,1.3rem + 4vw,3.75rem);line-height:1.1}
.hero .who{margin-top:1rem;font-size:clamp(1.0625rem,1rem + .5vw,1.3125rem);color:var(--ink-soft);max-width:38ch}
.hero .trade{margin-top:1.75rem}

/* ---------- the trade form ----------
   Written by forForm() in src/for.js: a visible label, a bordered field, one
   button. On the home page the button is the primary and sits under the
   field at its full width. Under an answer the form is .trade--quiet: the
   quiet button sits beside the field. The placeholder is in lake, which
   passes contrast where a paler grey does not. */
.trade{max-width:34rem}
.trade label{display:block;font-weight:600;margin-bottom:.5rem}
.trade .eyebrow{margin-bottom:1rem}
.trade .field{display:grid;gap:.6rem}
.trade input{
  -webkit-appearance:none;appearance:none;
  width:100%;margin:0;
  padding:.9rem 1rem;
  border:2px solid var(--ink);border-radius:6px;
  background:var(--card);color:var(--ink);
  font:inherit;font-size:1.1875rem;
}
.trade input::placeholder{color:var(--lake);opacity:1}
.trade .fine{margin-top:.7rem;font-size:.9375rem}
.trade--quiet .field{grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:1.25rem}

/* ---------- the rolodex, with no script ----------
   Written by rolodex() in src/library.js. This is the whole of it for a
   visitor without script, on a touch screen, or who asked for reduced
   motion: plain cards, every one a link, every sentence whole. Under 860px
   they are a strip that scrolls sideways and snaps card by card; the page
   itself never scrolls sideways. From 860px they are a stack. Nothing here
   clips a teaser: a card grows to fit its words. */
.rolo .eyebrow{margin-bottom:.75rem;display:flex;justify-content:space-between;align-items:baseline;gap:1rem}
.rolo .eyebrow a{letter-spacing:.06em;white-space:nowrap;padding-block:.9rem;margin-block:-.9rem}
.rolo-cards{
  list-style:none;
  margin:0 calc(-1 * var(--gutter));
  padding:.25rem var(--gutter) 1.25rem;
  display:flex;gap:.9rem;
  overflow-x:auto;
  scroll-snap-type:x mandatory;
  scroll-padding-inline:var(--gutter);
  overscroll-behavior-x:contain;
  scrollbar-width:none;
}
.rolo-cards::-webkit-scrollbar{display:none}
.rolo-cards li{flex:none;width:min(80vw,21rem);scroll-snap-align:start}
.card{
  position:relative;
  display:grid;gap:.55rem;align-content:start;
  height:100%;min-height:9.25rem;
  padding:1.05rem 1.15rem 1rem;
  border:1px solid var(--hair);border-radius:12px;
  background:var(--card);
  text-decoration:none;
  box-shadow:0 14px 26px -20px rgba(20,35,43,.45);
  transition:transform .18s ease,box-shadow .18s ease;
}
.card .k{font-family:var(--mono);font-weight:500;font-size:.8125rem;letter-spacing:.12em;text-transform:uppercase;color:var(--lake);overflow-wrap:anywhere}
.card .t{font-weight:600;font-size:1.125rem;line-height:1.3;letter-spacing:-.01em;overflow-wrap:anywhere}
.card .go{font-size:.9375rem;font-weight:600;align-self:end;justify-self:start;border-bottom:2px solid var(--amber);padding-bottom:.05rem;overflow-wrap:anywhere}
.card:active{transform:scale(.985)}
@media (min-width:860px){
  .hero{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:4.5rem;align-items:start}
  /* The stack: every card, compact, one under the other. The whole card is
     the link, so the cue line steps out to keep the stack short. */
  .rolo:not(.live) .rolo-cards{display:grid;gap:.6rem;margin:0;padding:0;overflow:visible}
  .rolo:not(.live) .rolo-cards li{width:auto}
  .rolo:not(.live) .card{min-height:0;padding:.8rem 1rem;gap:.3rem;box-shadow:none}
  .rolo:not(.live) .card .t{font-size:1rem}
  .rolo:not(.live) .card .go{display:none}
}

/* ---------- the rolodex, as a wheel ----------
   /rolodex.js adds .live to the rolodex only at 860px and up, with a fine
   pointer that can hover, and no reduced-motion preference. Then the cards
   stack on a dark stage: the front card full size and readable, its
   neighbours receding behind it. The script sets two numbers on each card,
   --d (its signed distance from the front) and --a (the same, unsigned),
   plus --ry on the list and --mx and --my on the front card for the pointer.
   Every position, size and opacity is worked out here from those. The stage
   is dark in both modes: it reads --band and --stage. */
.rolo-stage{outline-offset:4px}
.rolo.live .rolo-stage{
  position:relative;height:27rem;
  border-radius:18px;overflow:hidden;
  perspective:1100px;
  background:radial-gradient(120% 85% at 75% 0%,var(--stage) 0%,var(--band) 62%);
  cursor:grab;
  -webkit-user-select:none;user-select:none;
}
.rolo.live .rolo-stage.drag{cursor:grabbing}
.rolo.live .rolo-stage::before,
.rolo.live .rolo-stage::after{content:"";position:absolute;left:0;right:0;height:4.5rem;z-index:200;pointer-events:none}
.rolo.live .rolo-stage::before{top:0;background:linear-gradient(var(--band),rgba(20,35,43,0))}
.rolo.live .rolo-stage::after{bottom:0;background:linear-gradient(rgba(20,35,43,0),var(--band))}
.rolo.live .rolo-cards{
  position:absolute;inset:0;
  margin:0;padding:0;
  display:block;overflow:visible;
  transform-style:preserve-3d;
  transform:rotateY(var(--ry,0deg));
  transition:transform .25s ease-out;
}
.rolo.live .rolo-cards li{
  --d:0;--a:0;
  position:absolute;left:1.6rem;right:2.6rem;top:50%;
  width:auto;
  transform:translateY(calc(-50% + var(--d) * 5.5rem)) translateZ(calc(var(--a) * -120px)) rotateX(calc(var(--d) * -15deg)) scale(calc(1 - var(--a) * .07));
  opacity:clamp(0,calc(1.25 - var(--a) * .5),1);
  will-change:transform,opacity;
}
.rolo.live .card{
  min-height:8.75rem;
  overflow:hidden;
  background:var(--paper);
  border:0;border-left:3px solid transparent;
  box-shadow:0 26px 44px -22px rgba(0,0,0,.75);
  -webkit-user-drag:none;
}
.rolo.live li:not(.front) .card .go{opacity:0;transform:translateY(4px)}
.rolo.live li:not(.front) .card .t{-webkit-mask-image:linear-gradient(#000 45%,transparent 95%);mask-image:linear-gradient(#000 45%,transparent 95%)}
.rolo.live .front .card{border-left-color:var(--amber)}
.rolo.live .front .card::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  opacity:0;transition:opacity .2s ease;
  background:radial-gradient(14rem circle at var(--mx,50%) var(--my,50%),rgba(185,111,36,.2),rgba(185,111,36,0) 60%);
}
.rolo.live .front .card:hover,
.rolo.live .front .card:focus-visible{transform:translateY(-4px) scale(1.015);box-shadow:0 34px 54px -22px rgba(0,0,0,.85)}
.rolo.live .front .card:hover::after{opacity:1}
.rolo.live li:not(.front) .card{cursor:pointer}
.rolo.live .ticks{position:absolute;right:.85rem;top:50%;transform:translateY(-50%);z-index:210;display:grid;gap:.5rem;justify-items:end;pointer-events:none}
.rolo.live .ticks i{display:block;width:.5rem;height:2px;background:rgba(251,250,247,.32);transition:width .2s ease,background .2s ease}
.rolo.live .ticks i.on{width:1rem;background:var(--amber)}
.rolo.live .rolo-ctl{display:flex;align-items:center;justify-content:space-between;margin-top:.8rem;font-family:var(--mono);font-size:.8125rem;color:var(--lake)}
.rolo-hint{letter-spacing:.04em}
.rolo-ctl .nav{display:flex;align-items:center;gap:.6rem}
.rolo-ctl [data-pos]{white-space:nowrap}
@media (min-width:860px){
  .rolo.live{align-self:center}
}

/* ---------- sections ---------- */
.home > section{padding-block:clamp(2.75rem,8vh,5rem)}
.home h2{margin-top:.35rem;font-size:clamp(1.625rem,1.2rem + 2vw,2.5rem);max-width:20ch}
.home .lede{margin-top:.9rem;color:var(--ink-soft);max-width:46ch}
.band{background:var(--tint)}

/* ---------- what you get: the scene, and three points ----------
   The scene is written by scene() in src/scene.js. It is a drawing: the
   chips in it are plain spans and take nothing from the button component. */
.day{margin-top:2rem;display:grid;gap:1.75rem}
.scene{margin:0}
.chat{background:var(--card);border:1px solid var(--hair);border-radius:10px;padding:1.1rem 1.1rem .4rem;box-shadow:0 1px 0 rgba(20,35,43,.04)}
.chat .head{display:flex;justify-content:space-between;font-family:var(--mono);font-size:.75rem;letter-spacing:.08em;color:var(--lake);padding-bottom:.75rem;border-bottom:1px solid var(--hair)}
.msg{padding:.85rem 0;border-bottom:1px solid var(--hair);display:grid;grid-template-columns:3.2rem 1fr;gap:.25rem .5rem}
.msg:last-child{border-bottom:0}
.msg time{font-family:var(--mono);font-size:.75rem;color:var(--lake);padding-top:.2rem}
.msg p{font-size:1rem;line-height:1.5}
.msg .ok{grid-column:2;display:flex;gap:.5rem;margin-top:.45rem}
.msg .ok span{font-size:.8125rem;font-weight:600;line-height:1.5;padding:.25rem .65rem;border-radius:4px;border:1.5px solid var(--ink)}
.msg .ok span:first-child{background:var(--ink);color:var(--paper)}
.demo{font-size:.875rem;line-height:1.5;color:var(--lake);margin-top:.6rem}
.points{display:grid;gap:1.1rem;align-content:start}
.points h3{font-size:1.1875rem}
.points p{color:var(--ink-soft);margin-top:.2rem}

/* ---------- who builds it ---------- */
.who-row{margin-top:1.75rem;display:grid;grid-template-columns:5.5rem 1fr;gap:1.1rem;align-items:start;max-width:40rem}
.face{width:5.5rem;height:5.5rem;border-radius:8px;object-fit:cover;background:var(--tint)}
.who-row h2,
.who-row h3{font-size:1.25rem}
.who-row p{color:var(--ink-soft);margin-top:.3rem}
.who-row a{display:inline-block;margin-top:.5rem;font-weight:600}

/* ---------- terms: the numbers lead ---------- */
.terms{margin-top:1.75rem;display:grid}
.term{border-top:1px solid var(--hair);padding:1.1rem 0;display:grid;grid-template-columns:7.5rem 1fr;gap:1rem;align-items:baseline}
.term:last-child{border-bottom:1px solid var(--hair)}
.term b{font-family:var(--mono);font-weight:600;font-size:1.375rem;letter-spacing:-.03em}
.term p{color:var(--ink-soft)}
.term strong{color:var(--ink);display:block}

/* ---------- the close: the one dark band on the page ----------
   It reads --band and --on-band, which are the same in both modes, so it is
   a dark band on a dark page too. The footer follows it with no gap. */
.closing{background:var(--band);color:var(--on-band)}
.closing h2{max-width:18ch}
.closing .lede{color:rgba(251,250,247,.78)}
.closing .cta{margin-top:1.5rem;max-width:22rem;display:grid}
.closing .alt{display:inline-block;margin-top:1rem;color:var(--on-band);font-weight:600}
.home + footer{margin-top:0;border-top:0;padding-block:1.5rem 2.5rem}
@media (min-width:860px){
  .day{grid-template-columns:1.1fr .9fr;gap:3.5rem;align-items:start}
  .term{grid-template-columns:11rem 1fr}
  .who-row{grid-template-columns:12rem 1fr;gap:2rem;align-items:center;max-width:46rem}
  .face{width:12rem;height:12rem}
}

/* ---------- the work ---------- */
.item{
  border-top:1px solid var(--hair);
  padding-block:clamp(1.75rem,4vh,2.5rem);
  display:grid;
  gap:.75rem 2.5rem;
}
.item:last-of-type{border-bottom:1px solid var(--hair)}
.item h3{
  font-size:clamp(1.125rem,1rem + .8vw,1.375rem);
  display:flex;
  align-items:baseline;
  gap:.75rem;
}
.item h3::before{
  content:"";
  flex:none;
  width:1.125rem;
  height:2px;
  background:var(--lake);
  transform:translateY(-.3em);
}
.item p{color:var(--ink-soft);max-width:62ch}
@media (min-width:860px){
  .item{grid-template-columns:4.5rem minmax(0,1fr);align-items:baseline}
  .item .index{grid-row:1 / span 2}
  .item p{margin-top:.25rem}
}

/* a link onward from a section */
.more{margin-top:clamp(1.5rem,4vh,2rem);max-width:62ch}
.more a{
  display:inline-block;min-height:1.5rem;
  text-decoration:none;
  border-bottom:1px solid var(--hair);
  padding-bottom:.15rem;
  color:var(--ink-soft);
}
.more a:hover{border-bottom-color:var(--lake);color:var(--ink)}

/* ---------- ledger ---------- */
.ledger{margin:0;font-variant-numeric:tabular-nums}
.ledger .row{
  border-top:1px solid var(--hair);
  padding-block:clamp(1.5rem,3.5vh,2rem);
  display:grid;
  gap:.5rem 2.5rem;
}
.ledger .row:last-child{border-bottom:1px solid var(--hair)}
.ledger dt{font-weight:600;letter-spacing:-.01em}
.ledger dd{margin:0;color:var(--ink-soft);max-width:56ch}
@media (min-width:860px){
  .ledger .row{grid-template-columns:16rem minmax(0,1fr);align-items:baseline}
}

/* ---------- the name ---------- */
.lead{
  font-family:var(--sans);
  font-weight:700;
  letter-spacing:-.02em;
  font-size:clamp(1.25rem,1.05rem + 1.1vw,1.75rem);
  line-height:1.25;
  max-width:36ch;
  text-wrap:balance;
  margin-bottom:clamp(2rem,5vh,2.75rem);
}
.close{color:var(--ink-soft);max-width:66ch}

/* ---------- the mail link ---------- */
.mailto{
  display:inline-block;
  font-family:var(--mono);
  font-weight:500;
  font-size:clamp(1.0625rem,1rem + .7vw,1.375rem);
  letter-spacing:-.01em;
  color:var(--ink);
  text-decoration:none;
  padding-block:.5rem;
  border-bottom:2px solid var(--amber);
  transition:border-bottom-width .12s ease;
}
.mailto:hover{border-bottom-width:4px}
.next{margin-top:clamp(2.5rem,6vh,3.5rem);margin-bottom:1rem}

/* ---------- footer ----------
   Written by siteFooter() in src/shell.js, the same on every page: the
   address, the four page links at every width, and the two closing items. */
footer{
  border-top:1px solid var(--hair);
  margin-top:clamp(4rem,10vh,7rem);
  padding-block:1rem 2rem;
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:0 1.5rem;
  font-size:.875rem;
  color:var(--lake);
}
footer a{display:inline-flex;align-items:center;min-height:2.75rem;text-decoration:none}
footer a:hover{color:var(--ink)}

/* ---------- ask: the question box on /work and under an answer ---------- */
.ask form{
  position:relative;
  display:flex;
  align-items:baseline;
  gap:1.25rem;
  max-width:62ch;
  border-bottom:2px solid var(--ink);
}
.ask input{
  flex:1 1 auto;min-width:0;
  border:0;border-radius:0;background:transparent;
  font:inherit;font-size:clamp(1.0625rem,1rem + .6vw,1.3125rem);
  color:var(--ink);
  padding:.65rem 0;
  outline-offset:6px;
}
.ask input::placeholder{color:transparent}
.hint{
  position:absolute;left:0;right:4rem;top:.65rem;
  pointer-events:none;
  font-size:clamp(1.0625rem,1rem + .6vw,1.3125rem);
  color:var(--lake);
}
/* Three example lines take turns twice, then the first one stays. */
.hint span{
  position:absolute;left:0;top:0;max-width:100%;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  opacity:0;
  animation:hint 12s linear 2;
}
.hint span:first-child{animation:hint 12s linear 2,hold 1ms linear 24s forwards}
.hint span:nth-child(2){animation-delay:4s}
.hint span:nth-child(3){animation-delay:8s}
@keyframes hint{0%{opacity:0}4%,30%{opacity:1}34%,100%{opacity:0}}
@keyframes hold{to{opacity:1}}
.ask input:focus ~ .hint,
.ask input:not(:placeholder-shown) ~ .hint{display:none}
/* The small print under a field: sentence case, readable, in lake. */
.fine{
  margin-top:1rem;
  font-size:.8125rem;line-height:1.5;
  color:var(--lake);
  max-width:62ch;
}

/* ---------- hello: the short form under the offer ----------
   The fields are the trade form's: a label above, a bordered box. One form
   language on the page that takes the lead and the page that earns it. */
.hello{max-width:62ch;margin-top:1.25rem}
.hello .field{display:grid;gap:.4rem;margin-top:1.1rem}
.hello .field.missing input{border-color:var(--amber)}
.hello label{font-weight:600}
/* .field input, not input: the honeypot is a .vh input outside any field, and
   a width on it would stretch the page sideways. */
.hello .field input{
  -webkit-appearance:none;appearance:none;
  width:100%;margin:0;
  padding:.85rem 1rem;
  border:2px solid var(--ink);border-radius:6px;
  background:var(--card);color:var(--ink);
  font:inherit;font-size:1.125rem;
}
/* The gap between the fields and the action. It hangs on the hidden field's
   wrapper, the last thing before the button, which has no height of its own. */
.hello [aria-hidden="true"]{margin-bottom:1.5rem}
.hello .fine{margin-top:.75rem}
/* The talk page's form: what happens next sits beside the button. On a phone
   it follows the button; from 860px it is the second column. */
.hello--next{max-width:none;display:grid;gap:2.5rem}
.hello--next .fields{max-width:62ch}
.after .ledger{margin-top:.75rem}
.after .ledger .row{grid-template-columns:1fr;gap:.2rem;padding-block:1rem}
@media (min-width:860px){
  .hello--next{grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);gap:4rem;align-items:start}
}
/* the page as a QR code, for a reader at a desk */
.scan{display:flex;align-items:center;gap:1.25rem;margin-top:clamp(1.75rem,4vh,2.5rem);max-width:62ch}
.scan svg{width:6.5rem;height:6.5rem;flex:none;color:var(--ink)}
.scan p{
  font-family:var(--mono);font-weight:500;font-size:.75rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--lake);max-width:26ch;
}

/* Reduced motion. The rolodex stays in its plain form (the script does not
   start a wheel for a visitor who asks for this) and nothing transitions. */
@media (prefers-reduced-motion:reduce){
  .card,
  .rolo-cards{transition:none;scroll-behavior:auto}
  .card:active{transform:none}
  .hint span{animation:none}
  .hint span:first-child{opacity:1}
  *{transition-duration:.01ms !important}
}

/* ============================================================
   /work. Everything above is shared by every page. These are the
   rules only the work page uses.
   ============================================================ */
.page-head{
  padding-block:clamp(2.5rem,7vh,4rem) 0;
}
.page-head h1{
  margin-top:clamp(1rem,2.5vh,1.5rem);
  font-size:clamp(1.75rem,1.2rem + 2.6vw,2.75rem);
  max-width:20ch;
  text-wrap:balance;
}
.page-head .close{margin-top:clamp(1rem,2.5vh,1.5rem)}
.page-head .index{
  display:flex;flex-wrap:wrap;gap:.5rem 1.75rem;
  margin-top:clamp(1.75rem,4vh,2.5rem);
  letter-spacing:.18em;text-transform:uppercase;
}
.page-head .index a{text-decoration:none;padding-block:.35rem;border-bottom:1px solid transparent}
.page-head .index a:hover{border-bottom-color:var(--lake)}

/* a built system: title, what it is, then its facts in a ledger */
.case{
  border-top:1px solid var(--hair);
  padding-block:clamp(2rem,5vh,3rem);
}
.case:last-of-type{border-bottom:1px solid var(--hair)}
.case h3{
  font-family:var(--mono);
  font-weight:500;
  letter-spacing:-.02em;
  font-size:clamp(1.25rem,1.05rem + 1vw,1.625rem);
}
.case .state{margin-top:.5rem}
/* Skip the eyebrow: `.case > p` outranks `.eyebrow`, so without the :not()
   the status line loses its lake colour to the body ink. */
.case > p:not(.eyebrow){color:var(--ink-soft);max-width:62ch;margin-top:clamp(1rem,2.5vh,1.25rem)}
.case .facts{
  margin:clamp(1.25rem,3vh,1.75rem) 0 0;
  padding:0;
  list-style:none;
  display:grid;
  gap:.5rem 2.5rem;
  color:var(--ink-soft);
  max-width:72ch;
}
.case .facts li{
  padding-left:1.75rem;
  position:relative;
}
.case .facts li::before{
  content:"";
  position:absolute;
  left:0;
  top:.72em;
  width:1rem;
  height:1px;
  background:var(--lake);
}
@media (min-width:720px){
  .case .facts{grid-template-columns:1fr 1fr}
}
.case .out{
  margin-top:clamp(1.25rem,3vh,1.75rem);
  font-family:var(--mono);font-weight:500;font-size:.75rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--lake);
  display:flex;flex-wrap:wrap;gap:.5rem 1.5rem;
}
.case .out a{text-decoration:none;border-bottom:1px solid var(--hair)}
.case .out a:hover{border-bottom-color:var(--lake)}

.back{margin-top:clamp(2.5rem,6vh,3.5rem)}
.back a{display:inline-block;min-height:1.5rem;text-decoration:none;border-bottom:1px solid var(--hair)}
.back a:hover{border-bottom-color:var(--lake)}
/* Spacing the work page once wrote as style attributes. */
.close.follow{margin-top:1.25rem}
.ledger.spaced{margin-top:clamp(2rem,5vh,2.75rem)}

/* ============================================================
   The 404 page. Pages serves public/404.html, with a 404 status, for
   any path that is not a file in public/ and not a Function route.
   ============================================================ */
.lost{min-height:100svh;display:flex;flex-direction:column;justify-content:center;padding-block:clamp(3rem,8vh,5rem)}
.lost h1{margin-top:clamp(1rem,2.5vh,1.5rem);font-size:clamp(1.75rem,1.2rem + 2.6vw,2.75rem);max-width:20ch;text-wrap:balance}
.lost .close{margin-top:clamp(1rem,2.5vh,1.5rem)}
.lost .index{margin-top:clamp(1.75rem,4vh,2.5rem)}

/* ============================================================
   Function pages: /ask, /for and the library, /hello.
   ============================================================ */
.ask-page{padding-top:clamp(2.5rem,7vh,4.5rem)}
.q{font-size:clamp(1.375rem,1rem + 2.4vw,2.25rem);max-width:28ch;text-wrap:balance;margin-top:clamp(1rem,2.5vh,1.5rem)}
.for-page{padding-top:clamp(2.5rem,7vh,4.5rem)}
.b{font-size:clamp(1.375rem,1rem + 2.4vw,2.25rem);max-width:28ch;text-wrap:balance;margin-top:clamp(1rem,2.5vh,1.5rem)}

/* The answer. On /ask it is the model's plain text, so it takes the soft
   ink itself and keeps its line breaks. On /for it is shaped into
   paragraphs, headings and rows, which carry their own colour. */
.answer{margin-top:clamp(1.5rem,4vh,2.25rem);max-width:62ch;font-size:clamp(1.0625rem,1rem + .4vw,1.1875rem);line-height:1.6}
.ask-page .answer{color:var(--ink-soft);white-space:pre-line}
.answer p{color:var(--ink-soft);margin-top:.5rem}
.answer .eyebrow{color:var(--lake);margin-top:clamp(1.75rem,4vh,2.25rem);margin-bottom:.25rem}
.answer .eyebrow:first-child{margin-top:0}
.answer .row{padding-left:1.75rem;position:relative}
.answer .row::before{content:"";position:absolute;left:0;top:.72em;width:1rem;height:1px;background:var(--lake)}

/* The wait line ("taking longer than usual") and the failure line. The
   stream writes an empty marker after the wait line once the first words
   land or the answer fails, and the wait line is hidden from then on. No
   answer page runs a script, so this is done with :has(). The line is
   shown only inside @supports: a browser that could not hide it again
   never shows it. */
.wait,.miss{font-family:var(--mono);font-weight:500;font-size:.75rem;letter-spacing:.16em;text-transform:uppercase;color:var(--lake);margin-top:1.25rem}
/* On /for the wait line sits inside .answer and takes .answer p's colour
   and margin, as it always has. On /ask it keeps its own. */
.ask-page .wait{color:var(--lake);margin-top:1.25rem}
.wait{display:none}
.settled{display:none}
@supports selector(:has(+ *)){
  .wait{display:block}
  .wait:has(+ .settled){display:none}
}

/* the next step: the offer and the short form under an answer */
.then{margin-top:clamp(2rem,5vh,3rem)}
.then .mailto{font-size:clamp(1rem,.95rem + .5vw,1.25rem)}
.then .line{color:var(--ink-soft);max-width:48ch;margin:.5rem 0 1.25rem}
.then .more{margin-top:1rem}

/* the form again, under the answer */
.ask-page + .ask{padding-top:clamp(3rem,8vh,5rem)}
.for-page .more{margin-top:1rem}
.again{padding-top:clamp(3rem,8vh,5rem)}

/* A kept trade page is where ads land (/for/<slug>): the trade's own
   headline at the front page's size on a phone, the answer, the workspace
   scene, the ask. A long trade wraps inside the headline. */
.land{padding-top:clamp(1.5rem,5vh,4rem)}
.for-h{font-size:clamp(2.125rem,1.2rem + 4.6vw,3.25rem);line-height:1.08;max-width:20ch;text-wrap:balance;overflow-wrap:anywhere}
.draft{margin-top:.7rem;font-size:.9375rem;color:var(--lake)}
.shown{margin-top:clamp(2rem,5vh,3rem);max-width:34rem}
.shown .scene{margin-top:.75rem}

/* The gate on a kept page: block 1 open, the rest held until an email or a
   phone opens it. The held text is in the page either way; these rules show
   it when the page is marked open. Then the owner's confirmation page. */
.gated{display:none}
.gated .eyebrow:first-child{margin-top:clamp(1.75rem,4vh,2.25rem)}
.gate{margin-top:clamp(2.5rem,6vh,3.5rem);max-width:62ch}
.gate .rule{margin-bottom:0}
.gate .held{border-top:1px solid var(--hair);padding-block:.9rem;margin:0}
.gate .line{margin-top:1.25rem;color:var(--ink-soft)}
.gate .hello{margin-top:clamp(1.75rem,4vh,2.5rem)}
.gate .miss{margin-top:1rem}
[data-gate="open"] .gated{display:block}
[data-gate="open"] .gate{display:none}
.confirm{margin-top:clamp(1.5rem,4vh,2.25rem);max-width:62ch}
.confirm .hello{margin-top:2.75rem}

/* /hello: the page a posted form comes back as */
.hello-page{padding-top:clamp(2.5rem,7vh,4.5rem)}
.hello-page h1{font-size:clamp(1.375rem,1rem + 2.4vw,2.25rem);max-width:28ch;text-wrap:balance;margin-top:clamp(1rem,2.5vh,1.5rem)}
.hello-page .copy{margin-top:clamp(1.25rem,3vh,1.75rem);max-width:56ch;color:var(--ink-soft);font-size:clamp(1.0625rem,1rem + .4vw,1.1875rem);line-height:1.6}
.hello-page .hello{margin-top:clamp(1.5rem,4vh,2.25rem)}
.hello-page .more{margin-top:1.25rem}

/* ============================================================
   /trades: the home page's card, as a list. A card grows to fit
   its words; nothing here clips one.
   ============================================================ */
.trades{padding-top:clamp(2rem,5vh,3rem)}
.listed{list-style:none;margin:.75rem 0 0;padding:0;display:grid;gap:.9rem;grid-template-columns:repeat(auto-fill,minmax(min(100%,18rem),1fr))}

/* ============================================================
   /about: the two grandfathers, side by side from 860px.
   ============================================================ */
.pair{display:grid;gap:clamp(1.75rem,4vh,2.5rem);margin-bottom:clamp(2rem,5vh,2.75rem)}
.pair p{color:var(--ink-soft);max-width:56ch}
@media (min-width:860px){
  .pair{grid-template-columns:1fr 1fr;gap:3.5rem}
}
