// Vanta · Direction 3 — Garden
// Soft, biomorphic. Bricolage Grotesque + Instrument Serif italic accents.
// Each life-area is a "tile" with rounded organic shape + a single ring as
// its visual heartbeat. Warm dusty palette, very low chroma.
//
// Palettes: three low-chroma moods (Dusk / Fog / Clay), toggled by the
// button in the header. Active palette flows to every tile via context.
//
// Desktop shares the mobile app's store (vanta.app.v1) and its interactive
// pieces: VMoves, VHabitsCard, VHorizonPanel, VAreaDetail, VSourcesScreen.
// Tiles open a centered detail overlay; the hero grows a thought composer.

const GARDEN_PALETTES = {
  dusk: {
    name: 'dusk',
    bg: '#ebe5d8', bg2: '#f3eee2',
    ink: '#1c1a14', ink2: '#4a463c', ink3: '#7a7468', ink4: '#a8a094',
    tile: '#f7f3e8', tileWarm: '#f0e7d3', tileCool: '#e3e7df',
    tilePink: '#ecdfd7', tileSky: '#dde3e3', tileMoss: '#dde2cf',
    accent: '#5e6b4a', rust: '#8a5a3a', sky: '#4a6378',
    rule: 'rgba(28,26,20,0.08)', ring: 'rgba(28,26,20,0.07)',
    chip: 'rgba(28,26,20,0.05)', scrim: 'rgba(28,26,20,0.35)',
  },
  fog: {
    name: 'fog',
    bg: '#e3e6e6', bg2: '#eef1f0',
    ink: '#16191b', ink2: '#3d4347', ink3: '#697076', ink4: '#98a0a4',
    tile: '#f1f4f3', tileWarm: '#e8eceb', tileCool: '#dfe6e6',
    tilePink: '#e6e3e4', tileSky: '#d9e3e8', tileMoss: '#dce5e1',
    accent: '#3f706c', rust: '#8a6a5c', sky: '#4c6a86',
    rule: 'rgba(22,25,27,0.08)', ring: 'rgba(22,25,27,0.07)',
    chip: 'rgba(22,25,27,0.05)', scrim: 'rgba(22,25,27,0.35)',
  },
  // The house palette: vanta black ground, white ink, magenta accents.
  vanta: {
    name: 'vanta',
    bg: '#08080a', bg2: '#141118',
    ink: '#f4f2f7', ink2: '#cfcbd9', ink3: '#8f8a9e', ink4: '#5f5a6e',
    tile: '#161419', tileWarm: '#1b1512', tileCool: '#12161a',
    tilePink: '#1d1219', tileSky: '#10151c', tileMoss: '#131a13',
    accent: '#d946ef', rust: '#f472b6', sky: '#b4aecb',
    rule: 'rgba(244,242,247,0.10)', ring: 'rgba(244,242,247,0.09)',
    chip: 'rgba(244,242,247,0.07)', scrim: 'rgba(0,0,0,0.6)',
  },
};
const GARDEN_PALETTE_ORDER = ['dusk', 'fog', 'vanta'];

const GardenCtx = React.createContext(GARDEN_PALETTES.dusk);
const useP = () => React.useContext(GardenCtx);

// Layout tiers — a tab sharing the screen reflows instead of cracking.
// cols drives the tile grid; compact stacks the hero and themes; tight trims
// the header down to its controls. Below 700px the shell hands off to the
// phone app entirely (see index.html).
const GardenLayoutCtx = React.createContext({ cols: 4, compact: false, tight: false });
const useL = () => React.useContext(GardenLayoutCtx);

function useViewportWidth() {
  const [w, setW] = React.useState(window.innerWidth);
  React.useEffect(() => {
    const on = () => setW(window.innerWidth);
    window.addEventListener('resize', on);
    return () => window.removeEventListener('resize', on);
  }, []);
  return w;
}

function gStyles(P) {
  return {
    page: {
      background: `radial-gradient(1200px 800px at 30% -10%, ${P.bg2}, ${P.bg} 60%)`,
      color: P.ink,
      fontFamily: '"Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif',
      fontSize: 13, lineHeight: 1.45,
      width: '100%', minHeight: '100%',
      padding: '22px 28px 40px', boxSizing: 'border-box',
    },
    num: { fontFamily: '"Bricolage Grotesque", sans-serif', fontFeatureSettings: '"tnum" 1', fontVariationSettings: '"wdth" 90', letterSpacing: '-0.02em' },
    serif: { fontFamily: '"Instrument Serif", "Newsreader", serif', fontStyle: 'italic', fontWeight: 400, letterSpacing: '-0.01em' },
    caps: { fontSize: 10, letterSpacing: '0.18em', textTransform: 'uppercase', color: P.ink3, fontWeight: 500 },
  };
}

