/* Backfeed site — one shared stylesheet.
   No build step, no external fonts, no external requests. System fonts,
   one accent, light + dark via prefers-color-scheme with a manual override. */

:root {
  color-scheme: light dark;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  /* Light theme (default) */
  --bg: #faf9f7;
  --surface: #ffffff;
  --surface-2: #f2f0ea;
  --text: #1c1b19;
  --text-dim: #57544d;
  --text-faint: #85817a;
  --border: #e5e1d9;
  --border-strong: #d5d0c6;
  --accent: #0c7c6f;
  --accent-ink: #0a5c52;
  --accent-soft: #e2f1ee;
  --accent-contrast: #ffffff;
  --shadow: 0 1px 2px rgba(28, 27, 25, 0.04), 0 8px 24px rgba(28, 27, 25, 0.06);

  --maxw: 1210px;
  --maxw-text: 660px;
  --radius: 14px;
  --radius-sm: 9px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1312;
    --surface: #171c1b;
    --surface-2: #1d2422;
    --text: #e9eae7;
    --text-dim: #a3a8a3;
    --text-faint: #767c77;
    --border: #29302e;
    --border-strong: #353d3a;
    --accent: #34d3bf;
    --accent-ink: #5ee0cf;
    --accent-soft: #143430;
    --accent-contrast: #06231f;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 10px 30px rgba(0, 0, 0, 0.35);
  }
}

/* Manual override wins over the media query in both directions. */
:root[data-theme="light"] {
  --bg: #faf9f7;
  --surface: #ffffff;
  --surface-2: #f2f0ea;
  --text: #1c1b19;
  --text-dim: #57544d;
  --text-faint: #85817a;
  --border: #e5e1d9;
  --border-strong: #d5d0c6;
  --accent: #0c7c6f;
  --accent-ink: #0a5c52;
  --accent-soft: #e2f1ee;
  --accent-contrast: #ffffff;
  --shadow: 0 1px 2px rgba(28, 27, 25, 0.04), 0 8px 24px rgba(28, 27, 25, 0.06);
}

:root[data-theme="dark"] {
  --bg: #0f1312;
  --surface: #171c1b;
  --surface-2: #1d2422;
  --text: #e9eae7;
  --text-dim: #a3a8a3;
  --text-faint: #767c77;
  --border: #29302e;
  --border-strong: #353d3a;
  --accent: #34d3bf;
  --accent-ink: #5ee0cf;
  --accent-soft: #143430;
  --accent-contrast: #06231f;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 10px 30px rgba(0, 0, 0, 0.35);
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.62;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* --- Layout primitives --- */

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 40px);
}

.prose { max-width: var(--maxw-text); }

section { padding: clamp(48px, 8vw, 88px) 0; }
section + section { padding-top: 0; }

/* --- Type --- */

h1, h2, h3 { line-height: 1.15; letter-spacing: -0.02em; font-weight: 640; }
h1 { font-size: clamp(2.2rem, 6vw, 3.4rem); margin: 0 0 0.4em; }
h2 { font-size: clamp(1.5rem, 4vw, 2.05rem); margin: 0 0 0.6em; }
h3 { font-size: 1.14rem; letter-spacing: -0.01em; margin: 0 0 0.35em; }

p { margin: 0 0 1.05em; }
a { color: var(--accent-ink); text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent); text-underline-offset: 2px; }
a:hover { color: var(--accent); }

.lede { font-size: clamp(1.15rem, 2.4vw, 1.38rem); color: var(--text-dim); line-height: 1.5; }
.eyebrow {
  font-size: 0.78rem; font-weight: 660; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--accent-ink); margin: 0 0 0.9em;
}
.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.mono { font-family: var(--font-mono); font-size: 0.92em; }

.tag {
  display: inline-block; font-size: 0.7rem; font-weight: 640;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-faint);
  border: 1px dashed var(--border-strong); border-radius: 999px;
  padding: 2px 9px; vertical-align: middle;
}

/* --- Header --- */

