/* ============================================================
   THE TRACE - assessment interface, light
   A chart recorder, not a sci-fi console. Real instruments draw
   ink on paper: EKG, seismograph, polygraph. So the ground is
   warm paper with a faint chart grid and the trace is ink.
   Real client dog photos, never AI ones. Exactly one dark
   moment in the whole flow, and it is the reveal.
   Self-contained on purpose: index.html loads this INSTEAD of
   app.css, which still belongs to plan.html and the course.
   ============================================================ */

@font-face {
  font-family: "Figtree";
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url(/fonts/Figtree-var.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;
}

/* 2026-09-21: the instrument stays, the furniture moves.

   The chart recorder is the idea of this page and it survives untouched: the
   grid, the ink trace, the one dark reveal. What changed is everything AROUND
   it, so the free assessment and the plan it hands you are recognisably the
   same product. That is done by repointing these tokens rather than rewriting
   948 lines, the same trick the trainer app used to go from glass to Airbnb.

   Every value below is the same one plan.css and course.css use, and
   tests/design-tokens.test.js fails if the three ever drift apart. */
:root {
  /* was #f4f1e8 paper. The app's ground: a hair off white, almost neutral,
     just warm enough that the sage does not look pasted on. */
  --t-paper:    #faf8f4;
  /* was #ebe5d5. A panel is a WHITE card now, drawn with a shadow rather than
     with a border, which is what the card-to-ground step buys. */
  --t-panel:    #ffffff;
  --t-grid:     #ece7dd;
  --t-rule:     #e8e3d9;
  --t-ink:      #221f1c;
  --t-ink-2:    #45423c;
  --t-ink-3:    #6e6a60;   /* 5.4:1 on white */
  /* The logo's own green, hue 75, sampled off the mark. */
  --t-sage:     #808868;
  /* A primary button is a light pastel carrying INK at 13:1, never white on a
     deep fill. So this token is now the PASTEL, and --t-sage-ink is its text.

     2026-10-02, the green moved. Kelty: "the green color you chose, also, I'm
     not a huge fan of." The logo green (--t-sage #808868) is sampled off the
     mark and did not move. What moved is the two greens DERIVED from it, which
     had drifted yellow: the pastel was 44% saturated chartreuse and the deep
     was a 34% saturated olive. Both are now pulled back toward the mark's own
     desaturation, and the deep gained contrast on white doing it, 5.11 to 5.80.
     The same four values live in plan.css, course.css and course/learn.html,
     and tests/design-tokens.test.js fails if they ever disagree. */
  --t-sage-btn: #e3e8d6;
  --t-sage-press: #d4dcc2;
  --t-sage-deep:#5c6a4c;
  --t-sage-soft:#f1f4ea;
  --t-sage-shadow: 62, 68, 44;
  --t-line:     #5c6a4c;
  --t-signal:   #96552f;
  --t-dark:     #221f1c;

  --t-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  /* Both the display face and the body face are Figtree now. Weight does the
     work that condensed uppercase used to do. */
  --t-disp: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            "Helvetica Neue", Arial, sans-serif;
  --t-body: var(--t-disp);

  --t-r-sm: 14px;
  --t-r:    20px;
  --t-r-btn:16px;
  --t-sh-1: 0 1px 2px rgba(35, 31, 25, .06), 0 2px 5px -1px rgba(35, 31, 25, .06);
  --t-sh-2: 0 1px 2px rgba(35, 31, 25, .06), 0 8px 22px -6px rgba(35, 31, 25, .20);
  --t-sh-3: 0 2px 5px rgba(35, 31, 25, .08), 0 16px 36px -8px rgba(35, 31, 25, .24);
  --t-gloss: inset 0 1px 0 rgba(255, 255, 255, .9);

  --t-gut: 22px;
  --t-max: 540px;

  /* The fit multiplier, declared here as well as on .scr so that every
     calc(x * var(--fit)) below still resolves on a page that never runs the
     fit pass. An undefined var makes the whole calc invalid, and an invalid
     font-size is a wrecked screen, so this line is load-bearing. */
  --fit: 1;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;
  background: var(--t-paper);
  color: var(--t-ink);
  font-family: var(--t-body);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

/* Paper has tooth. Two very faint washes so it never reads as a flat beige
   field, which is the other way to be depressing. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(120% 80% at 50% -10%, rgba(255, 253, 246, 0.9), transparent 60%),
    radial-gradient(90% 60% at 100% 100%, rgba(206, 196, 172, 0.32), transparent 70%);
}

#app {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.scr {
  position: relative;
  /* ONE SCREEN, MEASURED, 2026-10-06. The type on these screens is sized for a
     390x844 phone. On a shorter phone a single multiplier walks down until the
     last thing on the screen is above the fold, so the fourth answer is never
     the one nobody sees. quiz.js fitScreen() sets it; 1 is the full size and
     nothing below 0.74 is allowed. Every compressible metric below is written
     as calc(x * var(--fit)); type gives up less than space does, which is the
     point of the second number in the blended calcs. */
  --fit: 1;
  /* clip, not hidden: the watermark still gets trimmed at the side, but a
     screen that does run long scrolls instead of losing its last answer. */
  overflow-x: clip;
  flex: 1;
  width: 100%;
  max-width: var(--t-max);
  margin: 0 auto;
  padding: 18px var(--t-gut) calc(22px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
}

/* ---------- instrument furniture ---------- */

.rail {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-height: calc(42px * var(--fit));
  /* The badge was sitting right on the progress bar. */
  margin-top: calc(18px * var(--fit));
}

.rail-l { display: flex; align-items: center; gap: 10px; }

.tag {
  /* 2026-10-02: was 10px monospace at 0.19em. Kelty: "something about it is
     hard for me to read, and it feels icky." Tiny wide-tracked uppercase
     monospace is the thing that made a friendly quiz read like a lab form. The
     counter keeps tabular figures, in the body face, two sizes up. */
  font-family: var(--t-body);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--t-ink-3);
  margin: 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tag.on { color: var(--t-sage); }

/* The badge, in the corner of every screen. Brand presence without a
   photograph that has to justify itself sixteen times. Sized to actually read
   as the logo rather than as a favicon. */
.mark {
  width: 42px;
  height: 42px;
  display: block;
  flex: none;
}

.hero .badge {
  width: 66px;
  height: 66px;
  display: block;
  margin: 0 0 16px;
}

/* Progress, because a quiz with no visible finish line is a chore. Sits at the
   very top edge where a phone browser's own progress bar would be.
   2026-10-06: one unbroken line across 21 readings is one long way to go. It
   is six blocks now, one per set, sized by how many readings the set holds, so
   what is in front of somebody is the block they are in and not the whole
   quiz. */
.prog {
  display: flex;
  gap: 3px;
  height: 4px;
  margin: 0 calc(var(--t-gut) * -1);
  padding: 0 var(--t-gut);
}
.prog i {
  display: block;
  height: 100%;
  background: var(--t-rule);
  border-radius: 2px;
  overflow: hidden;
}
.prog i b {
  display: block;
  height: 100%;
  width: 0;
  background: var(--t-sage);
  border-radius: 2px;
  transition: width 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---------- the chart ---------- */

.trace-wrap {
  margin: 12px 0 0;
  position: relative;
  background: var(--t-panel);
  border: 0;
  border-radius: var(--t-r-sm);
  box-shadow: var(--t-sh-1), var(--t-gloss);
  padding: calc(10px * var(--fit)) 12px calc(8px * var(--fit));
  overflow: hidden;
}

/* Chart paper, drawn rather than loaded. */
.trace-wrap::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(to right, var(--t-grid) 0 1px, transparent 1px 22px),
    repeating-linear-gradient(to bottom, var(--t-grid) 0 1px, transparent 1px 22px);
  opacity: 0.5;
}

canvas.trace {
  display: block;
  position: relative;
  z-index: 1;
  width: 100%;
  /* Squared on purpose: the chart is the first thing asked to give up room on
     a short phone, well before the words are. */
  height: calc(92px * var(--fit) * var(--fit));
}

.axis {
  display: flex;
  justify-content: space-between;
  font-family: var(--t-body);
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--t-ink-3);
  margin-top: calc(6px * var(--fit));
}

/* ---------- question ---------- */

/* The question sits in the room between the chart and the answers, centred.
   Pinned to the top it clung to the ceiling on the short qualifier screens and
   left a hole underneath; pinned to the bottom it floated away from its chart.
   Centred in the remaining space is right at both extremes. */
/* Tightened 2026-10-02 so four options and the question fit one phone screen
   without scrolling. A fourth answer you have to scroll to find is a fourth
   answer that gets picked less. */
.q-zone {
  flex: 1;
  display: flex;
  align-items: center;
  padding: calc(14px * var(--fit)) 0;
  min-height: calc(104px * var(--fit));
}

.q {
  font-family: var(--t-disp);
  font-weight: 700;
  text-transform: none;
  /* 2026-10-02: was weight 800, line-height 1.1, tracking -0.028em. These
     questions run three and four lines on a phone, and at that length a heavy
     face packed that tightly is a wall. Same size, loosened. */
  /* 2026-10-06: up from clamp(26px, 6.8vw, 36px). Kelty: the font feels too
     small. The fit pass, not a smaller base size, is what handles short
     phones now. */
  font-size: calc(clamp(27px, 7.2vw, 38px) * (0.72 + 0.28 * var(--fit)));
  line-height: 1.2;
  letter-spacing: -0.014em;
  text-wrap: balance;
  margin: 0;
  color: var(--t-ink);
}

/* ---------- setup screens ---------- */

/* No chart here. Nothing is being measured until the questions start, and a
   flat instrument at the top of a form only made the form look colder. */
.setup .setup-head {
  position: relative;
  margin-top: calc(26px * var(--fit));
  padding-bottom: calc(20px * var(--fit));
  min-height: calc(clamp(88px, 21vw, 118px) * var(--fit));
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* The setup headings run long once a dog's name is in them, so they sit a size
   below the question headings rather than shouting across three lines. */
.setup .setup-head .q { font-size: calc(clamp(26px, 7vw, 36px) * (0.74 + 0.26 * var(--fit))); }

.setup .setup-head .q { position: relative; z-index: 1; }

.setup .sub {
  position: relative;
  z-index: 1;
  font-size: calc(17.5px * (0.8 + 0.2 * var(--fit)));
  line-height: 1.55;
  color: var(--t-ink-2);
  margin: calc(14px * var(--fit)) 0 0;
  max-width: 34ch;
}

/* ONE QUESTION PER SCREEN, 2026-10-02. The screen now has three zones: the
   rail at the top, the question in the middle, and a floor. Without the floor a
   single field leaves the bottom half of a phone blank, which reads as a page
   that did not finish loading. The question block centers in what is left so
   the emptiness is shared rather than piled underneath. */
.setup { justify-content: flex-start; }
.setup-mid {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 10px 0;
  position: relative;
  z-index: 1;
}
.setup-foot {
  position: relative;
  z-index: 1;
  padding-top: calc(20px * var(--fit));
}
.proof {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: calc(14px * var(--fit)) 16px;
  background: var(--t-panel);
  border-radius: var(--t-r-sm);
  box-shadow: var(--t-sh-1);
}
.proof-stars { color: var(--t-sage); letter-spacing: 1px; font-size: 13px; flex: 0 0 auto; }
.proof-txt {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--t-ink-3);
}
.proof-txt strong { color: var(--t-ink); font-weight: 800; }
.setup-foot .back { margin-top: 12px; }

.setup .fields { margin-top: calc(28px * var(--fit)); }
.setup .back { margin-top: 22px; align-self: center; }
.setup .btn { margin-top: 6px; }

/* The badge, very light, holding the right corner. Bleeds past the edge on
   purpose so it reads as a watermark on the page rather than a picture placed
   on it. */
.watermark {
  position: absolute;
  right: -20%;
  top: -7%;
  width: clamp(250px, 72vw, 400px);
  height: auto;
  opacity: 0.026;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}

.setup > *:not(.watermark) { position: relative; z-index: 1; }

/* Said back to them before the screen moves, so it lands as an answer rather
   than a field that got filled. */
.confirm {
  font-family: var(--t-body);
  font-style: italic;
  font-size: 16px;
  color: var(--t-sage);
  margin: 20px 0 0;
  animation: t-rise 0.3s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.confirm[hidden] { display: none; }

/* ---------- a field that tells you it is done ---------- */

/* Real labels. Three reasons: the placeholder vanished the moment anyone typed,
   a placeholder reading "Dog's name" is exactly the token iOS AutoFill looks
   for when deciding a field wants a person's name, and four labelled fields
   have rhythm where four bare boxes are a wall. */
.field { position: relative; }

.flabel {
  display: block;
  font-family: var(--t-body);
  font-weight: 700;
  font-size: 13.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--t-ink-3);
  margin: 0 0 calc(7px * var(--fit)) 1px;
}

.field .input { padding-right: 46px; }

/* Four identical rectangles is a wall. Each one confirming itself turns the
   same four fields into four small wins on the way to the button. */
.field .tick {
  position: absolute;
  right: 15px;
  /* Measured from the bottom, because the label sits above the input now. */
  bottom: 18px;
  width: 17px;
  height: 17px;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.7);
  transition: opacity 0.22s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}