// The hero's little solar system. A white sun, faint orbit rings, and four
// brand-fixed planets that orbit at different speeds — vanta black, then
// purple-magenta, then rose-pink, then sage green. Like the vanta-black V,
// the planet colors never follow the palette; only the rings tint with it.
// Every body carries a hairline stroke so it reads on cream and on black.
// Motion is CSS (.v-orbit keyframes in index.html) so
// prefers-reduced-motion parks it.
const V_PLANETS = [
  { r: 30, dur: 9,  pr: 5.5, color: '#0a0a0a' },  // vanta black — innermost, fastest
  { r: 49, dur: 15, pr: 6.5, color: '#c026d3' },  // purple / magenta
  { r: 69, dur: 24, pr: 5,   color: '#f9a8d4' },  // rose / pink
  { r: 90, dur: 38, pr: 4.5, color: '#9caf88' },  // sage green — outermost, slowest
];
function VantaOrbit({ size = 230, P }) {
  return (
    <svg viewBox="0 0 200 200" width={size} height={size} style={{ display: 'block', overflow: 'visible' }}
      role="img" aria-label="an orbiting system of small planets">
      {V_PLANETS.map((o, i) => (
        <circle key={'ring' + i} cx="100" cy="100" r={o.r} fill="none"
          stroke={P.ink} strokeOpacity="0.11" strokeWidth="0.75" />
      ))}
      {/* Core: a white sun with a faint halo. */}
      <circle cx="100" cy="100" r="19" fill="none" stroke={P.ink} strokeOpacity="0.18" strokeWidth="1.4" />
      <circle cx="100" cy="100" r="12.5" fill="#ffffff" stroke={P.ink4} strokeOpacity="0.6" strokeWidth="0.7" />
      <circle cx="103" cy="96" r="3.5" fill={P.ink4} opacity="0.18" />
      {V_PLANETS.map((o, i) => (
        <g key={'planet' + i} className="v-orbit"
          style={{ animationDuration: o.dur + 's', animationDirection: i % 2 ? 'reverse' : 'normal' }}>
          <circle cx="100" cy={100 - o.r} r={o.pr} fill={o.color}
            stroke={P.ink4} strokeOpacity="0.55" strokeWidth="0.6" />
        </g>
      ))}
    </svg>
  );
}

function PaletteToggle({ current, onCycle }) {
  const P = useP();
  const S = gStyles(P);
  return (
    <button
      onClick={onCycle}
      style={{
        appearance: 'none', border: 0, cursor: 'pointer',
        display: 'flex', alignItems: 'center', gap: 8,
        padding: '5px 6px 5px 12px', borderRadius: 999,
        background: P.chip, color: P.ink2,
        fontFamily: 'inherit', fontSize: 11,
      }}
      title="Cycle palette"
    >
      <span style={{ ...S.caps, color: P.ink3 }}>palette · {current}</span>
      <span style={{ display: 'flex', gap: 3 }}>
        {[P.accent, P.rust, P.sky].map((c, i) => (
          <span key={i} style={{ width: 12, height: 12, borderRadius: '50%', background: c, boxShadow: `inset 0 0 0 1px ${P.bg}` }} />
        ))}
      </span>
    </button>
  );
}

