/* ==========================================================================
   Diora site stylesheet. THE single copy.

   Loaded by every page in the v2 family:
     /v2, /privacy, /terms, /delete-account, /release-notes, /support

   NOT loaded by /index.html, which is the older live homepage and keeps its
   own inlined design. Class names here overlap with that page's (.brand,
   .nav-cta, .eyebrow); nothing collides because it never links this file.
   If you promote v2 to /, port index.html onto this sheet deliberately
   rather than letting the two designs meet by accident.

   LAYOUT ORIGIN: the v2 page is a one-to-one adoption of the founder's
   concept (pill nav with two dropdowns, centred hero with an <em>, a 4-card
   feature grid with phone mockups bleeding off the card's bottom edge, a
   3-card grid, a green CTA block with two store buttons, a one-line footer).
   The layout, radii, spacing rhythm and 720px breakpoint are the concept's
   and must not be "improved". Only content, colour, typography and branding
   are Diora's.

   Deliberate deviations are marked DEVIATION below.

   Self-contained: no CDN, no remote fonts, no external request. Every asset
   referenced already ships in landing/.
   ========================================================================== */

@font-face{
  font-family:'Bricolage';
  src:url('/fonts/BricolageGrotesque_700Bold.ttf') format('truetype');
  font-weight:700; font-style:normal; font-display:swap;
}

:root {
  /* Diora tokens (DESIGN.md / constants/colors.ts) in the concept's slots. */
  --green: #1F3D2A;        /* accent. the single brand colour */
  --green-deep: #162C1E;   /* accent, pressed */
  --green-soft: #DDE6D9;   /* accent tint for eyebrow / icon wells */
  /* accent-2 from DESIGN.md, the warm ochre. It carries the SOLD status inside
     the app and is otherwise reserved for "a warm complement alongside the
     forest green". The v2 hero wash is the only thing on the site that uses it;
     nothing else here should reach for it as a general-purpose colour. */
  --accent2: #D4954D;
  --nav-bg: #FAF7F0;       /* surface. never pure white (DESIGN.md) */
  --page-bg: #F0EBE0;      /* base. warm linen */
  --card-bg: #E8E0CD;      /* secondary-bg. the deeper linen */
  --ink: #2A2620;          /* warm near-black */
  --muted: #655F53;        /* ink-secondary. warm taupe-grey */
  --border: #E3DCC9;
  --hover: #EDE6D6;        /* nav / dropdown hover well */
  --frame: #D3CAB6;        /* phone frame chrome */
  --on-accent: #FAF7F0;
  --radius-nav: 999px;
  --radius-card: 28px;

  --sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --display: 'Bricolage', var(--sans);
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; color-scheme: light; -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--sans);
  background: var(--page-bg);
  color: var(--ink);
  padding: 18px;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

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

/* DEVIATION: the concept's CTAs are <button>s that do nothing. They are <a>s
   here so the page works. Same box, same classes, same position. */
a.nav-cta, a.btn-primary, a.btn-ghost, a.store-btn { text-decoration: none; }

/* ---------- SKIP LINK ----------
   Off-screen until focused, so it costs the layout nothing. Carried over from
   the pre-v2 legal pages; removing it on the restyle would have been an
   accessibility regression on the longest documents on the site. */
.skip {
  position: absolute; left: 18px; top: -56px; z-index: 100;
  background: var(--green); color: var(--on-accent);
  padding: 10px 18px; border-radius: 999px;
  font-size: 14px; font-weight: 600; text-decoration: none;
  transition: top .15s ease;
}
.skip:focus { top: 18px; }

/* ---------- NAV ---------- */
nav {
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  background: var(--nav-bg);
  border-radius: var(--radius-nav);
  display: flex;
  align-items: center;
  gap: 40px;
  padding: 12px 12px 12px 26px;
  box-shadow: 0 2px 14px rgba(31,61,42,0.06);
}

.brand {
  display: flex;
  align-items: center;
}
/* The concept's brand is a green dot plus the word. Diora ships the WORDMARK
   ALONE in that slot: the leaf mark immediately followed by a wordmark opening
   on the same D read as a stutter, so the mark is gone (founder call). The
   wordmark keeps the concept's brand position and scale.

   The lockup is a link to the site root. It must inherit the brand's treatment
   and nothing else, so no underline, no colour, and the anchor is sized to the
   wordmark's own bounds (inline-flex, no padding) so it cannot swallow taps
   meant for the hamburger or the links beside it. */
