/* fairhandapp.com
   ============================================================================
   The site's job is to be recognisably the same object as the app, so every
   value here is taken from the app's design tokens rather than matched by eye. The app's rules apply: warm paper ground with tooth, brushed
   hairlines, an editorial serif for anything that SPEAKS and a neutral sans for
   anything that WORKS, generous whitespace, and nothing floating on a shadow.

   NOTHING IS SET IN CAPS, per the app's own standing rule.

   No third-party requests of any kind — the fonts are self-hosted and the grain
   is generated by the browser. /privacy says there is no tracking and no
   third-party anything, and a stylesheet that phoned a CDN would make that page
   a liar about itself.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts ---
   Playfair Display and Square Peg, subset to the characters these four pages
   set and renamed. The rename is a licence requirement, not a preference:
   Playfair Display carries a Reserved Font Name and OFL 1.1 §3 forbids a
   modified version — which a subset is — from using it. Both licences and the
   original copyright lines are in /fonts/. */

@font-face {
  font-family: "Fairhand Display";
  src: url("/fonts/fairhand-display-semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* ---------------------------------------------------------------- tokens ---
   Light values are the app's tokens verbatim. Contrast ratios in the comments are
   WCAG relative luminance against that theme's page ground, computed with the
   same maths the app's accessibility checks use; the bars are 4.5:1 for body text and
   3:1 for large text and non-text UI. */

:root {
  color-scheme: light;

  --paper: #f7f1e4;         /* ColorToken.background */
  --surface: #fcf8ef;       /* ColorToken.surface */
  --ink: #1c1b18;           /* textPrimary   15.30:1 */
  /* ⚠️ NOT the app's textSecondary (#6B6558) any more. That warm grey read as
     washed out under a page of paragraphs; this is the blue-grey picked from
     seven put on the real page, 6.59:1 on the grained ground against the
     grey's 4.70. It keeps a second voice under the ink — the table's row labels
     and the body copy are ink, and quiet text has to sit below them — while
     carrying the navy's cast rather than the paper's warmth. The app still sets
     its own secondary text; this is a web-side decision and the two differ. */
  --slate: #455065;         /* quiet text  6.59:1 */
  /* The chosen segment of the billing control: the paper's own hue taken down
     in lightness, so "this one" is said without adding a colour to a page that
     already carries blue, ochre and the alarm red. ⚠️ It is a FILL and not a
     boundary — 1.60 against the control's bone, which no cream clears. The
     label does that work; see the note in home.css. */
  --chosen: #d3c6a8;        /* chosen segment — ink on it 10.18:1 */
  --rule: #e2d9c8;          /* rule — decorative only, 1.24:1 by design */
  --edge: #8c8271;          /* controlBorder  3.36:1 */
  --accent: #1e3c78;        /* blue           9.46:1 */
  --accent-rgb: 30, 60, 120;
  --on-accent: #fcf8ef;     /* 10.04:1 on blue */

  /* The four household colours — the first four of the app's palette, which are
     also the four wedges of the Fairhand mark.

     ⚠️ **REPAINTED. These were #1e3c78 / #c6401a / #d8952f / #ddd0bb until
     2026-09-15 and every one of them was wrong**, because the app's palette was
     redrawn when the member cap went from four to eight. The fourth is the one
     that matters: it was SAND, a pale bone, and sand is not in the new palette
     at all — the app's own palette file says so in as many words. The mark here
     was quietly showing a colour the app had deleted.

     Taken from the app's single palette definition, the one it and the widget
     both read. The app has eight; the site draws four people, so it declares
     four. Ratios measured by
     tools/check-contrast.py against the GRAINED paper, which is why they sit a
     little under the numbers recorded alongside the palette itself. */
  --member-1: #184197;      /* ultramarine  7.61 on paper */
  --member-2: #d64724;      /* vermillion   3.56 */
  --member-3: #d08220;      /* ochre        2.47 — needs the drawn edge */
  --member-4: #2f6b54;      /* pine         5.09 */

  /* ⚠️ There are no `--on-member-N` tokens, and that is the answer to a real
     question rather than an omission. Text on a member's paint was tried and
     cannot be made to pass: on the repainted vermillion, bone measures 4.14:1
     and ink 3.93:1, so neither ink clears the 4.5 bar and the only fixes left
     would change a person's identity colour to suit a label. The app does not
     put text on a member's paint either — a member's colour arrives as their
     painted MARK and text sits on paper. See .chip in home.css. */

  /* The edge a pale mark needs to be found on paper.

     ⚠️ **It is INK, not `--edge`.** The app records two wrong answers
     before this one: `controlBorder` #8C8271 measured 1.24:1 against ochre and
     carried more blue than the paint, and darkening the member's own paint just
     made an olive. Near-black has no hue to be mistaken for. Only the two
     colours under the 3:1 floor take one. */
  --edge-member-1: transparent;
  --edge-member-2: transparent;
  --edge-member-3: #1c1b18;
  --edge-member-4: transparent;

  /* The allergen alarm. NOT member 2's vermillion, and the app is emphatic
     about why: brand red IS a household member's identity colour, so an unsafe
     badge and that member's perfectly-fine badge were the same colour separated
     by a small glyph. This deeper crimson is outside the member palette
     (DS.ColorToken.fitUnsafe) and it is also the only red on this page that
     carries bone text at a passing ratio. */
  --alarm: #8e1b0f;

  /* PaintedWash densities. The app's are 0.13 / 0.07 / 0.10. */
  --wash-a: 0.13;
  --wash-b: 0.07;
  --wash-c: 0.10;

  --grain-blend: multiply;
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='0.04' intercept='0.96'/%3E%3CfeFuncG type='linear' slope='0.04' intercept='0.96'/%3E%3CfeFuncB type='linear' slope='0.04' intercept='0.96'/%3E%3CfeFuncA type='linear' slope='0' intercept='1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23g)'/%3E%3C/svg%3E");

  --measure: 34rem;         /* prose */
  --wide: 68rem;            /* the week */
  --gutter: 1.5rem;
}

/* ------------------------------------------------------- one theme only ---
   THE SITE IS LIGHT. There is no dark mode and there must not be one.

   ⚠️ There WAS one until 2026-09-15, removed on sight the first time the site
   was opened on a machine set to dark: a dark page is not how the app looks.
   The reasoning it was built on is worth keeping, because it is the reasoning
   that will suggest rebuilding it:

     the app is light-only, but a website is a different problem — half of any
     audience arrives in dark mode and a page that ignores it flashes white at
     them at night. So: not an inversion, a DESIGNED dark ground, warm rather
     than grey, with the accent lifted to hold contrast.

   It measured well and it was still wrong, because it answered the wrong
   question. This page's job is not to be comfortable at night; it is to be
   recognisably the same object as the thing you are about to download. A
   visitor in dark mode met a warm near-black page, tapped through to an app
   that is bone paper in every light, and the page had quietly mis-sold it.
   Comfort is worth a lot; it is not worth the one promise the site makes.

   So `color-scheme: light` is declared above and no `prefers-color-scheme`
   query exists anywhere in this stylesheet. The declaration is what stops a
   dark-mode browser painting its own scrollbars, form controls and canvas
   dark around a page that is otherwise cream, which is the actual mechanism
   behind "it went dark" — the tokens alone do not prevent it.

   The illustrations, for the record, needed nothing either way: each dish is
   painted on its own paper field, which is exactly why the dark build could
   be made to work and exactly why it was never necessary. */

/* ------------------------------------------------------------------ base --- */

*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 1.0625rem/1.65 ui-sans-serif, -apple-system, BlinkMacSystemFont,
        "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* The tooth. Generated by the browser from a stitched fractal noise, so the page
   carries the app's paper without a second request or an image to cache;
   `stitchTiles` is what makes the 220px square seamless when it repeats.

   It is blended into the body's own background rather than laid over the page
   as a fixed overlay. The overlay was the first attempt: a full-viewport
   `position: fixed` element with `mix-blend-mode` forces its own compositing
   layer and repaints on every scroll frame, to do something a background can do
   for free. This version adds no layer at all, and the grain travels with the
   page the way grain in paper does.

   The strength lives in the SVG instead of in an opacity: the noise is already
   mapped into the top 5% of the range, so multiplying it into the paper darkens
   by at most that much. Dark mode maps it into the BOTTOM 5% and screens it,
   because multiply does nothing to a near-black ground. */
@supports (background-blend-mode: multiply) {
  body {
    background-image: var(--grain);
    background-size: 220px 220px;
    background-blend-mode: var(--grain-blend);
  }
}

::selection { background: rgba(var(--accent-rgb), 0.18); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Parked by clipping it to nothing rather than by pushing it off to the left.
   The old idiom (`left: -9999px`) is a real element sitting 9999px outside the
   page, which is the one thing on this site that made the document wider than
   the window — and the usual patch for that is `overflow-x: hidden` on the
   body, which then hides every future overflow bug too. */
.skip {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  background: var(--surface);
  color: var(--ink);
  padding: 0.6rem 1rem;
  border: 1px solid var(--edge);
  border-radius: 8px;
}

.skip:focus {
  width: auto;
  height: auto;
  overflow: visible;
  clip-path: none;
  left: 0.75rem;
  top: 0.75rem;
  z-index: 10;
}

/* ------------------------------------------------------------------ type --- */

h1, h2, h3, .wordmark, .dish-name {
  font-family: "Fairhand Display", Georgia, "Times New Roman", serif;
  font-weight: 600;
  letter-spacing: -0.012em;   /* DS.Tracking.display, in the serif only */
  line-height: 1.12;
  text-wrap: balance;
}

a { color: var(--accent); text-underline-offset: 0.18em; }
a:hover { text-decoration-thickness: 2px; }

strong { font-weight: 600; }

/* ------------------------------------------------------------ brush rule ---
   The app's brushed hairline draws a line with a brush's variation instead of a ruler's: it
   drifts and thins along its length. The gradient is the fade and is the whole
   effect on its own; the mask adds the drift and is layered on only where it is
   supported, so a browser without masking gets a clean hairline rather than a
   2px bar. */

.rule {
  height: 1px;
  border: 0;
  margin: 0;
  background: linear-gradient(
    90deg,
    rgba(0, 0, 0, 0) 0%,
    var(--rule) 12%,
    var(--rule) 88%,
    rgba(0, 0, 0, 0) 100%
  );
}

@supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
  .rule {
    height: 3px;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 3' preserveAspectRatio='none'%3E%3Cpath d='M0 1.6 C 150 1.1 300 2.1 450 1.5 S 750 1.0 900 1.7 S 1100 1.4 1200 1.6' stroke='%23000' stroke-width='1' fill='none'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 3' preserveAspectRatio='none'%3E%3Cpath d='M0 1.6 C 150 1.1 300 2.1 450 1.5 S 750 1.0 900 1.7 S 1100 1.4 1200 1.6' stroke='%23000' stroke-width='1' fill='none'/%3E%3C/svg%3E");
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
  }
}

/* ---------------------------------------------------------------- header --- */

header.site {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  align-items: center;
  justify-content: space-between;
  max-width: var(--wide);
  margin: 0 auto;
  padding: 1.25rem var(--gutter) 1rem;
}

/* The header logo is the app's painted wordmark — the one Home shows.
   It held the four-wedge mark beside "Fairhand" set in this page's own font
   until 2026-09-15, which was a reasonable stand-in and was not the logo. */
.wordmark {
  display: inline-block;
  line-height: 0;
  text-decoration: none;
}

.wordmark img {
  display: block;
  height: 1.75rem;
  width: auto;
}

/* The word is painted art, so a hover cannot recolour it the way it recoloured
   type. A small lift instead, and nothing on a touch screen that has no hover. */
.wordmark { transition: opacity 0.16s ease; }
.wordmark:hover { opacity: 0.78; }
.wordmark:focus-visible { outline: 2px solid var(--accent); outline-offset: 5px; border-radius: 4px; }

/* ⚠️ The `.mark` rule that sized the four wedges in the header is gone with the
   markup that used it. The MARK ITSELF is not retired — it is still the favicon
   (mark.svg), it is still what FairhandMark draws in the app, and it is still
   the idea the brand encodes: four different people, one plan. It simply is not
   what the header shows any more. `.wedge` below still sizes the marks the
   homepage draws for people. */

/* ⚠️ The HOMEPAGE has no header nav as of 2026-09-16 — it carries the wordmark
   alone, and its footer carries the three links. This still styles the nav on
   /support, /privacy and /terms, which keep theirs: those are reference pages
   people move between, `aria-current` marks which one they are on, and there is
   no design cost to a utility header. The wordmark links home from every page
   either way.

   The three PATHS are unaffected by any of this and must stay that way — they
   are linked from inside the app itself, and a dead legal link is a rejection. */
nav.site {
  display: flex;
  gap: 1.25rem;
  font-size: 0.95rem;
}

nav.site a {
  color: var(--slate);
  text-decoration: none;
}

nav.site a:hover,
nav.site a[aria-current="page"] {
  color: var(--ink);
  text-decoration: underline;
}

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

.hero {
  max-width: var(--wide);
  margin: 0 auto;
  padding: clamp(2.5rem, 8vw, 4.5rem) var(--gutter) clamp(2rem, 5vw, 3rem);
}

/* Wide screens get the mark beside the pitch rather than a column of nothing.
   It is the same four wedges as the header, drawn large: the one image in the
   hero is the household, which is what the sentence beside it is about. */
/* ⚠️ The hero's second column used to be the four-wedge mark, and the rules for
   it are gone with the markup. They are worth a note because one of them was a
   trap: `.hero > *:not(.hero-mark) { grid-column: 1 }` pinned every other child
   into column one, which was correct when the mark WAS the second column and
   fatal once the second column became the phone. home.css carried an override
   with the :not() repeated exactly, because a lighter selector loses to it — and
   that override is gone too, now that there is nothing to override. The hero's
   two-column behaviour is set in home.css and only there. */

.hero h1 {
  font-size: clamp(2.4rem, 8vw, 5rem);
  margin: 0 0 0.4rem;
  max-width: 13ch;
}

.lede {
  font-size: clamp(1.08rem, 2.1vw, 1.28rem);
  line-height: 1.55;
  color: var(--ink);
  max-width: 42ch;
  margin: 0 0 1.75rem;
}

/* -------------------------------------------------------------------- cta ---
   Shaped like the App Store badge slot it will become. It is not that badge
   today because the app is not on the App Store today, and a badge linking to a
   404 is the one thing a store badge must never do. */

.cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.9rem 1.25rem;
}

.badge {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  min-height: 3.25rem;
  padding: 0.5rem 1.4rem;
  border-radius: 10px;
  background: var(--ink);
  color: var(--paper);
  text-decoration: none;
  line-height: 1.25;
}

.badge .badge-small {
  font-size: 0.72rem;
  color: var(--paper);
  opacity: 0.82;
}

.badge .badge-large {
  /* SemiBold, like every other cut of the display face on this site. A regular
     weight was cut and shipped for this one word and then dropped: 17 KB and a
     third font request to set "iPhone" slightly lighter is not a trade worth
     making on a page most people reach over cellular. */
  font-family: "Fairhand Display", Georgia, serif;
  font-weight: 600;
  font-size: 1.2rem;
  letter-spacing: -0.01em;
}

.badge:hover { background: var(--accent); color: var(--on-accent); }
.badge:hover .badge-small { color: var(--on-accent); }

.cta-note {
  font-size: 0.95rem;
  color: var(--slate);
  margin: 0;
  max-width: 26ch;
}

/* ------------------------------------------------------------------ week ---
   Seven dinners is what the app makes, so seven dinners is the argument. */

.week {
  max-width: var(--wide);
  margin: 0 auto;
  padding: clamp(2rem, 5vw, 3.25rem) var(--gutter);
}

.section-head {
  max-width: 46ch;
  margin: 0 0 2rem;
}

.section-head h2 {
  font-size: clamp(1.6rem, 3.4vw, 2.15rem);
  margin: 0 0 0.6rem;
}

.section-head p {
  color: var(--slate);
  margin: 0;
}

.days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: clamp(0.6rem, 1.4vw, 1.1rem);
  margin: 0;
  padding: 0;
  list-style: none;
}

