/* =========================================================================
   Bioms — design system tokens (shared across every page)

   This file is the single source of truth for spacing, radii, shadows,
   easing, motion, and the palette beyond the bg/ink basics. Pages still
   carry their layout-specific styles inline, but they MUST consume these
   tokens — never hardcode magic values.

   Loaded as <link rel="stylesheet" href="/design.css?v=N"> on the very
   first line after nav-wallet.css. Any page-local <style> block comes
   AFTER and may override sparingly with a justification comment.

   When you bump anything here, bump the ?v= cache-buster on every <link>.
   ========================================================================= */

:root {
  /* ---- Surface palette (unchanged baseline; here so design.css alone
     can render an MVP page without inline overrides) ---- */
  --bg: #ece9e0;
  --paper: #f4f1e8;
  --ink: #1c1a16;
  --ink-2: #5b554a;
  --ink-3: #686356;
  --rule: rgba(28, 26, 22, 0.12);

  /* ---- One destructive accent. Used wherever the act is permanent
     (burn rituals, irreversible CTAs, disconnect, validation-fail).
     Refined from the previous trio (#9a4a2e / #c45a3a / #c8826a) into
     a single ember-rust that reads as warm-decisive, not cautionary. ---- */
  --burn: #b8492c;
  --burn-soft: rgba(184, 73, 44, 0.10);
  --burn-glow: rgba(184, 73, 44, 0.32);

  /* ---- Quiet success tone. No bright green — we want a paper-ink
     world. This sits in the ink family with a faint warmth, so a
     "completed" state reads as the system confirming, not celebrating. ---- */
  --ok: #4a6b4a;

  /* ---- Radius scale (used to be 6/8/10/12/14/18/20/22 ad-hoc).
     Strict stair-step. Anything outside this scale needs justification. ---- */
  --r-xs: 8px;     /* small controls — input chips, mini-buttons */
  --r-sm: 10px;    /* form inputs, inline pills, small cards */
  --r-md: 14px;    /* normal cards, modal sections */
  --r-lg: 22px;    /* hero stages, feature cards, big modals */
  --r-pill: 999px;

  /* ---- Shadow vocabulary. Three semantic levels. ---- */
  --shadow-rest:    0 1px 1px rgba(0,0,0,0.04);
  --shadow-hover:   0 1px 1px rgba(0,0,0,0.04), 0 12px 28px rgba(0,0,0,0.08);
  --shadow-feature: 0 1px 1px rgba(0,0,0,0.04),
                    0 24px 56px rgba(28,26,22,0.10),
                    0 8px 14px rgba(28,26,22,0.05);

  /* ---- Easing & timing. One curve, three speeds.
     Curve is the same ease-out-quart Apple uses in macOS — fast off the
     line, settles soft. Pages should pick the speed semantically:
       fast  — chrome that should feel instant (hovers, focus rings)
       base  — cards, lists, dropdowns
       slow  — modal overlays, large state transitions ---- */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --t-fast: 120ms;
  --t-base: 200ms;
  --t-slow: 320ms;

  /* ---- Section rhythm. Vertical pads scale with viewport; horizontal
     pads are tighter on mobile, breathing room on desktop. ---- */
  --pad-y: clamp(64px, 9vw, 112px);
  --pad-x: clamp(20px, 4vw, 32px);
}

/* =========================================================================
   PETRI DISH MATERIAL — brand container metaphor
   --------------------------------------------------------------------------
   Bioms are bacterial specimens; their natural container is the petri
   dish. The hero, catalog cells, lab panes, and drop CTA all sit on a
   shared "dish" surface so the site reads as a microscope lab counter
   rather than a generic NFT marketing page.

   The recipe avoids backdrop-filter on purpose — it's a heavy GPU effect
   that compounds painfully when applied to multiple elements scrolling
   together (especially mobile Safari). Instead we layer:
     1. radial-gradient top reflection — the dome catching light
     2. linear agar fill — the medium settling under the lid
     3. inset top rim catch-light — the glass lip
     4. inset bottom hairline — the dish floor edge
     5. outer hairline — the dish wall against the counter
     6. tight grounding shadow + soft cast shadow — the dish sits on
        something, not painted on it

   Three sizes: .petri-dish (default), .petri-dish-sm (catalog cells),
   .petri-dish-lg (hero, drop). They differ only in shadow depth — the
   inner light language is identical so the family reads.
   ========================================================================= */