.brand .brand-link {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: inherit;
  border-radius: 6px;
}
/* Founder, 2026-07-27: "make the logo bigger, by about 30-40%". 20px -> 26px
   is +30%, the value in that range that keeps the nav on ONE row at 390px
   (the wordmark is 431x104, so every 1px of height costs 4.1px of width, and
   the mobile row's budget is the tightest constraint on this page).

   The pill does NOT grow: its height is set by .nav-cta's 40px content box on
   desktop and .nav-toggle's 44px on mobile, both still taller than 26px, so
   the concept's vertical rhythm and padding are untouched.

   The -3px inset cancel scales with the glyph, so it goes to -4px at this
   size (-3 x 26/20 = -3.9). */
.brand .wordmark {
  height: 26px;
  width: auto;
  margin-left: -4px;   /* cancel the glyph's own left inset */
}

.nav-links {
  display: flex;
  gap: 6px;
  list-style: none;
  align-items: center;
}
.nav-links > li { position: relative; }
.nav-links a,
.nav-links .drop-trigger {
  text-decoration: none;
  color: var(--muted);
  font-size: 15px;
  font-weight: 500;
  padding: 9px 16px;
  border-radius: 999px;
  transition: background .18s, color .18s;
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.nav-links a:hover,
.nav-links .drop-trigger:hover { color: var(--ink); background: var(--hover); }
/* Marks the current page. On /v2 that is the Home item; on the document pages
   it is the matching entry inside a dropdown, which this selector also
   reaches because .dropdown sits inside .nav-links. */
.nav-links a.active { color: var(--green); font-weight: 650; }

.drop-trigger .chev {
  width: 12px; height: 12px;
  transition: transform .2s;
}

/* dropdown panel */
.dropdown {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 190px;
  background: var(--nav-bg);
  border-radius: 16px;
  box-shadow: 0 8px 28px rgba(31,61,42,.12);
  padding: 8px;
  list-style: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity .18s, transform .18s, visibility .18s;
  z-index: 10;
}
.has-dropdown:hover .dropdown,
.has-dropdown:focus-within .dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.has-dropdown:hover .drop-trigger .chev,
.has-dropdown:focus-within .drop-trigger .chev {
  transform: rotate(180deg);
}
.dropdown a {
  display: block;
  width: 100%;
  padding: 10px 14px;
  border-radius: 10px;
  font-size: 14.5px;
  color: var(--ink);
}
.dropdown a:hover { background: var(--hover); }

.nav-cta {
  background: var(--green);
  color: var(--on-accent);
  border: none;
  font-size: 15px;
  font-weight: 600;
  padding: 11px 22px;
  border-radius: 999px;
  cursor: pointer;
  transition: transform .12s, background .18s;
}
.nav-cta:hover { background: var(--green-deep); transform: translateY(-1px); }

a:focus-visible, button:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

/* ---------- HERO MESH WASH ----------
   SCOPE, read this first. Every rule in this block is a descendant of
   `.hero-bg`, and `.hero-bg` is used on exactly ONE element on the whole site:
   the wrapper around the hero in v2.html. /privacy, /terms, /delete-account,
   /release-notes and /support load this same sheet and none of them contains
   the class, so the wash cannot reach them. That is structural, not luck: no
   rule below is bare, and none touches `.hero`, `nav`, `body` or a token that
   another page reads. If you ever need the wash somewhere else, add the class
   there deliberately rather than loosening a selector here.
   (/index.html is the old homepage and does not link this sheet at all.)

   WHAT IT IS. Ochre light entering from the top right, forest settling into the
   left flank and the bottom right, over the linen page. The centre of the box
   is deliberately left near-clean so the headline keeps its ink.

   FOUR DEPARTURES from the draft this started as, all deliberate:

   1. NO `min-height: 100vh`. The hero is content-height and always has been
      (96px/72px of padding). A viewport-height hero would push the feature
      cards below the fold and restage the whole page, which is not what a
      background change should do. The wash covers the hero's own box.
   2. NO `background-attachment: fixed`. Known repaint jank on iOS Safari, and
      this page is read on phones more than anywhere else. It scrolls normally.
   3. IT FADES TO `--page-bg`, NOT to `--nav-bg`. The draft resolved to #FAF7F0,
      which is the SURFACE colour (nav pill, quote cards, paper). The page under
      the hero is #F0EBE0 warm linen. Ending on the surface cream would have
      left a visible band exactly at the hero's bottom edge. The fade is done by
      the first background layer below, which is opaque `--page-bg` at both
      ends, so the join is not "close", it is the same colour.
   4. THE GRAIN IS MUCH LIGHTER. See the ::after note.

   CONTRAST. `--ink`, `--green` and `--muted` all stayed. The draft put dark
   forest at the TOP of the box, which is where the headline sits: near-black on
   #2f5238 is about 1.5:1 and the green <em> would have disappeared into its own
   background. Rather than flip the hero to light type, the wash is aimed so the
   text corridor stays light. Two reasons that is the right trade here: the page
   already spends its one dark-green moment on the CTA block, and doubling it in
   the hero would cost that block its impact; and the <em> is the only piece of
   colour in the headline, so it has to keep `--green` or the emphasis the
   founder kept becomes decoration.

   MEASURED, not estimated. Every ratio below is the WORST single pixel found
   behind that text run, sampled from the real rendered page at 320, 390, 719,
   721 and 1280, by rendering the hero twice (once with the type painted in
   marker colours to locate the glyph pixels, once with the type hidden to read
   the true backdrop) and taking the minimum:

     h1   `--ink`     9.19:1   worst, 11.3 to 11.6 median   AA large needs 3.0
     em   `--green`   8.59:1   worst,  9.37 to 9.57 median  AA large needs 3.0
     p    `--muted`   4.73:1   worst,  5.11 to 5.15 median  AA body  needs 4.5

   Re-measure if you move a colour stop. The paragraph is the tight one and is
   the only real constraint on this whole block: `--muted` starts at just
   5.33:1 on bare linen, so the wash has under half a stop to spend behind it,
   which is why the centre corridor is kept clean and the colour lives at the
   flanks and the corners. */
.hero-bg {
  position: relative;
  /* Contains the ::after grain's blending. Without it, `mix-blend-mode` would
     composite against whatever is painted behind this wrapper instead of
     against the wash. Keep it. */
  isolation: isolate;
  /* Full bleed out of body's 18px padding, cancelled by equal padding so the
     hero's content box is byte-for-byte where it was. Not `100vw`, which
     overshoots by the scrollbar width and would cause a horizontal scroll. */
  margin-left: -18px;
  margin-right: -18px;
  padding-left: 18px;
  padding-right: 18px;
  /* The wash's own two colours, derived from palette tokens so they follow the
     palette if it moves. `--green` is #1F3D2A, a very dark, low-chroma forest:
     laid over warm linen at a low alpha it does NOT read as green, it reads as
     grey smudge (this was tried first and looked like smog). Lifting it toward
     `--green-soft` keeps the chroma while raising the lightness, so the tint
     reads as sage rather than dirt. The ochre needs no lifting; it is already
     a mid-light warm hue and sits on linen cleanly. */
  --wash-sage: color-mix(in oklab, var(--green) 22%, var(--green-soft));
  --wash-ochre: var(--accent2);
  background:
    /* LAYER 1, the seam killer, painted on top of the mesh. Opaque --page-bg at
       both ends resolves the first 16% and the last 26% of the box to exactly
       the page colour, so the wash has no top edge under the nav pill and no
       bottom edge against the feature grid. Everything below is only ever seen
       through the transparent middle of this layer. */
    linear-gradient(
      to bottom,
      var(--page-bg) 0%,
      transparent 16%,
      transparent 74%,
      var(--page-bg) 100%
    ),
    /* LAYER 2, ochre light entering top right. This is the loudest blob and it
       sits in the UPPER band on purpose. Two reasons. The headline has enormous
       contrast headroom (`--ink` needs only 3:1 as large display type, so it
       tolerates a backdrop down to about #6B6A5F), while the paragraph has
       almost none (`--muted` starts at 5.33:1 on bare linen and hits the 4.5:1
       floor by about #DFDACD). And the <em> is forest green, so putting the
       WARM colour behind it is what keeps it legible as a separate colour: a
       green blob there would have flattened the emphasis to nothing. */
    radial-gradient(
      52% 44% at 95% 15%,
      color-mix(in oklab, var(--wash-ochre) 52%, transparent) 0%,
      transparent 68%
    ),
    /* LAYER 3, a second, quieter ochre low on the left, so the warmth is a
       diagonal through the box rather than one corner glow. Its vertical
       radius is deliberately short (24%) and it is anchored ON the bottom
       edge: at the 719px breakpoint the headline wraps to four lines, which
       pushes the paragraph down, and a taller blob here was the one thing
       reaching up far enough to eat the paragraph's contrast margin. */
    radial-gradient(
      40% 24% at 2% 100%,
      color-mix(in oklab, var(--wash-ochre) 32%, transparent) 0%,
      transparent 68%
    ),
    /* LAYER 4, sage on the left flank, held HIGH (34%) so it sits beside the
       headline and has dissolved by the time the paragraph starts. */
    radial-gradient(
      40% 34% at -4% 34%,
      color-mix(in oklab, var(--wash-sage) 82%, transparent) 0%,
      transparent 70%
    ),
    /* LAYER 5, sage answering it on the bottom right, under the button row.
       The buttons carry their own opaque fills, so colour is free here. */
    radial-gradient(
      46% 38% at 102% 86%,
      color-mix(in oklab, var(--wash-sage) 72%, transparent) 0%,
      transparent 68%
    ),
    /* LAYER 6, the base cast: a sage lift down the diagonal that gives the
       blobs something to sit in rather than floating on flat linen. */
    linear-gradient(
      170deg,
      color-mix(in oklab, var(--wash-sage) 34%, transparent) 0%,
      transparent 58%
    );
  /* No background-color, so the bottom of the stack IS the body's --page-bg.
     The fade target and the page are therefore the same colour by construction
     and cannot drift apart if the palette moves. */
}

/* GRAIN. An inline SVG turbulence tile, so it costs no request and nothing is
   fetched cross-origin.

   z-index -1 is what puts it BEHIND the hero content and IN FRONT of the wash:
   a negative-z child paints above its parent's own background but below the
   parent's in-flow children. Together with pointer-events none it can never
   take a tap or a text selection.

   OPACITY. The draft asked for 0.45 with `overlay`. Over a light ground that
   read as dirt, not texture: overlay pushes both ways from mid grey, so on
   linen it mostly darkens, and at 0.45 the whole hero went grey-green and the
   paragraph lost most of its contrast headroom. Settled at 0.16, judged on a
   3x magnified crop against 0.00 / 0.10 / 0.18 / 0.30: 0.10 was invisible at
   1x, 0.30 speckled and read as dirt, 0.16 is a fine paper tooth measuring
   about 2 levels of deviation. It also does real work, breaking up the
   gradient banding an 8-bit display shows across a field this wide and flat.
   It is not free: the darkest grain trough costs the paragraph about 0.4 of
   its contrast ratio (5.13:1 without grain, 4.73:1 with). That is measured
   against a single worst noise pixel, not the local average a reader
   perceives, so it is the pessimistic number and it still clears AA.

   The mask fades the tile out at the same two ends as the wash. Without it the
   texture stops on a hard line even though the colour underneath has already
   dissolved, which is the exact seam this whole block exists to avoid. */
.hero-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.16;
  mix-blend-mode: overlay;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 18%, #000 72%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 18%, #000 72%, transparent 100%);
}

