/* ============================================================================
   Kove landing page

   The tokens below are MIRRORED from web/src/index.css, which stays the single
   styling authority for the product. This page adds no colour, no radius step
   and no third elevation: if a value here disagrees with the app, the app is
   right and this file is stale.

   Two rules from DESIGN.md are load-bearing here and easy to undo by accident:

   1. The No Overlay Rule. No whole-viewport wash, gradient, glow, vignette or
      grain, however subtle. iOS paints its own strips adjacent to the page and
      any full-page film seams against them as a visible colour band.
   2. The Rationed Lamp Rule. One lit thing per view. The filled lime button is
      that thing. The receipt's lime is a 5% wash and a 30% border, which is how
      the app itself lets a receipt chip and a primary button share a screen.

   The hairline rules and the frozen left rail are the page's structure, not
   decoration: they are the notation of the record Kove keeps, and they organise
   real content. That is the one sanctioned reason to draw them.
   ============================================================================ */

/* ---------------------------------------------------------------- fonts */
/* Self-hosted variable fonts, copied from the app's own @fontsource packages.
   Nothing loads from a CDN, here or in the app. */
@font-face {
  font-family: "Inter Variable";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("./fonts/inter.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk Variable";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("./fonts/space-grotesk.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono Variable";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("./fonts/jetbrains-mono.woff2") format("woff2");
}

/* ---------------------------------------------------------------- tokens */
:root {
  /* colour, mirrored from web/src/index.css */
  --background: #0a0b0a;
  --foreground: #f2f4f2;
  --card: #171917;
  --panel: #1e211e;
  --accent: #242824;
  --muted: #151715;
  --muted-foreground: #8e968f;
  --primary: #7ffa88;
  --primary-foreground: #07130a;
  --destructive: #f25e50;
  --border: #343933;
  --input: #4a514a;
  --selected: #233425;
  --selection: #335f36;

  /* shape: 12px base, laddered */
  --radius: 0.75rem;
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);

  /* type */
  --font-display: "Space Grotesk Variable", "Inter Variable", ui-sans-serif, system-ui, sans-serif;
  --font-sans: "Inter Variable", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono Variable", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* the pointer-density ramp, stepped once below */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;

  /* motion: three curves, ease-in deliberately absent */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  /* elevation: exactly two steps exist in the system; this page needs one */
  --shadow-resting: inset 0 1px 0 0 color-mix(in oklab, var(--foreground) 5%, transparent),
    0 1px 2px 0 rgb(0 0 0 / 0.4), 0 12px 32px -16px rgb(0 0 0 / 0.55);

  /* layout */
  --sheet: 1152px; /* the Data grid's own column width, since this page is a sheet */
  /* One reading column for every band below the hero: prose, the day fold, the offer chip,
     the plate, the form. 31rem and not 33 because the prose it governs is 16px now, and
     measured across the six real paragraphs 33rem ran 65-79 characters a line while this
     runs 61-74. Both ends matter differently: past ~75 the eye loses the line on its
     return sweep, whereas short of 65 is merely brisk, so the safer error is narrow. */
  --measure: 31rem;
  --gutter: 1rem;
  --rail-w: 5.5rem;
  --band-y: clamp(3.5rem, 7vw, 6.5rem);
}

/* The Pointer-Density Rule. Density is a property of the pointer, never the
   viewport: one ramp, stepped once, so every role moves by the same +2px and
   the body floor lands on 16px, which is the size below which iOS Safari zooms
   the page on input focus. This is why the email input is a plain --text-sm. */
@media (pointer: coarse) {
  :root {
    --text-xs: 0.875rem;
    --text-sm: 1rem;
  }
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The header is sticky, so it covers the top of whatever an anchor scrolls to. One
     declaration on the scroll container fixes every in-page link at once, which is the
     reason to put it here rather than a scroll-margin on each target. The value is the
     header's own height (.topbar > .sheet plus its safe-area padding), so it stays right
     on a notched phone.

     It was measured as invisible at 1440px, where the hero's 64px of padding happens to
     be one pixel taller than the header, and as a 25px bite out of the headline at 390px
     and 530px, where that padding steps down to 40px. A defect that only appears on
     narrow windows is the kind a desktop review cannot see.

     The 1px is the header's bottom border, and it is not pedantry: without it every
     anchor lands exactly one pixel high, which measured as a 1px sliver of #waitlist
     sitting behind the bar. */
  scroll-padding-top: calc(4rem + 1px + env(safe-area-inset-top));
}

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* The canvas stays absolutely flat. See the No Overlay Rule above. */
}