.field .tick path {
  stroke: var(--t-sage);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
}
.field.done .tick { opacity: 1; transform: scale(1); }
.field.done .tick path { animation: t-draw 0.34s cubic-bezier(0.22, 1, 0.36, 1) forwards; }
.field.done .input { box-shadow: inset 0 0 0 1.5px var(--t-sage); }

@keyframes t-draw { to { stroke-dashoffset: 0; } }

/* The first two screens ask for a dog's name and a person's name, and those
   two are the whole first impression. A bigger box with the answer typed in at
   24px reads as somebody being asked a question, not as a form being filled
   in. 2026-10-06, Kelty: "it needs to feel like they want to type it in."
   Named .big and not .hero: .hero is the first screen's own class and it
   centres everything it touches. */
.field.big .input {
  font-size: calc(24px * (0.86 + 0.14 * var(--fit)));
  min-height: calc(68px * var(--fit));
  font-weight: 600;
  letter-spacing: -0.01em;
}
.field.big .input::placeholder { font-weight: 400; }
.field.big .tick { width: 20px; height: 20px; bottom: 22px; }

/* The button waits until it can actually do something, then wakes up. */
.btn.waiting {
  background: #f2efe8;
  color: var(--t-ink-3);
  border: 0;
  box-shadow: none;
}
.btn.waiting::after { display: none; }
.btn.waiting:hover { background: #f2efe8; transform: none; box-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .field .tick { transition: none; }
  .field.done .tick path { animation: none; stroke-dashoffset: 0; }
}