/* ---------- HERO ---------- */
.hero {
  text-align: center;
  max-width: 820px;
  margin: 0 auto;
  padding: 96px 20px 72px;
}
.eyebrow {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--green);
  background: var(--green-soft);
  padding: 7px 15px;
  border-radius: 999px;
  margin-bottom: 26px;
}
.hero h1 {
  font-family: var(--display);
  font-size: clamp(44px, 7.5vw, 84px);
  line-height: 1.02;
  font-weight: 700;   /* DEVIATION: the concept sets 820. Bricolage ships here
                         as a static 700 face; anything higher synthesises. */
  letter-spacing: -0.035em;
}
.hero h1 em {
  font-style: normal;
  color: var(--green);
}
.hero p {
  margin: 26px auto 0;
  max-width: 560px;
  font-size: 19px;
  line-height: 1.55;
  color: var(--muted);
}
.hero-actions {
  margin-top: 34px;
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}
/* DEVIATION (founder, 2026-07-27): the concept's hero held ONE logo-less
   button. Diora ships both stores in the hero, and the founder asked why the
   top buttons had no store logos when the bottom ones did. So the hero pair
   now carries the SAME inline Apple and Android glyphs as .store-btn, at the
   same 20px size and 10px gap, and the two rows read as one family. The hero
   keeps its own button treatment (solid + ghost) rather than becoming
   .store-btn, because that treatment is the concept's. */
