// app.jsx — Sauvrn AI / v5
// ─────────────────────────────────────────────────────────────────────────────
// Reset of the v4 dispatch direction. Keeps the visual rigor — dark cool field,
// mono-heavy type system, heavy Bricolage display moments, coral as signal,
// strong horizontal rules — but strips the broadcast/dispatch vocabulary that
// read as cosplay. No "TRANSMISSION", no "ARTICLES OF OPERATION", no live
// clock, no scanlines. Just a serious, restrained dark page.

const { useState, useEffect, useRef } = React;

// ─── Tweakable defaults ──────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "mode": "dark",
  "accentHue": "vermillion",
  "messageFont": "grotesque",
  "wordmarkFont": "grotesque",
  "showPrinciples": true,
  "formStatePreview": "live",
  "headlineCopy": "default",
  "overlayOpacity": 0.9,
  "overlayColor": "cream"
}/*EDITMODE-END*/;

// Overlay color presets for the AI layer.
// Each pairs a 'dark' value (used on the dark surface) with a 'light' value
// (used on the light surface). 'cream' is brand-cohesive default; the others
// are exploratory complementaries.
const OVERLAYS = {
  teal:   { dark: '#4BB5A7', light: '#1B7A72', label: 'Teal' },
  cream:  { dark: '#E8E4D6', light: '#0F1117', label: 'Cream' },
  navy:   { dark: '#7894C2', light: '#1A2A45', label: 'Navy' },
  forest: { dark: '#62AE82', light: '#1D3F2F', label: 'Forest' },
  plum:   { dark: '#C16985', light: '#4A1F2E', label: 'Plum' },
  white:  { dark: '#FFFFFF', light: '#000000', label: 'White' },
};

// ─── Surfaces ────────────────────────────────────────────────────────────────
const MODES = {
  dark: {
    bg:        '#0A0B10',
    surface:   '#101218',
    fg:        '#E8E4D6',
    muted:     '#7A7C86',
    line:      'rgba(232,228,214,0.12)',
    lineStrong:'rgba(232,228,214,0.28)',
    isDark:    true,
  },
  light: {
    bg:        '#E5E6E1',
    surface:   '#EDEEE9',
    fg:        '#0F1117',
    muted:     '#5C5F66',
    line:      'rgba(15,17,23,0.13)',
    lineStrong:'rgba(15,17,23,0.30)',
    isDark:    false,
  },
};

// ─── Signal hues ─────────────────────────────────────────────────────────────
const ACCENTS = {
  vermillion: { light: '#D9483A', dark: '#E55A4B' },
  rust:       { light: '#C44132', dark: '#D2503E' },
  signal:     { light: '#E14431', dark: '#EE5440' },
};

// ─── Fonts ───────────────────────────────────────────────────────────────────
// Per-font tuning for the wordmark — different faces need different
// letter-spacing and weights to read at the same density.
const WM_TUNING = {
  inter:     { weight: 800, letterSpacing: '-0.04em' },
  onest:     { weight: 800, letterSpacing: '-0.04em' },
  grotesque: { weight: 700, letterSpacing: '-0.015em' },
  boldonse:  { weight: 400, letterSpacing: '-0.02em' },
};

const FONTS = {
  inter:     '"Inter", "Helvetica Neue", system-ui, sans-serif',
  grotesque: '"Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif',
  onest:     '"Onest", ui-sans-serif, system-ui, sans-serif',
  boldonse:  '"Boldonse", "Arial Black", sans-serif',
  serif:     '"Newsreader", Georgia, serif',
  mono:      '"JetBrains Mono", ui-monospace, monospace',
};

const HEADLINES = {
  default: 'A calmer way to work across browser tools.',
  shorter: 'A calmer way to work in the browser.',
  product: 'Stay oriented across every tab you open.',
};

