/*
  Hushbridge design tokens — "Reimagined" pass (warm paper/ink palette,
  Bricolage Grotesque + Instrument Sans). Fonts are self-hosted from
  src/assets/fonts rather than loaded from Google Fonts CDN: a CDN request
  would leak the visitor's IP to a third party on a privacy-first contact
  page (see ai/04-privacy-policy-draft.md "we don't share your info"), so
  we ship the two variable-font files ourselves instead.

  The @font-face src below uses './fonts/...', NOT './assets/fonts/...' —
  build.mjs copies this stylesheet to dist/assets/style.css and the font
  files to dist/assets/fonts/ (siblings), so a path relative to the
  *shipped* stylesheet's own location is './fonts/...'. An earlier version
  used './assets/fonts/...' (correct only for style.css's location in the
  source tree, relative to src/), which 404s once built and silently falls
  back to the system font stack — fixed 2026-07-14 after being caught via
  web/admin's copy of this file, verified live: the assets/assets/ URL
  403s, assets/fonts/ 200s.

  Theme: defaults to the OS/browser's prefers-color-scheme; an explicit
  choice (see theme.ts) is recorded as data-theme="light"|"dark" on <html>
  and always wins over the system setting. The dark palette is a deliberate
  second pass (surfaces step lighter than the page background for real
  elevation, the accent rust is brightened for contrast on dark), not a
  naive color inversion. --color-invert-* is a separate pair used only by
  the waitlist page's "coming soon" card, which is meant to read as a
  confident brand moment against *either* theme, not follow it.
*/

@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('./fonts/bricolage-grotesque-variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('./fonts/instrument-sans-variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Caveat';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('./fonts/caveat-variable.woff2') format('woff2');
}

:root {
  --color-paper: #F7F5F1;
  --color-surface: #FBFAF7;
  --color-surface-2: #FFFFFF;
  --color-ink: #171410;
  --color-ink-muted: #6F6A60;
  --color-ink-soft: #9C968A;
  --color-ink-faint: #E4DFD2;
  --color-ink-faintest: #E1DCCF;
  --color-accent: #D94F30;
  --color-accent-hover: #A03318;
  --color-accent-text: var(--color-accent-hover);
  --color-accent-ink: #F7F5F1;
  --color-accent-soft: #FADFD6;
  --color-accent-soft-hover: #F4D1C5;
  --color-warning: #C8862A;
  --color-warning-bg: #F7EEDD;
  --color-success: #3F6B34;
  --color-success-bg: #E1E8D5;
  --color-danger: #8A1F11;
  --color-danger-bg: #F5E3DE;
  --color-border: #8A806C;
  --color-invert-bg: #171410;
  --color-invert-text: #F7F5F1;
  --color-invert-muted: #BDB8AD;
  --color-invert-faint: rgba(247, 245, 241, 0.14);

  --font-display: "Bricolage Grotesque", "Avenir Next", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-body: "Instrument Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-note: "Caveat", "Segoe UI", system-ui, -apple-system, sans-serif;

  --radius: 16px;
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-6: 48px;

  --target-min: 48px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-paper: #1C1913;
    --color-surface: #242019;
    --color-surface-2: #2A251D;
    --color-ink: #F0EDE4;
    --color-ink-muted: #A39C8C;
    --color-ink-soft: #857F70;
    --color-ink-faint: #362F24;
    --color-ink-faintest: #332E25;
    --color-accent: #D68876;
    --color-accent-hover: #CE8574;
    --color-accent-text: var(--color-accent-hover);
    --color-accent-ink: #171410;
    --color-accent-soft: #6A2B17;
    --color-accent-soft-hover: #703823;
    --color-warning: #E0A44C;
    --color-warning-bg: #3A2E18;
    --color-success: #7FA66C;
    --color-success-bg: #2E3B24;
    --color-danger: #D24326;
    --color-danger-bg: #3A211B;
    --color-border: #746A5A;
    --color-invert-bg: #F7F5F1;
    --color-invert-text: #171410;
    --color-invert-muted: #6F6A60;
    --color-invert-faint: rgba(23, 20, 16, 0.1);
  }
}
:root[data-theme="dark"] {
  --color-paper: #1C1913;
  --color-surface: #242019;
  --color-surface-2: #2A251D;
  --color-ink: #F0EDE4;
  --color-ink-muted: #A39C8C;
  --color-ink-soft: #857F70;
  --color-ink-faint: #362F24;
  --color-ink-faintest: #332E25;
  --color-accent: #D68876;
  --color-accent-hover: #CE8574;
  --color-accent-text: var(--color-accent-hover);
  --color-accent-ink: #171410;
  --color-accent-soft: #6A2B17;
  --color-accent-soft-hover: #703823;
  --color-warning: #E0A44C;
  --color-warning-bg: #3A2E18;
  --color-success: #7FA66C;
  --color-success-bg: #2E3B24;
  --color-danger: #D24326;
  --color-danger-bg: #3A211B;
  --color-border: #746A5A;
  --color-invert-bg: #F7F5F1;
  --color-invert-text: #171410;
  --color-invert-muted: #6F6A60;
  --color-invert-faint: rgba(23, 20, 16, 0.1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  min-height: 100%;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

h1, h2, .display {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0;
}

main {
  flex: 1;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-2) var(--space-6);
}

/* Utility header: wordmark + language toggle (visual only, no i18n yet) + theme toggle */
.util-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-ink-faintest);
}
.util-header-brand {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 16px;
  letter-spacing: -0.01em;
  color: inherit;
  text-decoration: none;
}
.util-header-actions {
  display: flex;
  gap: 7px;
}
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 34px;
  padding: 0 11px;
  border-radius: 10px;
  border: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  color: var(--color-ink);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 12.5px;
  cursor: pointer;
}
.icon-btn:hover { background: var(--color-ink-faint); }
.icon-btn.icon-only { width: 34px; padding: 0; }
.icon-btn svg { flex-shrink: 0; }