.btn-primary, .btn-ghost {
  font-size: 16px;
  font-weight: 600;
  padding: 14px 28px;
  border-radius: 999px;
  cursor: pointer;
  border: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  transition: transform .12s, background .18s, box-shadow .18s;
}
.btn-primary svg, .btn-ghost svg { width: 20px; height: 20px; flex-shrink: 0; }
.btn-primary { background: var(--green); color: var(--on-accent); }
.btn-primary:hover { background: var(--green-deep); transform: translateY(-1px); }
.btn-ghost { background: var(--nav-bg); color: var(--ink); box-shadow: inset 0 0 0 1px var(--border); }
.btn-ghost:hover { box-shadow: inset 0 0 0 1px #CFC5AC; }

/* ---------- CARDS ---------- */
.cards {
  max-width: 1000px;
  margin: 0 auto 40px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 24px;
}
.card {
  background: var(--card-bg);
  border-radius: var(--radius-card);
  padding: 34px 34px 0;
  overflow: hidden;               /* clips the mockup at the bottom edge */
  display: flex;
  flex-direction: column;
  min-height: 420px;
}
/* The concept sets an icon tile beside the h3. The paragraph belongs in the
   SAME text column as that h3, not back at the icon's left edge, so the h3 and
   the p are wrapped in .card-copy and the whole copy block is the flex item
   next to the tile. align-items moves to flex-start so the tile stays level
   with the heading rather than centring against the taller block. */
.card-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
.card-copy { min-width: 0; }
.card .icon {
  width: 52px; height: 52px;
  flex-shrink: 0;
  border-radius: 14px;
  background: var(--nav-bg);
  display: flex; align-items: center; justify-content: center;
  color: var(--green);
  box-shadow: 0 2px 8px rgba(31,61,42,.08);
}
.card .icon svg { width: 26px; height: 26px; }
.card h3 {
  font-family: var(--display);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
}
.card p {
  margin-top: 12px;
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--muted);
}
/* phone mockup that bleeds off the bottom edge of the card.
   The concept's 230px was sized for a wireframe. A real screenshot wants the
   width, so the frame goes to 320px (about three quarters of the card's 420px
   inner width at the 1000px grid). max-width keeps it inside the column on the
   one-card mobile layout, where the card is narrower than 320px. The border,
   radius, padding and -30px bleed are the concept's, untouched. */
