/* ==========================================================================
   PinThis · tokens.css
   Generated from pinthis-design skill decisions (2026-04-30 v2):
     · Typeface : Satoshi (self-hosted woff2, /assets/fonts/)
                  weights 400 / 400i / 500 / 700  (Satoshi has no 600 — semibold falls back to 700)
     · Brand    : Rose — oklch(0.68 0.18 0)  ≈  #de4870
     · Density  : Notion-medium (component classes apply)
   Memory: project_pinthis_design_decisions.md

   Token naming:
     --c-*    color           --fw-*   font-weight
     --ff-*   font-family     --lh-*   line-height
     --fs-*   font-size       --sp-*   spacing (4px scale)
     --r-*    border-radius   --sh-*   shadow
     --d-*    duration        --e-*    easing
     --z-*    z-index
   ========================================================================== */

/* ─── self-hosted Satoshi ────────────────────────────────────────────────── */

@font-face {
  font-family: "Satoshi";
  src: url("/assets/fonts/satoshi-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Satoshi";
  src: url("/assets/fonts/satoshi-400i.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Satoshi";
  src: url("/assets/fonts/satoshi-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Satoshi";
  src: url("/assets/fonts/satoshi-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* Note: Satoshi static set has no 600 (Bold). Browser maps 600 → nearest
   available weight (700). Acceptable visually; if 600 becomes load-bearing,
   add satoshi-700 as both 600 and 700 declarations or buy Variable. */

/* Match metrics for the system fallback so the swap doesn't reflow.
   Satoshi has slightly tighter metrics than Arial; tune as needed after
   visual QA. */
@font-face {
  font-family: "Satoshi Fallback";
  src: local("Arial");
  size-adjust: 100%;
  ascent-override: 96%;
  descent-override: 21%;
  line-gap-override: 0%;
}

/* ─── HEX fallbacks for browsers without OKLCH (iOS <15.4, etc.) ──────────── */
:root {
  /* color · neutral ramp (HEX fallback values) */
  --c-gray-0:    #fcfcfd;
  --c-gray-50:   #f7f7f9;
  --c-gray-100:  #f0f0f3;
  --c-gray-200:  #e5e5ea;
  --c-gray-300:  #d1d1d8;
  --c-gray-400:  #a1a1aa;
  --c-gray-500:  #71717a;
  --c-gray-600:  #52525b;
  --c-gray-700:  #3f3f46;
  --c-gray-800:  #27272a;
  --c-gray-900:  #18181b;
  --c-gray-950:  #0a0a0c;

  /* color · rose brand (HEX fallback) */
  --c-brand-50:   #fbe9ee;
  --c-brand-100:  #f6ccd5;
  --c-brand-300:  #ed8298;
  --c-brand-500:  #de4870;
  --c-brand-700:  #b3284e;
  --c-brand-900:  #6e0d2a;

  /* color · semantics (HEX fallback) */
  --c-success:    #2ea05a;
  --c-warning:    #d49b1a;
  --c-error:      #d83a3a;
  --c-info:       #4a7ed1;
  /* AA-text-on-tinted variants — paired with badge/button surfaces
     (--c-error 12% on --c-bg-elevated etc.) to clear 4.5:1 at body
     font-size. Use these for foreground text/icons on tinted chips. */
  --c-error-strong: #a82828;
  --c-info-strong:  #2855a0;
  /* Social affordance — filled heart (liked) + filled bookmark (saved).
     Mode-independent on purpose: Instagram / Pinterest keep their red the
     same regardless of dark mode so the "I reacted" signal stays stable.
     Pure hex (not oklch) so Display P3 wide-gamut devices report it as
     rgb(255,59,92) in computed-style audits — easy to verify in tests. */
  --c-like:       #ff3b5c;

  /* semantic surface tokens */
  --c-bg:           var(--c-gray-50);
  --c-bg-elevated:  var(--c-gray-0);
  --c-bg-overlay:   rgba(15, 15, 17, 0.55);
  /* video play overlay (feed/profile pin thumb ▶). Light theme defaults:
     light circle + dark triangle. Dark theme overrides below. */
  --c-play-overlay-bg: rgba(255, 255, 255, 0.85);
  --c-play-overlay-fg: var(--c-gray-800);
  --c-text:         var(--c-gray-800);
  --c-text-muted:   var(--c-gray-600);
  --c-text-subtle:  var(--c-gray-500);
  --c-text-inverse: var(--c-gray-0);
  --c-text-link:    var(--c-brand-700);
  --c-text-on-brand: #ffffff;
  --c-border:       var(--c-gray-200);
  --c-border-strong: var(--c-gray-300);
  --c-focus-ring:   var(--c-brand-500);
  --c-brand:        var(--c-brand-500);
  --c-brand-strong: var(--c-brand-700);
  --c-brand-soft:   var(--c-brand-50);

  /* typography */
  --ff-sans:  "Satoshi", "Satoshi Fallback", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --ff-mono:  ui-monospace, SFMono-Regular, "JetBrains Mono", Menlo, Consolas, monospace;

  --fs-100:  12px;
  --fs-200:  14px;
  --fs-300:  16px;       /* body default · iOS no-zoom on inputs */
  --fs-400:  20px;
  --fs-500:  24px;
  --fs-600:  32px;
  --fs-700:  44px;
  --fs-800:  56px;
  --fs-900:  72px;

  --fw-regular:  400;
  --fw-medium:   500;
  --fw-semibold: 600;
  --fw-bold:     700;

  --lh-tight: 1.15;
  --lh-snug:  1.2;
  --lh-base:  1.5;
  --lh-loose: 1.75;

  /* spacing · 4px scale (Pinterest-dense leans on sp-1..sp-3) */
  --sp-0:   0;
  --sp-px:  1px;
  --sp-1:   4px;
  --sp-2:   8px;
  --sp-3:   12px;
  --sp-4:   16px;
  --sp-5:   20px;
  --sp-6:   24px;
  --sp-8:   32px;
  --sp-10:  40px;
  --sp-12:  48px;
  --sp-16:  64px;
  --sp-20:  80px;
  --sp-24:  96px;

  /* radii */
  --r-sm:    6px;
  --r-md:    12px;
  --r-lg:    20px;
  --r-pill:  9999px;

  /* shadows · soft, low-elevation, brand-neutral */
  --sh-sm:  0 1px 2px rgba(0, 0, 0, 0.05);
  --sh-md:  0 4px 12px rgba(0, 0, 0, 0.08);
  --sh-lg:  0 12px 32px rgba(0, 0, 0, 0.12);
  --sh-focus: 0 0 0 3px rgba(222, 72, 112, 0.32);

  /* layout */
  --container-max:  1280px;     /* Pinterest-friendly wide grid */
  --gutter:         var(--sp-4);

  /* z-index */
  --z-base:     0;
  --z-elevated: 10;
  --z-nav:      100;
  --z-overlay:  500;
  --z-modal:    1000;
  --z-toast:    2000;

  /* motion · duration */
  --d-fast:    100ms;
  --d-base:    150ms;
  --d-medium:  200ms;
  --d-slow:    300ms;

  /* motion · easing */
  --e-out:     cubic-bezier(0.2, 0, 0, 1);
  --e-in:      cubic-bezier(0.4, 0, 1, 1);
  --e-in-out:  cubic-bezier(0.4, 0, 0.2, 1);
  --e-snap:    cubic-bezier(0.5, 0, 0, 1);
  --e-emph:    cubic-bezier(0.05, 0.7, 0.1, 1);
}

/* ─── OKLCH override for capable browsers ────────────────────────────────── */
@supports (color: oklch(0 0 0)) {
  :root {
    /* neutrals — perceptually-even ramp */
    --c-gray-0:    oklch(0.99 0.003 270);
    --c-gray-50:   oklch(0.97 0.003 270);
    --c-gray-100:  oklch(0.94 0.005 270);
    --c-gray-200:  oklch(0.91 0.006 270);
    --c-gray-300:  oklch(0.85 0.008 270);
    --c-gray-400:  oklch(0.72 0.010 270);
    --c-gray-500:  oklch(0.55 0.012 270);
    --c-gray-600:  oklch(0.45 0.014 270);
    --c-gray-700:  oklch(0.35 0.012 270);
    --c-gray-800:  oklch(0.25 0.010 270);
    --c-gray-900:  oklch(0.15 0.008 270);
    --c-gray-950:  oklch(0.08 0.006 270);

    /* rose brand */
    --c-brand-50:   oklch(0.95 0.04 0);
    --c-brand-100:  oklch(0.90 0.07 0);
    --c-brand-300:  oklch(0.78 0.13 0);
    --c-brand-500:  oklch(0.68 0.18 0);
    --c-brand-700:  oklch(0.58 0.20 0);
    --c-brand-900:  oklch(0.40 0.18 0);

    /* semantics */
    --c-success:  oklch(0.65 0.15 145);
    --c-warning:  oklch(0.74 0.16 75);
    --c-error:    oklch(0.60 0.20 28);
    --c-info:     oklch(0.62 0.13 250);
    --c-error-strong: oklch(0.46 0.17 28);
    --c-info-strong:  oklch(0.45 0.16 250);
    /* --c-like stays mode-independent (Pinterest/IG convention). Do NOT
       redefine here — it would defeat the "always the same red" semantics. */

    /* surface overrides — using new OKLCH neutrals */
    --c-bg-overlay:  oklch(0.18 0.005 270 / 0.55);
    --sh-focus:      0 0 0 3px oklch(0.68 0.18 0 / 0.32);
  }
}

/* ─── Dark mode ──────────────────────────────────────────────────────────── *
 * Theme resolution rules:
 *   1. <html class="theme-dark">  → forced dark (manual toggle)
 *   2. <html class="theme-light"> → forced light (manual toggle)
 *   3. <html> (no class)          → follow OS prefers-color-scheme
 *
 * The :not(.theme-light) on the @media query keeps OS-dark from beating
 * the user's explicit "I want light" choice.
 * ──────────────────────────────────────────────────────────────────────── */

/* Reusable dark token block — duplicated in two places below.
   No CSS preprocessor here; one-time copy is the cost of clean overrides. */

@media (prefers-color-scheme: dark) {
  :root:not(.theme-light) {
    --c-bg:            #141417;
    --c-bg-elevated:   #1c1c20;
    --c-bg-overlay:    rgba(0, 0, 0, 0.7);
    /* Dark theme play overlay: dark circle + light triangle. */
    --c-play-overlay-bg: rgba(0, 0, 0, 0.55);
    --c-play-overlay-fg: #ffffff;
    --c-text:          #ededed;
    --c-text-muted:    #a8a8b0;
    --c-text-subtle:   #6e6e76;
    /* `--c-text-inverse` flips with the theme. Light mode has dark text
       on a light page → inverse is light (white), used as foreground on
       dark "system overlay" surfaces (toasts, inverse buttons). Dark mode
       inverts: text is light, so inverse must become DARK to keep these
       overlays readable. Without this override, toasts render as
       light-on-light and disappear into the dark page bg. */
    --c-text-inverse:  #18181b;
    --c-border:        #2c2c32;
    --c-border-strong: #3c3c44;
    /* --c-brand-soft INTENTIONALLY left as light pink (var(--c-brand-50) ≈ #fbe9ee).
       It's used as a chip/badge/active-row surface that pairs with --c-brand-strong
       text — the high-contrast light-pink + dark-pink combo from the language
       switcher. Re-tinting it to dark in dark mode produced low perceived
       contrast with white body text (2026-05-03 user feedback). */
    --c-text-link:     var(--c-brand-300);
    --sh-sm:  0 1px 2px rgba(0, 0, 0, 0.4);
    --sh-md:  0 4px 12px rgba(0, 0, 0, 0.5);
    --sh-lg:  0 12px 32px rgba(0, 0, 0, 0.6);
  }
  @supports (color: oklch(0 0 0)) {
    :root:not(.theme-light) {
      --c-bg:            oklch(0.18 0.005 270);
      --c-bg-elevated:   oklch(0.22 0.006 270);
      --c-text:          oklch(0.92 0.005 270);
      --c-text-muted:    oklch(0.65 0.008 270);
      --c-text-subtle:   oklch(0.45 0.008 270);
      --c-border:        oklch(0.30 0.008 270);
      --c-border-strong: oklch(0.40 0.010 270);
    }
  }
}

/* Manual override — wins regardless of OS preference. */
:root.theme-dark {
  --c-bg:            #141417;
  --c-bg-elevated:   #1c1c20;
  --c-bg-overlay:    rgba(0, 0, 0, 0.7);
  /* Dark theme play overlay: dark circle + light triangle. */
  --c-play-overlay-bg: rgba(0, 0, 0, 0.55);
  --c-play-overlay-fg: #ffffff;
  --c-text:          #ededed;
  --c-text-muted:    #a8a8b0;
  --c-text-subtle:   #6e6e76;
  --c-text-inverse:  #18181b;   /* same rationale as the @media block above */
  --c-border:        #2c2c32;
  --c-border-strong: #3c3c44;
  --c-text-link:     var(--c-brand-300);
  --sh-sm:  0 1px 2px rgba(0, 0, 0, 0.4);
  --sh-md:  0 4px 12px rgba(0, 0, 0, 0.5);
  --sh-lg:  0 12px 32px rgba(0, 0, 0, 0.6);
  color-scheme: dark;
}
@supports (color: oklch(0 0 0)) {
  :root.theme-dark {
    --c-bg:            oklch(0.18 0.005 270);
    --c-bg-elevated:   oklch(0.22 0.006 270);
    --c-text:          oklch(0.92 0.005 270);
    --c-text-muted:    oklch(0.65 0.008 270);
    --c-text-subtle:   oklch(0.45 0.008 270);
    --c-border:        oklch(0.30 0.008 270);
    --c-border-strong: oklch(0.40 0.010 270);
  }
}
:root.theme-light { color-scheme: light; }

/* ─── Brand color presets ────────────────────────────────────────────────── *
 * <html data-brand="rose|coral|amber|emerald|indigo|slate"> overrides
 * --c-brand / --c-brand-strong / --c-brand-soft. Default (no attribute) =
 * rose (already set in :root). Stored in localStorage as `pinthis_brand`.
 * HEX fallback first; OKLCH override inside @supports for modern browsers.
 * ──────────────────────────────────────────────────────────────────────── */
:root[data-brand="rose"]    { --c-brand: #de4870;  --c-brand-strong: #b3284e;  --c-brand-soft: #fbe9ee; --sh-focus: 0 0 0 3px rgba(222, 72, 112, 0.32); }
:root[data-brand="coral"]   { --c-brand: #ff6347;  --c-brand-strong: #d94522;  --c-brand-soft: #fff0ed; --sh-focus: 0 0 0 3px rgba(255, 99, 71, 0.32); }
:root[data-brand="amber"]   { --c-brand: #d49b1a;  --c-brand-strong: #a87900;  --c-brand-soft: #fdf4e0; --sh-focus: 0 0 0 3px rgba(212, 155, 26, 0.32); }
:root[data-brand="emerald"] { --c-brand: #107a4d;  --c-brand-strong: #0a5a39;  --c-brand-soft: #e6f5ee; --sh-focus: 0 0 0 3px rgba(16, 122, 77, 0.32); }
:root[data-brand="indigo"]  { --c-brand: #4a7ed1;  --c-brand-strong: #2e5fa8;  --c-brand-soft: #ecf2fb; --sh-focus: 0 0 0 3px rgba(74, 126, 209, 0.32); }
:root[data-brand="slate"]   { --c-brand: #52525b;  --c-brand-strong: #3f3f46;  --c-brand-soft: #f0f0f3; --sh-focus: 0 0 0 3px rgba(82, 82, 91, 0.32); }

@supports (color: oklch(0 0 0)) {
  :root[data-brand="rose"]    { --c-brand: oklch(0.68 0.18 0);   --c-brand-strong: oklch(0.58 0.20 0);   --c-brand-soft: oklch(0.95 0.04 0); }
  :root[data-brand="coral"]   { --c-brand: oklch(0.72 0.18 30);  --c-brand-strong: oklch(0.62 0.20 30);  --c-brand-soft: oklch(0.95 0.04 30); }
  :root[data-brand="amber"]   { --c-brand: oklch(0.74 0.16 75);  --c-brand-strong: oklch(0.62 0.18 75);  --c-brand-soft: oklch(0.96 0.04 75); }
  :root[data-brand="emerald"] { --c-brand: oklch(0.55 0.14 165); --c-brand-strong: oklch(0.45 0.16 165); --c-brand-soft: oklch(0.95 0.03 165); }
  :root[data-brand="indigo"]  { --c-brand: oklch(0.58 0.16 270); --c-brand-strong: oklch(0.46 0.18 270); --c-brand-soft: oklch(0.95 0.04 270); }
  :root[data-brand="slate"]   { --c-brand: oklch(0.45 0.02 270); --c-brand-strong: oklch(0.32 0.02 270); --c-brand-soft: oklch(0.95 0.005 270); }
}

/* ─── Reduced motion (skill hard rule #7) ────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration:    0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration:   0.01ms !important;
    scroll-behavior:       auto !important;
  }
}

/* ─── Body baseline (apply once globally) ────────────────────────────────── */

/* Global border-box reset — every page can rely on it, so per-page CSS
   never has to re-declare. Lived in feed.css before; promoted here so
   pages that don't load feed.css (preferences, categories) also get it.
   Without this, a `.container { max-width: 1280; padding: 16 }` element
   renders 1312px wide and the site-header looks visibly different from
   feed pages. */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  color-scheme: light dark;
  /* Prevent horizontal scroll from `100vw` break-outs (e.g. .site-header.is-scrolled
     spans full viewport via margin-left/right tricks; on platforms where the
     vertical scrollbar takes layout width, 100vw exceeds the layout viewport
     and triggers a phantom horizontal scrollbar during sticky transitions). */
  overflow-x: clip;
  /* Reserve scrollbar gutter so the layout doesn't shift width when the page
     becomes scrollable. Modern browsers (Chrome 94+, Safari 16+, FF 97+). */
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  font-family: var(--ff-sans);
  font-size: var(--fs-300);
  line-height: var(--lh-base);
  color: var(--c-text);
  background-color: var(--c-bg);
  /* Sandbox proved that browser-default rendering looks crispest with
     Satoshi on Mac. Avoid -webkit-font-smoothing / text-rendering / font-synthesis
     overrides — they can soften strokes on some font + OS combos. */
  font-feature-settings: "kern", "ss01";
}

/* Default focus ring (skill hard rule: visible focus on every focusable) */
:focus-visible {
  outline: 2px solid var(--c-focus-ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

::selection {
  background-color: var(--c-brand);
  color: var(--c-text-on-brand);
}

/* Form inputs: 16px to prevent iOS auto-zoom (skill rule) */
input, textarea, select { font-size: var(--fs-300); }