.days li { margin: 0; }

.day {
  display: block;
  font-size: 0.78rem;
  color: var(--slate);
  margin: 0 0 0.4rem;
}

/* The dish keeps a paper field in BOTH themes. The art is painted in five
   colours on bone, two of which disappear on a dark ground — so the tile is the
   paper, and in dark mode the page reads as prints pinned to a wall. */
.dish {
  display: block;
  aspect-ratio: 1;
  border-radius: 14px;          /* DS.Radius.card, scaled to this drawing size */
  background: #fcf8ef;
  border: 1px solid var(--rule);
  overflow: hidden;
}

.dish img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.dish-name {
  display: block;
  font-size: 0.88rem;
  line-height: 1.25;
  color: var(--accent);
  margin: 0.55rem 0 0;
  letter-spacing: 0;
}

.week-note {
  color: var(--slate);
  font-size: 0.95rem;
  margin: 1.75rem 0 0;
  max-width: 52ch;
}

/* ---------------------------------------------------------------- points ---
   Ruled sections on the page, not cards floating above it. */

.points {
  max-width: var(--wide);
  margin: 0 auto;
  padding: clamp(1rem, 3vw, 2rem) var(--gutter) clamp(2rem, 5vw, 3.5rem);
  display: grid;
  gap: 0;
  /* Two columns, not three: four points in a 2x2 read as a set, and three
     columns orphans the fourth on a row of its own.
     `min(24rem, 100%)` rather than a bare `24rem`, because a bare minmax
     minimum is a floor the track cannot go under — on a 320px phone each
     column stayed 384px wide and pushed the whole page sideways. */
  grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
  column-gap: clamp(1.5rem, 4vw, 3.5rem);
}

