/* HisaabFlow website - the page.
 *
 * LOAD ORDER: the product theme, the website tokens, site.css (the components), then this.
 *
 * site.css says what a button, a sticker or a receipt IS. This file says where things go in
 * each scene of index.html and how each scene moves. It holds no values of its own: every
 * length, colour and duration is a custom property, and a scene declares its ground with
 * data-ground rather than naming a colour.
 *
 * A SCENE THAT PINS is a tall section (.hf-pin) with a child that sticks (.hf-pin__stick).
 * The browser does the pinning. js/scenes.js only reports how far through it the
 * scroll is, as data-step or as text, and the rules below turn that into what is shown.
 */

/* ---------------------------------------------------------------- helpers */
/* NOTHING SCROLLS SIDEWAYS. Stickers, flatmates and lifted pieces are allowed to hang over the
   edge of the screen, and this is what stops them widening the page. `clip`, not `hidden`: a
   clipped box is not a scroll container, so the pinned scenes inside it still stick. */
main, footer { overflow-x: clip; }
/* A grid or flex child refuses to shrink below its longest unbreakable content unless told it
   may. Without this one long row makes its whole column, and so the page, wider than a phone. */
:where(.hf-hero, .hf-drain, .hf-how, .hf-left, .hf-rooms, .hf-gurus__grid, .hf-pricing, .hf-faqs, .hf-closing__grid, .hf-foot, .hf-group, .hf-blocks) > * { min-width: var(--sp-0); }

.hf-vh { position: absolute; width: var(--sp-2); height: var(--sp-2); overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.hf-skip { position: fixed; top: var(--w-nav-offset); left: var(--w-gutter); z-index: var(--w-z-overlay); transform: translateY(-400%); }
.hf-skip:focus-visible { transform: none; }
.hf-scene[id], .hf-pin[id] { scroll-margin-top: calc(var(--w-nav-h) + var(--w-nav-offset)); }

/* A headline whose words rise one after another. Each word sits in its own clip, with a
   little room so a descender or the highlighter's tilted corner is not cut. */
.hf-word { display: inline-block; overflow: clip; padding: var(--w-clip-pad); margin: calc(-1 * var(--w-clip-pad)); }
.hf-word__in { display: inline-block; transition: transform var(--w-t-reveal) var(--w-ease-out); transition-delay: calc(var(--hf-i, 0) * var(--w-stagger-word)); }
.hf-motion [data-reveal="wait"] .hf-word__in { transform: translateY(var(--w-rise)); }

/* --------------------------------------------- the first thing under the nav */
/* ON A PHONE, MORE AIR UNDER THE NAVIGATION. There it is a bar nearly as wide as the screen, and
   a headline that starts a few pixels under it reads as attached to it. So what clears the
   navigation clears it by more (--w-nav-air), on every page, and a pinned scene leaves the
   same at the foot of the screen so the space above and below what it shows is even. */
@media not all and (min-width: 900px) {
  .hf-page { --w-nav-clear: calc(var(--w-nav-h) + var(--w-nav-offset) + var(--w-nav-air)); }
  /* One class deeper than the pinned scene's own rule, which is further down this file and
     would otherwise win the tie and keep the old top. */
  .hf-page .hf-pin__stick { padding-block: var(--w-nav-clear) var(--w-nav-air); }
}

/* ---------------------------------------------------------------- the nav */
/* What the pill holds is decided by what fits beside the brand and the menu button. On a
   phone the action says its one word, and on the narrowest it steps out of the pill: it is in
   the menu, and in the page's own first screen. Only one of the two labels is ever shown. */
.hf-nav__short { display: none; }
@media not all and (min-width: 560px) {
  .hf-nav__long { display: none; }
  .hf-nav__short { display: inline; }
}
@media not all and (min-width: 400px) {
  .hf-nav__action { display: none; }
  .hf-nav { padding-inline-start: var(--sp-16); }
}

/* ------------------------------------------------------------------ phone */
/* The product's own proportions, so a captured screen fills it exactly. Corner and bezel are
   shares of the width, worked out here so a scene that resizes the phone resizes them too. */
.hf-phone {
  width: var(--w-phone-w); aspect-ratio: var(--w-phone-ratio); flex: none;
  padding: calc(var(--w-phone-w) * var(--w-phone-bezel-share));
  border-radius: calc(var(--w-phone-w) * var(--w-phone-r-share));
  background: var(--dark);
  box-shadow: 0 0 0 var(--w-stroke) var(--w-line);
  position: relative;
}
.hf-phone__screen {
  width: 100%; height: 100%; overflow: clip; position: relative;
  border-radius: calc(var(--w-phone-w) * (var(--w-phone-r-share) - var(--w-phone-bezel-share)));
  background: var(--canvas);
}
.hf-phone__screen img { width: 100%; height: auto; }
/* A screen taller than the phone, moved through it by the scroll: from its top to its end,
   whatever its length. The glass is the measure (100cqh), the picture's own height is 100%. */
.hf-phone__screen:has(> img[data-progress]) { container-type: size; }
.hf-phone__screen img[data-progress] { transform: translateY(calc(var(--hf-p, 0) * (100cqh - 100%))); }
/* A Room's screens are drawn on the surface, not the canvas, so where one is shorter than the
   glass the glass below it is the same ground. */
.hf-phone--room .hf-phone__screen { background: var(--surface); }

/* THE APP'S OWN SCREEN, LIVE. js/dashboard.js stands the real home screen up here, in a
   shadow root, at the app's own width; this scales it to the phone it is in. `--hf-ax` and
   `--hf-ay` move it, so a piece can show its own rectangle of the same screen. */
.hf-app {
  display: block; position: absolute; inset-block-start: var(--sp-0); inset-inline-start: var(--sp-0);
  overflow: clip; pointer-events: none; transform-origin: left top;
  transform: scale(var(--hf-k, 1)) translate(var(--hf-ax, 0), var(--hf-ay, 0));
}

/* THE PHONE THAT STANDS UP. An object with a thickness, leaning, in the page's own space. The
   two layers behind it are its body: three planes this close read as one solid edge. The
   screen is a flat plane on its face; the lift layer is the space above the glass. */
/* What turns it: `--hf-turn` is the scene's scroll, 0 to 1, and `--hf-lx` / `--hf-ly` are the
   lean toward the pointer. They are written every frame, so they are declared as values that
   do NOT inherit: without that, each frame would restyle every element of the live screen. */
@property --hf-turn { syntax: '<number>'; inherits: false; initial-value: .5; }
@property --hf-lx { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@property --hf-ly { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
.hf-phone--stands {
  /* The SCREEN has the app's proportions here, not the case, so the live screen fills the glass
     to the pixel and a piece measured in the app lands exactly on its own place. */
  aspect-ratio: auto;
  transform-style: preserve-3d;
  transform: perspective(var(--w-tilt-perspective))
    rotateX(calc(var(--w-tilt-x) + var(--hf-ly, 0deg)))
    rotateY(calc(var(--w-tilt-y) + var(--hf-turn, .5) * var(--w-tilt-swing) + var(--hf-lx, 0deg)))
    rotateZ(var(--w-tilt-z));
  box-shadow: none;
}
.hf-phone--stands .hf-phone__screen { height: auto; aspect-ratio: var(--w-phone-ratio); }
.hf-phone--stands::before, .hf-phone--stands::after {
  content: ''; position: absolute; inset: var(--sp-0); border-radius: inherit; pointer-events: none;
}
.hf-phone--stands::before { background: var(--dark-hover); transform: translateZ(calc(var(--w-phone-w) * var(--w-tilt-depth) * -.5)); }
.hf-phone--stands::after { background: var(--dark); transform: translateZ(calc(var(--w-phone-w) * var(--w-tilt-depth) * -1)); box-shadow: var(--w-sticker-shadow-lifted); }
/* The layer the pieces lift off from. Not clipped: a lifted piece leaves the phone. */
.hf-phone__lift { position: absolute; inset: calc(var(--w-phone-w) * var(--w-phone-bezel-share)); pointer-events: none; transform-style: preserve-3d; }
.hf-piece {
  /* Where it sits is written by the script from the capture's own measurements. `--hf-lift` is
     how far its face stands off the glass and `--hf-floor` is the height of what its shadow
     falls on - the glass, or the piece it is part of. Both are shares of the phone's width. */
  position: absolute; left: var(--hf-x, 0%); top: var(--hf-y, 0%); width: var(--hf-w, 0%); height: var(--hf-h, 0%);
  transform-style: preserve-3d;
  --hf-lift: 0; --hf-floor: 0; --hf-show: 0; --hf-dim: 0;
}
.hf-piece__face, .hf-piece__shade {
  position: absolute; inset: var(--sp-0); border-radius: calc(var(--hf-r, var(--sp-0)) * var(--hf-k, 1));
  transition: transform var(--w-t-slow) var(--w-ease-out), opacity var(--w-t-med) var(--w-ease);
}
/* A lit edge, so a dark piece lifted off a dark card still has a shape. */
.hf-piece__face { outline: var(--w-stroke) solid var(--w-tilt-edge); outline-offset: calc(var(--w-stroke) * -1); }
.hf-piece__face { overflow: clip; opacity: var(--hf-show); transform: translateZ(calc(var(--w-phone-w) * var(--hf-lift))); }
.hf-piece__face::after { content: ''; position: absolute; inset: var(--sp-0); background: var(--overlay); opacity: var(--hf-dim); transition: opacity var(--w-t-med) var(--w-ease); }
/* The shadow lies on the floor, a hair above it so the two never share a plane. It is as dark
   as the piece is high. */
.hf-piece__shade {
  background: var(--w-tilt-shade-c); filter: blur(calc(var(--w-phone-w) * var(--w-tilt-shade)));
  opacity: calc(var(--hf-show) * (var(--hf-lift) - var(--hf-floor)) / var(--w-tilt-high));
  transform: translateZ(calc(var(--w-phone-w) * var(--hf-floor) + var(--w-stroke)));
}

/* -------------------------------------------------------------------- pin */
.hf-pin { position: relative; }
.hf-pin[data-pin="drain"] { height: var(--w-pin-drain); }
.hf-pin[data-pin="left"] { height: var(--w-pin-left); }
.hf-pin[data-pin="guru"] { height: var(--w-pin-guru); }
.hf-pin__stick { position: sticky; top: var(--sp-0); height: 100svh; overflow: clip; display: grid; align-items: center; padding-top: calc(var(--w-nav-h) + var(--w-nav-offset)); }
.hf-pin .hf-phone { --w-phone-w: var(--w-phone-fit); }

/* ------------------------------------------------------------------- hero */
.hf-hero {
  min-height: var(--w-hero-min);
  padding-top: var(--w-nav-clear);
  padding-bottom: var(--g-sibling);
  display: grid; align-content: center; gap: var(--w-g-block);
}
/* THE HEADLINE DOES NOT REBALANCE. Balanced wrapping re-divides the lines every time the
   turning word changes length, so the whole headline would shuffle. The first line is fixed
   in the markup and the rest wraps plainly: only what follows the word on its own line moves,
   and it is carried there by a transform. */
.hf-hero__head { text-wrap: wrap; }
/* The word that turns, and the one on its way out. They pass each other inside the highlight. */
.hf-wheel { display: inline-block; }
.hf-wheel--out { position: absolute; left: var(--sp-0); top: var(--sp-0); pointer-events: none; }
.hf-hero__show { position: relative; justify-self: center; }
.hf-hero__show .hf-phone { rotate: var(--w-sticker-rot-4); transform: translateY(calc(var(--hf-p, .5) * var(--sp-80) - var(--sp-40))); }
.hf-hero__show .hf-sticker { position: absolute; }
.hf-hero__show .hf-sticker:nth-of-type(1) { left: -42%; top: 14%; }
.hf-hero__show .hf-sticker:nth-of-type(2) { right: -34%; top: 34%; }
.hf-hero__show .hf-sticker:nth-of-type(3) { left: -30%; top: 62%; }
.hf-hero__show .hf-sticker:nth-of-type(4) { right: -22%; top: 78%; }
@media (min-width: 900px) {
  /* The words and the action sit directly under the headline, so the action is on the first
     screen. The phone stands to the right and rises into the space the headline's second line
     leaves there. */
  .hf-hero { grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--w-g-block); align-items: start; align-content: start; row-gap: var(--w-g-group); }
  .hf-hero__head { grid-column: 1 / -1; }
  .hf-hero__show { margin-top: calc(var(--w-fs-mega) * -1.1); margin-inline-end: var(--w-g-block); }
}

/* ------------------------------------------------------------------- band */
.hf-band { padding-block: var(--w-g-group); border-block: var(--w-stroke) solid var(--w-line); }

/* ------------------------------------------------------------------ drain */
/* THE SALARY DRAINING, SHOWN IN THE APP'S OWN PARTS. On the left, the claim. On the right, two
   cards stacked the way the app's home screen stacks them: the reading (the app's hero ground,
   with the figure counting down) and the app's "Recent expenses" list, which gains a row at the
   top each time the scroll passes another expense. Every row is tappable, as it is in the app.

   The two columns are centred against each other and the right one is held to a phone's width,
   so the scene reads as one composition rather than a headline and a far-off number. */
.hf-drain { display: grid; gap: var(--w-g-group); align-items: center; justify-items: start; }
.hf-drain__say { display: flex; flex-direction: column; gap: var(--w-g-group); }
.hf-drain__app { display: flex; flex-direction: column; gap: var(--g-sibling); width: 100%; }
/* THE SMALL PRINT AND THE CLOSING LINE SHARE ONE PLACE. The small print is there while the
   month runs; the closing line takes its place when the month is over. Two things in one spot
   means no empty hole held open for a line that has not arrived. Only while the script is
   running: without it they simply follow one another. */
.hf-motion .hf-drain__swap { display: grid; align-items: start; }
.hf-motion .hf-drain__swap > * { grid-area: 1 / 1; }
.hf-drain__end, .hf-drain__note { transition: opacity var(--w-t-med) var(--w-ease), transform var(--w-t-reveal) var(--w-ease-out); }
.hf-motion [data-end="false"] .hf-drain__end { opacity: 0; transform: translateY(var(--w-rise-soft)); }
.hf-motion [data-end="true"] .hf-drain__note { opacity: 0; }
@media (min-width: 900px) {
  .hf-drain { grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--w-g-block); }
  .hf-drain__app { width: var(--w-app-w); }
}
@media not all and (min-width: 900px) {
  /* Stacked: the headline, the two cards, then the closing line under them. The headline's
     own wrapper steps aside so its children can be ordered among the cards. The small print
     and the list's hint give way, and the cards are a little tighter. */
  .hf-drain__say { display: contents; }
  .hf-drain__app { order: 2; }
  .hf-drain__swap { order: 3; }
  .hf-drain { --w-drain-rest: var(--w-drain-rest-stacked); --w-entries-min: var(--w-entries-min-stacked); gap: var(--g-control); align-content: start; }
  .hf-drain__note, .hf-entries__head .eg-cap { display: none; }
  .hf-drain .hf-reading { padding: var(--sp-20); }
  .hf-drain .hf-entries { padding: var(--sp-16) var(--sp-16) var(--sp-12); }
}
/* ON A PHONE THE SCENE FILLS THE SCREEN. The headline at the top, and the two cards take
   everything under it: the list's window is as tall as the screen has left, whatever phone it
   is, so there is the same air above the headline as below the list and no band of nothing at
   the foot. This is for the scene while it is pinned; where motion is reduced it is not pinned,
   has no screen to fill, and shows the whole month instead. */
@media not all and (min-width: 900px) {
  @media (prefers-reduced-motion: no-preference) {
    .hf-pin[data-pin="drain"] .hf-pin__stick { align-items: stretch; }
    /* Stretched, not centred: a centred card keeps its own height, and its own height here is
       nothing, because the list inside it is told to take what is left. */
    .hf-drain { height: 100%; grid-template-rows: auto minmax(0, 1fr); align-content: stretch; align-items: stretch; }
    .hf-drain__app { min-height: var(--sp-0); }
    .hf-drain .hf-reading { flex: none; }
    .hf-drain .hf-entries { flex: 1 1 0; min-height: var(--sp-0); }
    .hf-drain .hf-entries__window { flex: 1 1 0; min-height: var(--sp-0); height: auto; }
    /* The closing line has no row of its own to keep empty until the end. Where the script is
       running it takes the headline's place when the month is spent: one fades as the other
       arrives. */
    .hf-motion .hf-drain #drain-h, .hf-motion .hf-drain .hf-drain__swap { grid-area: 1 / 1; }
    .hf-motion .hf-drain .hf-drain__swap { align-self: end; }
    .hf-motion .hf-drain #drain-h { transition: opacity var(--w-t-med) var(--w-ease); }
    .hf-motion [data-end="true"] .hf-drain #drain-h { opacity: 0; }
  }
}

/* The reading: the app's hero ground, its radius and its padding, straight from its tokens. */
.hf-reading { display: flex; flex-direction: column; gap: var(--g-tight); padding: var(--c-hero-pad); border-radius: var(--c-hero-r); background: var(--c-hero-ground); color: var(--c-hero-fg); container-type: inline-size; }
.hf-reading__head { display: flex; align-items: center; justify-content: space-between; gap: var(--g-control); }
/* As wide as the card and no wider: nine characters of the money face in a little over five ems. */
.hf-reading .hf-fig { font-size: min(var(--w-fs-figure-xl), var(--w-figure-fit)); line-height: var(--w-lh-figure); }
/* How much of the salary is left, as a length. The fill is scaled, never resized. */
.hf-reading__bar { height: var(--sp-8); border-radius: var(--r-pill); background: var(--c-switch-bg); overflow: clip; }
.hf-reading__bar span { display: block; height: 100%; border-radius: var(--r-pill); background: var(--lime-500); transform-origin: left center; transform: scaleX(var(--hf-left, 1)); }

/* The list: the app's card, its rows, its hairlines. Only the window and the slide are ours. */
.hf-entries { padding: var(--sp-24) var(--sp-24) var(--sp-16); display: flex; flex-direction: column; gap: var(--g-control); color: var(--ink); }
.hf-entries__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--g-control); }
.hf-entries__window {
  position: relative; overflow: clip;
  height: clamp(calc(var(--w-entry-row) * var(--w-entries-min)), calc(100svh - var(--w-drain-rest)), calc(var(--w-entry-row) * var(--w-entries-max)));
}
/* The rows are laid out newest first, all of them. The list is then slid so that the newest
   expense the scroll has reached sits at the top of the window: one row's height per expense. */