.site-header {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  -webkit-backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--border);
}
.site-header .wrap {
  display: flex; align-items: center; gap: 18px;
  height: 60px;
}
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  font-weight: 680; font-size: 1.12rem; letter-spacing: -0.02em;
  color: var(--text); text-decoration: none;
}
.brand:hover { color: var(--text); }
.brand .mark { width: 22px; height: 22px; display: block; }
.nav { margin-left: auto; display: flex; align-items: center; gap: clamp(12px, 3vw, 26px); }
.nav a {
  color: var(--text-dim); text-decoration: none; font-size: 0.96rem;
  font-weight: 520;
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--text); }

.theme-toggle {
  appearance: none; background: transparent; border: 1px solid var(--border-strong);
  color: var(--text-dim); border-radius: 8px; width: 34px; height: 34px;
  cursor: pointer; display: inline-grid; place-items: center; padding: 0;
  line-height: 0;
}
.theme-toggle:hover { color: var(--text); border-color: var(--text-faint); }
.theme-toggle svg { width: 17px; height: 17px; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun,
:root:not([data-theme]) .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon,
:root:not([data-theme]) .theme-toggle .icon-moon { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .theme-toggle .icon-sun { display: none; }
  :root:not([data-theme]) .theme-toggle .icon-moon { display: block; }
}
:root[data-theme="light"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: block; }

/* --- Hero --- */

/* The phone shot sits IN the hero rather than in a section below it. Stacked,
   the copy's right half was empty for the whole height of the fold, and the
   shots sat under an 88px section gap plus the row's own centring void — the
   page opened on more emptiness than content. */
.hero { padding-top: clamp(40px, 6vw, 72px); padding-bottom: clamp(12px, 1.6vw, 22px); }
.hero-grid {
  display: grid;
  --hero-gap: clamp(20px, 2.2vw, 34px);
  gap: var(--hero-gap);
  /* The empty third track is the MacBook frame's own lid margin — see the
     .hero-phone note below. It is a track and not padding because .hero-grid
     IS the .wrap element: a padding-right here would replace the page gutter
     and resolve against the section, not against the wrap. A track percentage
     resolves against the grid's content box, which is exactly the width the
     mac image fills. The gap comes off because the grid puts one between the
     phone and the spacer. */
  grid-template-columns: minmax(0, 1.42fr) minmax(0, 0.58fr)
                         max(0px, calc(7.667% - var(--hero-gap)));
  align-items: center;
}
/* The phone is the only thing in the right column, so the grid gap is the only
   thing setting its distance from the copy — and at clamp(28,4vw,56) that read
   as a hole rather than a gutter beside a mac that is wider than both. */
.hero h1 { max-width: 14ch; }
.hero .lede { max-width: 46ch; margin-bottom: 1.8em; }

/* The phone goes beside the copy and the mac goes full width below, because
   the two have opposite needs. A phone shot is tall and narrow — it fills the
   hero's empty half at a size where its chunky UI still reads. The mac shot is
   the opposite: at 1760px source, readable text needs ~1050px of display
   width, so beside the copy it renders at ~5px and is decorative at best.
   Measured, not guessed (2026-08-05). */
/* The two frames carry different margins INSIDE the PNG, so filling their
   boxes is not enough to line them up. Apple's MacBook lid occupies x=115..1384
   of a 1500px image — 7.667% transparent on each side — while the iPhone body
   runs to x=2 of 460, effectively flush. Both images filling their boxes
   therefore left the phone overhanging the mac's right edge by the whole
   difference, ~87px at full width. The grid's spacer track reproduces the
   mac's margin and the phone pins to it, so the two devices share one right
   edge to within a pixel. Measured off the shipped PNGs, 2026-08-05. */
.hero-phone { display: flex; justify-content: flex-end; }
.hero-phone .shot-phone { width: 100%; max-width: 265px; }

.macstrip { padding-top: clamp(8px, 1vw, 16px); }
.mac-figure { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; }

/* Apple's own App Store badge, unmodified, per the Marketing Resources and
   Identity Guidelines: black lockup on light backgrounds, white on dark, and
   the artwork is never recoloured or redrawn. 48px tall clears Apple's 40px
   digital minimum, and the link gets 10% of the badge height as clear space
   on every side, which is the guideline's other hard requirement. */
.badge-link { display: inline-block; line-height: 0; padding: 5px; margin: -5px; }
.badge { display: block; width: auto; height: 48px; }
/* Same visibility dance the theme toggle uses, so the two can never disagree
   about which appearance is live. */
.badge-wht { display: none; }
:root[data-theme="dark"] .badge-wht,
:root:not([data-theme]) .badge-wht { display: block; }
:root[data-theme="dark"] .badge-blk,
:root:not([data-theme]) .badge-blk { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .badge-wht { display: none; }
  :root:not([data-theme]) .badge-blk { display: block; }
}
:root[data-theme="light"] .badge-wht { display: none; }
:root[data-theme="light"] .badge-blk { display: block; }
.badge-note { font-size: 0.84rem; color: var(--text-faint); }

/* --- Buttons / links --- */

.btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 580; font-size: 0.98rem; text-decoration: none;
  padding: 11px 20px; border-radius: 10px; border: 1px solid transparent;
  transition: transform 0.06s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); color: var(--accent-contrast); }