.tag-heading {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.tag-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 18px;
}
.tag-icon:empty { display: none; }
.tag-icon img { width: 100%; height: 100%; object-fit: cover; }

.tag-label {
  font-size: 31px;
  line-height: 1.05;
}

.owner-note {
  margin-top: 6px;
  color: var(--color-ink-muted);
  font-size: 15px;
  line-height: 1.5;
}

/* The tag owner's own handwritten note (not system copy) — Caveat, the one
   place in the app where a personal touch beats the display/body pairing. */
.owner-note--personal {
  font-family: var(--font-note);
  font-weight: 600;
  font-size: 22px;
  line-height: 1.3;
  color: var(--color-accent-hover);
}

.section {
  margin-top: var(--space-4);
}

.label-sm {
  font-family: var(--font-display);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
}

/* Trust line — static reassurance, always shown on the form state.
   Deliberately NOT a bordered card: it's informative text, and boxing it
   gave it the same visual weight as the actual actions (chips, channels,
   send). A quiet icon + muted line reassures without competing. */
.trust-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: var(--space-2);
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--color-ink-muted);
}
.trust-line svg { flex-shrink: 0; width: 14px; height: 14px; color: var(--color-accent); }
.trust-line span.trust-line-muted { color: var(--color-ink-muted); }

.chips {
  display: grid;
  grid-template-columns: 1fr;
  gap: 9px;
  margin-top: var(--space-1);
}
@media (min-width: 480px) {
  .chips {
    grid-template-columns: repeat(2, 1fr);
  }
}

.chip {
  text-align: left;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  padding: 15px 17px;
  border-radius: 14px;
  border: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  color: var(--color-ink);
  cursor: pointer;
  min-height: var(--target-min);
  display: flex;
  align-items: center;
  justify-content: space-between;
  transition: background-color 200ms ease-out, border-color 200ms ease-out;
}
.chip::after {
  content: '\203A';
  color: var(--color-ink-faintest);
  font-family: var(--font-display);
}
.chip:hover, .chip:focus-visible { background: var(--color-ink-faint); }
.chip:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

textarea, input[type="text"], input[type="email"], input[type="tel"] {
  width: 100%;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--color-ink);
  background: var(--color-surface-2);
  border: 1px solid var(--color-ink-faint);
  border-radius: var(--radius);
  padding: var(--space-2);
  resize: vertical;
}
textarea:focus, input:focus {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
  border-color: var(--color-accent);
}
textarea::placeholder, input::placeholder { color: var(--color-ink-soft); }

.char-counter {
  display: block;
  text-align: right;
  font-size: 11.5px;
  color: var(--color-ink-soft);
  margin-top: 4px;
}
.char-counter.over { color: var(--color-danger); }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: var(--target-min);
  padding: 12px 24px;
  border-radius: 15px;
  border: none;
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 16.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 200ms ease-out, opacity 200ms ease-out;
}
.btn:disabled { opacity: 0.55; cursor: not-allowed; }

.btn-primary {
  width: 100%;
  background: var(--color-accent);
  color: var(--color-accent-ink);
}
.btn-primary:hover:not(:disabled) { background: var(--color-accent-hover); }

.btn-secondary {
  background: var(--color-surface-2);
  color: var(--color-ink);
  border: 1px solid var(--color-ink-faint);
  width: 100%;
}
.btn-secondary:hover { background: var(--color-ink-faint); }