::selection {
  /* Desktop only in practice: iOS ignores ::selection for regular text and
     there is no CSS fix for it. Documented in the app, not a bug here. */
  background: var(--selection);
  color: var(--foreground);
}

img,
svg {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
}

.nums {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  padding: 0.5rem 0.75rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
}
.skip:focus {
  left: var(--gutter);
  top: var(--gutter);
}

:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* Reduced motion means gentler, not none: travel and scale collapse, the
   change of state still reads. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 120ms !important;
  }
  .answer[data-entering] {
    animation: none !important;
  }
}

/* ---------------------------------------------------------------- the sheet */
.sheet {
  width: 100%;
  max-width: var(--sheet);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.band > .sheet {
  display: grid;
  grid-template-columns: 1fr;
  padding-block: var(--band-y);
}

/* One hairline per band boundary, spanning the sheet. Not per row. */
.band + .band > .sheet,
.footer > .sheet {
  border-top: 1px solid var(--border);
}

.rail {
  display: none;
}

.cells {
  min-width: 0;
}

@media (min-width: 768px) {
  .band > .sheet,
  .footer > .sheet {
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
  }
  /* The frozen leading column, as the grid has one. It carries a row label
     where the band is about a day, and otherwise only the rule. */
  .rail {
    display: block;
    border-right: 1px solid var(--border);
    padding-right: 1rem;
  }
  .cells {
    padding-left: clamp(1.25rem, 3vw, 2.5rem);
  }
}

/* The sprite itself never renders. Taken out of flow rather than display:none, which is
   the older and safer of the two ways to hold <symbol> definitions. */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* What the rail carries instead of a word. It used to say "talking", "growing", "yours",
   "new" in mono, and the owner's read was that small text hanging off the left of every
   band is noise rather than notation.

   MUTED, all four, including the one that used to be lime. The rail is structure, the same
   register as the hairline beside it, and one accented glyph among four would read as a
   state rather than a label: as a WORD, lime was a label colour, but as a mark it is the
   app's own "something happened" signal, which is not what a section heading is. The lamp
   in each band stays the thing you can act on. */
.rail-glyph {
  display: block;
  margin-left: auto;
  width: 1rem;
  height: 1rem;
  color: var(--muted-foreground);
  /* Dropped onto the heading beside it. The rail, the cells and the h2 all begin on the
     same line, so an unshifted glyph sits level with the top of the heading's LINE BOX,
     which is above its letters: at 1.5rem / 1.15 the box is 27.6px, the cap band runs from
     3.5px to 20.3px inside it, and the glyph is 16px.

     Measured, not derived, because the offset depends on a font metric CSS does not expose
     (line-box centring would be 5.8px and lands 1.9px BELOW the cap, which reads as low).
     4px is within a third of a pixel of both readings of "aligned": the cap band's centre
     wants 3.9px and resting the glyph's bottom on the first baseline wants 4.3px, and the
     baseline is what the phone's inline copy does via align-items: baseline. Re-measure
     against the heading's cap band if the h2's size or line-height ever moves. */
  margin-top: 0.25rem;
}

/* ---------------------------------------------------------------- type */
h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin: 0;
}

h1 {
  /* Display: 3rem, down to 2.25rem on a small screen. Both ends are the ramp's
     own steps, so the landing hero uses the size the app's hero moments use. */
  font-size: clamp(2.25rem, 5vw, 3rem);
  line-height: 1.05;
}

h2 {
  /* Headline, flat at 24px. A section heading's hierarchy is carried by the
     display face and its weight, not by reaching for a size off the ramp. */
  font-size: 1.5rem;
  line-height: 1.15;
  margin-bottom: 0.75rem;
}

h3 {
  font-size: 1.125rem;
  line-height: 1.25;
}

/* A sub-heading INSIDE a band, for a second thought that does not deserve its own band
   and its own rail label. The gap above is what says "new thought" (2.5rem, well clear of
   the 1.5rem the band's own blocks sit at) and the tight gap below is what binds it to
   the paragraph it introduces. .cells is a plain block, so this margin collapses against
   its neighbours exactly as the band's other blocks do. */
.sub {
  margin: 2.5rem 0 0.5rem;
}