.points article {
  padding: 1.75rem 0;
  border-top: 1px solid var(--rule);
}

.points h2 {
  font-size: 1.3rem;
  margin: 0 0 0.5rem;
}

.points p {
  margin: 0;
  color: var(--slate);
  max-width: 44ch;
}

.points .wedge-row { margin: 0 0 0.9rem; }

/* A row of household wedges, drawn from the app's own shape. The outline is
   painted per colour, not per wedge — see --edge-member-*. */
.wedge path { stroke-width: 2.4; vector-effect: non-scaling-stroke; }

/* A row of household wedges, drawn from the app's own shape. */
.wedge-row {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}

.wedge {
  width: 2.4rem;
  height: 2.4rem;
  flex: none;
  display: block;
}

/* -------------------------------------------------------------- closing --- */

.closing {
  max-width: var(--wide);
  margin: 0 auto;
  padding: clamp(2rem, 5vw, 3.5rem) var(--gutter);
}

.closing h2 {
  font-size: clamp(1.7rem, 4vw, 2.5rem);
  margin: 0 0 0.75rem;
  max-width: 18ch;
}

.closing p { color: var(--slate); max-width: 44ch; margin: 0 0 1.75rem; }

/* --------------------------------------------------------------- footer --- */

footer.site {
  max-width: var(--wide);
  margin: 3rem auto 0;
  padding: 1.75rem var(--gutter) 3rem;
  position: relative;
  color: var(--slate);
  font-size: 1.0625rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
  align-items: baseline;
}