/* ---------- age and size, as chips ---------- */

/* Two native selects on a phone is two full-screen system pickers for what is
   really seven choices. These are seven buttons. */
/* Centred in the room left over, so the screen is not all weight at the top
   with a hole under it. */
/* One block with the heading, not a group floating in the middle of the screen.
   The slack belongs at the bottom, not between the title and the controls. */
.chip-groups {
  display: grid;
  gap: calc(24px * var(--fit));
  margin-top: 4px;
}

.chip-label {
  font-family: var(--t-body);
  font-weight: 700;
  font-size: 13.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--t-ink-3);
  margin: 0 0 calc(11px * var(--fit));
}

/* Grids, not wrap. Four ages wrapping three-then-one looked like an accident
   rather than a layout. */
.chips { display: grid; gap: calc(8px * var(--fit)); }
.chips.age { grid-template-columns: repeat(2, 1fr); }
.chips.size { grid-template-columns: repeat(3, 1fr); }
.chips.size .chip { min-height: calc(84px * var(--fit)); padding-top: calc(11px * var(--fit)); }

.chip {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  font-family: var(--t-body);
  color: var(--t-ink);
  background: var(--t-panel);
  border: 0;
  border-radius: var(--t-r-sm);
  box-shadow: var(--t-sh-1), var(--t-gloss);
  padding: calc(13px * var(--fit)) 14px;
  min-height: calc(66px * var(--fit));
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.12s ease, box-shadow 0.15s ease;
}