/* "Contact directly" — icon-tile grid for disclosed channels */
.disclose-row {
  display: flex;
  gap: 9px;
  margin-top: var(--space-1);
}
.disclose-pill {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 15px 8px;
  border-radius: 14px;
  border: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  color: var(--color-ink);
  text-decoration: none;
  font-size: 13px;
  font-weight: 600;
  font-family: var(--font-display);
  min-height: var(--target-min);
  text-align: center;
}
.disclose-pill::before {
  content: '';
  width: 21px;
  height: 21px;
  background-color: var(--color-ink);
  -webkit-mask: var(--disclose-icon) center / contain no-repeat;
  mask: var(--disclose-icon) center / contain no-repeat;
}
.disclose-pill[data-kind="phone"] { --disclose-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 4h3l1.5 5-2 1.5a12 12 0 0 0 6 6l1.5-2 5 1.5v3a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2z'/%3E%3C/svg%3E"); }
.disclose-pill[data-kind="email"] { --disclose-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l8 5 8-5'/%3E%3Crect x='4' y='6' width='16' height='12' rx='2'/%3E%3C/svg%3E"); }
.disclose-pill[data-kind="whatsapp"], .disclose-pill[data-kind="signal"] { --disclose-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 5h16v11H9l-4 3z'/%3E%3C/svg%3E"); }
.disclose-pill:hover { background: var(--color-ink-faint); }
.disclose-pill.revealed {
  border-color: var(--color-accent-text);
  color: var(--color-accent-text);
}
.disclose-pill.revealed::before { background-color: var(--color-accent-text); }

/* Attach-a-file (FR-12) — button matches btn-secondary, chips list
   uploads in progress/done/error below it. */
.attach-chips {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: var(--space-1);
}
.attach-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1);
  padding: 10px 14px;
  border-radius: 12px;
  border: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  font-size: 13px;
}
.attach-chip-name {
  color: var(--color-ink);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attach-chip-status { flex-shrink: 0; color: var(--color-ink-soft); }
.attach-chip--done .attach-chip-status { color: var(--color-success); }
.attach-chip--error { border-color: var(--color-danger); }
.attach-chip--error .attach-chip-status { color: var(--color-danger); }

/* faq-item keeps its card look (it lives on the marketing pages); the
   contact-info disclosure sheds it — an optional extra shouldn't carry
   the same weight as the message box or the send button. It regains a
   gentle inset (left rule) only when opened, to group its fields. */
details.faq-item {
  margin-top: var(--space-3);
  border: 1px solid var(--color-ink-faint);
  border-radius: var(--radius);
  padding: var(--space-2);
  background: var(--color-surface-2);
}
details.contact-info { margin-top: var(--space-2); }
details.contact-info[open] {
  padding-bottom: var(--space-1);
}
details.contact-info summary {
  color: var(--color-accent-text);
}
details.contact-info summary, details.faq-item summary {
  cursor: pointer;
  font-weight: 600;
  font-family: var(--font-display);
  font-size: 14px;
  min-height: var(--target-min);
  display: flex;
  align-items: center;
}
details.contact-info .contact-fields {
  padding-left: var(--space-2);
  border-left: 2px solid var(--color-ink-faint);
}
/* Flex containers trim the trailing space of a text node that ends right
   before a child element, so the space before "— optional" has to come
   from margin instead of relying on the HTML source's literal space. */
.optional-label { margin-left: 6px; color: var(--color-ink-soft); font-weight: 500; }
details.faq-item + details.faq-item { margin-top: var(--space-1); }
details.faq-item[open] summary { margin-bottom: 4px; }
.contact-fields { display: grid; gap: var(--space-1); margin-top: var(--space-2); }
.field-label { font-size: 12px; color: var(--color-ink-soft); margin-bottom: 5px; display: block; }

/* Informative caveat, not an alert — plain muted text reads as guidance;
   the old amber box made an optional field look like something went wrong. */
.warning {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin: 2px 0 var(--space-1);
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--color-ink-muted);
}

.privacy-notice {
  margin-top: var(--space-3);
  font-size: 12px;
  color: var(--color-ink-soft);
  text-align: center;
  line-height: 1.5;
}
.privacy-notice a { color: var(--color-accent-text); text-decoration: underline; }

/* A tool, not a call to action — quiet inline button instead of another
   full-width bordered box (the .btn-secondary base is overridden here). */
.attach-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: auto;
  min-height: var(--target-min);
  padding: 0 4px;
  border: none;
  background: none;
  color: var(--color-ink);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 150ms ease-out;
}
.attach-btn:hover { color: var(--color-accent-text); }