/* ⚠️ The hairline is INSET to the text column, not a border on the footer box.
   The box is the page's outer column and the gutter is its padding, so a border
   on the box is a gutter wider than the words beneath it at every width — and on
   a phone, where the column is the whole screen, it ran edge to edge while every
   section above it stopped at the gutter. That is the one place the page's spine
   was visibly broken. */
footer.site::before {
  content: "";
  position: absolute;
  inset: 0 var(--gutter) auto;
  border-top: 1px solid var(--rule);
}

footer.site p { margin: 0; }

/* No underline on the three document links. They are navigation rather than
   references inside a sentence, and an underline on a line of three separated by
   middots reads as clutter. The hover still moves — colour, and the underline
   arriving — so the affordance is not lost, it is just not the resting state. */
footer.site a { color: var(--slate); text-decoration: none; }
footer.site a:hover { color: var(--ink); text-decoration: underline; }

/* ------------------------------------------------------- document pages ---
   Support, privacy and terms. The words on privacy and terms have been through
   several honesty passes and are not touched here — only how they are set. */

/* The document keeps a prose measure but hangs off the same left gutter as the
   wordmark above it, so the page has one spine rather than a centred column
   floating under a left-aligned masthead. */
.doc {
  max-width: var(--wide);
  margin: 0 auto;
  padding: clamp(1.5rem, 4vw, 2.5rem) var(--gutter) 1rem;
}