.chip-word { font-size: calc(17.5px * (0.8 + 0.2 * var(--fit))); line-height: 1.15; }
.chip-qual {
  font-family: var(--t-body);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--t-ink-3);
}
.chip.on .chip-qual { color: var(--t-sage-deep); }

/* The dot is the size, so the control shows the answer instead of only naming
   it. Encodes information; it is not an icon for decoration. */
.chip-dot {
  width: var(--d);
  height: var(--d);
  border-radius: 50%;
  background: var(--t-sage);
  /* Pushes the dot to the top so the three labels sit on one line. Without
     this the bigger dot shunts its own label down and the row reads crooked. */
  margin-bottom: auto;
  transition: background 0.15s ease;
}
.chip.on .chip-dot { background: var(--t-sage-deep); }
.chip:hover { box-shadow: var(--t-sh-2), var(--t-gloss); }
.chip:active { transform: translateY(1px); box-shadow: var(--t-sh-1); }
.chip:focus-visible { outline: 2px solid var(--t-sage); outline-offset: 2px; }
.chip.on {
  background: var(--t-sage-btn);
  color: var(--t-ink);
  box-shadow: 0 2px 6px rgba(var(--t-sage-shadow), .26),
              0 10px 24px -8px rgba(var(--t-sage-shadow), .34), var(--t-gloss);
}

.q-zone { position: relative; }

.q-zone .q { position: relative; z-index: 1; }

/* The dog's name carries the brand colour wherever it appears in a heading. */
.q em {
  font-style: normal;
  color: var(--t-sage);
}

/* Fires once, on the first letter of the name. */
@keyframes t-pop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.035); }
  100% { transform: scale(1); }
}
.q.pop {
  animation: t-pop 0.42s cubic-bezier(0.22, 1, 0.36, 1);
  transform-origin: left center;
}

/* ---------- options ---------- */

/* These are buttons, so they look like buttons. As hairline rows on a phone
   they read as a list of statements and it was not obvious they were tappable.
   Raised cards, big tap targets, and a chip carrying the reading. */
.opts {
  display: grid;
  gap: calc(8px * var(--fit));
  margin-top: auto;
  padding-top: calc(14px * var(--fit));
}

.opt {
  display: block;
  width: 100%;
  min-height: calc(56px * var(--fit));
  text-align: left;
  background: var(--t-panel);
  color: var(--t-ink);
  border: 0;
  border-left: 3px solid transparent;
  border-radius: var(--t-r-sm);
  padding: calc(16px * var(--fit)) 44px calc(16px * var(--fit)) 17px;
  font-family: var(--t-body);
  /* 2026-10-02: 15.5/1.4 was below comfortable reading on a phone.
     2026-10-06: 18px, and it keeps most of that on a short phone. The answer
     is the thing being read, so it gives up 40 percent of what the boxes do. */
  font-size: calc(18px * (0.76 + 0.24 * var(--fit)));
  line-height: calc(1.45 - 0.1 * (1 - var(--fit)));
  cursor: pointer;
  position: relative;
  box-shadow: var(--t-sh-1), var(--t-gloss);
  transition: border-left-color 0.16s ease, background 0.16s ease,
              transform 0.12s ease, box-shadow 0.16s ease;
}

/* The chevron is always visible now, not a hover reveal. On a phone there is
   no hover, so a hover-only affordance is no affordance. */
.opt::after {
  content: "";
  position: absolute;
  right: 17px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--t-sage);
  border-top: 2px solid var(--t-sage);
  transform: translateY(-50%) rotate(45deg);
  opacity: 0.55;
  transition: opacity 0.16s ease, right 0.16s ease;
}

.opt:hover {
  background: #fff;
  border-left-color: var(--t-sage);
  box-shadow: var(--t-sh-2), var(--t-gloss);
}
.opt:hover::after { opacity: 1; right: 14px; }
.opt:focus-visible { outline: 2px solid var(--t-sage); outline-offset: 2px; }
/* A tappable card presses by SETTLING: the lift comes out of it, which reads
   as the card going down onto the page rather than the page flexing. */