.hf-entries__list { transform: translateY(calc((var(--hf-n, 0) - var(--hf-total, 0)) * var(--w-entry-row))); transition: transform var(--w-t-med) var(--w-ease-out); }
.hf-entries__list .eg-swipe { height: var(--w-entry-row); }
.hf-entries__list .eg-row { height: 100%; width: 100%; border: var(--sp-0); font: inherit; color: inherit; text-align: start; }
/* An expense that has not happened yet keeps its place in the list and cannot be seen or reached. */
.hf-entries__list .eg-swipe:not(.is-in) { visibility: hidden; }
.hf-entry__mid { flex: 1; min-width: var(--sp-0); display: flex; flex-direction: column; }
/* Both lines stay on one line each. A row that wraps is a row of a different height, and the
   list moves by exactly one row at a time. */
.hf-entry__mid > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hf-entries__head .eg-title { white-space: nowrap; }
.hf-entry__go { color: var(--faint); margin-inline-start: calc(var(--sp-4) * -1); }
.hf-entries__empty { position: absolute; inset: var(--sp-0); display: grid; place-items: center; text-align: center; transition: opacity var(--w-t-fast) var(--w-ease); }
.hf-entries:not([data-n="0"]) .hf-entries__empty { opacity: 0; }

/* The entry, opened. A native dialog, so focus is held inside it and Escape closes it; the
   panel inside is the app's own modal. */
.hf-dialog { width: 100%; height: 100%; max-width: none; max-height: none; margin: var(--sp-0); padding: var(--c-overlay-pad); border: var(--sp-0); background: transparent; color: var(--ink); }
.hf-dialog[open] { display: grid; place-items: center; }
.hf-dialog::backdrop { background: var(--c-overlay-scrim); }
.hf-dialog__head { display: flex; align-items: center; justify-content: space-between; gap: var(--g-control); margin-bottom: var(--sp-20); }
.hf-dialog__fields { display: flex; flex-direction: column; gap: var(--g-control); }
.hf-dialog__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--g-control); flex-wrap: wrap; margin-top: var(--sp-6); }
.hf-dialog .eg-input[readonly] { cursor: default; }
.hf-dialog .eg-circle { border: var(--sp-0); cursor: pointer; }