.petri-dish,
.petri-dish-sm,
.petri-dish-lg {
  background:
    /* Top catch-light — a soft elliptical highlight in the upper third
       where a glass lid would catch a ceiling light. */
    radial-gradient(ellipse at 50% 12%, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 50%),
    /* Agar gradient — slightly darker at the bottom like medium
       settling under gravity. */
    linear-gradient(180deg, var(--paper) 0%, #ebe7dd 100%);
  /* The visible glass rim — a 2px hairline at the very edge of the dish.
     Bumped from 1px to 2px because specimens fill the interior and the
     only place the dish identity reads is at the boundary. The colour
     is a warm ink at 14% alpha so it reads as a glass cross-section, not
     a hard border. */
  border: 2px solid rgba(28, 26, 22, 0.14);
  border-radius: var(--r-lg);
  position: relative;
  isolation: isolate;
}
.petri-dish {
  box-shadow:
    /* Inner ring — the cross-section of the glass wall, visible as
       a 1px darkening just inside the border. Reads as dish thickness. */
    inset 0 0 0 1px rgba(255, 255, 255, 0.6),
    /* Lip catch-light along the top inner edge — the glass rim. */
    inset 0 2px 0 rgba(255, 255, 255, 0.9),
    /* Floor shadow along the bottom inner edge — the dish has depth. */
    inset 0 -1px 0 rgba(0, 0, 0, 0.06),
    /* Tight contact shadow — the dish sits ON something. */
    0 1px 2px rgba(28, 26, 22, 0.05),
    /* Soft cast shadow — dish rests on a counter, light from above. */
    0 14px 28px -10px rgba(28, 26, 22, 0.12);
}
.petri-dish-sm {
  /* Smaller dishes (catalog cells, lab panes) — same dish anatomy as
     the default but tighter shadow. */
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.5),
    inset 0 1.5px 0 rgba(255, 255, 255, 0.85),
    inset 0 -1px 0 rgba(0, 0, 0, 0.05),
    0 1px 1px rgba(28, 26, 22, 0.05),
    0 8px 18px -8px rgba(28, 26, 22, 0.12);
}
.petri-dish-lg {
  /* Hero + drop — feature dish, the moment. */
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.7),
    inset 0 2.5px 0 rgba(255, 255, 255, 0.95),
    inset 0 -2px 0 rgba(0, 0, 0, 0.08),
    0 1px 2px rgba(28, 26, 22, 0.06),
    0 28px 60px -16px rgba(28, 26, 22, 0.20),
    0 12px 28px -10px rgba(28, 26, 22, 0.12);
}
/* Specimen-bearing dishes need a visible rim around the specimen so
   the dish identity reads. Without this internal padding the iframe
   or img fills 100% and the rim/agar disappear — looks like a plain
   card. Padding pulls the specimen inward by a small amount; the
   visible "rim of agar" surrounding it sells the dish metaphor. */
.petri-dish-sm.petri-specimen { padding: 6px; }
.petri-dish-lg.petri-specimen { padding: 10px; }
/* When the specimen sits at inset:0 absolute (iframes, full-bleed imgs),
   the padding alone doesn't push it. Apply inset shift to those too. */
.petri-dish-sm.petri-specimen > iframe,
.petri-dish-sm.petri-specimen > .composition,
.petri-dish-sm.petri-specimen > .lab-pane-render,
.petri-dish-sm.petri-specimen > .cell-render,
.petri-dish-sm.petri-specimen > picture { border-radius: calc(var(--r-md) - 6px); overflow: hidden; }
.petri-dish-lg.petri-specimen > iframe,
.petri-dish-lg.petri-specimen > .composition,
.petri-dish-lg.petri-specimen > .lab-pane-render { border-radius: calc(var(--r-lg) - 10px); overflow: hidden; }
/* Diagonal sheen — applied via ::after so it sits ABOVE backgrounds but
   below content (which gets z-index:1 from .petri-dish * rules). */
.petri-dish::after,
.petri-dish-sm::after,
.petri-dish-lg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg,
              rgba(255,255,255,0) 0%,
              rgba(255,255,255,0.18) 28%,
              rgba(255,255,255,0.32) 38%,
              rgba(255,255,255,0.10) 50%,
              rgba(255,255,255,0) 64%);
  mix-blend-mode: overlay;
  pointer-events: none;
  border-radius: inherit;
  z-index: 0;
}
.petri-dish > *,
.petri-dish-sm > *,
.petri-dish-lg > * { position: relative; z-index: 1; }

/* ---- Global motion baseline ---- */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---- Selection — paper-ink, no off-palette purple ---- */
::selection { background: rgba(28, 26, 22, 0.14); color: var(--ink); }

/* ---- Focus ring — keyboard only, Apple-style soft offset ---- */
a:focus-visible,
button:focus-visible,
input:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

/* =========================================================================
   Body baseline — every page inherits the same paper-and-ink ground.
   The noise overlay is the same SVG at 0.014 alpha (analog grain). Pages
   may override font-size only — everything else lives here.
   ========================================================================= */
html, body {
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, "SF Pro Text", "Inter", system-ui, sans-serif;
  line-height: 1.55;
  font-size: 17px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
* { box-sizing: border-box; }

body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='280' height='280'%3E%3Cfilter id='n'%3E%3CfeTurbulence baseFrequency='0.85' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0.05  0 0 0 0 0.05  0 0 0 0 0.05  0 0 0 0.014 0'/%3E%3C/filter%3E%3Crect width='280' height='280' filter='url(%23n)'/%3E%3C/svg%3E");
  z-index: 1;
}

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--rule);
  text-underline-offset: 4px;
  transition: text-decoration-color var(--t-fast) var(--ease);
}
a:hover { text-decoration-color: var(--ink); }