.mockup {
  margin: 30px auto -30px;
  width: 320px;
  max-width: 100%;
  background: var(--nav-bg);
  border: 9px solid var(--frame);
  border-bottom: none;
  border-radius: 34px 34px 0 0;
  padding: 16px 14px 0;
  box-shadow: 0 -2px 20px rgba(42,38,32,.05);
}
.mockup .notch {
  width: 74px; height: 6px; border-radius: 999px;
  background: var(--border); margin: 0 auto 16px;
}
/* The concept fills the frame with grey wireframe rows. Diora fills it with a
   real screenshot. The frame's width, border, radius and bleed are unchanged;
   the screenshots are 760x1651, so the card grows past its 420px MINIMUM to
   hold one, and the bleed clips the last 30px. */
.mockup .screen {
  border-radius: 10px 10px 0 0;
  overflow: hidden;
  background: var(--page-bg);
}
.mockup .screen img { width: 100%; height: auto; }

/* ---------- FOOTER ---------- */
footer {
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  padding: 40px 0 12px;
}
footer a { color: var(--muted); }

/* ---------- TESTIMONIALS ----------
   The concept's grid, card shape, spacing and heading position are the
   concept's. The QUOTES are real and verbatim, sourced from
   docs/marketing/user-testimonials.md, which carries binding rules:

     - verbatim, trimmed only by taking a contiguous span. No rewording, no
       typo fixes ("adds" below is the reviewer's spelling), emoji kept.
     - the two Reddit quotes are attributed GENERICALLY. No invented names,
       initials, cities or handles.
     - the App Store handle may be named, because that review is public.
     - NO star ratings and NO install counts anywhere on the page. Deliberate:
       too early to flatter, let the quotes carry it. So the concept's
       .stars row is DELETED rather than restyled, and nothing stands in for
       it. Do not reinstate five-star rows here.

   .avatar therefore holds a neutral quote glyph, never a person's initials. */
/* Founder, 2026-07-27: wrap the heading AND the three quote cards in a
   container built like the .cta block below, but with no fill and a 1px
   border in the feature cards' background colour.

   So this borrows .cta's container treatment EXACTLY: the same 1000px
   max-width, the same `auto` centring, the same 32px radius and the same
   72px/32px padding. The only departures are the two the founder asked for,
   and they are the whole point of the block:
     - background stays TRANSPARENT, so the linen page shows through
     - a 1px border in var(--card-bg), the same token .card fills with, so the
       stroke and the feature cards above it can never drift apart

   The stroke is deliberately quiet: --card-bg (#E8E0CD) on --page-bg
   (#F0EBE0) is a near-tone hairline, not a hard rule. That is what asking for
   the cards' fill colour buys.

   OUTER rhythm is unchanged, and measured: the 40px top margin collapses
   against .cards' 40px bottom margin exactly as it did before, so the gap to
   the feature grid is still 40px, and the gap to the CTA is still the 72px
   .cta sets on itself. The block is taller than it was because the padding is
   now the CTA's, which is what makes the two read as siblings. */