/* -------------------------------------------------------------------- how */
.hf-how { display: grid; gap: var(--w-g-block); align-items: start; }
.hf-how__chat { display: flex; flex-direction: column; gap: var(--w-g-sibling); align-items: flex-start; }
.hf-points { display: flex; flex-direction: column; gap: var(--g-control); }
.hf-points li { display: flex; flex-direction: column; gap: var(--sp-2); padding-top: var(--g-control); border-top: var(--w-stroke) solid var(--w-line); }
@media (min-width: 900px) { .hf-how { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* The conversation, live. The log scrolls, so its children must not shrink to fit. */
.hf-chat--live { width: min(100%, var(--c-device-w)); gap: var(--w-g-control); }
.hf-chat__log { display: flex; flex-direction: column; gap: var(--c-bubble-gap); height: var(--w-chat-h); overflow-y: auto; overscroll-behavior: contain; padding-block: var(--sp-4); scrollbar-width: none; }
.hf-chat__log > * { flex: none; }
/* THE CHAT ON THE PHONE IT HAPPENS ON. The first screen of Personal logging: a dark ground, the
   phone in the middle and the words either side, and nothing else. Stacked, it is the
   headline, the phone, then the rest.

   The phone's screen is the chat as WhatsApp's dark theme draws one (tokens.js, mock). It is
   drawn in proportion: one size, a message's text, is a share of the screen's width, and every
   other size on the screen is a multiple of it, so the whole drawing scales with the phone and
   is right at any size. That is why the sizes below are in em and not tokens: they are the
   drawing's own proportions, and they mean nothing off this screen. */
.hf-texting__grid { position: relative; display: grid; justify-items: center; align-items: center; gap: var(--w-g-block); }
/* A page that stands alone has no navigation. Where the navigation would be, it has the name,
   which is also the way back to the site. */
.hf-solo__brand { position: absolute; z-index: 1; inset-block-start: var(--w-nav-offset); inset-inline-start: var(--w-gutter); min-height: var(--w-nav-h); color: var(--w-fg); }
.hf-texting__say, .hf-texting__try { width: 100%; }
.hf-texting .hf-phone { --w-phone-w: var(--w-phone-fit-chat-stacked); box-shadow: 0 0 0 var(--w-stroke) var(--dark-hover); }
@media (min-width: 1180px) {
  .hf-texting__grid { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); justify-items: stretch; }
  .hf-texting .hf-phone { --w-phone-w: var(--w-phone-fit-chat); }
  /* The headline takes its size from the column it stands in, as a headline beside anything does. */
  .hf-texting__say { container-type: inline-size; }
  .hf-texting__say .hf-display { font-size: min(var(--w-fs-display), var(--w-headline-fit)); }
}

.hf-wa .hf-phone__screen { container-type: inline-size; background: var(--w-wa-chat); }
.hf-wa__screen { display: flex; flex-direction: column; height: 100%; font-family: var(--w-wa-font); font-size: var(--w-wa-fs); line-height: 1.35; font-weight: var(--fw-regular); letter-spacing: 0; color: var(--w-wa-text); text-align: start; }
.hf-wa__screen .hf-icon { font-size: 1.5em; color: var(--w-wa-quiet); flex: none; }
.hf-wa__status { display: flex; align-items: center; justify-content: space-between; padding: .7em 1.5em .2em; background: var(--w-wa-bar); font-size: .86em; font-weight: var(--fw-semibold); }
.hf-wa__signs { display: flex; align-items: center; gap: .3em; }
.hf-wa__signs .hf-icon { font-size: 1.25em; color: var(--w-wa-text); }
.hf-wa__head { display: flex; align-items: center; gap: .55em; padding: .5em .7em .6em .5em; background: var(--w-wa-bar); }
.hf-wa__head > .hf-icon { color: var(--w-wa-text); }
/* Two classes: the phone's own rule for a picture on its screen (fill the width) is one class
   and an element, and would win. */
.hf-wa .hf-wa__photo { width: 2.6em; height: 2.6em; border-radius: var(--r-pill); flex: none; }
.hf-wa__who { display: grid; flex: 1; min-width: var(--sp-0); white-space: nowrap; }
.hf-wa__who b { font-size: 1.12em; font-weight: var(--fw-medium); }
.hf-wa__who span { font-size: .84em; color: var(--w-wa-quiet); }
.hf-wa__acts { display: flex; align-items: center; gap: 1.1em; }
.hf-wa__acts .hf-icon { color: var(--w-wa-text); }
.hf-wa__chat { position: relative; display: flex; flex: 1 1 0; min-height: var(--sp-0); }
.hf-wa .hf-chat__log { position: relative; flex: 1 1 0; height: auto; min-height: var(--sp-0); gap: .45em; padding: .7em .6em; }
.hf-wa__day { align-self: center; padding: .32em .85em; border-radius: .55em; background: var(--w-wa-chip); color: var(--w-wa-quiet); font-size: .84em; }
.hf-wa .hf-bubble { position: relative; max-width: 82%; padding: .42em .65em .5em; border-radius: .6em; font-size: 1em; line-height: 1.35; background: var(--w-wa-incoming); color: var(--w-wa-text); border-start-start-radius: var(--sp-0); }
.hf-wa .hf-bubble--me { background: var(--w-wa-outgoing); border-start-start-radius: .6em; border-start-end-radius: var(--sp-0); border-end-end-radius: .6em; }
.hf-wa .hf-bubble--guru { border-end-start-radius: .6em; }
.hf-wa .hf-bubble b { color: inherit; font-weight: var(--fw-bold); }
/* The little tail at the top corner of a message, on the side of whoever sent it. */
.hf-wa .hf-bubble::before { content: ""; position: absolute; inset-block-start: var(--sp-0); inset-inline-start: -.5em; width: .55em; height: .75em; background: var(--w-wa-incoming); clip-path: polygon(0 0, 100% 0, 100% 100%); }
.hf-wa .hf-bubble--me::before { inset-inline: auto -.5em; background: var(--w-wa-outgoing); clip-path: polygon(0 0, 100% 0, 0 100%); }
.hf-bubble__meta { display: inline-flex; align-items: center; gap: .25em; float: inline-end; margin: .55em 0 -.15em .8em; font-size: .74em; line-height: 1; color: var(--w-wa-quiet); font-variant-numeric: tabular-nums; }
.hf-wa .hf-bubble__meta .hf-icon { font-size: 1.45em; color: var(--w-wa-read); }
.hf-wa .hf-chat__form { gap: .4em; min-height: 0; padding: .35em .4em .55em; border-radius: var(--sp-0); background: transparent; color: var(--w-wa-text); box-shadow: none; }
.hf-wa__field { display: flex; align-items: center; gap: .6em; flex: 1; min-width: var(--sp-0); min-height: 3em; padding: 0 .8em; border-radius: 1.6em; background: var(--w-wa-bar); }
.hf-wa .hf-chat__input { font: inherit; font-size: 1.05em; color: var(--w-wa-text); }
.hf-wa .hf-chat__input::placeholder { color: var(--w-wa-quiet); }
.hf-wa .hf-chat__form:focus-within { box-shadow: none; }
.hf-wa__field:focus-within { box-shadow: 0 0 0 var(--w-stroke) var(--w-wa-accent); }
.hf-wa .hf-chat__send { width: 3em; height: 3em; background: var(--w-wa-accent); }
.hf-wa .hf-chat__send .hf-icon { color: var(--w-wa-chat); font-size: 1.4em; }
.hf-wa .hf-chat__send:disabled, .hf-wa .hf-chat__send[aria-busy="true"] { background: var(--w-wa-bar); }
/* A phone's own browser enlarges the page when a field's text is small. On a touch screen the
   field's text is the size that stops it, and the drawing gives up a little truth for that. */
@media (pointer: coarse) { .hf-wa .hf-chat__input { font-size: var(--w-fs-body); } }
/* The scene is one screen: the phone is as tall as that allows, and the chat fills the phone. */
.hf-texting .hf-chat__chips { justify-content: flex-start; }
.hf-texting__try > .hf-row:first-child { flex-wrap: nowrap; gap: var(--g-control); }
.hf-chat__chips { display: flex; flex-wrap: wrap; gap: var(--w-g-tight); }
.hf-chat__form { display: flex; align-items: center; gap: var(--c-composer-gap); min-height: var(--c-composer-h); padding: var(--c-composer-py) var(--c-composer-py) var(--c-composer-py) var(--c-composer-px); border-radius: var(--c-composer-r); background: var(--c-composer-bg); color: var(--ink); }
.hf-chat__input { flex: 1; min-width: var(--sp-0); border: var(--sp-0); background: transparent; color: inherit; font-size: var(--w-fs-body); }
.hf-chat__input::placeholder { color: var(--mute); opacity: 1; }
.hf-chat__input:focus-visible { outline: none; }
.hf-chat__form:focus-within { box-shadow: 0 0 0 var(--w-focus-w) var(--lime-500); }
.hf-chat__send { display: grid; place-items: center; flex: none; width: var(--w-touch); height: var(--w-touch); border: var(--sp-0); border-radius: var(--r-pill); background: var(--deep); color: var(--lime-500); cursor: pointer; transition: transform var(--w-t-fast) var(--w-ease); }
.hf-chat__send:active { transform: var(--w-hover-press); }
.hf-chat__send:disabled, .hf-chat__send[aria-busy="true"] { background: var(--field); color: var(--mute); cursor: not-allowed; }
.hf-chat__input:disabled { cursor: not-allowed; }

/* ------------------------------------------------------------------- left */
.hf-left { display: grid; gap: var(--w-g-group); justify-items: center; align-items: center; height: 100%; align-content: center; }
.hf-left__stage { position: relative; }
.hf-left .hf-phone__screen::after { content: ''; position: absolute; inset: var(--sp-0); background: var(--overlay); opacity: 0; transition: opacity var(--w-t-med) var(--w-ease); }
.hf-notes { display: grid; gap: var(--w-g-sibling); }
.hf-notes li { display: flex; flex-direction: column; gap: var(--sp-4); padding-inline-start: var(--w-g-sibling); border-inline-start: var(--w-focus-w) solid var(--w-line); opacity: .32; transition: opacity var(--w-t-med) var(--w-ease), border-color var(--w-t-med) var(--w-ease); }
[data-step="0"] .hf-notes li:nth-child(1), [data-step="1"] .hf-notes li:nth-child(2),
[data-step="2"] .hf-notes li:nth-child(3), [data-step="3"] .hf-notes li:nth-child(4),
[data-step="all"] .hf-notes li { opacity: 1; border-color: var(--w-fg); }
/* WHAT STANDS WHERE. With no step - reduced motion, or the scene at rest - every widget stands
   a little off the glass and nothing is dimmed. On a step, the one being described stands
   high and the rest lie back under the dimmed glass. The week's chart is part of the hero
   card, so on its step the card is held half way up, dimmed, and the chart rises off it. */
[data-step="all"] .hf-piece { --hf-show: 1; --hf-lift: var(--w-tilt-rest); }
[data-step="all"] [data-piece="week"] { --hf-floor: var(--w-tilt-rest); --hf-lift: calc(var(--w-tilt-rest) * 2); }
[data-step="0"] [data-piece="hero"], [data-step="2"] [data-piece="daily"], [data-step="3"] [data-piece="pace"] { --hf-show: 1; --hf-lift: var(--w-tilt-high); }
[data-step="1"] [data-piece="hero"] { --hf-show: 1; --hf-lift: var(--w-tilt-mid); --hf-dim: 1; }
[data-step="1"] [data-piece="week"] { --hf-show: 1; --hf-floor: var(--w-tilt-mid); --hf-lift: var(--w-tilt-high); }
/* Riding on the hero card whenever the card is up, so the two lift as one and part only on
   the chart's own step. */
[data-step="0"] [data-piece="week"] { --hf-floor: var(--w-tilt-high); --hf-lift: var(--w-tilt-high); }
/* A WIDGET IN THE AIR IS NOT ALSO ON THE GLASS. The live screen takes each of these as its
   widget's opacity (tools/capture-screens.mjs writes the rule), so the place a piece
   lifted from is empty, with the piece's shadow on it. `--hf-part-t` is how long the screen
   waits before letting go, which is how long the piece takes to fade in over it. */
.hf-phone--stands { --hf-part-t: var(--w-t-med); }
[data-step="all"] .hf-phone--stands { --hf-part-hero: 0; --hf-part-week: 0; --hf-part-daily: 0; --hf-part-pace: 0; }
[data-step="0"] .hf-phone--stands, [data-step="1"] .hf-phone--stands { --hf-part-hero: 0; }
[data-step="1"] .hf-phone--stands { --hf-part-week: 0; }
[data-step="2"] .hf-phone--stands { --hf-part-daily: 0; }
[data-step="3"] .hf-phone--stands { --hf-part-pace: 0; }
/* The step is the scene's, so it is read from the scene. */
[data-pin="left"]:not([data-step="all"]) .hf-phone__screen::after { opacity: .5; }
@media not all and (min-width: 900px) {
  /* Stacked, the scene has to fit a headline, the phone and one note on a small screen. The
     scene's own lead sentence gives way - that one sentence, not everything set in its size:
     a note's heading is in that size too, and a note without its heading has no subject. The
     phone takes the height that is left. */
  .hf-left > .hf-group > .hf-lead { display: none; }
  .hf-pin .hf-left .hf-phone { --w-phone-w: var(--w-phone-fit-stacked); }
  /* One screen cannot hold four notes and a phone, so only the note being shown is shown. */
  [data-pin="left"]:not([data-step="all"]) .hf-notes li { display: none; }
  [data-step="0"] .hf-notes li:nth-child(1), [data-step="1"] .hf-notes li:nth-child(2),
  /* Same weight as the rule above and written after it, so it wins without being forced. */
  [data-step="2"] .hf-notes li:nth-child(3), [data-step="3"] .hf-notes li:nth-child(4) { display: flex; }
}
/* ON A PHONE THE SCENE FILLS THE SCREEN, AND THE PHONE IS WHAT GROWS. The headline sits at the
   top, as near the navigation as in every other pinned scene; the note sits at the foot; and
   the phone takes every pixel between them. It is sized by the room itself (the stage is a
   container, and the phone's width is worked out from the stage's height), not by a guess at
   what the headline and the note will need, so it is right on a short phone, a tall one and a
   tablet alike.

   The four notes share one place, the size of the longest, so the phone does not change size
   as the scroll moves from one note to the next. This is for the scene while it is pinned;
   where motion is reduced it is not pinned, and every note is listed. */
@media not all and (min-width: 900px) {
  @media (prefers-reduced-motion: no-preference) {
    .hf-pin[data-pin="left"] .hf-pin__stick { align-items: stretch; }
    .hf-left { grid-template-rows: auto minmax(0, 1fr) auto; align-content: stretch; align-items: stretch; }
    .hf-left__stage { container-type: size; inline-size: 100%; min-block-size: var(--sp-0); display: grid; place-items: center; }
    /* Not all of the room: a tenth of it is left as air, and the phone is raised a little so
       that air is the same above it as below it (tokens.js says why it needs raising). */
    .hf-pin .hf-left .hf-phone { --w-phone-w: min(var(--w-tilt-wide), calc(100cqh * var(--w-tilt-fit) / var(--w-phone-stands-tall))); translate: 0 calc(var(--w-phone-w) * var(--w-tilt-rise) * -1); }
    .hf-left .hf-notes { inline-size: 100%; gap: var(--sp-0); }
    [data-pin="left"]:not([data-step="all"]) .hf-notes li { display: flex; grid-area: 1 / 1; visibility: hidden; opacity: 0; }
    [data-pin="left"][data-step="0"] .hf-notes li:nth-child(1), [data-pin="left"][data-step="1"] .hf-notes li:nth-child(2),
    [data-pin="left"][data-step="2"] .hf-notes li:nth-child(3), [data-pin="left"][data-step="3"] .hf-notes li:nth-child(4) { visibility: visible; opacity: 1; }
  }
}
@media (min-width: 900px) {
  .hf-left { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); column-gap: var(--w-g-block); justify-items: start; }
  .hf-left__stage { justify-self: center; }
  .hf-pin .hf-left .hf-phone { --w-phone-w: var(--w-phone-fit-stands); }
}