/* THE PHONE'S copy of the rail mark, on every band heading (owner, 2026-08-20). On a
   desktop the rail carries these and they are hidden here; below 768px there is no rail, so
   each mark rides its own heading. Same symbol, same band, one place at a time.

   MUTED, and that is the part worth defending, because it took the lime off the chat mark
   that used to be the page's one accented heading. Lime was arguable while this was the ONLY
   marked heading and the app paints that exact glyph lime for the current Chat tab. Four of
   them is a different proposition: on a phone each band is roughly its own view, and band 5
   would then put a lime mail glyph directly above a filled lime button, band 4 a lime glyph
   over the fold's lime date, band 3 over an offer chip whose header is already primary. The
   Rationed Lamp Rule spends a view's one lit thing on what you can act on, so the marks are
   structure in both places and the accent stays on the button.

   16px, the size this system draws a Lucide glyph at when it sits inside something else.

   It hangs in its own column to the LEFT of the heading rather than sitting inside the
   text flow, so a heading that wraps keeps both of its lines aligned to one edge instead
   of the second line starting under the icon. That is what the flex row is for, and
   `align-items: baseline` places the glyph's bottom edge on the first line's baseline,
   which lands it on the cap height without a magic number to maintain.

   Signal Lime, on the owner's call (2026-08-20). It is worth knowing what that bends:
   the Rationed Lamp Rule gives a view one lit thing, and this band has no action, so by
   the letter of the rule a lime glyph here is decoration. What argues for it is that the
   app paints this exact glyph lime when the Chat tab is the current one, so it reads as
   the product's own mark for the conversation. It stays a 16px stroke and never a fill,
   which is what keeps it from competing with the filled CTA.

   The width and height are ALSO attributes on the element, and that is not redundancy.
   An inline SVG carrying only a viewBox has no intrinsic size, so the base `svg` rule's
   `max-width: 100%` lets it expand to the whole column: measured at 1424px across when
   this stylesheet was merely late, which is what a stale cache looks like. Attributes
   bound it before CSS is involved. */
h2:has(> .h2-glyph) {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

h2 > .h2-glyph + span {
  min-width: 0;
}

.h2-glyph {
  width: 1rem;
  height: 1rem;
  color: var(--muted-foreground);
  flex: none;
}

/* One mark, one place. As soon as there is a rail to hold it (768px, where .rail stops
   being display:none) the heading hands it over and reverts to a plain block, identical to
   the three headings that never carried one. The flex row above exists only for the phone,
   where there is no rail and the glyph sits inline with the text.

   This block MUST stay after the two rules it overrides. Both selectors are repeated
   verbatim, so specificity ties and source order decides; sitting in the layout media
   query further up the file, it lost to `display: flex` and did nothing at all. That is the
   second time this stylesheet has done exactly that (see .topbar-cta). */
@media (min-width: 768px) {
  h2:has(> .h2-glyph) {
    display: block;
  }
  h2 > .h2-glyph {
    display: none;
  }
}

.lede {
  font-size: var(--text-base);
  line-height: 1.55;
  color: var(--foreground);
  max-width: 26rem;
  margin: 1rem 0 0;
}

/* ONE prose treatment for every band. There used to be two, and the split followed no
   role: the two bands with a single paragraph got 16px at --foreground, while the four
   paragraphs in the record and waitlist bands got 14px at --muted-foreground. Same job,
   two weights, and the weaker one landed on the band carrying the trust argument.

   Measurement decided it, not taste. At 14px the wide column ran 73-81 characters a line,
   past the 65-75 the craft floor asks for, so that treatment was not merely quieter: it was
   small type on over-long lines, which is what "too small" actually feels like. At 16px the
   SAME column runs about 70, dead centre. The old lead's narrower 30rem was therefore
   solving the wrong problem, preserving the character count of a measure that was already
   too wide, and it cost the page its single right edge, since prose sat at 30rem while the
   day fold, the plate and the form all sat at --measure.

   Colour follows the app, where primary prose is text-foreground and --muted-foreground is
   secondary. Band prose is primary; the mono captions and the footer's fine print are what
   stay muted, so the quiet register still means something.

   --text-base and not --text-sm deliberately: base does not step on a coarse pointer, so
   this changes nothing on a phone (--text-sm already resolved to 16px there) and fixes only
   the desktop, which is where the two treatments visibly disagreed. */
.body {
  font-size: var(--text-base);
  color: var(--foreground);
  max-width: var(--measure);
  margin: 0 0 1.5rem;
  line-height: 1.6;
}
/* What .body-lead's margin-bottom: 0 was for, generalised: a band's last paragraph adds no
   trailing space, and every other one collapses against whatever follows it. */
.body:last-child {
  margin-bottom: 0;
}

/* Captions stay MUTED and stay mono: they are the one secondary register left now that band
   prose is all --foreground, and they label a thing rather than argue anything.

   Capped to --measure like everything else in a band. Every caption on the page is short
   enough to fit today, so this changes nothing visible; without it the shared right edge is
   true by luck rather than by construction, and the first caption long enough to wrap would
   run past the prose it sits under. */
.caption {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  max-width: var(--measure);
  margin: 1rem 0 0;
  line-height: 1.5;
}

/* ---------------------------------------------------------------- topbar */
/* Solid, and the page's only separator here is the hairline under it. It was 88% opaque
   over a blur(12px), which this canvas gives nothing back for: the background is a
   dead-flat single colour, so the blur spent a GPU pass a frame compositing #0a0b0a
   against #0a0b0a, and at 88% the near-black content ghosting through was invisible
   anyway. The app reached the same conclusion for the gym dock (Training.tsx) and its own
   desktop header carries no background or blur at all, just `border-b`.

   It also removes a risk this codebase has already paid for once: iOS paints the
   status-bar strip ADJACENT to the page, no page layer can cover it, and a translucent
   blurred layer against that strip seams as a visible colour band. This element is the
   one that borders it (note the safe-area padding), so translucency here was the exact
   configuration DESIGN.md's canvas ruling warns about. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--background);
  border-bottom: 1px solid var(--border);
  padding-top: env(safe-area-inset-top);
}

.topbar > .sheet {
  height: 4rem; /* 64px, inside the 80px cap, one line at every width */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  min-width: 0;
}