.opt:active { transform: translateY(1px); box-shadow: var(--t-sh-1); }

/* The answer you picked lights up and holds while the line draws. */
.opt.chosen {
  background: var(--t-sage-btn);
  border-left-color: var(--t-sage-deep);
  color: var(--t-ink);
  box-shadow: 0 2px 6px rgba(var(--t-sage-shadow), .26),
              0 10px 24px -8px rgba(var(--t-sage-shadow), .34), var(--t-gloss);
  transform: translateX(3px);
}
.opt.chosen .lv { background: rgba(34, 31, 28, 0.12); color: var(--t-ink); }
.opt.chosen::after { border-color: var(--t-sage-deep); opacity: 1; right: 14px; }

/* Everything you did not pick steps back. */
.opts.locked .opt:not(.chosen) { opacity: 0.4; }
.opts.locked .opt { pointer-events: none; }

.opt .txt { display: block; }

.opt .lv {
  display: inline-block;
  /* 2026-10-02: 9px was not a readable size for anybody. */
  font-family: var(--t-body);
  font-weight: 700;
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--t-sage-deep);
  background: var(--t-sage-soft);
  padding: 3px 8px;
  border-radius: 999px;
  margin-bottom: 8px;
  transition: background 0.16s ease, color 0.16s ease;
}

/* A quiet hint on one option of one question, not a sales badge: same shape
   as .lv but in the neutral ink tones instead of sage, so it reads as a small
   fact sitting next to the text rather than a recommendation shouting at you. */
.opt .hint {
  display: inline-block;
  margin-left: 8px;
  font-family: var(--t-body);
  font-weight: 700;
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--t-ink-3);
  background: var(--t-rule);
  padding: 2px 7px;
  border-radius: 999px;
}

.back {
  align-self: flex-start;
  margin-top: calc(14px * var(--fit));
  background: none;
  border: 0;
  color: var(--t-ink-3);
  font-family: var(--t-body);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: none;
  padding: calc(10px * var(--fit)) 2px;
  cursor: pointer;
}
.back:hover { color: var(--t-ink); }

/* ---------- inputs ---------- */

.input {
  width: 100%;
  background: var(--t-panel);
  /* --edge, not --t-rule: this is the boundary of a CONTROL, which WCAG
     1.4.11 wants at 3:1, and the hairline that divides content is far too
     faint for that. 3.06:1 on white, measured. */
  border: 0;
  box-shadow: inset 0 0 0 1.5px #948f84;
  color: var(--t-ink);
  font-family: var(--t-body);
  /* 2026-10-06: was 17/52. Kelty: the first questionnaire needs to be bigger
     on the screen, it should feel like something you want to type in. 20px is
     also the size iOS stops zooming the page on focus. */
  font-size: calc(20px * (0.86 + 0.14 * var(--fit)));
  padding: calc(16px * var(--fit)) 15px;
  min-height: calc(60px * var(--fit));
  border-radius: var(--t-r-btn);
  appearance: none;
}
.input::placeholder { color: var(--t-ink-3); }
.input:focus { outline: 0; box-shadow: inset 0 0 0 2px var(--t-sage-deep); }

select.input {
  background-image: linear-gradient(45deg, transparent 50%, var(--t-sage) 50%),
                    linear-gradient(135deg, var(--t-sage) 50%, transparent 50%);
  background-position: right 16px center, right 10px center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

.fields { display: grid; gap: calc(8px * var(--fit)); margin-top: calc(22px * var(--fit)); }

.note {
  font-family: var(--t-body);
  font-size: 14px;
  color: var(--t-ink-3);
  line-height: 1.55;
  margin: 16px 0 2px;
  max-width: 46ch;
}

/* ---------- buttons ---------- */

.btn {
  background: var(--t-sage-btn);
  color: var(--t-ink);
  border: 0;
  width: 100%;
  min-height: calc(58px * var(--fit));
  padding: calc(17px * var(--fit)) 18px;
  font-family: var(--t-disp);
  font-weight: 800;
  text-transform: none;
  letter-spacing: -0.01em;
  /* 2026-10-06: up from 16px. It is the one control on the screen. */
  font-size: calc(18px * (0.88 + 0.12 * var(--fit)));
  cursor: pointer;
  border-radius: var(--t-r-btn);
  box-shadow: 0 1px 2px rgba(var(--t-sage-shadow), .22),
              0 10px 24px -8px rgba(var(--t-sage-shadow), .36), var(--t-gloss);
  transition: background 0.16s ease, transform 0.12s ease, box-shadow 0.16s ease;
}
.btn:hover { background: var(--t-sage-press); box-shadow: 0 2px 4px rgba(var(--t-sage-shadow), .24), 0 14px 30px -8px rgba(var(--t-sage-shadow), .4), var(--t-gloss); transform: translateY(-1px); }
.btn:active { background: var(--t-sage-press); transform: translateY(1px); box-shadow: 0 1px 2px rgba(var(--t-sage-shadow), .24), var(--t-gloss); }

/* A quiet sheen that crosses the button once on arrival. One flourish, on the
   one control that matters. */
.btn { position: relative; overflow: hidden; }
.btn::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 42%;
  left: -50%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.55), transparent);
  transform: skewX(-18deg);
  animation: t-sheen 2.6s cubic-bezier(0.22, 1, 0.36, 1) 0.5s 1 forwards;
}
@keyframes t-sheen {
  0%   { left: -50%; }
  55%  { left: 120%; }
  100% { left: 120%; }
}
.btn:disabled { background: var(--t-rule); color: var(--t-ink-3); box-shadow: none; cursor: default; }
.btn:focus-visible { outline: 2px solid var(--t-ink); outline-offset: 3px; }