/* --------------------------------------------------------------- the story */
/* The About page is a story with something to do in each chapter. These are the things.

   THE LINE-UP. Six suspects, each a button. Pressed, a suspect is cleared: it turns to the
   accent and gives its alibi. The alibi keeps its room before it is shown, so pressing one
   moves nothing, and it is hidden, not merely transparent, so it is not read out early. */
.hf-lineup { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--g-control); }
@media (min-width: 1180px) { .hf-lineup { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--w-g-sibling); } }
.hf-suspect {
  display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; justify-items: start; gap: var(--g-inline);
  width: 100%; height: 100%; padding: var(--sp-20); border: var(--sp-0); border-radius: var(--r-lg);
  background: var(--w-panel); color: var(--w-fg); font: inherit; text-align: start; cursor: pointer;
  transition: background var(--w-t-fast) var(--w-ease), color var(--w-t-fast) var(--w-ease), transform var(--w-t-fast) var(--w-ease);
}
.hf-suspect:active { transform: var(--w-hover-press); }
.hf-suspect__seat { display: grid; place-items: center; width: var(--w-btn-seat); height: var(--w-btn-seat); border-radius: var(--r-pill); background: var(--w-well); font-size: var(--i-lg); margin-bottom: var(--sp-6); }
.hf-suspect__what { font-size: var(--w-fs-label); font-weight: var(--fw-bold); }
.hf-suspect__amount { font-size: var(--w-fs-cap); color: var(--w-quiet); }
.hf-suspect__alibi { font-size: var(--w-fs-cap); font-weight: var(--fw-medium); visibility: hidden; }
.hf-suspect[aria-pressed="true"] { background: var(--w-accent); color: var(--w-on-accent); }
.hf-suspect[aria-pressed="true"] .hf-suspect__seat { background: var(--w-on-accent); color: var(--w-accent); }
.hf-suspect[aria-pressed="true"] .hf-suspect__amount { color: inherit; }
.hf-suspect[aria-pressed="true"] .hf-suspect__alibi { visibility: visible; }

/* THE RACE. One chai, logged two ways, each with its own clock. The usual way is a form on a
   card; the other is the one field the chat has, on the deep ground. */
.hf-race { display: grid; gap: var(--w-g-sibling); align-items: start; }
@media (min-width: 900px) { .hf-race { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }
.hf-race__way { display: flex; flex-direction: column; gap: var(--g-control); padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--w-panel); }
.hf-race__way--text { background: var(--w-bg); color: var(--w-fg); }
.hf-race__head { display: flex; align-items: center; justify-content: space-between; gap: var(--g-control); }
.hf-race__fields { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--g-control); }
.hf-race__fields .hf-field { width: auto; gap: var(--g-inline); }
.hf-race__fields .hf-field__input { width: 100%; min-width: var(--sp-0); }
.hf-race__wide { grid-column: 1 / -1; }
/* On a narrow phone a field needs the whole row to show what is in it. */
@media not all and (min-width: 480px) { .hf-race__fields { grid-template-columns: minmax(0, 1fr); } }
.hf-race [data-race-time] { font-variant-numeric: tabular-nums; }
.hf-race [data-race-time][data-state="done"] { background: var(--w-accent); color: var(--w-on-accent); }
.hf-race__way .hf-bubble { align-self: flex-start; }
/* The Guru's bubble is the deep green of this card, so here it takes the panel to be seen. */
.hf-race__way--text .hf-bubble--guru { background: var(--w-panel); color: var(--w-fg); }
.hf-race__way > .hf-btn { align-self: flex-start; }

/* The Guru as something to press: the button is the Guru and nothing else. */
.hf-poke { border: var(--sp-0); padding: var(--sp-0); background: transparent; border-radius: var(--r-pill); cursor: pointer; }

/* ----------------------------------------------------------- written pages */
/* The blog and Help. Their words are files (content/), put into these shapes by the build.

   BESIDE A LONG LIST the headline stays where it is while the list goes by, from the width at
   which the two stand side by side. */
/* Two classes, because .hf-split centres its columns and is written later in this file: with
   one class each, the later rule wins and the headline floats halfway down the list. */
.hf-split.hf-help { align-items: start; }
.hf-help__list { display: grid; gap: var(--w-g-block); min-width: var(--sp-0); }
@media (min-width: 1180px) { .hf-help__head { position: sticky; top: var(--w-nav-clear); } }

/* THE BLOG'S FRONT PAGE. One column of bands: the headline with its sentence beside it, the
   choices, the newest note on a wide card, the rest in a grid, and the way to the sign-up. */
.hf-blog { display: grid; gap: var(--w-g-block); padding-block-start: var(--w-g-group); }
.hf-blog__head { display: grid; gap: var(--w-g-group); align-items: end; }
@media (min-width: 1024px) { .hf-blog__head { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); column-gap: var(--w-g-block); } }
.hf-blog__picks { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-tight); margin-block-start: calc(var(--w-g-group) - var(--w-g-block)); }
.hf-blog__picks .hf-tag { min-height: var(--w-touch); padding-inline: var(--sp-20); font-size: var(--w-fs-label); }
.hf-blog__picks [data-blog-count] { margin-inline-start: var(--g-control); }
.hf-blog [data-post][hidden], .hf-blog [data-blog-none][hidden] { display: none; }

/* A card that is one link. The title is the link, and it is stretched over the whole card, so
   the card is pressed anywhere and read out once, by its title. What must still be pressed for
   itself (the Guru) is lifted above the link. */
.hf-stretch::after { content: ""; position: absolute; inset: var(--sp-0); border-radius: inherit; }
.hf-stretch:focus-visible { outline: none; }
.hf-stretch:focus-visible::after { outline: var(--w-focus-w) solid var(--w-fg); outline-offset: var(--w-focus-offset); }

/* THE NEWEST NOTE. Its words on one side; on the other the Guru, in the mood the note ends on,
   and under it what the Guru says at the end of that note. */
.hf-feature { position: relative; display: grid; gap: var(--w-g-block); padding: var(--w-g-block) var(--w-card-pad); border-radius: var(--w-scene-r); align-items: center; }
@media (min-width: 900px) { .hf-feature { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); padding: var(--w-g-block); } }
.hf-feature__words { display: grid; gap: var(--w-g-group); justify-items: start; }
.hf-feature__title { font-size: var(--w-fs-h2); line-height: var(--w-lh-h2); letter-spacing: var(--w-ls-h2); font-weight: var(--w-fw-display); text-wrap: balance; }
.hf-feature .hf-lead { max-width: var(--w-measure); }
.hf-feature__guru { position: relative; z-index: 1; display: grid; justify-items: center; gap: var(--w-g-sibling); pointer-events: none; }
.hf-feature__guru .hf-guru { pointer-events: auto; }
.hf-feature__says { max-width: var(--w-measure-lead); padding: var(--w-card-pad); border-radius: var(--r-lg); border-start-start-radius: var(--r-xs); background: var(--w-panel); }
@media (hover: hover) and (pointer: fine) { .hf-feature:hover .hf-btn .hf-btn__seat { transform: var(--w-hover-grow); } }

/* THE REST, AS CARDS. As many across as fit, and never one stranded: two notes share the row. */
.hf-pcards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--w-pcard-min)), 1fr)); gap: var(--w-g-sibling); }
.hf-pcard { position: relative; display: grid; grid-template-rows: auto auto 1fr auto; gap: var(--w-g-sibling); height: 100%; padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--surface); transition: transform var(--w-t-fast) var(--w-ease); }
@media (hover: hover) and (pointer: fine) {
  .hf-pcard:hover { transform: var(--w-hover-lift); }
  .hf-pcard:hover .hf-post__go { background: var(--w-accent); color: var(--w-on-accent); }
}
.hf-pcard__top { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-tight) var(--g-control); }
.hf-pcard__top > .hf-cap { margin-inline-start: auto; }
.hf-pcard__seat { display: grid; place-items: center; flex: none; width: var(--w-btn-seat); height: var(--w-btn-seat); border-radius: var(--r-pill); background: var(--deep); color: var(--lime-500); font-size: var(--i-lg); }
.hf-pcard__seat--big { width: var(--w-guru-voice); height: var(--w-guru-voice); font-size: var(--w-fs-h2); }
.hf-pcard__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--g-control); }
.hf-pcard .hf-post__go { position: static; translate: none; }

/* The way on, at the foot: the accent as a ground, the words and the one action. */
.hf-blog__end { display: grid; gap: var(--w-g-group); align-items: center; padding: var(--w-g-block) var(--w-card-pad); border-radius: var(--w-scene-r); }
@media (min-width: 900px) { .hf-blog__end { grid-template-columns: minmax(0, 1fr) auto; padding: var(--w-g-block); } }
.hf-blog__end .hf-lead { max-width: var(--w-measure); }

/* A POST IN A LIST. A row: what it is, when, how long; its title; its sentence; and an arrow in
   a seat at the end, which takes the accent when the row is pointed at. The whole row is the
   link. The newest post is the same things on a card of its own. */