.testimonials {
  max-width: 1000px;
  margin: 40px auto 0;
  padding: 72px 32px;
  background: transparent;
  /* 3px, not 1px: `--card-bg` is the feature cards' fill, which sits close in
     tone to the linen page, so a hairline in it read as almost invisible.
     Founder asked for +2px on 2026-07-27. Keep the COLOUR tied to the same
     token the cards fill with, so the two move together if the palette does. */
  border: 3px solid var(--card-bg);
  border-radius: 32px;
}
.testimonials h2 {
  text-align: center;
  font-family: var(--display);
  font-size: clamp(30px, 4.5vw, 44px);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
  margin-bottom: 44px;
}
.quote-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
.quote-card {
  background: var(--nav-bg);
  border-radius: 22px;
  padding: 30px 28px;
  display: flex;
  flex-direction: column;
  box-shadow: 0 2px 14px rgba(31,61,42,.05);
}
.quote-card blockquote {
  font-size: 16.5px;
  line-height: 1.55;
  color: var(--ink);
  flex: 1;
}
.quote-card .who {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 24px;
}
.quote-card .avatar {          /* an icon well, never a person's initials */
  width: 40px; height: 40px;
  border-radius: 50%;
  background: var(--green-soft);
  color: var(--green);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.quote-card .avatar svg { width: 20px; height: 20px; }
.quote-card .name {
  font-weight: 650;
  font-size: 15px;
}
.quote-card .handle {
  font-size: 13.5px;
  color: var(--muted);
}

/* ---------- CTA ---------- */
.cta {
  max-width: 1000px;
  margin: 72px auto 0;
  background: var(--green);
  border-radius: 32px;
  text-align: center;
  padding: 72px 32px;
  color: var(--on-accent);
}
.cta h2 {
  font-family: var(--display);
  font-size: clamp(32px, 5vw, 52px);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
  max-width: 620px;
  margin: 0 auto;
}
.cta p {
  margin: 22px auto 0;
  max-width: 480px;
  font-size: 18px;
  line-height: 1.55;
  color: rgba(250,247,240,.82);
}
.cta-actions {
  margin-top: 36px;
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
}
.store-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--nav-bg);
  color: var(--green);
  font-size: 16px;
  font-weight: 650;
  padding: 15px 28px;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  transition: transform .12s, background .18s;
}
.store-btn svg { width: 20px; height: 20px; flex-shrink: 0; }

/* OPTICAL WEIGHT of the Tabler store glyphs (all four buttons, both rows).
   Tabler draws at stroke-width 2 in a 24px viewBox. Rendered into a 20px box
   that is 2 x 20/24 = 1.67px on screen, visibly lighter than the stems of the
   16px/600 label beside it, so the icon read as a faint outline rather than a
   sibling of the text. 2.4 renders at exactly 2.0px and matches. The value
   lives here, once, rather than on four inline attributes; the attributes stay
   on the SVGs as the no-CSS fallback. */