// ─── App ─────────────────────────────────────────────────────────────────────
function App() {
  // Tweaks panel was a dev affordance; deleted before launch. The defaults
  // ARE the locked-in design, so we just freeze them here. setTweak is kept
  // as a no-op so child components that still accept the prop don't crash.
  const t = TWEAK_DEFAULTS;
  const setTweak = () => {};
  const mode = MODES[t.mode] || MODES.dark;
  const accentSet = ACCENTS[t.accentHue] || ACCENTS.vermillion;
  const accent = mode.isDark ? accentSet.dark : accentSet.light;
  const messageFont = FONTS[t.messageFont] || FONTS.inter;
  const wordmarkFont = FONTS[t.wordmarkFont] || FONTS.inter;

  const rootStyle = {
    '--bg': mode.bg,
    '--surface': mode.surface,
    '--fg': mode.fg,
    '--accent': accent,
    '--secondary': mode.isDark ? (OVERLAYS[t.overlayColor]?.dark || '#4BB5A7') : (OVERLAYS[t.overlayColor]?.light || '#1B7A72'),
    '--muted': mode.muted,
    '--line': mode.line,
    '--line-strong': mode.lineStrong,
    '--font-message': messageFont,
    '--font-wordmark': wordmarkFont,
    '--font-mono': FONTS.mono,
    background: mode.bg,
    color: mode.fg,
    minHeight: '100vh',
    fontFamily: FONTS.mono,
  };

  return (
    <div style={rootStyle}>
      <Page t={t} setTweak={setTweak} mode={mode} />
    </div>
  );
}

// ─── Page ────────────────────────────────────────────────────────────────────
function Page({ t, setTweak, mode }) {
  const headline = HEADLINES[t.headlineCopy] || HEADLINES.default;

  return (
    <main className="page" data-screen-label="01 Teaser">
      <PageStyles />

      {/* Minimal top bar — abstract mark placeholder (left) + stealth tag (right) */}
      <header className="top">
        <div className="top-mark" aria-label="Mark">
          <img src="mark.png" alt="" className="mark-img" />
        </div>
        <span className="top-tag">
          <span className="top-dot" aria-hidden="true" />
          Stealth · 2026
        </span>
      </header>

      {/* Wordmark — the showpiece, full column width */}
      <section className="wm-band">
        <SauvrnWordmark
          size={260}
          fillContainer
          overlayColor={mode.isDark ? (OVERLAYS[t.overlayColor]?.dark || '#E8E4D6') : (OVERLAYS[t.overlayColor]?.light || '#0F1117')}
          overlayOpacity={t.overlayOpacity}
          fontFamily={FONTS[t.wordmarkFont] || FONTS.inter}
          fontWeight={(WM_TUNING[t.wordmarkFont] || WM_TUNING.inter).weight}
          letterSpacing={(WM_TUNING[t.wordmarkFont] || WM_TUNING.inter).letterSpacing}
        />
      </section>

      {/* Headline + sub */}
      <section className="message">
        <h1 className="headline">{headline}</h1>
        <p className="subpara">
          Most modern work happens across dozens of tabs, documents, and apps. Sauvrn is building privacy-first tools to help people stay oriented and reduce context switching.
        </p>
      </section>

      {/* Form */}
      <section className="form-band">
        <SignupForm previewState={t.formStatePreview} />
      </section>

      {/* Principles */}
      {t.showPrinciples && (
        <section className="principles">
          <Principles />
        </section>
      )}

      {/* Footer */}
      <footer className="footer">
        <span>Sauvrn AI</span>
        <span className="sep">·</span>
        <span className="desktop-only">Toronto, Canada</span>
        <span className="sep desktop-only">·</span>
        <a href="mailto:hello@sauvrn.ai">hello@sauvrn.ai</a>
        <span className="sep desktop-only">·</span>
        <span className="desktop-only">© 2026</span>
      </footer>
    </main>
  );
}

