/* OBSKIRA — Drop 01 React app
   Single file. No external deps beyond React+Babel from CDN. */

const { useState, useEffect, useCallback, useRef } = React;

/* ============ Data ============ */
/* 8 pieces, single-word names, prices spread USD 2,400 — 6,000.
   "Remaining" is /20. Placeholder material/measurement copy. */
const PIECES = [
  {
    id: 'threshold',
    roman: 'I',
    name: 'Threshold',
    price: 4800,
    remaining: 14,
    bg: 'assets/bg_door.jpg',
    bgPos: 'center 30%',
    material: 'Calf shoulder leather, vegetable-tanned in Tuscany. Bone bridle stitched in single-thread linen. Brushed steel hardware, hand-cut.',
    summary: 'A long coat cut to read as architecture before it reads as garment. Shoulders held by the line. Skirt held by the weight.',
    measurements: { Shoulder: '46 cm', Chest: '108 cm', Length: '124 cm', Sleeve: '67 cm' }
  },
  {
    id: 'obsidian',
    roman: 'II',
    name: 'Obsidian',
    price: 3600,
    remaining: 9,
    bg: 'assets/bg_ribbons.jpg',
    bgPos: 'center 35%',
    material: 'Tightly-woven Japanese wool, double-faced. Inner seams hand-bound in raw silk. Horn closure, single placket.',
    summary: 'A tailored mid-length, cut close at the rib and released at the hip. The fabric is so dense it falls like sculpture.',
    measurements: { Shoulder: '43 cm', Chest: '102 cm', Length: '92 cm', Sleeve: '64 cm' }
  },
  {
    id: 'heir',
    roman: 'III',
    name: 'Heir',
    price: 6000,
    remaining: 18,
    bg: 'assets/bg_cathedral.jpg',
    bgPos: 'center center',
    material: 'Italian shearling, reverse-finished. Lining: undyed merino. Closures: jet-set brass, oxidised to a near-matte black.',
    summary: 'The drop\'s anchor piece. Shoulder of a sentinel; hem of a vestment. Worn open it reads architectural; closed, monastic.',
    measurements: { Shoulder: '48 cm', Chest: '116 cm', Length: '108 cm', Sleeve: '66 cm' }
  },
  {
    id: 'vesper',
    roman: 'IV',
    name: 'Vesper',
    price: 2400,
    remaining: 6,
    bg: 'assets/bg_door.jpg',
    bgPos: 'center 20%',
    material: 'Brushed virgin wool with a fine cashmere weft. Edges raw-finished. Mother-of-pearl closure, single.',
    summary: 'A vest that behaves like outerwear. Cut long enough to define the line of the body without overstating it.',
    measurements: { Shoulder: '42 cm', Chest: '100 cm', Length: '78 cm', Sleeve: '—' }
  },
  {
    id: 'reliquary',
    roman: 'V',
    name: 'Reliquary',
    price: 5400,
    remaining: 11,
    bg: 'assets/bg_cathedral.jpg',
    bgPos: 'center 45%',
    material: 'Hand-loomed black silk, double-twill weave. Interior cape lining in raw cotton. Hammered brass cuffs at the throat.',
    summary: 'A draped over-piece that reads as ceremony. The shoulders fall; the back holds its column.',
    measurements: { Shoulder: '46 cm', Chest: '—', Length: '136 cm', Sleeve: '69 cm' }
  },
  {
    id: 'cinder',
    roman: 'VI',
    name: 'Cinder',
    price: 2800,
    remaining: 17,
    bg: 'assets/bg_ribbons.jpg',
    bgPos: 'center 55%',
    material: 'Charcoal selvedge denim, washed twice, hand-pressed flat. Bone shank buttons. Selvedge cuffs preserved.',
    summary: 'A trouser cut wide at the thigh and held narrow at the ankle. The break is intentional, deep, and singular.',
    measurements: { Waist: '82 cm', Inseam: '76 cm', Rise: '32 cm', Hem: '21 cm' }
  },
  {
    id: 'aperture',
    roman: 'VII',
    name: 'Aperture',
    price: 3200,
    remaining: 4,
    bg: 'assets/bg_ribbons.jpg',
    bgPos: 'center 25%',
    material: 'Compact silk poplin, mercerised. Cuffs and collar reinforced in raw horsehair. No printed marks, no exterior labels.',
    summary: 'A shirt with the shoulder of a coat. Worn loose at the wrist, structured at the yoke.',
    measurements: { Shoulder: '44 cm', Chest: '108 cm', Length: '78 cm', Sleeve: '65 cm' }
  },
  {
    id: 'chorus',
    roman: 'VIII',
    name: 'Chorus',
    price: 4200,
    remaining: 12,
    bg: 'assets/bg_cathedral.jpg',
    bgPos: 'center 20%',
    material: 'Italian nappa leather, glove-thin. Interior fully bagged in silk satin. Closures: blackened steel hook-and-eye.',
    summary: 'A second-skin jacket. The leather is finished to a near-matte and breaks only where the body breaks.',
    measurements: { Shoulder: '42 cm', Chest: '98 cm', Length: '62 cm', Sleeve: '64 cm' }
  }
];