.doc > * { max-width: var(--measure); }

.doc h1 {
  font-size: clamp(2rem, 5.5vw, 2.9rem);
  margin: 0 0 0.4rem;
}

.doc .updated {
  color: var(--slate);
  font-size: 0.92rem;
  margin: 0 0 2rem;
}

/* Sections are separated by the same brushed rule the rest of the site uses,
   drawn as the heading's own line so the two treatments cannot drift apart. */
.doc h2 {
  font-size: 1.35rem;
  margin: 2.75rem 0 0.6rem;
  padding-top: 1.6rem;
  position: relative;
}

.doc h2::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    rgba(0, 0, 0, 0) 0%,
    var(--rule) 8%,
    var(--rule) 92%,
    rgba(0, 0, 0, 0) 100%
  );
}

@supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
  .doc h2::before {
    height: 3px;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 3' preserveAspectRatio='none'%3E%3Cpath d='M0 1.6 C 150 1.1 300 2.1 450 1.5 S 750 1.0 900 1.7 S 1100 1.4 1200 1.6' stroke='%23000' stroke-width='1' fill='none'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 3' preserveAspectRatio='none'%3E%3Cpath d='M0 1.6 C 150 1.1 300 2.1 450 1.5 S 750 1.0 900 1.7 S 1100 1.4 1200 1.6' stroke='%23000' stroke-width='1' fill='none'/%3E%3C/svg%3E");
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
  }
}