.banner {
  margin-top: var(--space-2);
  padding: 12px 14px;
  border-radius: 12px;
  background: var(--color-surface);
  border: 1px solid var(--color-ink-faintest);
  color: var(--color-ink);
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 9px;
}
.banner.danger { background: var(--color-danger-bg); color: var(--color-danger); border-color: transparent; }
.banner-link {
  text-decoration: none;
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font-weight: 600;
  font-family: var(--font-display);
  font-size: 13.5px;
  border: 1px solid var(--color-accent-soft-hover);
  min-height: var(--target-min);
  transition: background-color 150ms ease-out;
}
.banner-link:hover { background: var(--color-accent-soft-hover); }

.centered-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-top: var(--space-6);
}
.icon-circle {
  width: 88px;
  height: 88px;
  border-radius: 999px;
  background: var(--color-surface);
  border: 1px solid var(--color-ink-faintest);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-3);
  font-size: 36px;
}
.icon-circle.success { background: var(--color-success-bg); color: var(--color-success); border-color: transparent; }
.icon-circle.warning { background: var(--color-warning-bg); color: var(--color-warning); border-color: transparent; }

/* "Want a reply?" card on the sent-confirmation screen */
.sent-card {
  width: 100%;
  margin-top: var(--space-4);
  padding: 20px;
  border-radius: var(--radius);
  background: var(--color-surface-2);
  border: 1px solid var(--color-ink-faintest);
  text-align: left;
}
.sent-card-title { font-family: var(--font-display); font-weight: 700; font-size: 16px; }
.sent-card-body { font-size: 13.5px; color: var(--color-ink-muted); line-height: 1.45; margin: 6px 0 0; }

.rate-badge {
  margin-top: var(--space-3);
  display: inline-block;
  padding: 10px 20px;
  border-radius: 999px;
  background: var(--color-surface-2);
  border: 1px solid var(--color-ink-faintest);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 21px;
  letter-spacing: 0.02em;
}

footer {
  padding: var(--space-4) var(--space-2);
  border-top: 1px solid var(--color-ink-faint);
  background: var(--color-surface);
  text-align: center;
  font-size: 12.5px;
  color: var(--color-ink-soft);
}
footer a { color: inherit; text-decoration: none; margin: 0 12px; padding: 8px 0; display: inline-block; }
footer a:hover { color: var(--color-accent-text); }
footer strong { color: var(--color-ink); font-family: var(--font-display); }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: var(--space-1);
  padding: 8px 14px;
  border: 1px solid var(--color-ink-faint);
  border-radius: 999px;
  background: var(--color-surface-2);
  color: var(--color-ink-muted);
  font-family: var(--font-body);
  font-size: 13px;
  cursor: pointer;
  min-height: 36px;
  transition: background-color 150ms ease-out, color 150ms ease-out;
}
.theme-toggle:hover { background: var(--color-ink-faint); color: var(--color-ink); }

.hidden { display: none !important; }

/*
  Utility/modifier classes below replace what used to be inline `style="..."`
  attributes and JS-driven `.style.x = y` mutations. The CSP is intentionally
  strict (style-src 'self', no 'unsafe-inline') — both inline style attributes
  and CSSOM mutations are covered by that directive, so anything not expressed
  as a stylesheet rule is silently dropped by the browser instead of erroring.
*/
.mt-1 { margin-top: var(--space-1); }
.label-block { display: block; margin-bottom: 6px; }
.owner-note--wide { margin-top: 16px; max-width: 380px; }
.banner--block { display: block; margin-top: var(--space-1); }
.banner--warning { margin-top: var(--space-2); background: var(--color-warning-bg); border-left: 3px solid var(--color-warning); }
.link-accent { color: var(--color-accent-text); }
.link-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--color-accent-text);
  cursor: pointer;
  text-decoration: underline;
}
.sent-back-btn { margin-top: var(--space-3); }
.sent-waitlist-hint { margin-top: var(--space-3); font-size: 13px; }

/* Honeypot field: pulled off-screen, never `display:none` (see contact.html comment). */
.honeypot { position: absolute; left: -9999px; top: -9999px; }
.thread-link-cta { margin-top: var(--space-3); max-width: 280px; }

/* Home page */
.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: var(--space-2) var(--space-2) 0;
}
.header-owner-link,
.header-lang-link {
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--color-ink-muted);
  text-decoration: none;
  padding: 8px 14px;
  border-radius: 999px;
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  transition: background-color 150ms ease-out, color 150ms ease-out;
}
.header-owner-link:hover,
.header-lang-link:hover { background: var(--color-ink-faint); color: var(--color-ink); }
.header-lang-link { font-weight: 600; gap: 5px; cursor: pointer; }
.header-lang-link svg { flex-shrink: 0; }

/* Language switcher — a <details>/<summary> disclosure so the language
   list works even if JS never loads (each option is a plain <a> to its
   sibling-locale URL, see build.mjs's langSwitcherMenu). */