.btn-primary:hover { background: var(--accent-ink); color: var(--accent-contrast); }
.btn-ghost { border-color: var(--border-strong); color: var(--text); }
.btn-ghost:hover { border-color: var(--text-faint); color: var(--text); }

/* --- Device frames --- */

/* The screenshots are composited into Apple's own product bezels (see
   apple-assets/), so the frame IS the image. Every scrap of the old CSS
   costume — dashed placeholder border, hatch fill, surface background,
   rounded clipping, drop shadow — now fights the PNG instead of helping it,
   because the bezel already carries the rounding, the bevel and the shadow,
   and the area around the device is transparent. */
/* Two stacked layers per shot, crossfaded. A single <img> whose src is
   reassigned goes blank for the frame between "src set" and "bitmap decoded"
   — measured on the live page 2026-08-05: `complete` reads false in the same
   tick as the assignment, and `decoding="async"` explicitly permits painting
   that empty frame. Both tiles swapped at once, so the reader saw two holes
   punch open together every 4.5s. The container carries the aspect ratio so
   the absolutely-positioned layers have a box to fill, which also pins the
   height against any layout shift. */
.shot { display: block; position: relative; }
.shot-mac { aspect-ratio: 1500 / 916; }
.shot-phone { aspect-ratio: 460 / 950; }
.shot img {
  display: block;
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  opacity: 0;
}
/* The layer that ships in the HTML carries is-on, so the first paint needs no
   JavaScript and the page degrades to a single static screenshot. */
.shot img.is-on { opacity: 1; }

/* NO OPACITY IS EVER ANIMATED HERE, and that is the point.
   Two rounds of trying to make a crossfade behave both failed the same way:
   any partially-transparent frame lets the page background through, and on a
   dark page the whole device reads as dimming. A dissolve dips to ~0.75
   coverage at its midpoint; even fading only the incoming layer leaves the
   result at the mercy of a dropped frame or a stale stylesheet.
   So: the incoming layer is FULLY OPAQUE from the first frame and is revealed
   by clip-path instead, wiping left to right across a device that is already
   opaque underneath. Alpha never moves, so no combination of timing, throttle
   or cache can produce a dim frame. Because the shots in each set have
   pixel-identical alpha (verified 2026-08-05: 0 differing alpha pixels within
   a set), the bezel does not move during the wipe — only the screen content
   changes, and the device outline stays rock solid.
   z-index is required: both layers are absolutely positioned, so without it
   the DOM-later layer always wins and every second transition would hard-cut. */
.shot img.is-entering {
  z-index: 1;
  clip-path: inset(0 0 0 0);
  transition: clip-path 0.42s cubic-bezier(0.4, 0, 0.2, 1);
}
/* The start of the wipe: fully clipped from the right, so the layer is present
   and opaque but shows nothing. */
.shot img.is-entering.is-clipped { clip-path: inset(0 100% 0 0); }

.shot-kind {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 18px;
  font-size: 0.68rem; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-faint); font-weight: 600;
}
.shot-dots { display: inline-flex; gap: 6px; }
.shot-dot {
  width: 6px; height: 6px; padding: 0; border-radius: 50%;
  border: 0; background: var(--border-strong); cursor: pointer;
  transition: background 0.18s ease, transform 0.18s ease;
}
.shot-dot[aria-selected="true"] { background: var(--accent); transform: scale(1.35); }
.shot-dot:hover { background: var(--text-faint); }
.shot-dot[aria-selected="true"]:hover { background: var(--accent); }

/* --- Cards / tiers --- */