function GardenHeader({ data, palette, onCyclePalette, onArrange, horizon, onHorizon }) {
  const P = useP();
  const L = useL();
  const S = gStyles(P);
  const d = data.user.date;
  return (
    <header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 10, marginBottom: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <VantaM P={P} size={30} fontSize={26} />
        <div style={{ ...S.serif, fontSize: 24, color: P.ink }}>vanta</div>
        {!L.compact && <span style={{ ...S.caps, marginLeft: 8 }}>tending {data.user.name}'s garden</span>}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: L.tight ? 10 : 16, flexWrap: 'wrap' }}>
        {!L.tight && <span style={{ ...S.caps }}>{fmtDate(d)}</span>}
        {!L.tight && <span style={{ ...S.caps }}>{data.greeting.weather.temp}° · {data.greeting.weather.sky}</span>}
        <button onClick={() => { window.location.href = '/vantage'; }} style={{
          appearance: 'none', border: 0, cursor: 'pointer',
          padding: '6px 12px', borderRadius: 999,
          background: P.chip, fontFamily: 'inherit',
          ...S.caps, color: P.ink2,
        }}>vantage</button>
        <button onClick={() => { window.location.href = '/agents'; }} style={{
          appearance: 'none', border: 0, cursor: 'pointer',
          padding: '6px 12px', borderRadius: 999,
          background: P.chip, fontFamily: 'inherit',
          ...S.caps, color: P.ink2,
        }}>agents</button>
        <button onClick={onArrange} aria-label="arrange cards" title="Arrange cards" style={{
          appearance: 'none', border: 0, cursor: 'pointer',
          padding: '6px 10px', borderRadius: 999,
          background: P.chip, fontFamily: 'inherit',
          display: 'grid', placeItems: 'center', color: P.ink2,
        }}>
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round">
            <rect x="3" y="4" width="8" height="7" rx="1.5" />
            <rect x="14" y="4" width="7" height="10" rx="1.5" />
            <rect x="3" y="14" width="8" height="6" rx="1.5" />
          </svg>
        </button>
        <PaletteToggle current={palette} onCycle={onCyclePalette} />
        <div style={{
          display: 'flex', padding: 3, gap: 2,
          background: P.chip, borderRadius: 999, fontSize: 11,
        }}>
          {['today', 'week', 'month', 'year'].map((h) => (
            <button key={h} onClick={() => onHorizon(h)} style={{
              appearance: 'none', border: 0, cursor: 'pointer', fontFamily: 'inherit', fontSize: 11,
              padding: '4px 12px', borderRadius: 999,
              background: horizon === h ? P.tile : 'transparent',
              color: horizon === h ? P.ink : P.ink3,
              fontWeight: horizon === h ? 500 : 400,
              transition: 'background .15s',
            }}>{h}</button>
          ))}
        </div>
      </div>
    </header>
  );
}

function GardenHero({ data, store, update, onSources, onOpenArea }) {
  const P = useP();
  const L = useL();
  const S = gStyles(P);
  const [plantOpen, setPlantOpen] = React.useState(false);
  const [text, setText] = React.useState('');
  const inputRef = React.useRef(null);
  React.useEffect(() => { if (plantOpen && inputRef.current) inputRef.current.focus(); }, [plantOpen]);

  const plant = () => {
    const t = text.trim();
    if (!t) { setPlantOpen(false); return; }
    vantaPost('/vanta/thought', { text: t });
    update(s => ({
      thoughts: [{ text: t, ts: Date.now() }, ...s.thoughts].slice(0, 50),
      lastTouch: { ...s.lastTouch, thoughts: Date.now() },
    }));
    setText('');
    setPlantOpen(false);
  };

  const chips = [
    { t: 'plant a thought', fill: true, on: () => setPlantOpen(true) },
    { t: 'see the week', on: () => update({ horizon: 'week' }) },
    { t: 'manage sources', on: onSources },
  ];

  return (
    <section style={{
      display: 'grid', gridTemplateColumns: L.compact ? '1fr' : '1.6fr 1fr', gap: 14, marginBottom: 14,
    }}>
      <div style={{
        background: P.tileWarm,
        borderRadius: 26, padding: '20px 26px',
        position: 'relative', overflow: 'hidden',
      }}>
        {!L.tight && (
          <div style={{ position: 'absolute', right: 26, top: '50%', transform: 'translateY(-50%)', opacity: 0.95, pointerEvents: 'none' }}>
            <VantaOrbit size={210} P={P} />
          </div>
        )}
        <div style={{ ...S.caps, color: P.rust, marginBottom: 14, position: 'relative', display: 'flex', alignItems: 'center', gap: 12 }}>
          <span>vanta · the morning</span>
          <VMoodFace P={P} size={26} />
        </div>
        <h1 style={{
          ...S.serif, fontSize: L.tight ? 26 : 32, lineHeight: 1.05, margin: 0,
          color: P.ink, maxWidth: 520, textWrap: 'pretty',
        }}>
          {data.greeting.salutation}<br />
          <span style={{ color: P.ink2 }}>“{data.reflections[new Date().getDate() % data.reflections.length]}”</span>
        </h1>
        <p style={{ marginTop: 12, fontSize: 13, color: P.ink2, maxWidth: 460 }}>
          {data.greeting.pulse}
        </p>
        <VGlance P={P} style={{ marginTop: 12, position: 'relative' }} />
        {plantOpen ? (
          <div style={{
            display: 'grid', gridTemplateColumns: '1fr auto', gap: 10, alignItems: 'center',
            background: P.ink, borderRadius: 999, padding: '6px 6px 6px 20px',
            minHeight: 48, maxWidth: 480, marginTop: 14, boxSizing: 'border-box',
            position: 'relative',
          }}>
            <input
              ref={inputRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if (e.key === 'Enter') plant(); if (e.key === 'Escape') { setText(''); setPlantOpen(false); } }}
              placeholder="what's on your mind…"
              style={{
                appearance: 'none', border: 0, outline: 'none', background: 'transparent',
                color: P.bg, fontFamily: '"Instrument Serif", serif', fontStyle: 'italic', fontSize: 16,
                width: '100%', padding: 0,
              }}
            />
            <button onClick={plant} aria-label="plant thought" style={{
              appearance: 'none', border: 0, cursor: 'pointer',
              width: 36, height: 36, borderRadius: '50%', background: P.bg, color: P.ink,
              display: 'grid', placeItems: 'center',
            }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 19V5M5 12l7-7 7 7" /></svg>
            </button>
          </div>
        ) : (
          <div style={{ display: 'flex', gap: 10, marginTop: 14, flexWrap: 'wrap', position: 'relative' }}>
            {chips.map((c) => (
              <button key={c.t} className="g-chip" onClick={c.on} style={{
                appearance: 'none', border: 0, padding: '8px 14px', borderRadius: 999,
                background: c.fill ? P.ink : 'transparent',
                color: c.fill ? P.bg : P.ink2,
                boxShadow: c.fill ? 'none' : `inset 0 0 0 0.5px ${P.ink3}`,
                fontFamily: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
              }}>{c.t}</button>
            ))}
          </div>
        )}
      </div>

      <div style={{
        display: 'grid',
        gridTemplateColumns: L.compact ? '1fr 1fr' : '1fr',
        gridTemplateRows: L.compact ? 'auto' : '1fr 1fr',
        gap: L.compact ? 14 : 16,
      }}>
        <VMoves P={P} store={store} update={update} inRow />
        <VHabitsCard P={P} data={data} store={store} vertical onOpen={() => onOpenArea('habits')} />
      </div>
    </section>
  );
}

function tileBg(P, k) {
  return ({
    health: P.tileWarm, sleep: P.tileSky, thoughts: P.tilePink,
    finance: P.tile, work: P.tile, professional: P.tilePink,
    learning: P.tileWarm, home: P.tileCool, hobbies: P.tileMoss, habits: P.tileMoss,
  })[k] || P.tile;
}
function tileRing(P, k) {
  return ({
    health: P.rust, sleep: P.sky, thoughts: P.rust,
    finance: P.accent, work: P.ink, professional: P.rust,
    learning: P.accent, home: P.sky, hobbies: P.accent, habits: P.accent,
  })[k] || P.accent;
}

function GardenAreaTile({ area, areaKey, big = false, onAct, onOpen, onLink }) {
  const P = useP();
  const S = gStyles(P);
  const ringColor = tileRing(P, areaKey);

  // Whole tile drills in; Enter/Space from the tile itself does the same.
  // Inner act-rows and the link CTA stop propagation so a tap stays a tap.
  const tileProps = {
    className: 'g-tile',
    role: 'button',
    tabIndex: 0,
    onClick: () => onOpen && onOpen(),
    onKeyDown: (e) => {
      if (e.target !== e.currentTarget) return;
      if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onOpen && onOpen(); }
    },
  };

  // ── Not-yet-connected state (Finance). Calm, no numbers, a single CTA. ──
  if (area.connect) {
    return (
      <article {...tileProps} style={{
        background: tileBg(P, areaKey),
        borderRadius: 22, padding: big ? '16px 18px' : '13px 15px',
        display: 'flex', flexDirection: 'column', gap: 9,
        gridColumn: big ? 'span 2' : 'span 1',
        position: 'relative',
      }}>
        <header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span style={{ ...S.caps, color: ringColor }}>{area.label.toLowerCase()}</span>
          <span style={{ ...S.caps, color: P.ink4 }}>manage ↗</span>
        </header>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <div style={{
            width: 52, height: 52, borderRadius: '50%', flex: '0 0 auto',
            display: 'grid', placeItems: 'center',
            background: P.chip, border: `1px dashed ${ringColor}`,
          }}>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none"
              stroke={ringColor} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
              <rect x="4" y="10" width="16" height="10" rx="2" />
              <path d="M8 10V7a4 4 0 0 1 8 0v3" />
            </svg>
          </div>
          <div style={{ minWidth: 0 }}>
            <div style={{ ...S.serif, fontSize: big ? 28 : 22, color: P.ink, lineHeight: 1.1 }}>
              {area.headline}
            </div>
            <div style={{ fontSize: 11.5, color: P.ink3, marginTop: 2 }}>{area.sub}</div>
          </div>
        </div>
        <p style={{ fontSize: 12, color: P.ink2, margin: 0, maxWidth: big ? 460 : 240, lineHeight: 1.45 }}>
          {area.connectCopy}
        </p>
        <div style={{ marginTop: 'auto', display: 'flex', gap: 10, alignItems: 'center' }}>
          <button onClick={(e) => { e.stopPropagation(); onLink && onLink(); }} style={{
            appearance: 'none', border: 0, cursor: 'pointer',
            padding: '8px 16px', borderRadius: 999,
            background: P.ink, color: P.bg,
            fontFamily: 'inherit', fontSize: 12, fontWeight: 500,
          }}>Link accounts →</button>
          <span style={{ ...S.caps, color: P.ink4 }}>plaid · read-only</span>
        </div>
      </article>
    );
  }

  const pcts = area.details.map(d => typeof d.pct === 'number' ? d.pct : null).filter(v => v != null);
  const avg = pcts.length ? pcts.reduce((a, b) => a + b, 0) / pcts.length : 0.5;

  // One detail/stat row. Rows with an `act` are one-tap logs (chores, habits,
  // hobbies, objectives); done rows read as tended, the rest stay static text.
  const renderRow = (d, i) => {
    const done = d.pct === 1 || d.done === true;
    const Row = d.act ? 'button' : 'div';
    // Habits stay clickable when done — they toggle back off.
    const clickable = d.act && (!done || d.act.type === 'habit');
    return (
      <Row key={i}
        onClick={d.act ? (e) => { e.stopPropagation(); if (vantaTapAct(d, window.VANTA_DATA)) onAct && onAct(d); } : undefined}
        style={{
          ...(d.act ? { appearance: 'none', border: 0, background: 'transparent', cursor: clickable ? 'pointer' : 'default', fontFamily: 'inherit', textAlign: 'left', padding: 0 } : {}),
          display: 'flex', justifyContent: 'space-between', gap: 10, alignItems: 'center',
          fontSize: 11.5, minHeight: 24, width: '100%', boxSizing: 'border-box',
        }}>
        <span style={{ display: 'flex', alignItems: 'center', gap: 7, minWidth: 0 }}>
          {d.act && (
            <span style={{
              width: 14, height: 14, flex: '0 0 auto',
              borderRadius: d.act.type === 'habit' ? 4 : '50%',
              border: `1.2px solid ${done ? ringColor : P.ink4}`,
              background: done ? ringColor : 'transparent',
              display: 'grid', placeItems: 'center', transition: 'background .15s',
            }}>
              {done && <svg width="8" height="8" viewBox="0 0 24 24" fill="none" stroke={P.bg} strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>}
            </span>
          )}
          <span style={{
            color: d.act && done ? P.ink4 : P.ink2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            textDecoration: d.act && done ? 'line-through' : 'none',
          }}>{d.k}</span>
        </span>
        <span style={{ ...S.num, color: P.ink, fontWeight: 500, flex: '0 0 auto' }}>
          {d.v}{d.streak ? <span style={{ color: P.ink4, marginLeft: 4 }}>·{d.streak}d</span> : null}
        </span>
      </Row>
    );
  };

  // Identity block — ring + headline + sub, then the trend spark beneath it.
  const identity = (
    <React.Fragment>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
        <Ring pct={avg} size={big ? 56 : 52} stroke={5} color={ringColor} track={P.ring}>
          <span style={{ ...S.num, fontSize: 11, fontWeight: 600 }}>{Math.round(avg * 100)}</span>
        </Ring>
        <div style={{ minWidth: 0 }}>
          <div style={{ ...S.serif, fontSize: big ? 28 : 22, color: P.ink, lineHeight: 1.1 }}>
            {area.headline}
          </div>
          <div style={{ fontSize: 11.5, color: P.ink3, marginTop: 2 }}>{area.sub}</div>
        </div>
      </div>
      <Spark data={area.trend} w={big ? 250 : 220} h={big ? 34 : 28} stroke={ringColor} fill={ringColor + '22'} strokeWidth={1.4} showDots />
    </React.Fragment>
  );

  const note = big && area.note ? (
    <div style={{ ...S.serif, fontSize: 15, color: P.ink2, lineHeight: 1.3 }}>{area.note}</div>
  ) : null;

  return (
    <article {...tileProps} style={{
      background: tileBg(P, areaKey),
      borderRadius: 24, padding: big ? '22px 26px' : '18px 20px',
      display: 'flex', flexDirection: 'column', gap: 12,
      gridColumn: big ? 'span 2' : 'span 1',
      position: 'relative',
    }}>
      <header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <span style={{ ...S.caps, color: ringColor }}>{area.label.toLowerCase()}</span>
        <span style={{ ...S.caps, color: P.ink4 }}>open ↗</span>
      </header>

      {big ? (
        // Two columns: identity + trend on the left, the stat rows on the
        // right so a wide tile's empty half carries the detail instead.
        <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 0.85fr) minmax(0, 1fr)', gap: 24, alignItems: 'stretch', flex: 1 }}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12, justifyContent: 'center' }}>
            {identity}
            {note}
          </div>
          <div style={{
            display: 'flex', flexDirection: 'column', gap: 6, justifyContent: 'center',
            borderLeft: `1px solid ${P.rule}`, paddingLeft: 24,
          }}>
            {area.details.slice(0, 6).map(renderRow)}
          </div>
        </div>
      ) : (
        <React.Fragment>
          {identity}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            {area.details.slice(0, 5).map(renderRow)}
          </div>
        </React.Fragment>
      )}
    </article>
  );
}