/* The mark is the app's icon FILE, so all this rules is the box it sits in: the artwork
   draws the tile and the letter.

   The radius restates the SVG's own corner as a proportion (rx="96" on a 512 viewBox =
   18.75%), which is what keeps the beacon ring tracing the artwork's edge instead of the
   element's square box, at whatever size the tile is ever set to. It was drawn in CSS at
   var(--radius-xl) before, and that is worth recording because it did not look like a
   token being slightly off: 16px of radius on a 24px box exceeds the side, so CSS scales
   both corners down to 12px and the mark rendered as a perfect CIRCLE, which is the one
   shape Kove's identity is not.

   The beacon is the app's own brand-mark treatment (.beacon in web/src/index.css) and the
   one place on this page a glow is allowed to exist. */
.mark {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 18.75%;
  flex: none;
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--primary) 35%, transparent),
    0 8px 30px -8px color-mix(in oklab, var(--primary) 55%, transparent);
}

.wordmark-text {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.125rem; /* Title tier */
  letter-spacing: -0.02em;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* Two segments, pill-shaped, marked the way the app marks a current tab:
   the label goes primary over Selected Tint, never a filled lime. */
.langswitch {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  border: 1px solid var(--border);
  border-radius: 9999px;
  background: color-mix(in oklab, var(--card) 60%, transparent);
}

.lang {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--muted-foreground);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 0.25rem 0.625rem;
  border-radius: 9999px;
  cursor: pointer;
  transition: color 150ms var(--ease-out), background-color 150ms var(--ease-out);
}
.lang:hover {
  color: var(--foreground);
}
.lang[aria-pressed="true"] {
  background: var(--selected);
  color: var(--primary);
}

@media (max-width: 26.25rem) {
  /* A 420px phone keeps the wordmark and the language on one comfortable line by
     dropping the header's button, which the hero repeats immediately below it.
     Two class names, not one: .btn sets its own display later in this file, so at
     equal specificity source order was winning and this rule did nothing. */
  .topbar-right .topbar-cta {
    display: none;
  }
}

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: 2.25rem; /* 36px comfortable: a primary CTA */
  padding-inline: 1rem;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 150ms var(--ease-out), border-color 150ms var(--ease-out),
    color 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.btn:active {
  transform: scale(0.98);
}

.btn-primary {
  background: var(--primary);
  color: var(--primary-foreground);
}
.btn-primary:hover {
  background: color-mix(in oklab, var(--primary) 90%, transparent);
}

.btn-outline {
  background: color-mix(in oklab, var(--input) 30%, transparent);
  border-color: var(--input);
  color: var(--foreground);
}
.btn-outline:hover {
  background: var(--accent);
}