.tiers { display: grid; gap: 20px; grid-template-columns: repeat(3, 1fr); }
.tier {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 26px 24px;
  display: flex; flex-direction: column; box-shadow: var(--shadow);
}
.tier.featured { border-color: var(--accent); }
.tier .tier-name { font-weight: 660; font-size: 1.16rem; letter-spacing: -0.01em; }
.tier .tier-kind { font-size: 0.8rem; color: var(--text-faint); margin-top: 2px; letter-spacing: 0.02em; }
.tier .price { margin: 16px 0 4px; font-size: 1.7rem; font-weight: 660; letter-spacing: -0.02em; }
.tier .price small { font-size: 0.92rem; font-weight: 500; color: var(--text-dim); letter-spacing: 0; }
.tier .price-note { font-size: 0.78rem; color: var(--text-faint); margin-bottom: 14px; }
.tier .tier-desc { color: var(--text-dim); font-size: 0.96rem; margin-bottom: 16px; }
.tier ul.checklist { margin-top: auto; }

ul.checklist { list-style: none; padding: 0; margin: 0; display: grid; gap: 9px; }
ul.checklist li {
  position: relative; padding-left: 26px; font-size: 0.95rem;
  color: var(--text-dim); line-height: 1.45;
}
ul.checklist li::before {
  content: ""; position: absolute; left: 0; top: 0.34em;
  width: 15px; height: 9px;
  border-left: 2px solid var(--accent); border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg); border-radius: 1px;
}
ul.checklist li.no { color: var(--text-faint); }
ul.checklist li.no::before {
  content: "—"; border: 0; width: auto; height: auto; transform: none;
  color: var(--text-faint); top: 0; font-weight: 700;
}

/* --- Feature grid: Local vs Cloud --- */

.compare { display: grid; gap: 20px; grid-template-columns: 1fr 1fr; }
.compare .col {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 24px;
}
.compare .col h3 { display: flex; align-items: baseline; gap: 8px; }
.compare .col .col-kind { font-size: 0.78rem; color: var(--text-faint); font-weight: 500; letter-spacing: 0.02em; }

/* --- Callout / scope line --- */

.callout {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2); padding: 20px 22px;
  display: flex; gap: 14px; align-items: flex-start;
}
.callout .co-mark { flex: none; width: 22px; height: 22px; color: var(--accent); margin-top: 2px; }
.callout p:last-child { margin-bottom: 0; }
.callout.accent { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }

/* --- Support: contact + FAQ --- */

.contact {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 24px; box-shadow: var(--shadow);
}
.contact .email { font-family: var(--font-mono); font-size: 1.05rem; }