.lang-switcher { position: relative; }
.lang-switcher summary { list-style: none; }
.lang-switcher summary::-webkit-details-marker { display: none; }
.lang-switcher[open] .header-lang-link { background: var(--color-ink-faint); color: var(--color-ink); }
.lang-switcher-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  min-width: 140px;
  padding: 6px;
  border: 1px solid var(--color-ink-faint);
  border-radius: 12px;
  background: var(--color-surface-2);
}
.lang-switcher-menu a,
.lang-switcher-menu button {
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 13px;
  color: var(--color-ink);
  text-decoration: none;
}
.lang-switcher-menu button {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
}
.lang-switcher-menu a:hover,
.lang-switcher-menu button:hover { background: var(--color-ink-faint); }

.home-hero { padding-top: 32px; }
.home-title { font-size: 34px; }
.home-lede { max-width: 400px; margin-top: 16px; }
.home-hint { margin-top: 24px; font-size: 13px; text-align: center; }
.waitlist-section { max-width: 400px; margin: var(--space-4) auto 0; }

/*
  page-block gives each top-level section on the landing page its own
  visible boundary — before this, every section was just a label-sm span
  followed by plain text at the same indent as everything else, so the
  page read as one undifferentiated column instead of distinct topics.
*/
.page-block {
  border: 1px solid var(--color-ink-faint);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-2);
  background: var(--color-surface);
}
.page-block-label { display: block; margin-bottom: var(--space-2); }

/*
  page-block--primary is the "coming soon" waitlist ask — the one section
  on the page that's an actual request, not information. It flips to the
  inverted ink surface (--color-invert-*, always dark-on-light-text
  regardless of the active theme) as a confident brand moment, matching
  the mockup's single ink screen among an otherwise light-paper flow.
*/
.page-block--primary {
  background: var(--color-invert-bg);
  color: var(--color-invert-text);
  border-color: transparent;
}
.coming-soon-title { font-size: 24px; }
.page-block--primary .page-block-label { color: var(--color-invert-muted); }
.page-block--primary .owner-note { color: var(--color-invert-muted); }
.page-block--primary .field-label { color: var(--color-invert-muted); }
.page-block--primary input {
  background: var(--color-invert-faint);
  border-color: var(--color-invert-muted);
  color: var(--color-invert-text);
}
.page-block--primary input::placeholder { color: var(--color-invert-muted); }
/* No .btn-primary override here — Signal clay reads fine against this
   section's always-inverted background in both themes, same as every
   other primary button; a separate invert-swapped button was the one
   place this section's CTA didn't look "branded" after the palette update. */
.page-block--primary .privacy-notice { color: var(--color-invert-muted); }
.page-block--primary .privacy-notice a { color: var(--color-accent); }

/* Pre-printed-tag interest, folded into the main waitlist form as an
   opt-in checkbox instead of a second standalone email-capture section
   (the two used to sit stacked on the page reading as duplicate asks). */
.toggle-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--color-ink);
  cursor: pointer;
}
.toggle-row input[type="checkbox"] { width: 17px; height: 17px; accent-color: var(--color-accent); cursor: pointer; flex-shrink: 0; }
.page-block--primary .toggle-row { color: var(--color-invert-muted); }

.how-step { display: flex; gap: var(--space-2); align-items: flex-start; margin-top: var(--space-2); }
.how-step-num {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: var(--color-accent-soft);
  color: var(--color-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
}
.how-step-body h3 { font-family: var(--font-display); font-size: 16px; font-weight: 700; margin: 0 0 4px; }
.how-step-body p { margin: 0; color: var(--color-ink-muted); font-size: 14px; }

/*
  Story carousel ("Where it goes") — Instagram-style story surface: tap the
  left/right edge to step, hold to pause, auto-advances otherwise. Built
  from the illustrated objects in the "Hushbridge home page illustrations"
  design project (StoryCarouselHD). Colors/type route entirely through the
  site's real tokens (--color-*, --font-*) instead of the source file's own
  local light/dark palette, so it follows data-theme automatically.
*/
.story-carousel {
  position: relative;
  width: 100%;
  max-width: 300px;
  aspect-ratio: 5 / 9;
  margin: var(--space-2) auto 0;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  touch-action: pan-y;
  outline-offset: 3px;
}
.story-track {
  position: absolute;
  inset: 0;
  display: flex;
  height: 100%;
  transition: transform 0.42s cubic-bezier(0.4, 0, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) {
  .story-track { transition: none; }
}
.story-slide {
  flex: 0 0 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  padding: 40px 20px 24px;
}
.story-illustration { display: block; width: 100%; max-width: 210px; height: auto; }
.usecase-hero-illustration { display: flex; justify-content: center; padding: 8px 0 4px; }
.usecase-hero-illustration svg { display: block; width: 100%; max-width: 240px; height: auto; }
.story-caption { text-align: center; max-width: 240px; }
.story-kicker {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-accent);
}
.story-heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.02em;
  margin: 6px 0 0;
  color: var(--color-ink);
}
.story-copy { font-size: 13px; line-height: 1.5; color: var(--color-ink-muted); margin: 7px 0 0; }