/* A question inside "Common questions" sits under it, not beside it. */
.doc h3 {
  font-size: 1.1rem;
  margin: 2rem 0 0.5rem;
  line-height: 1.3;
}

.doc h2 + h3 { margin-top: 1.25rem; }

.doc p,
.doc li { color: var(--ink); }

.doc p { margin: 0 0 1rem; }

.doc ul {
  padding-left: 1.15rem;
  margin: 0 0 1.25rem;
}

.doc li { margin: 0 0 0.5rem; }

.doc li::marker { color: var(--slate); }

/* PaintedWash: pigment loaded at the leading edge, a thin wash across the body.
   Used where the app uses it — to stop someone without becoming a plastic card. */
.callout {
  position: relative;
  margin: 0 0 2rem;
  padding: 1.1rem 1.25rem 1.1rem 1.5rem;
  border-left: 3.5px solid var(--accent);
  background: linear-gradient(
    90deg,
    rgba(var(--accent-rgb), var(--wash-a)) 0%,
    rgba(var(--accent-rgb), var(--wash-b)) 55%,
    rgba(var(--accent-rgb), var(--wash-c)) 86%,
    rgba(var(--accent-rgb), 0) 100%
  );
}

.callout p:last-child { margin-bottom: 0; }

/* --------------------------------------------------------- small screens ---
   Most traffic arrives from a phone, so this is the case that has to be right
   rather than the one that has to survive. */

@media (max-width: 60rem) {
  .days { grid-template-columns: repeat(4, 1fr); }
  .days li:nth-child(n + 5) { grid-column: span 1; }
}

@media (max-width: 34rem) {
  :root { --gutter: 1.15rem; }

  .days {
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem 0.9rem;
  }

  .hero h1 { max-width: none; }
  .lede { max-width: none; }
  .badge { flex: 1 1 auto; }
  .cta-note { max-width: none; }

  footer.site { display: block; }
  footer.site p + p { margin-top: 0.5rem; }

  /* A phone is the case this page has to be right on, and on a phone the whole
     hero was taller than the screen — so the first dinner, which is the fastest
     answer to "what is this", sat entirely below the fold. Everything below is
     the same layout with the air taken out of it, worth about 120px, which is
     the difference between a strip of dinners you can see and one you have to
     be told about. */
  .hero { padding-top: 1.75rem; padding-bottom: 1.25rem; }
  .lede { font-size: 1.02rem; margin-bottom: 1.25rem; }
  .cta { gap: 0.7rem 1rem; }
  .cta-note { font-size: 0.9rem; }
  .week { padding-top: 1.5rem; }
  .section-head { margin-bottom: 1.25rem; }
}

/* ------------------------------------------------------------- printing --- */

@media print {
  body::before { display: none; }
  body { background: #fff; color: #000; }
  header.site nav, .cta { display: none; }
  a { color: #000; }
  .doc { max-width: none; }
}

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

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