// ─── Styles ──────────────────────────────────────────────────────────────────
function PageStyles() {
  return (
    <style>{`
      .page {
        position: relative; z-index: 2;
        max-width: 880px;
        margin: 0 auto;
        padding: 24px 28px 56px;
        display: flex; flex-direction: column;
        min-height: 100vh;
        box-sizing: border-box;
      }
      @media (min-width: 720px) {
        .page { padding: 32px 56px 80px; }
      }
      @media (min-width: 1024px) {
        .page { padding: 40px 72px 96px; }
      }

      /* Top bar — abstract mark on the left, stealth tag on the right */
      .top {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 16px;
        margin-bottom: clamp(48px, 9vh, 96px);
      }
      .top-mark {
        display: inline-flex; align-items: center;
        min-width: 32px;
      }
      .mark-img {
        display: block;
        width: 28px; height: 28px;
        border-radius: 4px;
        /* The mark is a high-saturation orange that drifts toward Claude's
           hue; the brand's coral is redder. Slight hue shift via filter
           pulls it back toward vermillion without re-rendering the asset. */
        filter: hue-rotate(-6deg) saturate(0.92);
      }
      /* Placeholder retained for when the mark isn't ready. */
      .mark-placeholder {
        display: inline-flex; align-items: center; justify-content: center;
        width: 32px; height: 32px;
        font-family: var(--font-mono);
        font-size: 9px;
        letter-spacing: 0.18em;
        text-transform: uppercase;
        color: rgba(122,124,134,0.55);
        border: 1px dashed rgba(232,228,214,0.18);
        border-radius: 2px;
      }
      .top-tag {
        font-family: var(--font-mono);
        font-size: 11px;
        letter-spacing: 0.18em;
        text-transform: uppercase;
        color: var(--muted);
        display: inline-flex; align-items: center; gap: 10px;
        white-space: nowrap;
      }
      .top-dot {
        width: 6px; height: 6px;
        border-radius: 50%;
        background: var(--accent);
        animation: pulse 3.5s ease-in-out infinite;
      }
      @keyframes pulse { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } }
      @media (prefers-reduced-motion: reduce) { .top-dot { animation: none; } }

      /* Wordmark band — generous breathing room above and below */
      .wm-band {
        display: flex;
        align-items: center;
        justify-content: flex-start;
        margin-bottom: clamp(64px, 12vh, 128px);
      }

      /* Message — heavy display headline + mono body */
      .message {
        display: flex; flex-direction: column; gap: 28px;
        margin-bottom: clamp(56px, 10vh, 112px);
        max-width: 620px;
      }
      .headline {
        font-family: var(--font-message);
        font-weight: 700;
        font-size: clamp(34px, 5.2vw, 60px);
        line-height: 1.04;
        letter-spacing: -0.03em;
        color: var(--fg);
        margin: 0;
        max-width: 18ch;
        text-wrap: balance;
        font-variation-settings: 'wdth' 92, 'opsz' 96;
      }
      .subpara {
        font-family: var(--font-mono);
        font-size: 13.5px;
        line-height: 1.65;
        color: var(--fg);
        opacity: 0.72;
        margin: 0;
        max-width: 60ch;
      }

      /* Form */
      .form-band { margin-bottom: clamp(56px, 10vh, 112px); max-width: 520px; }
      .form-row {
        display: grid;
        grid-template-columns: 1fr auto;
        gap: 18px;
        align-items: stretch;
        /* Subtle cream wash hints at the input field as a surface —
           a quiet nod to the secondary color used by the AI overlay
           and the principle numbers. 6% at rest reads as "interactive
           field" before interaction; deepens on focus. */
        background: color-mix(in srgb, var(--secondary) 6%, transparent);
        padding: 14px 16px 18px 16px;
        border-radius: 2px;
        border-bottom: 1px solid var(--line);
        position: relative;
        transition: background 200ms ease;
      }
      .form-row:focus-within {
        background: color-mix(in srgb, var(--secondary) 10%, transparent);
      }
      .form-row::after {
        content: '';
        position: absolute; left: 0; right: 0; bottom: -1px;
        height: 1px;
        background: var(--accent);
        transform: scaleX(0); transform-origin: left;
        transition: transform 320ms cubic-bezier(.2,.7,.2,1);
      }
      .form-row:focus-within::after { transform: scaleX(1); }
      .form-row[data-state="invalid"]::after,
      .form-row[data-state="error"]::after { transform: scaleX(1); }
      .form-input {
        font-family: var(--font-mono);
        font-size: 15px;
        background: transparent;
        border: 0; outline: none;
        color: var(--fg);
        padding: 8px 0;
        min-width: 0;
      }
      /* Autofill override — kill Chrome/Safari yellow, match it to the
         subtle cream wash so the field stays visually unified when populated. */
      .form-input:-webkit-autofill,
      .form-input:-webkit-autofill:hover,
      .form-input:-webkit-autofill:focus {
        -webkit-text-fill-color: var(--fg);
        -webkit-box-shadow: 0 0 0 1000px color-mix(in srgb, var(--secondary) 6%, var(--bg)) inset;
        caret-color: var(--fg);
        transition: background-color 9999s ease-in-out 0s;
      }
      .form-input::placeholder { color: color-mix(in srgb, var(--muted) 75%, transparent); }
      .form-input:disabled { opacity: 0.7; }
      .form-submit {
        font-family: var(--font-mono);
        font-size: 13px;
        font-weight: 500;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        background: transparent;
        color: var(--fg);
        border: 0;
        cursor: pointer;
        padding: 8px 4px 8px 0;
        display: inline-flex; align-items: center; gap: 10px;
        white-space: nowrap;
        transition: color 150ms ease, gap 200ms cubic-bezier(.2,.7,.2,1);
      }
      .form-submit:hover:not(:disabled) { color: var(--accent); gap: 14px; }
      .form-submit:disabled { opacity: 0.5; cursor: default; }
      .form-msg {
        min-height: 22px;
        margin-top: 12px;
        font-family: var(--font-mono);
        font-size: 12.5px;
        color: var(--accent);
      }
      .form-success {
        font-family: var(--font-mono);
        font-size: 14.5px;
        color: var(--fg);
        line-height: 1.5;
        padding: 14px 0 18px;
        border-bottom: 1px solid var(--accent);
        max-width: 520px;
      }
      .form-success .ack {
        display: inline-flex; gap: 8px; align-items: center;
        font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase;
        color: var(--accent); margin-bottom: 10px;
      }
      .form-success .ack::before {
        content: ''; display: inline-block;
        width: 7px; height: 7px;
        background: var(--accent);
        transform: rotate(45deg);
      }
      .spinner {
        width: 9px; height: 9px;
        border-radius: 50%;
        border: 1.4px solid color-mix(in srgb, var(--fg) 30%, transparent);
        border-top-color: var(--fg);
        animation: spin 700ms linear infinite;
      }
      @keyframes spin { to { transform: rotate(360deg); } }

      /* Principles — minimal label, hairline-separated rows */
      .principles {
        margin-bottom: clamp(56px, 10vh, 112px);
        max-width: 720px;
      }
      .principles-label {
        font-family: var(--font-mono);
        font-size: 11px;
        font-weight: 500;
        letter-spacing: 0.18em;
        text-transform: uppercase;
        color: var(--muted);
        margin: 0 0 24px 0;
      }
      .principle {
        display: grid;
        grid-template-columns: 64px 1fr;
        gap: 28px;
        padding: 24px 0;
        border-top: 1px solid var(--line);
        align-items: baseline;
      }
      .principle:last-child { border-bottom: 1px solid var(--line); }
      .principle-num {
        font-family: var(--font-mono);
        font-size: 11px;
        font-weight: 500;
        letter-spacing: 0.14em;
        color: var(--secondary);
      }
      .principle-text {
        font-family: var(--font-message);
        font-size: clamp(20px, 2.4vw, 26px);
        font-weight: 600;
        line-height: 1.2;
        color: var(--fg);
        margin: 0;
        letter-spacing: -0.018em;
        font-variation-settings: 'wdth' 96, 'opsz' 28;
        text-wrap: balance;
      }
      @media (max-width: 560px) {
        .principle { grid-template-columns: 44px 1fr; gap: 16px; padding: 18px 0; }
      }

      /* Footer */
      .footer {
        margin-top: auto;
        padding-top: 24px;
        border-top: 1px solid var(--line);
        display: flex; flex-wrap: wrap; gap: 6px 16px;
        font-family: var(--font-mono);
        font-size: 11.5px;
        letter-spacing: 0.04em;
        color: var(--muted);
        align-items: center;
      }
      .footer .sep { opacity: 0.5; }
      .footer a {
        color: var(--accent);
        text-decoration: none;
        border-bottom: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
        padding-bottom: 1px;
        transition: color 150ms ease, border-color 150ms ease;
      }
      .footer a:hover { color: var(--fg); border-bottom-color: var(--fg); }
      @media (max-width: 560px) {
        .footer .desktop-only { display: none; }
      }
    `}</style>
  );
}