.hf-posts { display: grid; }
.hf-post { position: relative; display: grid; gap: var(--g-tight); padding-block: var(--w-g-group); padding-inline-end: calc(var(--w-btn-seat) + var(--w-g-sibling)); border-top: var(--w-stroke) solid var(--w-line); }
.hf-posts > li:last-child > .hf-post { border-bottom: var(--w-stroke) solid var(--w-line); }
.hf-post__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-tight) var(--g-control); }
.hf-post__go { position: absolute; inset-block-start: 50%; inset-inline-end: var(--sp-0); translate: 0 -50%; display: grid; place-items: center; width: var(--w-btn-seat); height: var(--w-btn-seat); border-radius: var(--r-pill); background: var(--w-well); font-size: var(--i-lg); transition: background var(--w-t-fast) var(--w-ease), color var(--w-t-fast) var(--w-ease), transform var(--w-t-fast) var(--w-ease); }
@media (hover: hover) and (pointer: fine) { .hf-post:hover .hf-post__go { background: var(--w-accent); color: var(--w-on-accent); transform: var(--w-hover-grow); } }

/* A POST. One column, centred, at the width its headline needs; the words inside it at the
   reading measure. Not a scene: it is as long as it is. */
.hf-article { display: grid; gap: var(--w-g-block); max-width: var(--w-article); }
.hf-article__back { display: inline-flex; align-items: center; gap: var(--g-inline); align-self: flex-start; min-height: var(--w-touch); color: var(--w-quiet); }
.hf-article__by { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-tight) var(--w-g-sibling); }
.hf-article__foot { display: grid; gap: var(--w-g-block); }
.hf-article__more { padding-top: var(--w-g-block); border-top: var(--w-stroke) solid var(--w-line); }

/* THE WORDS OF A POST. The reset takes the dots and numbers off every list on the site; here
   they are put back, because in running text a list has to look like one. A link is
   underlined, since colour alone would not say it is one, and takes the highlighter when
   pointed at. */
.hf-prose { display: grid; gap: var(--w-g-block); max-width: var(--w-measure); font-size: var(--w-fs-prose); line-height: var(--w-lh-prose); font-weight: var(--w-fw-prose); letter-spacing: var(--w-ls-prose); }
.hf-prose__part { display: grid; gap: var(--w-g-sibling); scroll-margin-top: var(--w-nav-clear); }
.hf-prose :is(ul, ol) { display: grid; gap: var(--g-tight); padding-inline-start: 1.3em; }
.hf-prose ul { list-style: disc; }
.hf-prose ol { list-style: decimal; }
.hf-prose li { padding-inline-start: var(--sp-4); }
.hf-prose li::marker { color: var(--w-quiet); font-weight: var(--fw-bold); }
.hf-prose strong { font-weight: var(--fw-bold); }
.hf-prose a { text-decoration: underline; text-decoration-thickness: from-font; text-underline-position: from-font; text-decoration-skip-ink: auto; overflow-wrap: break-word; }
@media (hover: hover) and (pointer: fine) { .hf-prose a:hover { background: var(--lime-500); color: var(--ink); text-decoration-color: transparent; } }
.hf-prose blockquote { padding-inline-start: var(--w-g-sibling); border-inline-start: var(--sp-4) solid var(--lime-500); font-size: var(--w-fs-lead); line-height: var(--w-lh-lead); font-weight: var(--fw-bold); }
/* An answer in Help is the same words at the size of the page's other answers. */
.hf-prose--answer { gap: var(--g-control); font-size: var(--w-fs-body); line-height: var(--w-lh-body); }

/* The Guru's remark at the end of a post: its face and one line. */
.hf-post__aside { display: flex; align-items: center; gap: var(--w-g-group); max-width: var(--w-measure); padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--w-panel); }
.hf-post__aside .hf-lead { max-width: none; }
/* On a phone the line needs the width, so the face sits above it. */
@media not all and (min-width: 560px) { .hf-post__aside { flex-direction: column; align-items: flex-start; gap: var(--w-g-sibling); } }

/* HELP. A part at a time: its name with its icon in a seat, then its questions, which are the
   page's own FAQ rows. The search is shown only when the script that runs it is. */
.hf-help__group { display: grid; gap: var(--w-g-group); scroll-margin-top: var(--w-nav-clear); }
.hf-help__group > h2 { display: flex; align-items: center; gap: var(--g-control); }
.hf-help__seat { display: grid; place-items: center; flex: none; width: var(--w-btn-seat); height: var(--w-btn-seat); border-radius: var(--r-pill); background: var(--dark); color: var(--lime-500); font-size: var(--i-lg); }
.hf-help__search { display: grid; gap: var(--g-tight); max-width: var(--w-measure-lead); }
.hf-help__search[hidden], .hf-help__none[hidden], .hf-help__group[hidden] { display: none; }
.hf-help__search .hf-field, .hf-help__search .hf-field__input { width: 100%; }
.hf-help .hf-faq { scroll-margin-top: var(--w-nav-clear); }
/* BEFORE LAUNCH, THE GURU'S REMARKS. The Guru, large, and under it what it says, in its own
   bubble at the lead size. The bubble keeps room for its longest remark, so a poke changes the
   words and moves nothing. The list the remarks are read from is not shown. */
.hf-roast { display: grid; justify-items: center; gap: var(--w-g-group); text-align: center; }
.hf-roast__says { display: grid; align-content: center; width: 100%; max-width: var(--w-measure-lead); min-height: calc(5 * var(--w-lh-lead) * 1em + 2 * var(--w-card-pad)); padding: var(--w-card-pad); border-radius: var(--r-lg); border-start-start-radius: var(--r-xs); background: var(--dark); color: var(--ink-inv); text-align: start; }
.hf-roast__lines[hidden], .hf-roast [data-roast-hint][hidden] { display: none; }
.hf-roast[data-poked] [data-roast-hint] { visibility: hidden; }
/* Where it stands. Side by side, the headline and the facts are one column and the Guru is the
   other, beside both; the two on the left close up on each other in the middle. Stacked, the
   order is the page's own: headline, Guru, facts, and the Guru is as large as the screen lets
   it be, because it is the page. */
@media (min-width: 1180px) {
  .hf-split.hf-prelaunch { grid-template-areas: "head roast" "rest roast"; row-gap: var(--w-g-group); }
  .hf-prelaunch__head { grid-area: head; align-self: end; }
  .hf-prelaunch__rest { grid-area: rest; align-self: start; }
  .hf-prelaunch > .hf-roast { grid-area: roast; }
}
@media not all and (min-width: 1180px) {
  .hf-split.hf-prelaunch { gap: var(--w-g-group); }
  .hf-roast .hf-guru--hero { width: var(--w-guru-fit-stacked); height: var(--w-guru-fit-stacked); }
}
/* While a search is on, the trail is put away: it counts parts, and parts are being hidden. */
.hf-trail[hidden] { display: none; }

/* ---------------------------------------------------------------- the admin */
/* The owner's dashboard. A working page, not a scene: it starts at the top, runs as long as
   its data, and is dense where the site is airy. One part is alive (the waitlist) and one is
   drawn and locked (customers).

   The bar across the top is the page's whole navigation: the name, that this is the admin,
   who is signed in, and the way out. */
.hf-admin { background: var(--canvas); }
.hf-admin__bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-control); padding-block: var(--w-g-group); }
.hf-admin__who { margin-inline-start: auto; overflow-wrap: anywhere; }
.hf-admin__part { display: grid; gap: var(--w-g-group); padding-block: var(--w-g-group) var(--w-g-block); }
.hf-admin__head { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--g-tight) var(--g-control); }
.hf-admin__head > .hf-tag { justify-self: start; }
.hf-admin__head > .hf-body { grid-column: 1 / -1; }
/* On a phone the label goes under the heading: beside it, it ran off the screen. */
@media not all and (min-width: 560px) { .hf-admin__head { grid-template-columns: minmax(0, 1fr); } }
.hf-admin [data-admin-when][hidden], .hf-admin [hidden] { display: none; }

/* A FIGURE: the number, what it counts, and the small print that says exactly what is counted.
   The first is the one the page is for, and it is on the deep ground. */
.hf-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--g-control); }
@media (min-width: 720px) { .hf-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1180px) { .hf-stats { grid-template-columns: minmax(0, 1.4fr) repeat(4, minmax(0, 1fr)); } }
.hf-stat { display: grid; align-content: start; gap: var(--sp-4); padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--surface); }
/* Two classes: the ground gives this box its colours, and the box's own rule would put the
   white surface back over them. */
.hf-stat.hf-stat--lead { grid-column: 1 / -1; background: var(--w-bg); color: var(--w-fg); }
@media (min-width: 1180px) { .hf-stat.hf-stat--lead { grid-column: auto; } }
.hf-stat__n { font-size: var(--w-fs-h2); line-height: var(--w-lh-h2); letter-spacing: var(--w-ls-h2); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.hf-stat--lead .hf-stat__n { color: var(--lime-500); }

/* A box on the page: the chart, the sources, the list. */
.hf-card-box { display: grid; gap: var(--w-g-sibling); min-width: var(--sp-0); margin: var(--sp-0); padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--surface); }
.hf-admin__pair { display: grid; gap: var(--g-control); }
@media (min-width: 900px) { .hf-admin__pair { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }

/* THE DAYS. A bar a day, as tall as that day's share of the busiest day. The number stands on
   the bar and the date under it, so the chart is read without hovering and without colour. A
   day with nobody is a hairline, not a gap, so the days stay evenly apart. */
.hf-days { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); align-items: end; gap: var(--sp-6); height: var(--w-chat-h); }
.hf-days__day { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; justify-items: center; gap: var(--sp-4); height: 100%; }
.hf-days__n { font-size: var(--w-fs-cap); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; min-height: 1.35em; }
.hf-days__bar { align-self: end; width: 100%; height: calc(var(--hf-share, 0) * 100%); min-height: var(--w-stroke); border-radius: var(--r-xs); background: var(--deep); }
.hf-days__bar[data-none="true"] { background: var(--field); }
.hf-days__name { font-size: var(--fs-tiny); color: var(--mute); font-variant-numeric: tabular-nums; }

/* WHERE THEY CAME FROM. A row a page: its name, how many, and a line as long as its share. */
.hf-sources { display: grid; gap: var(--g-control); }
.hf-sources__row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-6) var(--g-control); font-size: var(--w-fs-label); }
.hf-sources__row > span:first-child { overflow-wrap: anywhere; }
.hf-sources__bar { grid-column: 1 / -1; height: var(--sp-6); border-radius: var(--r-pill); background: linear-gradient(to right, var(--deep) calc(var(--hf-share, 0) * 100%), var(--field) 0); }

/* THE LIST. The tools above it, then the table, which scrolls sideways on a narrow screen
   inside its own box and never the page. */
.hf-admin__tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-control); }
.hf-admin__find { flex: 1 1 var(--c-pop-w); width: auto; }
.hf-admin__find .hf-field__input { width: 100%; }
.hf-admin__tools > .hf-cap { flex: 1 1 auto; }
/* The list scrolls inside its own box, so however long it grows the page stays short enough
   to reach what is under it, and the column names stay in view while it does. */
.hf-table__scroll { overflow: auto; max-height: calc(var(--w-screen) * 0.62); }
.hf-table thead th { position: sticky; top: var(--sp-0); background: var(--surface); }
.hf-table { width: 100%; border-collapse: collapse; font-size: var(--w-fs-label); text-align: start; }
.hf-table th { padding: var(--g-tight) var(--g-control); font-size: var(--w-fs-cap); font-weight: var(--fw-semibold); color: var(--mute); text-align: start; white-space: nowrap; border-bottom: var(--w-stroke) solid var(--hairline); }
.hf-table td { padding: var(--g-control); border-bottom: var(--w-stroke) solid var(--hairline); white-space: nowrap; }
.hf-table td:first-child { font-weight: var(--fw-semibold); white-space: normal; overflow-wrap: anywhere; min-width: var(--c-pop-w); }
.hf-table tr[data-status="left"] td { color: var(--mute); }
.hf-table tr[data-status="left"] td:first-child { text-decoration: line-through; }
.hf-table tr[data-status="unthanked"] td:last-child { color: var(--danger); font-weight: var(--fw-semibold); }