.story-bars {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  gap: 4px;
  padding: 12px 14px 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .story-bars { display: none; }
}
.story-bar { flex: 1; height: 3px; border-radius: 2px; overflow: hidden; background: var(--color-ink-faint); }
.story-bar i { display: block; height: 100%; width: 0%; background: var(--color-ink); border-radius: 2px; }

.story-tapzone { position: absolute; inset: 0; z-index: 2; cursor: pointer; }

.story-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  color: var(--color-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 150ms ease-out, background-color 150ms ease-out;
}
.story-nav--prev { left: 10px; }
.story-nav--next { right: 10px; }
.story-carousel:hover .story-nav,
.story-nav:focus-visible {
  opacity: 1;
}
.story-nav:hover { background: var(--color-ink-faint); }
.story-nav:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.story-carousel:focus-visible { outline: 2px solid var(--color-accent); }

/* how-it-works + story carousel sit stacked on mobile; wide viewports get
   a bigger main column so the pair can run side by side instead of the
   story carousel's narrow 300px card floating alone in the middle. */
.home-two-col { display: flex; flex-direction: column; gap: var(--space-3); }
.home-two-col > .page-block { flex: 1; min-width: 0; }
@media (min-width: 760px) {
  main { max-width: 880px; }
  .home-two-col { flex-direction: row; align-items: stretch; }
}

/*
  Questions gets deliberately less visual weight than the boxed sections
  above it (how-it-works, story carousel, coming-soon) — a plain divider
  list instead of a bordered card, so the page doesn't read as a stack of
  equally-important panels once the carousel is added.
*/
.faq-block { margin-top: var(--space-4); }
.faq-block .page-block-label { margin-bottom: var(--space-1); color: var(--color-ink-soft); }
.faq-block details.faq-item {
  margin-top: 0;
  border: none;
  border-radius: 0;
  padding: 0;
  background: none;
  border-bottom: 1px solid var(--color-ink-faint);
}
.faq-block details.faq-item:first-of-type { border-top: 1px solid var(--color-ink-faint); }
.faq-block details.faq-item + details.faq-item { margin-top: 0; }
.faq-block details.faq-item summary { font-size: 13px; font-weight: 500; min-height: auto; padding: 11px 0; }
.faq-block details.faq-item[open] summary { margin-bottom: 0; }
.faq-block details.faq-item p { padding-bottom: 11px; font-size: 13px; }

/* Legal pages */
.legal-main { max-width: 640px; }
.legal-main--wide { max-width: 680px; }
.legal-main table { width: 100%; border-collapse: collapse; margin: var(--space-2) 0; font-size: 14px; }
.legal-main th, .legal-main td { text-align: left; padding: 8px; border-bottom: 1px solid var(--color-ink-faint); vertical-align: top; }
.legal-main th { font-family: var(--font-display); font-size: 13px; }
.legal-main h2 { font-family: var(--font-display); font-size: 18px; margin-top: var(--space-4); }

@media (max-width: 380px) {
  main { padding: var(--space-3) var(--space-2) var(--space-4); }
  .tag-label { font-size: 26px; }
}

/*
  Two-way anonymous thread page (t.html). Fixed header/input-bar with an
  independently-scrolling message list in between — the one layout in this
  app that isn't the standard centered-column page shell.
*/
/* Layered height fallback, each line only wins if the browser understands
   it and (for --vvh) if thread.ts has run:
   1. 100vh — universal fallback.
   2. 100dvh — tracks mobile browser chrome (address bar/toolbar) show/hide,
      which 100vh does not.
   3. var(--vvh, 100dvh) — tracks the live visualViewport height, which also
      shrinks when the on-screen keyboard opens; dvh alone does not cover
      the keyboard case, which is what let the reply input end up hidden
      behind it (or the browser's nav overlay) even after the dvh fix. */