/* The second option, quieter than the first. Outline against the filled
   primary, so the plan stays the obvious move and the consult is there for
   somebody who would rather just talk to a person. */
.btn-2 {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--t-panel);
  color: var(--t-ink);
  border: 0;
  box-shadow: inset 0 0 0 1.5px #948f84;
  text-decoration: none;
  font-size: 15px;
}
.btn-2::after { display: none; }
.btn-2:hover {
  background: #f2efe8;
  color: var(--t-ink);
  box-shadow: inset 0 0 0 1.5px #948f84;
  transform: translateY(-1px);
}

/* A rule with the word sitting in it, rather than a floating "or". */
.or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 20px 0 16px;
  font-family: var(--t-body);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--t-ink-3);
}
.or::before, .or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--t-rule);
}

/* ---------- photographs ---------- */

.photo {
  display: block;
  width: 100%;
  object-fit: cover;
  border: 1px solid var(--t-rule);
}

/* One photograph in the whole flow, on the capture panel. Framed on the
   faces: the source is a tall portrait and a centred crop cuts them out. */
.photo.band { height: clamp(150px, 32vw, 210px); }
.photo.tall {
  height: clamp(210px, 50vw, 300px);
  object-position: center 34%;
}

/* ---------- hero ---------- */

/* Centred, and actually centred: the old one hung high with the weight all in
   the top half. Everything is on the middle axis now, badge included. */
.hero {
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: 0;
  padding-top: 32px;
  padding-bottom: 32px;
}

.hero .badge {
  width: 76px;
  height: 76px;
  display: block;
  margin: 0 0 26px;
}

.hero h1 {
  font-family: var(--t-disp);
  font-weight: 800;
  text-transform: none;
  font-size: clamp(30px, 7.8vw, 42px);
  line-height: 1.07;
  letter-spacing: -0.03em;
  text-wrap: balance;
  margin: 0;
}
.hero h1 em { font-style: normal; color: var(--t-sage-deep); }

.hero .lead {
  font-size: 16.5px;
  line-height: 1.58;
  color: var(--t-ink-2);
  margin: 18px auto 0;
  max-width: 40ch;
}

.hero .trace-wrap { width: 100%; margin-top: 24px; }
.hero canvas.trace { height: 78px; }

.hero .btn { margin-top: 22px; }

.hero .meta {
  font-family: var(--t-body);
  font-size: 12.5px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--t-ink-3);
  margin: 14px 0 0;
}

@media (max-height: 760px) {
  .hero .badge { width: 62px; height: 62px; margin-bottom: 18px; }
  .hero canvas.trace { height: 62px; }
  .hero .trace-wrap { margin-top: 18px; }
  .hero .lead { margin-top: 14px; }
}

/* ---------- result: the one dark moment ---------- */

.rev .verdict {
  background: var(--t-dark);
  color: #f4f1e8;
  padding: 26px 22px 28px;
  margin: 18px calc(var(--t-gut) * -1) 0;
}