// ─── Principles ──────────────────────────────────────────────────────────────
function Principles() {
  const items = [
    'Organization without disruption.',
    'Privacy by default.',
    'Practical AI, grounded in your own work.',
  ];
  return (
    <>
      <h2 className="principles-label">Principles</h2>
      {items.map((text, i) => (
        <div className="principle" key={i}>
          <span className="principle-num">{String(i + 1).padStart(2, '0')}</span>
          <p className="principle-text">{text}</p>
        </div>
      ))}
    </>
  );
}

// ─── Signup Form ─────────────────────────────────────────────────────────────
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function SignupForm({ previewState = 'live' }) {
  const [email, setEmail] = useState('');
  const [state, setState] = useState('idle');
  const honeyRef = useRef(null);

  useEffect(() => {
    if (previewState && previewState !== 'live') {
      setState(previewState);
      if (previewState === 'success') setEmail('cam@sauvrn.ai');
      else if (previewState === 'invalid') setEmail('not-an-email');
      else if (previewState === 'error') setEmail('cam@sauvrn.ai');
      else if (previewState === 'loading') setEmail('cam@sauvrn.ai');
    } else {
      setState('idle');
    }
  }, [previewState]);

  const onSubmit = async (e) => {
    e.preventDefault();
    if (previewState !== 'live') return;
    if (honeyRef.current?.value) return;
    if (!EMAIL_RE.test(email)) { setState('invalid'); return; }
    setState('loading');
    try {
      const res = await fetch('/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, website: honeyRef.current?.value || '' }),
      });
      const data = await res.json();
      setState(data.ok ? 'success' : 'error');
    } catch {
      setState('error');
    }
  };

  if (state === 'success') {
    return (
      <div className="form-success" role="status">
        <span className="ack">Subscribed</span>
        <div>Thanks. We'll be in touch when there's something to share.</div>
      </div>
    );
  }

  return (
    <form onSubmit={onSubmit} noValidate>
      <div className="form-row" data-state={state}>
        <input
          className="form-input"
          type="email"
          name="email"
          autoComplete="email"
          spellCheck={false}
          placeholder="your@email.com"
          value={email}
          onChange={(e) => { setEmail(e.target.value); if (state === 'invalid' || state === 'error') setState('idle'); }}
          disabled={state === 'loading'}
          aria-invalid={state === 'invalid' ? 'true' : 'false'}
        />
        <input ref={honeyRef} type="text" name="website" tabIndex={-1} autoComplete="off"
               aria-hidden="true"
               style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, opacity: 0 }} />
        <button className="form-submit" type="submit" disabled={state === 'loading'}>
          <span>{state === 'loading' ? 'Sending' : 'Get updates'}</span>
          {state === 'loading' ? <span className="spinner" aria-hidden="true" /> : <span aria-hidden="true">→</span>}
        </button>
      </div>
      <div className="form-msg" role="status" aria-live="polite">
        {state === 'invalid' && 'Please enter a valid email address.'}
        {state === 'error'   && 'Something went wrong. Please try again.'}
      </div>
    </form>
  );
}

// ─── Mount ───────────────────────────────────────────────────────────────────
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