const fmtPrice = (n) => 'USD ' + n.toLocaleString('en-US');

/* ============ Header ============ */
function Header() {
  return (
    <header className="header fade-up" style={{ animationDelay: '0s' }} data-screen-label="Header">
      <div className="wordmark">OBSKIRA<span className="runway">RUNWAY</span></div>
      <nav className="nav">
        <a href="#drop">Drop</a>
        <a href="#manifesto" className="hide-mobile">About</a>
        <a href="#reserve">Reserve</a>
      </nav>
    </header>
  );
}

/* ============ Hero ============ */
function Hero({ totalRemaining }) {
  return (
    <section className="hero fade-up" style={{ animationDelay: '0.05s' }} data-screen-label="Hero">
      <div className="hero-img"></div>
      <div className="hero-overlay"></div>
      <div className="hero-inner">
        <div className="hero-status">
          <span className="live-dot"></span>
          <span className="hero-status-text">Live · Drop 01</span>
        </div>
        <h1 className="hero-title">Drop&nbsp;01</h1>
        <div className="hero-sub">
          <span>Eight Pieces</span>
          <span className="sep">·</span>
          <span>Twenty Each</span>
          <span className="sep">·</span>
          <span>May 2026</span>
        </div>
        <div className="hero-counts">
          <div className="hero-count">
            <div className="n">{totalRemaining}</div>
            <div className="l">Remaining</div>
          </div>
          <div className="hero-count">
            <div className="n">160</div>
            <div className="l">Total Pieces</div>
          </div>
          <div className="hero-count">
            <div className="n">08</div>
            <div className="l">Designs</div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ============ Manifesto ============ */
function Manifesto() {
  return (
    <section id="manifesto" className="manifesto fade-up" style={{ animationDelay: '0.10s' }} data-screen-label="Manifesto">
      <div className="eyebrow">I · The Drop</div>
      <p>
        OBSKIRA RUNWAY opens its first drop with eight designs. Each is cut in a run of twenty. When a piece closes, it closes. There is no restock, no second cut, no waiting list rolled forward into the next season.
      </p>
      <div className="pullquote">
        Scarcity is not a constraint — it&rsquo;s the architecture.
      </div>
      <p>
        The garments are made slowly and in small numbers, by hand, in workshops we have used for a decade. We do not chase trends and we do not name silhouettes after weather. What we make is for people who have already decided what they wear.
      </p>
      <p>
        Drop 01 is open now. It closes when it closes.
      </p>
    </section>
  );
}

/* ============ Pieces Grid ============ */
function PieceCard({ piece, onOpen }) {
  const remaining = piece.remaining;
  const sold = remaining === 0;
  const className = 'piece-remaining' + (sold ? ' sold' : (remaining <= 5 ? ' low' : ''));
  return (
    <button className="piece" onClick={() => onOpen(piece)} aria-label={`Open ${piece.name}`}>
      <div className="thumb" style={{ backgroundImage: `url('${piece.bg}')`, backgroundPosition: piece.bgPos }}>
        <span className="roman">{piece.roman}</span>
      </div>
      <div className="piece-meta">
        <div className="piece-name">{piece.name}</div>
        <div className="piece-price">{fmtPrice(piece.price)}</div>
        <div className={className}>
          {sold ? 'Closed' : `${String(remaining).padStart(2,'0')} of 20 Remaining`}
        </div>
      </div>
    </button>
  );
}

function PiecesGrid({ onOpen }) {
  return (
    <section id="drop" className="fade-up" style={{ animationDelay: '0.15s' }} data-screen-label="Pieces">
      <div className="section-head">
        <div className="h">The Eight</div>
        <div className="meta">II · 20 Pieces Each · Hand-Cut</div>
      </div>
      <div className="grid">
        {PIECES.map(p => <PieceCard key={p.id} piece={p} onOpen={onOpen} />)}
      </div>
    </section>
  );
}

/* ============ Modal ============ */
function Modal({ piece, onClose, onReserve }) {
  const [activeImg, setActiveImg] = useState(0);

  // Gallery: 4 crops of the same atmospheric placeholder, varied position
  const imgs = [
    { bg: piece.bg, pos: piece.bgPos },
    { bg: 'assets/bg_cathedral.jpg', pos: 'center 60%' },
    { bg: 'assets/bg_ribbons.jpg', pos: 'center 70%' },
    { bg: 'assets/bg_door.jpg', pos: 'center 50%' }
  ];

  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prev;
    };
  }, [onClose]);

  const remaining = piece.remaining;
  const sold = remaining === 0;

  return (
    <div className="modal-backdrop" onClick={(e) => { if (e.target.classList.contains('modal-backdrop')) onClose(); }}>
      <div className="modal" role="dialog" aria-modal="true" aria-label={piece.name}>
        <button className="modal-close" onClick={onClose}>Close</button>
        <div className="gallery">
          <div
            className="gallery-main"
            style={{ backgroundImage: `url('${imgs[activeImg].bg}')`, backgroundPosition: imgs[activeImg].pos }}
          ></div>
          <div className="gallery-thumbs">
            {imgs.map((im, i) => (
              <button
                key={i}
                className={i === activeImg ? 'active' : ''}
                style={{ backgroundImage: `url('${im.bg}')`, backgroundPosition: im.pos }}
                onClick={() => setActiveImg(i)}
                aria-label={`View ${i+1}`}
              ></button>
            ))}
          </div>
        </div>

        <div className="detail-pane">
          <div className="detail-roman">{piece.roman} · Drop&nbsp;01</div>
          <h2 className="detail-name">{piece.name}</h2>
          <div className="detail-price">{fmtPrice(piece.price)}</div>
          <div className="detail-remaining">
            {sold ? 'Closed' : `${String(remaining).padStart(2,'0')} of 20 Remaining`}
          </div>

          <div className="detail-section">
            <span className="label">Summary</span>
            <p>{piece.summary}</p>
          </div>

          <div className="detail-section">
            <span className="label">Material</span>
            <p>{piece.material}</p>
          </div>

          <div className="detail-section">
            <span className="label">Measurements · Size 02</span>
            <table className="measurements">
              <tbody>
                {Object.entries(piece.measurements).map(([k, v]) => (
                  <tr key={k}><th>{k}</th><td>{v}</td></tr>
                ))}
              </tbody>
            </table>
          </div>

          <div className="detail-cta">
            <button className="btn large" onClick={onReserve}>
              {sold ? 'Notify · Next Drop' : 'Reserve This Piece'}
            </button>
            <button className="btn large" onClick={onClose}>Return</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============ Reserve ============ */
function Reserve() {
  const [email, setEmail] = useState('');
  const [submitted, setSubmitted] = useState(false);

  const submit = (e) => {
    e.preventDefault();
    if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return;
    setSubmitted(true);
  };

  return (
    <section id="reserve" className="reserve fade-up" style={{ animationDelay: '0.2s' }} data-screen-label="Reserve">
      <div className="h">Reserve List</div>
      <div className="sub">First access on drop day.</div>
      {submitted ? (
        <div className="reserve-confirm">
          You are on the list. <span className="em">{email}</span>
        </div>
      ) : (
        <form className="reserve-form" onSubmit={submit}>
          <input
            type="email"
            className="reserve-input"
            placeholder="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
          <button type="submit" className="btn">Reserve</button>
        </form>
      )}
    </section>
  );
}

/* ============ Footer ============ */
function Footer() {
  return (
    <footer className="footer fade-up" style={{ animationDelay: '0.25s' }}>
      <div className="small">© 2026 OBSKIRA Runway</div>
      <div className="links">
        <a href="#">Instagram</a>
        <a href="#">Terms</a>
        <a href="#">Privacy</a>
      </div>
    </footer>
  );
}

/* ============ App ============ */
function App() {
  const [openPiece, setOpenPiece] = useState(null);
  const totalRemaining = PIECES.reduce((s, p) => s + p.remaining, 0);

  const reserveFromModal = () => {
    setOpenPiece(null);
    setTimeout(() => {
      const el = document.getElementById('reserve');
      if (!el) return;
      const y = el.getBoundingClientRect().top + window.scrollY - (window.innerHeight - el.offsetHeight) / 2;
      window.scrollTo({ top: y, behavior: 'smooth' });
    }, 200);
  };

  return (
    <>
      <div className="vignette"></div>
      <main className="page">
        <Header />
        <Hero totalRemaining={totalRemaining} />
        <Manifesto />
        <div className="divider"></div>
        <PiecesGrid onOpen={setOpenPiece} />
        <div className="divider"></div>
        <Reserve />
        <Footer />
      </main>
      {openPiece && <Modal piece={openPiece} onClose={() => setOpenPiece(null)} onReserve={reserveFromModal} />}
    </>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