.rev .verdict .tag { color: #a6b48c; }

.rev h1 {
  font-family: var(--t-disp);
  font-weight: 800;
  text-transform: none;
  font-size: clamp(30px, 8vw, 44px);
  line-height: 0.98;
  text-wrap: balance;
  margin: 10px 0 0;
  color: #f4f1e8;
}
.rev h1 em { font-style: normal; color: #a6b48c; }

/* THE READING, 2026-10-02. The bars stay; what they mean got added.

   Each row is now three things in two lines: the type name with its
   percentage, the bar, and a plain-language line under it. 10px uppercase
   monospace was the old row type, which is unreadable at arm's length and was
   a real part of why this screen felt cold, so the name and the meaning line
   are set in the body face at a size you can actually read. The monospace is
   kept only for the number, where tabular figures earn it. */

.stat {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 20px;
}
.stat-n {
  font-family: var(--t-disp);
  font-weight: 800;
  font-size: clamp(54px, 15vw, 76px);
  line-height: 0.86;
  letter-spacing: -0.04em;
  color: #cbd6ae;
  margin: 0;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
.stat-n i { font-style: normal; font-size: 0.42em; margin-left: 2px; vertical-align: 0.5em; }
.stat-txt { min-width: 0; }
.stat-type {
  font-family: var(--t-disp);
  font-weight: 800;
  font-size: 19px;
  line-height: 1.12;
  letter-spacing: -0.018em;
  color: #f4f1e8;
  margin: 0;
}
.stat-mean {
  font-size: 14.5px;
  line-height: 1.38;
  color: rgba(244, 241, 232, 0.72);
  margin: 4px 0 0;
}
.stat-split {
  font-size: 14.5px;
  line-height: 1.45;
  color: rgba(244, 241, 232, 0.72);
  margin: 14px 0 0;
}

.bars { display: grid; gap: 14px; margin: 20px 0 0; }

.brow {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  color: rgba(244, 241, 232, 0.72);
}
.bname {
  font-family: var(--t-disp);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
}
.bpct {
  font-family: var(--t-mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.bmean {
  grid-column: 1 / -1;
  font-size: 13.5px;
  line-height: 1.35;
  color: rgba(244, 241, 232, 0.55);
  margin-top: 4px;
}
.brow.lead-row { color: #cbd6ae; }
.brow.lead-row .bmean { color: rgba(203, 214, 174, 0.78); }

.btrack {
  grid-column: 1 / -1;
  height: 4px;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(244, 241, 232, 0.17);
  margin-top: 6px;
}
.bval {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: rgba(244, 241, 232, 0.45);
  width: 0;
  transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
.brow.lead-row .bval { background: #cbd6ae; }

.bars-note {
  font-size: 13.5px;
  line-height: 1.45;
  color: rgba(244, 241, 232, 0.55);
  margin: 18px 0 0;
}

.rev p.serif {
  font-size: 16.5px;
  line-height: 1.62;
  color: var(--t-ink-2);
  margin: 18px 0 0;
  max-width: 48ch;
}
.rev p.close {
  font-size: 16.5px;
  line-height: 1.62;
  color: var(--t-ink);
  margin: 18px 0 0;
  max-width: 48ch;
}

/* ---------- the $9.99 card, the share, the consult line ---------- */

/* This screen used to stack three equal buttons with "or" between them, which
   asks for a fourth decision from somebody who has just made sixteen. One
   button is the next step. The plan is a card you can read. The consult is a
   sentence. */

.offer {
  margin: 20px 0 0;
  padding: 20px;
  background: var(--t-panel);
  border-radius: var(--t-r);
  box-shadow: var(--t-sh-2);
}
.offer-eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--t-ink-3);
  margin: 0;
}
.offer h3 {
  font-family: var(--t-disp);
  font-weight: 800;
  font-size: 22px;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--t-ink);
  margin: 6px 0 0;
}
.offer-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
.offer-list li {
  position: relative;
  padding-left: 26px;
  font-size: 15.5px;
  line-height: 1.42;
  color: var(--t-ink-2);
}
/* A drawn tick rather than a bullet glyph, so it reads as something included
   rather than an item in a list. */
.offer-list li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 6px;
  width: 6px;
  height: 11px;
  border: solid var(--t-sage-deep);
  border-width: 0 2.2px 2.2px 0;
  transform: rotate(42deg);
}
.offer .btn { margin-top: 18px; }

/* A white card with a sage edge rather than a sage card: the primary button is
   itself a pale sage, and a pale button on a pale ground is a button nobody
   sees. */
.share {
  margin: 20px 0 0;
  padding: 20px;
  background: var(--t-panel);
  border-radius: var(--t-r);
  box-shadow: var(--t-sh-2), inset 0 0 0 1.5px var(--t-sage-soft);
}
.share-eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--t-sage-deep);
  margin: 0;
}
.share-h {
  font-family: var(--t-disp);
  font-weight: 800;
  font-size: 22px;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--t-ink);
  margin: 6px 0 0;
  text-transform: none;
}
.share-sub { font-size: 15px; line-height: 1.45; color: var(--t-ink-2); margin: 8px 0 0; }
.share .btn { margin-top: 14px; }
.share .btn-2 { margin-top: 8px; }

/* The confirmation of a copy. role="status" so it is announced, not just seen. */
.share-toast {
  position: fixed;
  left: 50%;
  bottom: calc(22px + env(safe-area-inset-bottom));
  transform: translate(-50%, 140%);
  max-width: calc(100vw - 40px);
  padding: 12px 18px;
  background: var(--t-ink);
  color: #f4f1e8;
  border-radius: var(--t-r-btn);
  font-size: 14.5px;
  box-shadow: var(--t-sh-3);
  z-index: 90;
  transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
  margin: 0;
}
.share-toast.on { transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) {
  .share-toast { transition: none; }
}

.consult-line {
  font-size: 15px;
  line-height: 1.5;
  color: var(--t-ink-3);
  margin: 20px 0 0;
  max-width: 48ch;
}
.consult-line a { color: var(--t-sage-deep); font-weight: 700; }

/* ---------- clients ---------- */

/* Ten real clients, crossfading. Fixed aspect so the panel never jumps when a
   photograph of a different shape comes round. */
.slides {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--t-rule);
  border: 0;
  border-radius: var(--t-r);
  box-shadow: var(--t-sh-2);
}

.slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.9s ease;
}
.slide.on { opacity: 1; }