// "Themes for 2026" — brought over from Direction A (Quiet): a left label
// column + four themes as thin progress bars in a row. Re-skinned to Garden.
function GardenThemes({ goals }) {
  const P = useP();
  const L = useL();
  const S = gStyles(P);
  return (
    <section style={{
      background: P.tile, borderRadius: 28, padding: L.compact ? '22px 22px' : '28px 32px', marginTop: 16,
      display: 'grid', gridTemplateColumns: L.compact ? '1fr' : '1fr 3fr', gap: L.compact ? 22 : 48, alignItems: 'start',
    }}>
      <div>
        <div style={{ ...S.caps, color: P.accent, marginBottom: 8 }}>the year, in four lines</div>
        <h2 style={{ ...S.serif, fontSize: 34, margin: 0, color: P.ink, lineHeight: 1.08 }}>
          Themes for 2026
        </h2>
        <p style={{ fontSize: 12.5, color: P.ink3, marginTop: 10, maxWidth: 240 }}>
          On pace for three. The fourth is a slow river.
        </p>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${L.compact ? 2 : 4}, 1fr)`, gap: L.compact ? 20 : 28 }}>
        {goals.details.map((g, i) => (
          <div key={i}>
            <div style={{ ...S.serif, fontSize: 18, color: P.ink, marginBottom: 8, lineHeight: 1.1 }}>{g.k}</div>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
              <span style={{ ...S.num, fontSize: 24, fontWeight: 500, color: P.ink }}>{g.v}</span>
              <span style={{ ...S.num, fontSize: 11, color: P.ink4 }}>{g.goal}</span>
            </div>
            <ProgBar pct={g.pct} h={2}
              color={[P.rust, P.accent, P.sky, P.ink2][i]}
              track={P.rule} radius={999} />
          </div>
        ))}
      </div>
    </section>
  );
}

// Centered overlay for drill-ins — wraps the mobile detail + sources screens
// so desktop gets them as modals instead of navigations.
function GardenOverlay({ P, onClose, children }) {
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 50,
      background: P.scrim,
      display: 'grid', placeItems: 'center', padding: 24, boxSizing: 'border-box',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: '100%', maxWidth: 560, maxHeight: '86vh',
        overflowY: 'auto', overscrollBehavior: 'contain',
        background: P.bg2, borderRadius: 26, paddingBottom: 8,
        boxShadow: '0 24px 80px rgba(28,26,20,0.28)',
      }}>
        {children}
      </div>
    </div>
  );
}

function VantaGarden({ data = window.VANTA_DATA, visibleAreas, density = 'regular' }) {
  const A = data.areas;
  const show = (k) => !visibleAreas || visibleAreas[k] !== false;
  const gap = density === 'sparse' ? 20 : density === 'dense' ? 12 : 16;

  // One store (vanta.app.v1) across desktop, mobile, and the agents page:
  // palette, horizon, card order, thoughts, and checks follow the user.
  const [store, update] = useVantaStore();
  const P = GARDEN_PALETTES[store.palette] || GARDEN_PALETTES.dusk;
  const S = gStyles(P);
  const cyclePalette = () => update(s => ({
    palette: GARDEN_PALETTE_ORDER[(GARDEN_PALETTE_ORDER.indexOf(s.palette) + 1) % GARDEN_PALETTE_ORDER.length],
  }));

  const w = useViewportWidth();
  const L = { cols: w >= 1360 ? 4 : w >= 1040 ? 3 : 2, compact: w < 1040, tight: w < 860 };

  // Tap-to-complete rows mutate VANTA_DATA in place; bump forces the
  // re-render that shows the change.
  const [, bump] = React.useReducer(x => x + 1, 0);

  const [arranging, setArranging] = React.useState(false);
  const [detailKey, setDetailKey] = React.useState(null);
  const [sourcesOpen, setSourcesOpen] = React.useState(false);
  const [restOpen, setRestOpen] = React.useState(false);

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') { setDetailKey(null); setSourcesOpen(false); } };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  // Live thoughts headline (mirrors the mobile app).
  const todayThoughts = store.thoughts.filter(t => Date.now() - t.ts < 86400000).length;
  const thoughtsArea = {
    ...A.thoughts,
    headline: todayThoughts > 0 ? `${todayThoughts} today` : A.thoughts.headline,
    sub: store.thoughts[0] ? `last planted ${vantaAgo(store.thoughts[0].ts)}` : A.thoughts.sub,
  };
  const areaFor = (k) => (k === 'thoughts' ? thoughtsArea : A[k]);
  const dataView = { ...data, areas: { ...A, thoughts: thoughtsArea } };

  // User-arranged card order (shared with mobile); the first two positions
  // render as big tiles. Hidden areas (unconnected seed data) stay off.
  const orderKeys = vantaCardOrder(store.cardOrder);
  const visible = orderKeys.filter(k => show(k) && !store.hidden[k]);
  const saveOrder = (next) => { update({ cardOrder: next }); setArranging(false); };

  const linkArea = (k) => update(s => ({
    linked: { ...s.linked, [k]: true },
    lastTouch: { ...s.lastTouch, [k]: Date.now() },
  }));

  return (
    <GardenCtx.Provider value={P}>
      <GardenLayoutCtx.Provider value={L}>
        <div style={{
          ...S.page,
          padding: L.compact ? '18px 18px 32px' : S.page.padding,
          transform: arranging ? 'scale(0.92)' : 'none',
          opacity: arranging ? 0.35 : 1,
          transition: 'transform .3s cubic-bezier(.4,0,.2,1), opacity .3s ease',
          transformOrigin: '50% 10%',
        }}>
          <GardenHeader
            data={data} palette={store.palette} onCyclePalette={cyclePalette}
            onArrange={() => setArranging(true)}
            horizon={store.horizon} onHorizon={(h) => update({ horizon: h })}
          />

          {store.horizon !== 'today' && (
            <section style={{ maxWidth: 700, marginBottom: 14 }}>
              <VHorizonPanel P={P} horizon={store.horizon} flush />
            </section>
          )}

          <GardenHero
            data={data} store={store} update={update}
            onSources={() => setSourcesOpen(true)}
            onOpenArea={setDetailKey}
          />

          <section style={{ maxWidth: 700, marginBottom: gap }}>
            <VRhythm P={P} flush />
          </section>

          {(() => {
            // Smart-open (Level 2): the first two areas and anything notable
            // today stay full; the quiet rest collapse into one calm strip you
            // expand on demand. 'open' depth shows everything as full tiles.
            const depth = store.depth || 'smart';
            const tileAct = (d) => {
              if (d?.act?.type === 'habit') {
                update(s => ({ habits: { ...s.habits, [d.k]: !!d.done }, lastTouch: { ...s.lastTouch, habits: Date.now() } }));
              } else bump();
            };
            const renderTile = (k, big) => (
              <GardenAreaTile
                key={k} area={areaFor(k)} areaKey={k} big={big}
                onAct={tileAct} onOpen={() => setDetailKey(k)} onLink={() => linkArea(k)}
              />
            );
            const { notable: notableKeys, resting: restingKeys } =
              vPartitionAreas(visible, areaFor, (k) => store.lastTouch?.[k] || 0, depth, 2);
            const gridStyle = { display: 'grid', gridTemplateColumns: `repeat(${L.cols}, 1fr)`, gridAutoFlow: 'dense', gap };

            return (
              <React.Fragment>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
                  <span style={{ ...S.caps, color: P.ink4 }}>your areas</span>
                  <button onClick={() => update({ depth: depth === 'open' ? 'smart' : 'open' })} className="g-chip" style={{
                    appearance: 'none', border: 0, background: 'transparent', color: P.ink3,
                    fontFamily: 'inherit', fontSize: 11, letterSpacing: '0.06em', cursor: 'pointer',
                  }}>{depth === 'open' ? 'showing all · calm it' : 'smart view · show all'}</button>
                </div>
                <section style={{ ...gridStyle, marginBottom: restingKeys.length ? 12 : gap }}>
                  {notableKeys.map((k, i) => renderTile(k, i < 2))}
                </section>
                {restingKeys.length > 0 && (
                  <section style={{ marginBottom: gap }}>
                    <button onClick={() => setRestOpen(o => !o)} style={{
                      appearance: 'none', border: 0, cursor: 'pointer', width: '100%', textAlign: 'left',
                      background: P.tile, borderRadius: 16, padding: '11px 16px', fontFamily: 'inherit',
                      display: 'flex', justifyContent: 'space-between', alignItems: 'center', color: P.ink2,
                    }}>
                      <span style={{ ...S.serif, fontSize: 15 }}>
                        {restingKeys.length} area{restingKeys.length === 1 ? '' : 's'} resting
                        <span style={{ ...S.caps, color: P.ink4, marginLeft: 10 }}>{restingKeys.map(k => areaFor(k).label.toLowerCase()).join(' · ')}</span>
                      </span>
                      <span style={{ ...S.caps, color: P.ink3 }}>{restOpen ? 'tuck away ↑' : 'tend ↓'}</span>
                    </button>
                    {restOpen && <section style={{ ...gridStyle, marginTop: gap }}>{restingKeys.map(k => renderTile(k, false))}</section>}
                  </section>
                )}
              </React.Fragment>
            );
          })()}

          {show('goals') && <GardenThemes goals={A.goals} />}
        </div>

        {arranging && (
          <VArrange
            P={P}
            order={orderKeys}
            labels={Object.fromEntries(Object.entries(A).map(([k, a]) => [k, a.label]))}
            onDone={saveOrder}
            onCancel={() => setArranging(false)}
          />
        )}

        {detailKey && (
          <GardenOverlay P={P} onClose={() => setDetailKey(null)}>
            <VAreaDetail
              areaKey={detailKey} data={dataView} P={P}
              store={store} update={update}
              onBack={() => setDetailKey(null)}
            />
          </GardenOverlay>
        )}

        {sourcesOpen && (
          <GardenOverlay P={P} onClose={() => setSourcesOpen(false)}>
            <VSourcesScreen
              data={data} P={P} store={store} update={update}
              onBack={() => setSourcesOpen(false)}
            />
          </GardenOverlay>
        )}
      </GardenLayoutCtx.Provider>
    </GardenCtx.Provider>
  );
}

window.VantaGarden = VantaGarden;
window.GARDEN_PALETTES = GARDEN_PALETTES;
window.GARDEN_PALETTE_ORDER = GARDEN_PALETTE_ORDER;