.faq { display: grid; gap: 14px; }
.faq details {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); overflow: hidden;
}
.faq summary {
  cursor: pointer; list-style: none; padding: 16px 20px;
  font-weight: 580; font-size: 1.03rem; letter-spacing: -0.01em;
  display: flex; align-items: center; gap: 12px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; margin-left: auto; flex: none;
  width: 9px; height: 9px; border-right: 2px solid var(--text-faint);
  border-bottom: 2px solid var(--text-faint); transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.faq details[open] summary::after { transform: rotate(-135deg); }
.faq summary:hover { color: var(--accent-ink); }
.faq .faq-body { padding: 0 20px 18px; color: var(--text-dim); }
.faq .faq-body > :last-child { margin-bottom: 0; }
.faq .faq-body p { font-size: 0.97rem; }

/* --- Steps (self-hosting) --- */

.steps { list-style: none; counter-reset: step; padding: 0; margin: 0; display: grid; gap: 18px; }
.steps li {
  position: relative; padding-left: 46px;
}
.steps li::before {
  counter-increment: step; content: counter(step);
  position: absolute; left: 0; top: 0;
  width: 30px; height: 30px; border-radius: 8px;
  background: var(--accent-soft); color: var(--accent-ink);
  font-weight: 660; font-size: 0.95rem;
  display: grid; place-items: center;
}
.steps li h3 { margin-bottom: 0.25em; }
.steps li p { margin-bottom: 0; color: var(--text-dim); font-size: 0.97rem; }

/* --- Barcode setup cards (scanner guide) --- */

/* A scanner reads the real image off the screen, so the barcode stays
   white-background / black-bars in both themes — never tinted by the theme. */
.barcode-card {
  background: #ffffff;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 18px 20px 16px;
  margin: 14px 0 4px;
  max-width: 460px;
}
.barcode-card img {
  display: block; width: 100%; height: auto;
  image-rendering: pixelated;   /* keep the bars crisp if the browser scales */
}
.barcode-card .barcode-cap {
  margin: 12px 0 0; font-size: 0.82rem; color: #57544d;
  font-family: var(--font-mono);
}
.barcode-hint { font-size: 0.9rem; color: var(--text-faint); margin: 2px 0 0; }

/* --- Requirements list --- */

.reqs { list-style: none; padding: 0; margin: 0; display: grid; gap: 12px; }
.reqs li { display: flex; gap: 12px; align-items: flex-start; color: var(--text-dim); font-size: 0.97rem; }
.reqs li .r-mark { flex: none; width: 18px; height: 18px; color: var(--accent); margin-top: 3px; }
.reqs li strong { color: var(--text); font-weight: 600; }

/* --- Legal pages (privacy / terms) — long-form reading --- */

.effective { color: var(--text-dim); font-size: 0.98rem; margin: 0; }
.effective strong { color: var(--text); font-weight: 620; }

.legal h2 { margin-top: 1.7em; }
.legal h3 { margin-top: 1.5em; }
.legal ul {
  padding-left: 1.3em; margin: 0 0 1.05em; display: grid; gap: 8px;
}
.legal ul li { color: var(--text-dim); padding-left: 0.15em; }
.legal ul li::marker { color: var(--accent); }
.legal strong { color: var(--text); font-weight: 620; }
.legal hr {
  border: 0; border-top: 1px solid var(--border);
  margin: clamp(30px, 5vw, 46px) 0;
}

/* --- Code block --- */

pre {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 14px 16px; overflow-x: auto;
  font-family: var(--font-mono); font-size: 0.85rem; line-height: 1.5;
  color: var(--text);
}
code { font-family: var(--font-mono); font-size: 0.88em; }
p code, li code {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 5px; padding: 1px 5px;
}

/* --- Divider heading --- */
.section-head { margin-bottom: clamp(24px, 4vw, 40px); }
.section-head .lede { margin-bottom: 0; }

/* --- Footer --- */

.site-footer {
  border-top: 1px solid var(--border); margin-top: clamp(40px, 8vw, 96px);
  padding: 40px 0 52px; color: var(--text-dim); font-size: 0.92rem;
}
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: 24px 40px; align-items: flex-start; }
/* 48ch, not 30ch: the tagline is 45 characters and 30ch broke it across two
   lines mid-sentence, which reads as a mistake rather than as a wrap. */
.site-footer .foot-brand { display: flex; flex-direction: column; gap: 6px; max-width: 48ch; }
.site-footer .foot-brand .faint { white-space: nowrap; }
.site-footer .foot-brand .brand { font-size: 1rem; }
.site-footer nav { display: flex; flex-wrap: wrap; gap: 8px 22px; margin-left: auto; }
.site-footer nav a { color: var(--text-dim); text-decoration: none; }
.site-footer nav a:hover { color: var(--text); }
.site-footer .fineprint { width: 100%; color: var(--text-faint); font-size: 0.82rem; border-top: 1px solid var(--border); padding-top: 18px; margin-top: 4px; }

/* --- Responsive --- */

@media (max-width: 860px) {
  .tiers { grid-template-columns: 1fr; }
  .compare { grid-template-columns: 1fr; }
  /* One column: side by side, the copy and the phone both become too narrow
     to read long before the breakpoint. The phone goes ABOVE the copy so the
     fold still opens on the product rather than on a wall of text. And with
     one column there is no mac edge to line up with, so the spacer track goes
     and the phone re-centres — otherwise it sits off to one side of its own
     row. */
  .hero-grid { grid-template-columns: 1fr; gap: 28px; }
  .hero-phone { order: -1; justify-content: center; }
  .hero-phone .shot-phone { max-width: 240px; }
}

@media (max-width: 560px) {
  body { font-size: 16px; }
  .nav { gap: 14px; }
  .nav a { font-size: 0.9rem; }
  .site-footer nav { margin-left: 0; width: 100%; }
}

@media (max-width: 380px) {
  .nav .nav-link-support { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}