.thread-body {
  height: 100vh;
  height: 100dvh;
  height: var(--vvh, 100dvh);
  /* html,body's global rule sets min-height: 100vh, a DIFFERENT property
     from the height above, so it isn't overridden by selector specificity
     — it stacks. Since var(--vvh)/100dvh is normally shorter than 100vh
     (100vh ignores the browser's address/toolbar chrome, dvh/vvh don't),
     that global min-height was forcing this page taller than the actually
     visible viewport, and the overflow:hidden below then clipped the
     composer, which sits at the bottom of that oversized box, out of
     view. Mirroring the same fallback chain here as a min-height keeps
     the box exactly viewport-sized instead of floor-clamped to the
     larger, chrome-inclusive 100vh. */
  min-height: 100vh;
  min-height: 100dvh;
  min-height: var(--vvh, 100dvh);
  overflow: hidden;
  margin: 0;
  background: var(--color-surface);
}
.thread-chat {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  height: var(--vvh, 100dvh);
  max-width: 560px;
  margin: 0 auto;
  /* Overrides the sitewide `main { padding: ... }` rule (this element is
     a <main>) — that padding is meant for ordinary centered content pages
     and was adding 32px above the header plus 48px below the composer
     inside this otherwise edge-to-edge, exact-height app shell, stealing
     space from the message list and pushing the composer further toward
     (and on short viewports, past) the bottom edge. */
  padding: 0;
  background: var(--color-paper);
}
.thread-header {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  padding-top: calc(var(--space-1) + env(safe-area-inset-top));
  border-bottom: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  flex-shrink: 0;
}
.thread-back {
  background: none;
  border: none;
  padding: 0;
  color: var(--color-ink);
  text-decoration: none;
  font-size: 20px;
  min-width: 44px;
  min-height: 44px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color 150ms ease-out;
  flex-shrink: 0;
}
.thread-back:hover { background: var(--color-ink-faint); }
.thread-tag-label {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.thread-tag-sub {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--color-accent-text);
  font-weight: 400;
}
.thread-tag-sub::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--color-accent);
}
.notify-btn {
  flex-shrink: 0;
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: none;
  font-size: 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
.notify-btn:hover { background: var(--color-ink-faint); }
.notify-btn-denied { color: var(--color-warning); }
.theme-toggle-icon {
  flex-shrink: 0;
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: none;
  font-size: 16px;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
.theme-toggle-icon:hover { background: var(--color-ink-faint); }
/* thread.html's header is icon-only throughout (back/notify/theme all
   44x44 square buttons, no text) — the lang switcher's <summary> reuses
   .theme-toggle-icon for that same sizing/hover, this rule just centers
   the globe icon inside it (summary isn't a flex container by default). */
.thread-header .lang-switcher summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.thread-scroll {
  flex: 1;
  /* min-height: 0 overrides the flex default (min-height: auto), which
     otherwise refuses to let this item shrink below its content size —
     with .thread-body's overflow: hidden blocking page-level scroll,
     that made scrolling silently stop working once a thread grew past
     one screen of messages, since the scroll container itself never
     actually became shorter than its content. */
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-2);
}
/* The pre-conversation area used to be three equal stacked bordered cards
   (privacy note, shared-info, notify toggle) pushing the actual messages
   below the fold. All three are informative/secondary — flattened to
   quiet lines so the conversation is the first thing that reads. */
.thread-banner {
  display: flex;
  gap: 7px;
  align-items: flex-start;
  justify-content: center;
  margin: 0 0 var(--space-1);
  font-size: 12px;
  color: var(--color-ink-muted);
}
.thread-banner svg { flex-shrink: 0; width: 13px; height: 13px; margin-top: 1px; color: var(--color-accent); }
.thread-shared-info {
  margin: 0 0 var(--space-1);
  font-size: 12px;
  text-align: center;
  color: var(--color-ink-muted);
}
.thread-shared-info > .label-sm { font-size: 10.5px; }
.thread-shared-list { display: inline-flex; flex-wrap: wrap; justify-content: center; gap: 4px 14px; margin-left: 8px; }
.thread-shared-item { color: var(--color-ink-muted); }
.thread-shared-item strong { color: var(--color-ink); font-weight: 600; }

.thread-notify-section { margin: 0 0 var(--space-2); text-align: center; }
.thread-notify-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: var(--target-min);
  padding: 0 6px;
  background: none;
  border: none;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--color-accent-text);
  cursor: pointer;
  text-align: left;
}
.thread-notify-toggle:hover { opacity: 0.75; }
.thread-notify-toggle svg { flex-shrink: 0; color: var(--color-accent); }
.thread-notify-form { display: flex; gap: 8px; margin-top: 8px; }
.thread-notify-form .btn { width: auto; flex-shrink: 0; padding: 0 16px; }
.thread-notify-input {
  flex: 1;
  padding: 9px 12px;
  border: 1px solid var(--color-ink-faint);
  border-radius: 10px;
  background: var(--color-surface-2);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 13px;
}
.thread-notify-confirmed {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  justify-content: center;
  margin: 0 0 var(--space-2);
  font-size: 12px;
  color: var(--color-ink-muted);
}
.thread-notify-confirmed svg { flex-shrink: 0; width: 13px; height: 13px; margin-top: 1px; color: var(--color-accent); }
.thread-notify-confirmed strong { color: var(--color-ink); font-weight: 600; }
.bubble-row {
  display: flex;
  flex-direction: column;
  margin-bottom: var(--space-2);
  max-width: 80%;
}
.bubble-row-owner { align-items: flex-start; margin-right: auto; }
.bubble-row-visitor { align-items: flex-end; margin-left: auto; }
.bubble-label {
  font-size: 11px;
  font-weight: 600;
  font-family: var(--font-display);
  color: var(--color-ink-soft);
  margin-bottom: 4px;
  padding: 0 4px;
}
.bubble {
  margin: 0;
  padding: 11px 14px;
  border-radius: 16px;
  font-size: 14.5px;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
}
.bubble-owner {
  background: var(--color-surface-2);
  color: var(--color-ink);
  border: 1px solid var(--color-ink-faint);
  border-top-left-radius: 5px;
}
.bubble-visitor {
  background: var(--color-ink);
  color: var(--color-paper);
  border-top-right-radius: 5px;
}