.btn-sm {
  height: 2rem; /* 32px compact: secondary and toolbar actions */
  padding-inline: 0.75rem;
  font-size: var(--text-xs);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

.quiet-link {
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
  padding-bottom: 1px;
  transition: color 150ms var(--ease-out), border-color 150ms var(--ease-out);
}
.quiet-link:hover {
  color: var(--foreground);
  border-color: var(--input);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.5rem;
  margin-top: 2rem;
}

/* ---------------------------------------------------------------- hero */
.band-hero > .sheet {
  padding-top: clamp(2.5rem, 5vw, 4rem);
}

.hero {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
  align-items: start;
}

@media (min-width: 62rem) {
  .hero {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    align-items: center;
  }
}

/* ---------------------------------------------------------------- the demo */
.demo {
  min-width: 0;
}

/* The chips lead, because they are the six things a chat can be asked to do and
   they have to be readable without tapping anything. */
.chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding-bottom: 1.25rem;
}

/* One turn of a conversation: the ask on the right the way a person's own message
   sits, the answer on the left. Enough to read as a chat at a glance, which is
   the entire claim of the page. */
.thread {
  display: grid;
  gap: 0.875rem;
}

.said {
  justify-self: end;
  max-width: 88%;
  margin: 0;
  background: var(--accent);
  border-radius: var(--radius-lg);
  border-end-end-radius: var(--radius-sm);
  padding: 0.625rem 0.875rem;
  font-size: var(--text-sm);
  line-height: 1.5;
}

.answer {
  display: grid;
  gap: 0.75rem;
  min-width: 0;
}

.answer[data-entering] {
  animation: receipt-in 200ms var(--ease-out);
}

/* A turn that consulted the record says so, muted and never in the accent: the
   accent marks something created or changed, and a read created nothing. */
.provenance {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

.reply {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.6;
  max-width: 40rem;
}

/* What the app leaves in the thread when a turn created or set something: a mono
   label and the thing's name. No fill, so it never competes with the CTA. */
.marker {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}

.marker-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  flex: none;
}

.marker-value {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--primary);
  min-width: 0;
}

.chip {
  appearance: none;
  border: 1px solid transparent;
  background: var(--accent);
  color: var(--muted-foreground);
  border-radius: 9999px;
  padding: 0.25rem 0.75rem;
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
  transition: color 150ms var(--ease-out), background-color 150ms var(--ease-out);
}
.chip:hover {
  color: var(--foreground);
}
/* Tint plus mark: the selected chip takes Selected Tint and its label goes
   primary. Never a lime fill, which would read as an action. */
.chip[aria-pressed="true"] {
  background: var(--selected);
  color: var(--primary);
}

/* The receipt chip, to the app's own spec: 5% lime wash, 30% lime border,
   12px radius, resting elevation, values in mono at body size. */
.receipt {
  background: color-mix(in oklab, var(--primary) 5%, var(--card));
  border: 1px solid color-mix(in oklab, var(--primary) 30%, transparent);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-resting);
  padding: 1rem;
}

@keyframes receipt-in {
  from {
    opacity: 0;
    transform: scale(0.985);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.receipt-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.75rem;
}

.receipt-title {
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--foreground);
}

.receipt-date {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
}

.pill {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  background: color-mix(in oklab, var(--background) 45%, transparent);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.375rem 0.625rem;
}

.pill dt {
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

.pill dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.receipt-note {
  margin: 0.875rem 0 0;
  padding-top: 0.875rem;
  border-top: 1px solid color-mix(in oklab, var(--primary) 18%, transparent);
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  line-height: 1.55;
}
.receipt-note:empty {
  display: none;
}

/* The offer chip, to the app's spec: an offer wears a NEUTRAL dashed frame on a card
   wash, and the accent rides the header rather than a fill. That is what separates an
   offer from an action in this system, and it is why nothing here is a filled button.
   The pills stay neutral too, because an offered field has not been chosen yet, and
   Selected Tint would say it had. */
.thread-tight {
  gap: 0.625rem;
  margin-top: 1.5rem;
  max-width: var(--measure);
}

.offer {
  border: 1px dashed var(--border);
  background: color-mix(in oklab, var(--card) 40%, transparent);
  border-radius: var(--radius-lg);
  padding: 0.875rem 1rem 1rem;
}

.offer-head {
  margin: 0 0 0.75rem;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--primary);
}

.offer-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.offer-pill {
  background: var(--accent);
  border-radius: 9999px;
  padding: 0.25rem 0.75rem;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--foreground);
}

/* ---------------------------------------------------------------- ledger */
.day {
  max-width: var(--measure);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  overflow: hidden;
}