.btn-primary svg, .btn-ghost svg, .store-btn svg { stroke-width: 2.4; }
.store-btn:hover { transform: translateY(-1px); background: #F2ECE0; }
.store-btn.outline {
  background: transparent;
  color: var(--on-accent);
  box-shadow: inset 0 0 0 1.5px rgba(250,247,240,.5);
}
.store-btn.outline:hover { background: rgba(250,247,240,.1); }

/* ---------- EQUAL-WIDTH STORE BUTTON PAIRS ----------
   Founder, 2026-07-27: "make sure the buttons are equal length".

   Applies to BOTH rows, and both rows keep their own gap and margin-top from
   the rules above; only the display model changes here.

   WHY GRID AND NOT FLEX. The obvious flex answer (`width: fit-content` on the
   row, `flex: 1 1 0` on the buttons) was tried first and is WRONG. It does
   make the two widths identical, but it sizes them to the AVERAGE of the two
   labels, not to the wider one: a wrapping flex container's max-content size
   is the SUM of its items' max-content contributions, so the row resolved to
   480px and each button to 234px, while "Download for Android" needs 261px.
   The result passed an equal-width assertion and still shipped a button whose
   label wrapped onto two lines. Measuring the widths is not enough; the
   screenshot is what caught it.

   `grid-auto-columns: 1fr` under a fit-content width is the primitive that
   actually means "equal columns, sized to the WIDEST item": equal-flex grid
   tracks resolve against the largest max-content contribution rather than the
   sum. So both buttons take the wider label's width and neither wraps.

   Nothing is pinned to a pixel value, so a reworded or localised label moves
   both buttons together. Below the 720px breakpoint the pair stacks (see the
   media query). */
.hero-actions, .cta-actions {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  justify-content: center;
  width: fit-content;
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px; height: 44px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 10px;
  border-radius: 12px;
}
.nav-toggle span {
  display: block;
  height: 2px;
  width: 100%;
  background: var(--ink);
  border-radius: 2px;
  transition: transform .2s, opacity .2s;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ==========================================================================
   DOCUMENT PAGES
   /privacy, /terms, /delete-account, and the two short pages /release-notes
   and /support.

   These pages did not exist in the concept, so nothing here overrides a
   concept decision. Everything is assembled from v2's own parts: the linen
   page, the floating pill nav, the .eyebrow pill, Bricolage headings, and a
   paper card in the same surface colour and radius as .quote-card.

   The one thing a 300-line legal document needs that no v2 surface needed is
   a MEASURE. The card is capped at 720px and carries up to 56px of padding,
   so the text column lands near 70 characters at the 17px body size. Widen
   the card and the legal pages stop being readable; that cap is the point.
   ========================================================================== */
.doc {
  max-width: 1000px;
  margin: 0 auto;
  padding: clamp(56px, 8vw, 84px) 4px 0;
}
.doc-head {
  text-align: center;
  max-width: 760px;
  margin: 0 auto clamp(32px, 5vw, 44px);
}
.doc-head h1 {
  font-family: var(--display);
  font-size: clamp(36px, 6vw, 58px);
  line-height: 1.04;
  font-weight: 700;
  letter-spacing: -0.035em;
}
.doc-head .updated {
  margin-top: 18px;
  font-size: 14px;
  letter-spacing: .02em;
  color: var(--muted);
}
.doc-head .lede {
  margin: 20px auto 0;
  max-width: 52ch;
  font-size: 17.5px;
  line-height: 1.6;
  color: var(--muted);
}

/* the paper card the long copy sits on */
.prose {
  background: var(--nav-bg);
  border-radius: var(--radius-card);
  box-shadow: 0 2px 14px rgba(31,61,42,.05);
  max-width: 720px;
  margin: 0 auto;
  padding: clamp(28px, 5vw, 56px) clamp(22px, 5vw, 56px);
  font-size: 17px;
  line-height: 1.7;
  color: var(--ink);
}
.prose h2 {
  font-family: var(--display);
  font-size: clamp(20px, 2.4vw, 25px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  margin: 44px 0 14px;
}
.prose > *:first-child { margin-top: 0; }
.prose p { margin: 0 0 16px; }
.prose strong { font-weight: 650; color: var(--ink); }

/* Bulleted lists keep the green dot the pre-v2 legal pages used. It is already
   v2's accent, so it needed no recolouring. Scoped to ul > li so the ordered
   list on /delete-account keeps its NUMBERS instead of getting a dot as well,
   which is what the old sheet did to it. */
.prose ul { list-style: none; margin: 0 0 18px; padding-left: 2px; }
.prose ul > li {
  position: relative;
  margin: 0 0 11px;
  padding-left: 22px;
  line-height: 1.62;
}
.prose ul > li::before {
  content: "";
  position: absolute;
  left: 2px; top: .72em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--green);
}
.prose ol { margin: 0 0 18px; padding-left: 26px; }
.prose ol > li { margin: 0 0 11px; padding-left: 4px; line-height: 1.62; }
.prose ol > li::marker { color: var(--green); font-weight: 650; }

.prose a {
  color: var(--green);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(31,61,42,.35);
}
.prose a:hover { text-decoration-color: var(--green); }

.prose code {
  font-family: var(--mono);
  font-size: .9em;
  background: var(--card-bg);
  padding: 2px 7px;
  border-radius: 7px;
  overflow-wrap: anywhere;   /* long URLs must never widen the page */
}
.prose hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 40px 0 26px;
}
.prose .closing-note {
  color: var(--muted);
  font-size: 15.5px;
  margin-bottom: 0;
}

/* A short single-message page: /release-notes and /support. Same paper card
   and the same surface as .prose, sized to its content rather than to a
   measure, and centred because there is only ever a sentence or two. */
.panel {
  background: var(--nav-bg);
  border-radius: var(--radius-card);
  box-shadow: 0 2px 14px rgba(31,61,42,.05);
  max-width: 620px;
  margin: 0 auto;
  padding: clamp(32px, 6vw, 56px) clamp(24px, 5vw, 48px);
  text-align: center;
}
.panel p {
  margin: 0 auto;
  max-width: 46ch;
  font-size: 17px;
  line-height: 1.65;
  color: var(--muted);
}
.panel p + p { margin-top: 14px; }
.panel .mail {
  display: inline-block;
  margin-top: 28px;
  padding: 15px 28px;
  border-radius: 999px;
  background: var(--green-soft);
  color: var(--green);
  font-family: var(--display);
  font-size: clamp(19px, 3.2vw, 25px);
  letter-spacing: -0.02em;
  text-decoration: none;
  overflow-wrap: anywhere;
  transition: transform .12s, background .18s;
}
.panel .mail:hover { transform: translateY(-1px); background: #D2DECD; }
.panel .fineprint {
  margin-top: 26px;
  font-size: 14.5px;
  color: var(--muted);
}

/* The document pages have no CTA block, so the one-line footer would otherwise
   sit right against the card. */
.doc + footer { padding-top: 56px; }

@media (max-width: 720px) {
  .cards { grid-template-columns: 1fr; }
  .quote-grid { grid-template-columns: 1fr; }

  nav {
    width: 100%;
    flex-wrap: wrap;
    border-radius: 24px;
    padding: 14px 16px 14px 22px;
    row-gap: 4px;
    /* the concept's 40px column gap pushes "Get the app" onto its own row on a
       normal phone. column-gap only, so the concept's 4px row-gap survives for
       the links panel that opens underneath. */
    column-gap: 12px;
  }
  .brand { margin-right: auto; }
  .nav-toggle { display: flex; order: 2; }
  .nav-cta { order: 3; }

  .nav-links {
    order: 4;
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    max-height: 0;
    overflow: hidden;
    transition: max-height .28s ease;
  }
  .nav-links.open { max-height: 460px; padding-top: 8px; }

  .nav-links > li { width: 100%; }
  .nav-links a,
  .nav-links .drop-trigger {
    width: 100%;
    justify-content: space-between;
    padding: 12px 16px;
    font-size: 16px;
  }

  /* dropdowns become inline accordions on mobile */
  .dropdown {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    box-shadow: none;
    padding: 2px 0 6px 14px;
    min-width: 0;
    max-height: 0;
    overflow: hidden;
    transition: max-height .24s ease;
  }
  .has-dropdown.open .dropdown { max-height: 160px; }
  .has-dropdown.open .drop-trigger .chev { transform: rotate(180deg); }
  /* disable the hover-open behavior on touch layout */
  .has-dropdown:hover .dropdown,
  .has-dropdown:focus-within .dropdown { max-height: 0; }
  .has-dropdown.open:hover .dropdown,
  .has-dropdown.open .dropdown { max-height: 160px; }

  .prose h2 { margin-top: 36px; }

  /* Stack the store pair rather than squeeze it. Side by side, each button
     would fall under its own label width here and the text would wrap to two
     lines. Stacked they are trivially equal (both are the single column's
     full width) and the row reads as deliberate. The 340px cap stops a
     full-width button looking stretched on a 719px tablet. */
  .hero-actions, .cta-actions {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    width: 100%;
    max-width: 340px;
  }
}

/* On a narrow phone the 52px tile plus its 16px gap eats too much of the copy
   column, so the tile moves ABOVE the copy instead of beside it. The title and
   the paragraph stay in one shared column either way, which is the alignment
   that matters. */
@media (max-width: 480px) {
  .card { padding: 28px 24px 0; }
  .card-head { flex-direction: column; gap: 14px; }
  .prose { font-size: 16.5px; }

  /* At 320px the row is 244px wide. "Download for Android" plus a 20px glyph
     needs the trim to stay on ONE line inside it. */
  .btn-primary, .btn-ghost, .store-btn { padding: 14px 20px; font-size: 15px; }

  /* The CTA and the testimonials box carry 72px/32px, which at 320px left the
     CTA's buttons only 220px and wrapped "Download for Android" onto a second
     line. Trimmed on the SMALL breakpoint only: the concept's 72px/32px is
     untouched everywhere it was designed for. The two stay in lockstep because
     they are now sibling containers. */
  .cta, .testimonials { padding: 56px 20px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation: none !important; transition: none !important; }
}