.slide-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 9px;
  display: flex;
  justify-content: center;
  gap: 5px;
}
.slide-dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(251, 249, 243, 0.45);
  box-shadow: 0 0 3px rgba(35, 38, 31, 0.4);
  transition: background 0.3s ease;
}
.slide-dots i.on { background: #fbf9f3; }

.slide-cap {
  font-family: var(--t-body);
  font-style: italic;
  font-size: 15px;
  line-height: 1.45;
  color: var(--t-ink-2);
  margin: 12px 0 0;
  min-height: 2.9em;
}

/* ---------- capture ---------- */

.capture {
  margin-top: 30px;
  padding: 24px 22px 26px;
  background: var(--t-panel);
  border: 1px solid var(--t-rule);
}

.capture .slides { margin-bottom: 0; }

.capture h2 {
  font-family: var(--t-disp);
  font-weight: 800;
  text-transform: none;
  letter-spacing: -0.026em;
  font-size: clamp(23px, 6vw, 30px);
  line-height: 1.12;
  margin: 10px 0 0;
  text-wrap: balance;
}

.capture p {
  font-size: 15.5px;
  line-height: 1.56;
  color: var(--t-ink-2);
  margin: 12px 0 0;
  max-width: 44ch;
}

/* Was 10px uppercase mono, same as the setup note used to be. Cold, and hard
   to read for a line whose job is telling somebody where their plan is going. */
.capture .note {
  font-family: var(--t-body);
  font-size: 14px;
  color: var(--t-ink-3);
  line-height: 1.55;
  margin-top: 14px;
}

.capture .btn { margin-top: 20px; }

.err {
  color: var(--t-signal);
  font-family: var(--t-body);
  font-weight: 600;
  font-size: 14.5px;
  letter-spacing: 0;
  line-height: 1.5;
  margin: 12px 0 0;
}
.err[hidden] { display: none; }

/* ---------- motion ---------- */

@keyframes t-rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.q, .opts, .rev .verdict, .capture, .hero .badge {
  animation: t-rise 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.opts { animation-delay: 0.05s; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

@media (max-width: 380px) {
  /* The size chips used to stack into one column here, which turned a 3-wide
     row into three rows and put the age/size screen 205px past the fold on a
     360px phone. Three across fits: 100px a column at 360. */
  .chip { padding-left: 11px; padding-right: 11px; }
  .chip-qual { font-size: 11.5px; letter-spacing: 0.01em; }
}

/* The hand-tuned short-phone tier is gone: it guessed at four fixed numbers
   and still left the fourth answer 165px under the fold on a 375x667 phone.
   --fit measures instead. What stays here is only what is not a size: the
   gutters and the mark, which have nothing to do with the type scale. */
@media (max-height: 760px) {
  .scr { padding-top: 12px; }
  .mark { width: 36px; height: 36px; }
}

@media (min-width: 560px) {
  .scr { padding-top: 30px; padding-bottom: 34px; }
  canvas.trace { height: calc(108px * var(--fit) * var(--fit)); }
  .hero canvas.trace { height: 86px; }
}

/* ---------- tight: what goes when a short phone needs the room ---------- */

/* quiz.js adds .tight once --fit has dropped below 0.94. These are the things
   worth less than a readable answer: the axis captions under the chart, the
   chart's own breathing room, and the weight of the Back button. */
.scr.tight .axis { display: none; }
.scr.tight .trace-wrap { padding-bottom: 3px; margin-top: 8px; }
.scr.tight .back { margin-top: 4px; padding: 7px 2px; font-size: 12.5px; }
.scr.tight .proof { padding: 11px 14px; }
.scr.tight .proof-txt { font-size: 12.5px; }

/* Last resort, set by the fit pass when the screen runs long even at the
   floor: the review strip is the one block here that is furniture rather than
   the task. It exists to fill the bottom half of a tall phone. On a phone with
   no spare bottom half, half of it showing is worse than none of it. */
.scr.squeezed .proof { display: none; }

/* ---------- the beat between two sets ---------- */

/* Twelve questions is not a lot until you are on number seven with no idea how
   many are left. Finishing a set of three is a real thing to have finished, so
   it gets a second of its own: a tick that draws, what just got banked, and
   how small the next piece is. It leaves by itself, and a tap anywhere skips
   it, so it can never become one more thing to get past. */
.scr.beat { cursor: pointer; }
.beat-mid {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: relative;
  z-index: 1;
  padding: 10px 0;
}
.beat-tick {
  width: 46px;
  height: 46px;
  margin: 0 0 20px;
}
.beat-tick path {
  stroke: var(--t-sage);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
  animation: t-draw 0.42s cubic-bezier(0.22, 1, 0.36, 1) 0.06s forwards;
}
.beat .q {
  font-size: calc(clamp(27px, 7.2vw, 38px) * (0.72 + 0.28 * var(--fit)));
  animation: t-rise 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.beat .sub {
  font-size: calc(17.5px * (0.8 + 0.2 * var(--fit)));
  line-height: 1.55;
  color: var(--t-ink-2);
  margin: 14px 0 0;
  max-width: 34ch;
  animation: t-rise 0.4s cubic-bezier(0.22, 1, 0.36, 1) 0.1s both;
}
.beat-foot {
  position: relative;
  z-index: 1;
  padding-top: 18px;
  text-align: center;
  opacity: 0.55;
}
@media (prefers-reduced-motion: reduce) {
  .beat-tick path { animation: none; stroke-dashoffset: 0; }
}