.day > summary {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.875rem 1rem;
  cursor: pointer;
  list-style: none;
  transition: background-color 150ms var(--ease-out);
  min-height: 2.75rem;
}
.day > summary::-webkit-details-marker {
  display: none;
}
.day > summary:hover {
  background: color-mix(in oklab, var(--accent) 40%, transparent);
}
.day > summary:active {
  background: color-mix(in oklab, var(--accent) 60%, transparent);
}
.day[open] > summary {
  background: var(--selected);
}

.day-date {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  white-space: nowrap;
}
.day[open] .day-date {
  color: var(--primary);
}

.day-peek {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.day[open] .day-peek {
  opacity: 0;
}

/* A chevron drawn from a border, not an icon file: two edges of a square,
   rotated. Nothing in the system needs an SVG for this. */
.day-toggle {
  width: 0.5rem;
  height: 0.5rem;
  border-right: 1.5px solid var(--muted-foreground);
  border-bottom: 1.5px solid var(--muted-foreground);
  transform: rotate(45deg);
  transition: transform 200ms var(--ease-out);
  margin-right: 2px;
}
.day[open] .day-toggle {
  transform: rotate(-135deg);
}

.day-body {
  padding: 0 1rem 1.25rem;
  display: grid;
  gap: 1rem;
}

.day-said {
  display: grid;
  gap: 0.375rem;
}
.day-said p {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--foreground);
  line-height: 1.5;
}

.cells-out {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
  padding-top: 0.25rem;
}

.cell {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  background: color-mix(in oklab, var(--background) 45%, transparent);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.375rem 0.625rem;
}
.cell dt {
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}
.cell dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.day-note {
  margin: 0;
  display: grid;
  gap: 0.25rem;
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  line-height: 1.55;
  border-top: 1px solid var(--border);
  padding-top: 0.875rem;
}
.day-note-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

/* ---------------------------------------------------------------- plate */
.plate {
  margin: 1.5rem 0 0;
  min-width: 0;
  max-width: var(--measure);
}

/* A reserved plate, not a fake screenshot and not a hole. An empty cell is
   ordinary in a ledger, so it is drawn as one and it states what belongs in it.
   The dimensions are inside the well because the person filling it needs them. */
.plate-well {
  border: 1px dashed var(--input);
  border-radius: var(--radius-lg);
  background: var(--muted);
  display: grid;
  place-items: center;
  padding: 1rem;
}

.plate-slug {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  text-align: center;
  line-height: 1.6;
}
.plate-slug .nums {
  display: block;
  color: var(--input);
}

.plate-well {
  aspect-ratio: 16 / 10;
  /* Kept short on purpose: it is the one thing on the page still waiting for its
     content, and the live example day above it already makes the same argument. */
  max-height: 16rem;
}

.plate .caption {
  margin-top: 0.75rem;
  max-width: 28rem;
}

/* ---------------------------------------------------------------- waitlist */
.band-close > .sheet {
  padding-bottom: clamp(4rem, 8vw, 7rem);
}

/* The waitlist form moved to Tally, so the email field, its states and the status line
   that reported the POST are all gone with it. What is left in that band is one .btn. */

/* ---------------------------------------------------------------- footer */
.footer > .sheet {
  padding-block: clamp(2rem, 4vw, 3rem);
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .footer > .sheet {
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
  }
}

.footer-cells {
  display: grid;
  gap: 0.625rem;
  padding-bottom: env(safe-area-inset-bottom);
}

.footer-tag {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-base);
  letter-spacing: -0.02em;
  max-width: 28rem;
}

.footer-fine {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  max-width: 34rem;
  line-height: 1.55;
}
/* The credit is a NAME, so it sits at full --foreground rather than at the muted colour
   of the fine print around it. That is the app's own answer for the same line
   (`text-foreground underline` in Auth.tsx and Settings.tsx), and this rule had drifted
   off it: the link rested at --muted-foreground and only reached foreground on hover,
   which on a phone is a state nobody ever sees.

   The rule under it then has to firm up too. Left at --border (1.50:1) beneath white
   text it reads as a rendering artifact rather than as a link, so it rests at --input,
   the tier this system reserves for a boundary that must be identifiable, and comes up
   to the word itself on hover. That hover is the one this link has: the text cannot get
   any brighter. */
.footer-fine a {
  color: var(--foreground);
  text-decoration: underline;
  text-decoration-color: var(--input);
  text-underline-offset: 2px;
  transition: text-decoration-color 150ms var(--ease-out);
}
.footer-fine a:hover {
  text-decoration-color: currentColor;
}