/* Skip-link — WCAG 2.4.1, hidden until keyboard-focused.
   Off-screen until focus pulls it into the top-left. */
.skip-link {
  position: absolute; left: -9999px; top: 0;
  background: var(--ink); color: var(--bg);
  padding: 12px 18px; border-radius: 0 0 var(--r-xs) 0;
  font-size: 14px; font-weight: 500; text-decoration: none;
  z-index: 9999;
}
.skip-link:focus { left: 0; outline: 2px solid var(--bg); outline-offset: -2px; }

/* =========================================================================
   Nav — canonical recipe for every page. Sticky top, cream paper at
   94% opacity so the scroll content tints it slightly. Hairline bottom
   rule sets the page off from the chrome.
   ========================================================================= */
nav {
  position: sticky; top: 0; z-index: 50;
  padding: 18px 32px;
  display: flex; justify-content: space-between; align-items: center;
  background: rgba(236, 233, 224, 0.94);
  border-bottom: 1px solid var(--rule);
}
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 17px; font-weight: 500; letter-spacing: -0.005em;
  color: var(--ink); text-decoration: none;
  transition: opacity var(--t-fast) var(--ease);
}
.brand:hover { opacity: 0.65; text-decoration: none; }
.brand-mark { width: 18px; height: 18px; display: block; }
.nav-links {
  display: flex; align-items: center;
  gap: 22px; font-size: 14px;
}
.nav-links a {
  color: var(--ink-3); text-decoration: none;
  transition: color var(--t-fast) var(--ease);
  /* ≥ 44px tap target without shifting visible layout — WCAG 2.5.5 */
  padding: 12px 6px; margin: -12px -6px;
}
.nav-links a:hover { color: var(--ink); text-decoration: none; }
.nav-links a.active { color: var(--ink); font-weight: 500; }
.nav-x {
  display: inline-flex; align-items: center;
  color: var(--ink-3); transition: color var(--t-fast) var(--ease);
  margin-left: 4px;
}
.nav-x:hover { color: var(--ink); text-decoration: none; }
.x-icon { width: 15px; height: 15px; display: block; }
@media (max-width: 600px) {
  nav { padding: 14px 18px; }
  .nav-links { gap: 14px; font-size: 13px; }
}

/* =========================================================================
   Footer — same recipe everywhere, one shape, two meta rows.
   ========================================================================= */
footer {
  padding: 32px;
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: 12px;
  font-size: 13px; color: var(--ink-3);
  border-top: 1px solid var(--rule);
  position: relative; z-index: 2;
}
footer a {
  color: inherit;
  text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}
footer a:hover { color: var(--ink); }
.footer-meta { display: flex; align-items: center; gap: 14px; }
/* Separator dots — quieter than the 0.4 they used to be. The eye now
   tracks the links instead of the punctuation. */
.footer-meta .sep { opacity: 0.32; user-select: none; }
@media (max-width: 600px) {
  footer { padding: 24px 18px; flex-direction: column; text-align: center; gap: 12px; }
  .footer-meta { flex-wrap: wrap; justify-content: center; gap: 8px 14px; }
  .footer-meta > span:not(.sep), .footer-meta > a { white-space: nowrap; }
}

/* =========================================================================
   Button system
   .btn          — primary, solid ink pill
   .btn.ghost    — secondary, hairline pill
   .btn.warn     — destructive, ember-rust pill (use sparingly)
   .btn.tight    — for form submits where pill feels too loud
   ========================================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--ink);
  color: var(--bg);
  border: 1px solid var(--ink);
  font-family: inherit;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  line-height: 1;
  padding: 13px 22px;
  border-radius: var(--r-pill);
  cursor: pointer;
  text-decoration: none;
  transition:
    background var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease),
    opacity var(--t-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { background: #000; border-color: #000; text-decoration: none; }
.btn:active { transform: scale(0.985); }
.btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  background: var(--ink-3);
  border-color: var(--ink-3);
}
.btn.ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule);
}
.btn.ghost:hover {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}
.btn.warn {
  background: var(--burn);
  border-color: var(--burn);
  color: #fff5ee;
}
.btn.warn:hover { background: #a23f24; border-color: #a23f24; }
.btn.tight {
  border-radius: var(--r-sm);
  padding: 12px 18px;
}
.btn.lg {
  font-size: 16px;
  padding: 15px 28px;
}

/* =========================================================================
   Eyebrow — the small-caps monospace label used over headings, section
   counts, signoffs, and similar. One recipe, one letter-spacing.
   ========================================================================= */
.eyebrow,
.group-label {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
}