/* LOCKED. What customer management will hold, drawn in its place and shut. Each is a box with
   its icon, its name, what it will say, and a lock where its figure will be. Dashed and quiet,
   so nobody takes one for something that is working. */
.hf-locked { display: grid; gap: var(--g-control); }
@media (min-width: 560px) { .hf-locked { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .hf-locked { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.hf-lock { position: relative; display: grid; align-content: start; gap: var(--sp-6); padding: var(--w-card-pad); border-radius: var(--r-lg); border: var(--w-stroke) dashed var(--mute-dark); color: var(--mute); }
.hf-lock b { color: var(--ink); }
.hf-lock__seat { display: grid; place-items: center; width: var(--w-btn-seat); height: var(--w-btn-seat); border-radius: var(--r-pill); background: var(--field); color: var(--mute); font-size: var(--i-lg); margin-bottom: var(--sp-6); }
.hf-lock__key { position: absolute; inset-block-start: var(--w-card-pad); inset-inline-end: var(--w-card-pad); font-size: var(--i-lg); }
.hf-admin .hf-tag .hf-icon { font-size: var(--i-sm); }

/* THE ADMIN'S OWN WAY ROUND: its pages, in the bar, the one open held down. A page that is
   planned and not built is named and shut. On a narrow screen they take the line under the name. */
.hf-admin__tabs { display: flex; align-items: center; gap: var(--w-g-inline); }
.hf-admin__tabs .hf-nav__link { gap: var(--g-inline); }
.hf-admin__tabs .hf-icon { font-size: var(--i-sm); }
@media not all and (min-width: 720px) { .hf-admin__tabs { order: 1; flex-basis: 100%; } }

/* A head with something to do in it: the action keeps to the end of the heading's line.
   Two classes, because the head's own columns are set above at one. */
.hf-admin__head.hf-admin__head--act { grid-template-columns: auto auto minmax(0, 1fr); }
.hf-admin__head.hf-admin__head--act > .hf-btn { grid-column: 3; grid-row: 1; justify-self: end; }
@media not all and (min-width: 560px) {
  .hf-admin__head.hf-admin__head--act { grid-template-columns: minmax(0, 1fr); }
  .hf-admin__head.hf-admin__head--act > .hf-btn { grid-column: 1; grid-row: auto; justify-self: start; }
}

/* A NOTICE: one fact about the whole page, said once and quietly, with an icon for its kind. */
.hf-notice { display: flex; align-items: center; gap: var(--g-control); padding: var(--sp-16) var(--sp-20); border-radius: var(--r-md); background: var(--field); font-size: var(--w-fs-label); }
.hf-notice .hf-icon { flex: none; font-size: var(--i-lg); }

/* WHERE A NOTE STANDS, on its tag: a draft is plain, a published one wears the accent, and one
   with changes waiting is ringed. The words say it; the dress only makes a column of them
   quick to read down. */
.hf-tag[data-is="published"] { background: var(--w-accent); color: var(--w-on-accent); }
.hf-tag[data-is="changed"] { background: var(--lime-100); box-shadow: inset 0 0 0 var(--w-stroke) var(--ink); }
.hf-tag[data-is="new"] { background: transparent; box-shadow: inset 0 0 0 var(--w-stroke) var(--faint); }

/* THE NOTES. A row is a note: its title is the way into it, and under the title the address it
   has, or will have. */
.hf-posts__picks { display: flex; flex-wrap: wrap; gap: var(--g-tight); flex: 1 1 auto; }
.hf-admin__tools > .hf-posts__picks + .hf-cap { flex: none; }
/* On a phone a row folds: the title on a line of its own, and what is true of the note under
   it. On a wide screen the same things stand in columns, so a state is found by looking down. */
.hf-pline { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-tight) var(--g-control); padding-block: var(--sp-16); border-top: var(--w-stroke) solid var(--hairline); font-size: var(--w-fs-label); }
.hf-pline__name { display: grid; flex: 1 1 100%; min-width: var(--sp-0); }
.hf-pline__fact { color: var(--w-quiet); white-space: nowrap; }
/* On the narrowest screens what is true of a note has to fit one line under its title, so it
   is set smaller and the reading time, which the editor also shows, is left to the editor. */
@media not all and (min-width: 560px) {
  .hf-pline__fact { font-size: var(--w-fs-cap); }
  .hf-pline__fact:last-child { display: none; }
}
@media (min-width: 900px) {
  .hf-pline { display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 3fr) repeat(3, minmax(0, 2fr)); }
}
.hf-posts__name { display: inline-block; font-size: var(--w-fs-body); font-weight: var(--fw-bold); text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 0.2em; transition: text-decoration-color var(--w-t-fast) var(--w-ease); }
@media (hover: hover) and (pointer: fine) { .hf-posts__name:hover { text-decoration-color: currentColor; } }
.hf-posts__at { display: block; font-weight: var(--fw-regular); overflow-wrap: anywhere; }

/* ---------------------------------------------------------------- the editor */
/* One note, written on one side and read on the other. Where there is room for only one, a
   switch chooses. The bar above both stays in reach however long the note is: what just
   happened, and the two things to do next. */
.hf-edit__head { display: grid; gap: var(--g-tight); }
.hf-edit__name { display: flex; flex-wrap: wrap; align-items: center; gap: var(--g-tight) var(--g-control); }
.hf-edit { --hf-bar: calc(var(--w-btn-h-sm) + 2 * var(--sp-12)); display: grid; gap: var(--g-control); align-items: start; }
.hf-edit__bar { position: sticky; top: var(--sp-0); z-index: var(--w-z-sticker); display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--g-tight) var(--g-control); min-height: var(--hf-bar); padding-block: var(--sp-12); background: var(--canvas); }
.hf-edit__switch { display: flex; gap: var(--g-tight); margin-inline-end: auto; }
.hf-edit__said { flex: 1 1 auto; text-align: end; color: var(--w-quiet); }
.hf-edit__said[data-kind="dirty"] { color: var(--ink); font-weight: var(--fw-semibold); }
@media not all and (min-width: 720px) {
  /* On a phone the bar is two lines: which side is showing, then the two buttons sharing the
     width. The word about what happened takes a line of its own under them, when there is one. */
  .hf-edit__switch { flex-basis: 100%; }
  .hf-edit__bar > .hf-btn { flex: 1 1 0; }
  .hf-edit__said { order: 1; flex-basis: 100%; text-align: start; }
  .hf-edit__said:empty { display: none; }
}
@media (min-width: 1180px) {
  .hf-edit { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--g-control) var(--w-g-sibling); }
  .hf-edit__bar, .hf-edit__wrong { grid-column: 1 / -1; }
  .hf-edit__switch { display: none; }
  /* The reading side stays beside the form as the form scrolls, and scrolls within itself:
     the bar above it, then the checks, then the preview in whatever height is left. */
  .hf-edit__side { position: sticky; top: var(--hf-bar); height: calc(var(--w-screen) - var(--hf-bar) - var(--g-control)); grid-template-rows: auto minmax(0, 1fr); }
  .hf-edit .hf-preview { overflow: auto; overscroll-behavior: contain; }
}
@media not all and (min-width: 1180px) {
  .hf-admin [data-view="write"] .hf-edit__side, .hf-admin [data-view="preview"] .hf-edit__form { display: none; }
}