/* Attachments — only ever on the thread's first (visitor) message. Mirrors
   the dashboard's .attachment-list/.attachment-chip/.attachment-thumb
   (web/dashboard/src/style.css) so a visitor's own copy of what they sent
   on first contact looks the same as what the owner sees. .attachment-thumb
   is a <button> here (not an <a>, see thread.ts's openLightbox) so it needs
   its own reset on top of the shared button-appearance defaults. */
.attachment-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.attachment-thumb {
  display: block;
  width: 96px;
  height: 96px;
  padding: 0;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--color-ink-faint);
  background: none;
  cursor: pointer;
}
.attachment-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.attachment-chip {
  display: inline-flex;
  align-items: center;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--color-ink-faint);
  background: var(--color-surface);
  color: var(--color-ink);
  text-decoration: none;
  font-size: 13px;
}
.attachment-chip:hover { background: var(--color-ink-faint); }
.attachment-chip--pending { color: var(--color-ink-muted); }
.attachment-chip--blocked { color: var(--color-danger); border-color: var(--color-danger); }

/* Lightbox — full-size in-page image preview (thread.ts's openLightbox),
   replacing the old "opens a new tab that just downloads the file"
   behavior. Same page, same tab; Escape/backdrop-click/× all close it. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.85);
}
.lightbox-img {
  max-width: min(92vw, 900px);
  max-height: 88vh;
  object-fit: contain;
  border-radius: 8px;
}
.lightbox-close {
  position: absolute;
  top: max(16px, env(safe-area-inset-top));
  right: max(16px, env(safe-area-inset-right));
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  border: none;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
}
.lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }

.thread-input-bar {
  position: relative;
  display: flex;
  gap: var(--space-1);
  align-items: flex-end;
  padding: var(--space-2);
  padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--color-ink-faint);
  background: var(--color-surface-2);
  flex-shrink: 0;
}
/* Floats above the input bar instead of being a normal flex child of
   .thread-chat — as a static child it competed with .thread-scroll for
   the fixed-height column's space, shrinking the message list and
   overlapping the latest message whenever an error (or the notify-denied
   explanation) appeared. */
#error-banner-wrap {
  position: absolute;
  left: var(--space-2);
  right: var(--space-2);
  bottom: 100%;
  margin-bottom: var(--space-1);
}
.thread-input-bar textarea {
  flex: 1;
  border-radius: 999px;
  padding: 13px 16px;
  max-height: 120px;
  resize: none;
}
.thread-send {
  width: 48px;
  height: 48px;
  border-radius: 999px;
  border: none;
  background: var(--color-accent);
  color: var(--color-accent-ink);
  font-size: 18px;
  cursor: pointer;
  flex-shrink: 0;
}
.thread-send:hover:not(:disabled) { background: var(--color-accent-hover); }
.thread-send:disabled { opacity: 0.55; cursor: not-allowed; }

/* --- Shop page (/shop) --- */
.shop-hero { text-align: center; padding-top: var(--space-4); }
.shop-badge {
  display: inline-block;
  background: var(--color-accent-soft);
  color: var(--color-accent-text);
  border-radius: 999px;
  padding: 4px 14px;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: var(--space-2);
}
.shop-badge--nfc { margin: var(--space-1) 0; }
.shop-highlight {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  background: var(--color-surface-2);
  border: 1px solid var(--color-ink-faint);
  border-radius: var(--radius);
  padding: var(--space-3);
}
.shop-highlight .btn { flex-shrink: 0; width: auto; padding: 12px 20px; }
.shop-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.shop-card {
  background: var(--color-surface-2);
  border: 1px solid var(--color-ink-faint);
  border-radius: var(--radius);
  padding: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.shop-card .btn { width: auto; margin-top: var(--space-1); }
.shop-price { font-size: 20px; font-weight: 700; margin: 0; }
.shop-interest { max-width: 420px; }