/* THE FORM. Each box is one kind of fact about the note. A field runs the width of its box. */
.hf-edit__form, .hf-edit__side { display: grid; gap: var(--g-control); min-width: var(--sp-0); }
.hf-edit .hf-field { width: 100%; }
.hf-edit__pair { display: grid; gap: var(--w-g-sibling); align-items: start; }
@media (min-width: 560px) { .hf-edit__pair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
/* A field that holds more than a line is a box and not a pill, and grows downward. */
.hf-edit textarea.hf-field__input, .hf-edit textarea.hf-field__input:focus-visible { border-radius: var(--r-md); }
.hf-edit textarea.hf-field__input { width: 100%; padding: var(--sp-16) var(--sp-20); font-family: inherit; line-height: var(--w-lh-body); resize: vertical; }
.hf-edit textarea.hf-edit__body { min-height: calc(var(--w-screen) * 0.5); }
.hf-edit .hf-field__input[readonly] { background: var(--field); color: var(--mute); }
.hf-edit__count { align-self: flex-end; font-variant-numeric: tabular-nums; }
.hf-edit__count[data-over="true"] { color: var(--danger); font-weight: var(--fw-semibold); }
/* The address, after the part of it that is always the same. */
.hf-edit__at { display: flex; align-items: center; gap: var(--g-tight); }
.hf-edit__at .hf-field__input { flex: 1 1 auto; min-width: var(--sp-0); }
.hf-edit__site { flex: none; font-size: var(--w-fs-label); }
@media not all and (min-width: 560px) { .hf-edit__at { flex-direction: column; align-items: stretch; } }
.hf-edit__tools { display: flex; flex-wrap: wrap; gap: var(--g-tight); }

/* WHAT STANDS IN THE WAY. A line for each thing to fix, and each line is the way to its field. */
.hf-edit__todo { display: grid; gap: var(--g-tight); }
.hf-edit__todo ul { display: grid; gap: var(--g-inline); }
/* It is a list of things to do. It wears the colour of something wrong only once publishing
   has been tried with some of it still undone. */
.hf-edit__fix { display: flex; align-items: flex-start; gap: var(--g-tight); width: 100%; padding: var(--sp-8) var(--sp-12); border: var(--sp-0); border-radius: var(--r-sm); background: var(--field); color: var(--ink); font-size: var(--w-fs-cap); line-height: var(--w-lh-cap); text-align: start; cursor: pointer; }
.hf-edit__checks[data-tried="true"] .hf-edit__fix { background: var(--danger-tint); color: var(--danger); }
.hf-edit__fix .hf-icon { flex: none; font-size: var(--i-sm); line-height: inherit; }
@media (hover: hover) and (pointer: fine) { .hf-edit__fix:hover { text-decoration: underline; } }
.hf-edit__wrong[hidden] { display: none; }

/* TAKING DOWN. What it does is said first; the question it asks is on the colour that means it. */
.hf-edit__danger { display: grid; gap: var(--g-control); justify-items: start; }
.hf-edit__sure { display: grid; gap: var(--g-control); justify-self: stretch; padding: var(--sp-16) var(--sp-20); border-radius: var(--r-md); background: var(--danger-tint); }

/* THE PREVIEW: a window onto the site, on the site's own ground. The post inside wears the
   post's own classes and is read by the build's own reader, so it is the post and not a sketch
   of it. ITS SIZES FOLLOW THE WINDOW, NOT THE SCREEN: the site's type and gaps grow with the
   width of the screen, and here they are given the width of this box to grow with instead
   (the -in tokens, styles/tokens.css). So the post reads as it does on a screen as wide as the
   window. Nothing is scaled down, so no word in it is smaller than on the site. */
.hf-preview { container-type: inline-size; display: grid; align-content: start; gap: var(--w-g-sibling); padding: var(--w-card-pad); border-radius: var(--r-lg); box-shadow: inset 0 0 0 var(--w-stroke) var(--hairline); min-width: var(--sp-0); }
.hf-preview__page {
  --w-fs-display: var(--w-fs-display-in); --w-fs-h2: var(--w-fs-h2-in); --w-fs-h3: var(--w-fs-h3-in);
  --w-fs-lead: var(--w-fs-lead-in); --w-fs-body: var(--w-fs-body-in); --w-fs-prose: var(--w-fs-prose-in);
  --w-g-group: var(--w-g-group-in); --w-g-block: var(--w-g-block-in);
  min-width: var(--sp-0);
}
.hf-preview [data-empty="true"] { color: var(--mute); }
.hf-preview__stale { color: var(--danger); }

/* ---------------------------------------------------------- feature pages */
/* One page per feature (Personal logging, Rooms, the Guru), each built from the same few
   pieces so they read as one site: a split scene of words and real screens, a row of phones, a
   list of what to type and what it does, the facts, the questions, and the way on. What the
   page holds, and how far through it the reader is, is the trail's job (styles/site.css). */

/* The words lead in the markup, so that is the order on a phone and for a screen reader. On a
   wide screen a flipped scene shows its screens first. */
/* Two classes, on purpose: the plain split's columns are set further down this file at the same
   weight, and a rule that loses that contest loses it silently (PROTOCOL.md, section 3). */
@media (min-width: 1180px) {
  .hf-split.hf-split--flip { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .hf-split.hf-split--flip > :first-child { order: 2; }
  .hf-panel.hf-split.hf-split--flip { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* PHONES IN A ROW. Each is a figure with a word under it. Side by side on a wide screen, the
   second one dropped a step so the row reads as a sequence and not a grid. */
.hf-shots { display: flex; justify-content: center; align-items: flex-start; gap: var(--w-g-group); }
.hf-shot { display: grid; gap: var(--g-control); justify-items: center; flex: none; }
.hf-shots .hf-phone { --w-phone-w: var(--w-phone-fit-pair); }
.hf-shots--one .hf-phone { --w-phone-w: var(--w-phone-fit-one); }
.hf-shots--one .hf-phone--stands { --w-phone-w: var(--w-phone-fit-stands); }
.hf-shots--three .hf-phone { --w-phone-w: var(--w-phone-fit-trio); }
.hf-shots--three { gap: var(--w-g-sibling); }
/* Inside a panel there is the panel's own padding to fit as well, so the phones give way. */
.hf-panel .hf-shots .hf-phone { --w-phone-w: var(--w-phone-fit); }
@media (min-width: 900px) { .hf-shot:nth-child(even) { margin-top: var(--w-g-group); } }
@media not all and (min-width: 900px) {
  /* On a phone the row is a strip to swipe along. It runs to both edges of the screen, the
     next phone shows at the edge to say there is more, and each one stops in place. */
  .hf-shots:not(.hf-shots--one) {
    justify-content: flex-start; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none;
    margin-inline: calc(var(--w-gutter) * -1); padding-inline: var(--w-gutter); scroll-padding-inline: var(--w-gutter);
  }
  .hf-shots:not(.hf-shots--one) .hf-shot { scroll-snap-align: start; }
  .hf-shots:not(.hf-shots--one) .hf-phone { --w-phone-w: var(--w-phone-row); }
  .hf-shots--one .hf-phone { --w-phone-w: var(--w-phone-fit); }
}

/* WHAT YOU TYPE, AND WHAT COMES BACK. Each example is a small conversation on a card of its
   own: the one thing it shows, said in a line, then your message and the reply to it, set as
   they are in the chat - yours to the right, the reply to the left. A sentence about a message
   is an explanation; a message and its reply is the thing itself. */
.hf-exchanges { display: grid; gap: var(--w-g-sibling); }
.hf-exchange { display: flex; flex-direction: column; gap: var(--g-control); padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--w-panel); }
.hf-exchange__chat { display: flex; flex-direction: column; gap: var(--c-bubble-gap); }
.hf-exchange .hf-bubble { max-width: 88%; }
@media (min-width: 560px) { .hf-exchanges--grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* THE GURU'S MOODS. One face, and the names of what it can feel: choosing one puts it on the
   face and says what earns it. */
.hf-moods { display: grid; gap: var(--w-g-group); justify-items: center; text-align: center; }
.hf-moods__picks { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--g-tight); }
.hf-moods__when { min-height: calc(2 * var(--w-lh-body, 1.5) * 1em); max-width: var(--w-measure-lead); }
/* The two voices, side by side: one face each, and a line under it. */
.hf-voices { display: grid; gap: var(--w-g-group); }
.hf-voice { display: grid; gap: var(--g-control); justify-items: start; padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--w-well); }
@media (min-width: 560px) { .hf-voices { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* ONE SCREEN, ON A SHORT ONE TOO. Beside its screens, a list of points is set at the size a
   card's text is: it follows the height of the screen, so three points still fit a small
   laptop. And the Guru on the mood board gives way to the names under it. */
@media (min-width: 1180px) {
  .hf-split .hf-points .hf-body { font-size: var(--w-fs-card); }
  .hf-split .hf-exchange { padding: var(--sp-16) var(--sp-20); gap: var(--g-tight); }
  .hf-split .hf-exchange .hf-bubble { font-size: var(--w-fs-card); }
  .hf-panel .hf-moods .hf-guru--hero { width: min(var(--w-guru-hero), 30svh); height: min(var(--w-guru-hero), 30svh); }
  .hf-panel .hf-moods { gap: var(--w-g-sibling); }
}

/* THE WAY ON, at the foot of a feature page: the other two features, and the action. */
.hf-next { display: grid; gap: var(--w-g-block); }
.hf-next__links { display: grid; gap: var(--w-g-sibling); }
.hf-next__link {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--g-tight) var(--w-g-group);
  padding: var(--w-card-pad); border-radius: var(--r-lg); background: var(--w-well);
  transition: background var(--w-t-fast) var(--w-ease);
}
.hf-next__link .hf-btn__seat { grid-row: 1 / span 2; grid-column: 2; }
@media (hover: hover) and (pointer: fine) { .hf-next__link:hover .hf-btn__seat { transform: var(--w-hover-grow); } }
@media (min-width: 900px) { .hf-next__links { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* ------------------------------------------------------------------ rooms */
.hf-rooms { display: grid; gap: var(--w-g-block); align-items: center; }
.hf-rooms__stage { position: relative; display: flex; gap: var(--w-g-group); justify-content: center; align-items: flex-start; }
.hf-rooms__stage .hf-phone:nth-of-type(2) { margin-top: var(--w-g-block); }
.hf-rooms__stage .hf-actor { animation: hf-wander var(--w-t-marquee) var(--w-ease-in-out) infinite alternate; }
.hf-rooms__stage .hf-actor:nth-of-type(1) { left: 4%; top: 18%; }
.hf-rooms__stage .hf-actor:nth-of-type(2) { left: 46%; top: 58%; animation-duration: calc(var(--w-t-marquee) * .7); animation-direction: alternate-reverse; }
.hf-rooms__stage .hf-actor:nth-of-type(3) { left: 70%; top: 8%; animation-duration: calc(var(--w-t-marquee) * .85); }
@keyframes hf-wander { 0% { translate: 0 0; } 35% { translate: var(--sp-48) var(--sp-32); } 70% { translate: calc(-1 * var(--sp-24)) var(--sp-56); } 100% { translate: var(--sp-80) var(--sp-12); } }
@media (min-width: 900px) { .hf-rooms { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); } }
@media not all and (min-width: 560px) { .hf-rooms__stage .hf-phone:nth-of-type(2) { display: none; } }

/* ------------------------------------------------------------------ gurus */
/* One Guru, four lines. The ground steps through the accent ramp as the mood changes. */
.hf-gurus { transition: background-color var(--w-t-med) var(--w-ease); background: var(--w-bg); }
/* The step is reported on the section; the ground is on the part that sticks. Matched with the
   ground attribute as well, so these outrank the ground's own answer for the background. */
[data-step="1"] .hf-gurus[data-ground] { --w-bg: var(--lime-tint); }
[data-step="2"] .hf-gurus[data-ground] { --w-bg: var(--lime-100); }
[data-step="3"] .hf-gurus[data-ground] { --w-bg: var(--lime-300); }
.hf-gurus__grid { display: grid; gap: var(--w-g-group); align-items: center; justify-items: center; }
.hf-gurus__lines { display: grid; width: 100%; }
.hf-gurus__lines li { grid-area: 1 / 1; display: flex; flex-direction: column; gap: var(--w-g-group); align-items: flex-start; opacity: 0; transform: translateY(var(--w-rise-soft)); transition: opacity var(--w-t-med) var(--w-ease), transform var(--w-t-reveal) var(--w-ease-out); pointer-events: none; }
[data-step="0"] .hf-gurus__lines li:nth-child(1), [data-step="1"] .hf-gurus__lines li:nth-child(2),
[data-step="2"] .hf-gurus__lines li:nth-child(3), [data-step="3"] .hf-gurus__lines li:nth-child(4) { opacity: 1; transform: none; pointer-events: auto; }
[data-step="all"] .hf-gurus__lines li { grid-area: auto; opacity: 1; transform: none; pointer-events: auto; }
[data-step="all"] .hf-gurus__lines { gap: var(--w-g-block); }
.hf-gurus .hf-said { transform: none; }
@media (min-width: 900px) { .hf-gurus__grid { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); } }
@media not all and (min-width: 900px) {
  /* Stacked, the Guru leads: it is the subject of the scene, so it is as large as the screen
     allows and everything is centred under it. The introduction and the small print give way,
     so the face and the line it goes with always fit one screen. */
  .hf-gurus__grid { justify-items: center; text-align: center; }
  .hf-gurus__lines li { align-items: center; }
  .hf-gurus .hf-guru--hero { order: -1; width: var(--w-guru-fit-stacked); height: var(--w-guru-fit-stacked); }
  :not([data-step="all"]) > .hf-gurus .hf-lead, :not([data-step="all"]) > .hf-gurus .hf-cap { display: none; }
}

/* ------------------------------------------------------------------- said */
.hf-said-wall { display: flex; flex-wrap: wrap; gap: var(--w-g-sibling); align-items: flex-start; }
/* The wall stands on the deep ground, so its bubbles are the light surfaces: paper, and the
   accent for every third. Ink on both, the two measured pairs. */
.hf-said-wall .hf-said { background: var(--surface); color: var(--ink); position: relative; }
.hf-said-wall .hf-said--accent { background: var(--lime-500); color: var(--ink); }
/* On the wall a line is set a step smaller and its bubble is tighter, so two share a row. */
.hf-said-wall .hf-said { transform: translate(var(--hf-x, 0), var(--hf-y, 0)) rotate(var(--hf-rot, var(--w-sticker-rot-2))); font-size: var(--w-fs-lead); line-height: var(--w-lh-lead); letter-spacing: var(--w-ls-lead); padding: var(--g-control) var(--sp-20); }
.hf-said-wall .hf-said:nth-child(3n + 2) { --hf-rot: var(--w-sticker-rot-3); }
.hf-said-wall .hf-said:nth-child(3n) { --hf-rot: var(--w-sticker-rot-4); }

/* ---------------------------------------------------------------- pricing */
.hf-pricing { display: grid; gap: var(--w-g-sibling); }
.hf-pricing .hf-price { height: 100%; gap: var(--g-control); padding: var(--sp-24); position: relative; }
/* A sticker thrown onto the card that is keeping a secret. It hangs over the card's top edge. */
.hf-price__sticker { position: absolute; inset-block-start: calc(var(--sp-20) * -1); inset-inline-end: var(--sp-20); --hf-rot: var(--w-sticker-rot-3); }
.hf-pricing .hf-fig { font-size: var(--w-fs-figure-sm); }
.hf-pricing .hf-price__list { font-size: var(--w-fs-label); line-height: var(--w-lh-cap); gap: var(--g-inline); }
/* The action sits at the foot of its card, so the two cards' buttons line up. */
.hf-pricing .hf-price > .hf-btn { margin-top: auto; }
.hf-plans { display: inline-flex; gap: var(--c-switch-gap); padding: var(--c-switch-pad); border-radius: var(--r-pill); background: var(--w-well); align-self: flex-start; }
.hf-plans button { min-height: var(--w-touch); padding-inline: var(--w-g-sibling); border: var(--sp-0); border-radius: var(--r-pill); background: transparent; color: var(--w-quiet); font-size: var(--w-fs-label); font-weight: var(--w-fw-label); cursor: pointer; transition: background var(--w-t-fast) var(--w-ease), color var(--w-t-fast) var(--w-ease); }
.hf-plans button[aria-pressed="true"] { background: var(--w-accent); color: var(--w-on-accent); }
@media (min-width: 900px) { .hf-pricing { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* ---------------------------------------------------------------- privacy */
.hf-facts { display: grid; gap: var(--w-g-group); }
.hf-facts li { display: flex; flex-direction: column; gap: var(--g-inline); }
.hf-facts .hf-icon { font-size: var(--w-fs-h3); }
@media (min-width: 900px) { .hf-facts { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* -------------------------------------------------------------------- faq */
/* One list of questions, the first one open. It is taller than a screen on purpose - ten
   questions are a list to scroll, not a thing to see at once - so the heading and the question
   box stay beside it while it passes. */
.hf-faqs { display: grid; gap: var(--w-g-block); align-items: start; }
.hf-ask { display: flex; flex-direction: column; gap: var(--w-g-sibling); width: min(100%, var(--c-device-w)); }
/* The field and its button share a row when there is room and stack when there is not. */
.hf-ask__row { display: flex; flex-wrap: wrap; gap: var(--w-g-tight); align-items: center; }
.hf-ask__row .hf-field__input { flex: 1 1 var(--c-pop-w); min-width: var(--sp-0); }
.hf-ask__answer { display: flex; flex-direction: column; gap: var(--w-g-tight); padding: var(--sp-24); border-radius: var(--r-lg); background: var(--w-panel); }
.hf-ask__answer[hidden] { display: none; }
@media (min-width: 900px) {
  .hf-faqs { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--w-g-block); }
  .hf-faqs__ask { position: sticky; top: var(--w-nav-clear); }
}

/* ---------------------------------------------------------------- closing */
.hf-closing { position: relative; overflow: clip; }
.hf-closing__grid { display: grid; gap: var(--w-g-block); align-items: end; }
.hf-closing__stickers { display: flex; flex-wrap: wrap; gap: var(--w-g-group); align-items: center; }
@media (min-width: 900px) and (max-width: 1180px) { .hf-closing__grid { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); } }

/* ----------------------------------------------------------------- footer */
.hf-foot { display: grid; gap: var(--w-g-block); padding-block: var(--w-g-block); border-top: var(--w-stroke) solid var(--w-line); }
.hf-foot__links { display: flex; flex-wrap: wrap; gap: var(--w-g-tight) var(--w-g-group); }
.hf-foot__links a, .hf-foot__switch { display: inline-flex; align-items: center; min-height: var(--w-touch); }
.hf-foot__switch { gap: var(--w-g-tight); border: var(--sp-0); background: transparent; color: var(--w-quiet); font-size: var(--w-fs-label); cursor: pointer; padding: var(--sp-0); }
.hf-foot__switch::before { content: ''; width: var(--sp-32); height: var(--sp-20); border-radius: var(--r-pill); background: var(--w-well); box-shadow: inset 0 0 0 var(--w-stroke) var(--w-quiet); transition: background var(--w-t-fast) var(--w-ease); }
.hf-foot__switch[aria-pressed="true"] { color: var(--w-fg); }
.hf-foot__switch[aria-pressed="true"]::before { background: var(--w-accent); box-shadow: none; }
@media (min-width: 900px) { .hf-foot { grid-template-columns: auto minmax(0, 1fr); column-gap: var(--w-g-block); align-items: end; } }

/* ------------------------------------------------------------- one screen */
/* ON A LAPTOP OR A DESKTOP, A SCENE IS ONE SCREEN. Its headline, its content and its action are
   visible together; nobody scrolls back and forth to read one thought. Three things make it so:

     1. the scene is as tall as the screen, starts under the navigation, and centres what is in it
     2. a scene with a headline and a body of content sets them SIDE BY SIDE, using the width
        the screen has rather than the height it does not
     3. the large type, the gaps and the phone all answer to the screen's height (tokens.js)

   tools/audit-fit.mjs measures every scene against this at six screen sizes.

   Below this width a scene stacks and scrolls. A paragraph and a picture cannot share a phone
   screen, and a tablet held upright is closer to a phone than to a laptop. */
.hf-strong { font-weight: var(--fw-semibold); }
.hf-stack { display: flex; flex-direction: column; gap: var(--w-g-tight); }
.hf-split { display: grid; gap: var(--w-g-block); align-items: center; }

@media (min-width: 1180px) {
  .hf-page .hf-scene { min-height: var(--w-screen); display: grid; align-content: center; padding-block: var(--w-nav-clear) var(--g-sibling); }
  .hf-page .hf-panel { padding-block: var(--w-g-group); }
  .hf-split { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  /* Inside a panel the columns are even: the panel's own padding has already narrowed the
     headline's side, and an even split lets a long headline settle in three lines. */
  .hf-panel.hf-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  /* A headline beside its content takes its size from the column it stands in. */
  .hf-split > :first-child { container-type: inline-size; }
  .hf-split > :first-child .hf-display { font-size: min(var(--w-fs-display), var(--w-headline-fit)); }

  .hf-how { align-items: center; }

  /* A SCENE THAT IS A BOX FILLS THE SCREEN. A panel or a pair of cards left at the height of
     its content floats in the middle with empty page above and below it, while what is inside
     it is packed tight. So the box runs from under the navigation to the foot of the screen,
     and its contents spread into the room: the gaps open up rather than the type being
     squeezed. The spreading is done by distributing free space, not by fixed larger gaps, so a
     short screen simply has less to distribute and nothing overflows. */
  .hf-page .hf-scene--fill { align-content: stretch; }
  /* Under a box the page keeps only a sibling's gap, because the scene that follows brings
     its own room. A scene on another ground brings none: the paper under the box is then a
     sliver against the dark. There the box stands off the next ground by what it stands off
     the sides of the screen, so it sits in an even frame; a short screen gives a little less. */
  .hf-page .hf-scene--fill:has(+ [data-ground]) { padding-block-end: var(--w-box-foot); }
  .hf-scene--fill > .hf-wrap { display: grid; }
  .hf-scene--fill .hf-panel { padding-block: var(--w-g-group); }

  /* A SCENE THAT IS EXACTLY ONE SCREEN, however much is put in it. The chat is the case: every
     message adds to the conversation, and a scene that grew with it would push the page down
     under the reader's hands. So the scene's height is fixed, every box between it and the
     conversation is told it may be smaller than its contents, and the conversation - the one
     thing that is allowed to be longer than its box - scrolls. */
  .hf-page .hf-scene--fixed { height: var(--w-screen); grid-template-rows: minmax(0, 1fr); }
  .hf-scene--fixed > .hf-wrap { grid-template-rows: minmax(0, 1fr); min-height: var(--sp-0); }
  .hf-scene--fixed .hf-how { grid-template-rows: minmax(0, 1fr); min-height: var(--sp-0); }
  .hf-scene--fixed .hf-how__chat { align-self: stretch; min-height: var(--sp-0); }
  .hf-scene--fixed .hf-chat--live { flex: 1 1 0; min-height: var(--sp-0); }
  .hf-scene--fixed .hf-chat__log { flex: 1 1 0; height: auto; min-height: var(--sp-0); }
  .hf-scene--fill .hf-how > .hf-group { gap: var(--w-g-group); }

  /* The privacy panel: the four facts share the height evenly, with room round each. */
  .hf-scene--fill .hf-facts { align-self: stretch; align-content: space-evenly; gap: var(--w-g-group) var(--w-g-block); }
  .hf-scene--fill .hf-facts li { gap: var(--w-g-tight); }
  .hf-scene--fill .hf-facts .hf-icon { font-size: var(--w-fs-h2); }

  /* The pricing cards: as tall as the screen allows, with the action at the foot and what a
     plan includes spread through the middle. */
  .hf-scene--fill .hf-pricing { align-self: stretch; }
  .hf-scene--fill .hf-pricing .hf-price { padding: var(--w-card-pad); justify-content: space-between; }
  .hf-scene--fill .hf-pricing .hf-price__list { font-size: var(--w-fs-card); gap: var(--g-inline); }
  /* The term sits under the figure, on its own line, instead of being squeezed beside it. */
  .hf-scene--fill .hf-pricing .hf-price__amount { flex-direction: column; align-items: flex-start; gap: var(--sp-4); }

  .hf-rooms { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .hf-rooms > .hf-group, .hf-how > .hf-group { gap: var(--g-sibling); }
  .hf-rooms .hf-points { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--g-control) var(--w-g-group); }
  .hf-rooms__stage .hf-phone:nth-of-type(2) { margin-top: var(--w-g-group); }

  /* The questions scroll, so their scene starts at the top instead of centring a list that is
     taller than the screen. */
  .hf-page .hf-scene[data-fit="scroll"] { align-content: start; padding-block: var(--w-nav-clear) var(--w-g-block); }

  /* The Guru and the stickers leave the flow and gather at the right, so the headline and the
     form keep the screen. The stickers can still be picked up, and the Guru still poked. */
  .hf-closing .hf-blocks { gap: var(--w-g-group); }
  .hf-closing__grid { grid-template-columns: minmax(0, 1fr); }
  .hf-closing .hf-guru--hero { position: absolute; right: 14%; bottom: 12%; }
  .hf-closing__stickers { position: absolute; inset: var(--sp-0); display: block; pointer-events: none; }
  .hf-closing__stickers .hf-sticker { position: absolute; pointer-events: auto; }
  .hf-closing__stickers .hf-sticker:nth-child(1) { left: 54%; top: 50%; }
  .hf-closing__stickers .hf-sticker:nth-child(2) { left: 84%; top: 44%; }
  .hf-closing__stickers .hf-sticker:nth-child(3) { left: 58%; top: 76%; }
  .hf-closing__stickers .hf-sticker:nth-child(4) { left: 62%; top: 90%; }
  .hf-closing__stickers .hf-sticker:nth-child(5) { left: 88%; top: 70%; }
}

/* ---------------------------------------------------------- reduced motion */
/* Every pinned scene is released and laid out as an ordinary section, in its complete state. */
@media (prefers-reduced-motion: reduce) {
  .hf-pin[data-pin] { height: auto; }
  .hf-pin__stick { position: static; height: auto; padding-block: var(--w-g-scene); }
  .hf-word__in, .hf-piece__face, .hf-piece__shade, .hf-gurus__lines li, .hf-drain__end { transition-duration: var(--w-t-fast); transition-delay: 0s; }
  .hf-motion [data-reveal="wait"] .hf-word__in { transform: none; }
  .hf-motion [data-end="false"] .hf-drain__end { opacity: 1; transform: none; }
  .hf-drain__note { display: none; }
  .hf-hero__show .hf-phone { transform: none; }
  .hf-phone__screen img[data-progress] { transform: none; }
  .hf-rooms__stage .hf-actor { animation: none; }
  /* The whole month, newest first, with nothing hidden and nothing sliding. */
  .hf-entries__window { height: auto; }
  .hf-entries__list { transform: none; transition: none; }
}
