// direction-noir.jsx — B · NOIR
// Dark cinematic. WebGL liquid gradient hero, grainy noise, mono details.

function NoirShader({ accent }) {
  const canvasRef = React.useRef(null);
  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const gl = canvas.getContext('webgl');
    if (!gl) return;
    const resize = () => {
      canvas.width = canvas.offsetWidth * Math.min(window.devicePixelRatio, 2);
      canvas.height = canvas.offsetHeight * Math.min(window.devicePixelRatio, 2);
      gl.viewport(0, 0, canvas.width, canvas.height);
    };
    resize();
    window.addEventListener('resize', resize);

    const vs = `attribute vec2 p; void main(){ gl_Position = vec4(p,0.,1.); }`;
    const hex = accent.replace('#', '');
    const r = parseInt(hex.substr(0,2),16)/255;
    const g = parseInt(hex.substr(2,2),16)/255;
    const b = parseInt(hex.substr(4,2),16)/255;
    const fs = `
      precision mediump float;
      uniform vec2 uRes; uniform float uT; uniform vec2 uM; uniform vec3 uAcc;
      float hash(vec2 p){ return fract(sin(dot(p,vec2(27.619,57.583)))*43758.5); }
      float noise(vec2 p){ vec2 i=floor(p); vec2 f=fract(p); f=f*f*(3.-2.*f);
        return mix(mix(hash(i),hash(i+vec2(1,0)),f.x), mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x), f.y); }
      float fbm(vec2 p){ float v=0., a=.5; for(int i=0;i<5;i++){ v+=a*noise(p); p*=2.02; a*=.5; } return v; }
      void main(){
        vec2 uv = gl_FragCoord.xy/uRes.xy;
        vec2 p = uv*2.-1.; p.x *= uRes.x/uRes.y;
        float t = uT*.08;
        vec2 q = vec2(fbm(p+vec2(t,0.)), fbm(p+vec2(5.2,1.3)+t));
        vec2 r = vec2(fbm(p+4.*q+vec2(1.7,9.2)+t*1.5), fbm(p+4.*q+vec2(8.3,2.8)-t*1.2));
        float f = fbm(p + 4.*r);
        float d = distance(uv, uM);
        f += (1.-smoothstep(0., .4, d))*.3;
        vec3 col = mix(vec3(.04,.03,.05), vec3(.1,.06,.08), f);
        col = mix(col, uAcc, smoothstep(.55,.9,f)*.6);
        col = mix(col, vec3(.95,.88,.75), pow(smoothstep(.72,.95,f),2.)*.5);
        col *= 1. - length(uv - .5)*.6;
        gl_FragColor = vec4(col, 1.);
      }`;

    const compile = (type, src) => { const s=gl.createShader(type); gl.shaderSource(s,src); gl.compileShader(s); return s; };
    const prog = gl.createProgram();
    gl.attachShader(prog, compile(gl.VERTEX_SHADER, vs));
    gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, fs));
    gl.linkProgram(prog);
    gl.useProgram(prog);

    const buf = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buf);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
    const loc = gl.getAttribLocation(prog, 'p');
    gl.enableVertexAttribArray(loc);
    gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);

    const uRes = gl.getUniformLocation(prog, 'uRes');
    const uT = gl.getUniformLocation(prog, 'uT');
    const uM = gl.getUniformLocation(prog, 'uM');
    const uAcc = gl.getUniformLocation(prog, 'uAcc');
    gl.uniform3f(uAcc, r, g, b);

    let mx = .5, my = .5, tmx = .5, tmy = .5;
    const onMouse = (e) => { tmx = e.clientX / window.innerWidth; tmy = 1 - e.clientY / window.innerHeight; };
    window.addEventListener('mousemove', onMouse);

    let raf, start = performance.now();
    const tick = () => {
      mx += (tmx - mx) * .08; my += (tmy - my) * .08;
      gl.uniform2f(uRes, canvas.width, canvas.height);
      gl.uniform1f(uT, (performance.now() - start) / 1000);
      gl.uniform2f(uM, mx, my);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
      raf = requestAnimationFrame(tick);
    };
    tick();
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); window.removeEventListener('mousemove', onMouse); };
  }, [accent]);
  return <canvas ref={canvasRef} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} />;
}

function DirNoir({ tweaks }) {
  const accent = tweaks.accent;
  const density = tweaks.density;
  const heroVar = tweaks.hero;
  const displayFont = tweaks.displayFont;
  const pad = { tight: 80, normal: 140, generous: 200 }[density];

  return (
    <div style={{
      '--accent': accent,
      '--bg': '#0a0908',
      '--ink': '#f0ebe2',
      '--muted': 'rgba(240,235,226,.55)',
      '--rule': 'rgba(240,235,226,.14)',
      background: 'var(--bg)', color: 'var(--ink)',
      fontFamily: "'Montserrat', system-ui, sans-serif", fontSize: 14, lineHeight: 1.55,
    }}>
      <style>{`
        .noir-display { font-family: 'Fraunces', Georgia, serif; letter-spacing: -.025em; font-weight: 300; }
        .noir-mono { font-family: ui-monospace, "JetBrains Mono", SFMono-Regular, Menlo, monospace; }
        .noir-caps { font-size: 11px; letter-spacing: .2em; text-transform: uppercase; font-family: ui-monospace, monospace; }
        .noir-link { position: relative; display: inline-block; }
        .noir-link::after { content:''; position:absolute; left:0; right:0; bottom:-3px; height:1px; background:currentColor; transform:scaleX(0); transform-origin:right; transition: transform .5s cubic-bezier(.2,.7,.1,1); }
        .noir-link:hover::after { transform:scaleX(1); transform-origin:left; }
      `}</style>

      <NoirNav />
      {heroVar === 'stacked' && <NoirHeroShader accent={accent} />}
      {heroVar === 'marquee' && <NoirHeroMarquee accent={accent} />}
      {heroVar === 'portrait' && <NoirHeroSplit accent={accent} />}

      {/* ticker removed */}
      <NoirStories pad={pad} />
      <NoirManifesto pad={pad} />
      <NoirClients pad={pad} />
      <NoirScope pad={pad} />
      <NoirJournal pad={pad} />
      <NoirContact pad={pad} accent={accent} />
      <NoirFooter />
    </div>
  );
}

function NoirNav() {
  const [hidden, setHidden] = React.useState(false);
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [scopeOpen, setScopeOpen] = React.useState(false);
  React.useEffect(() => {
    const stage = document.querySelector('.c1-stage');
    if (!stage) return;
    let lastY = stage.scrollTop;
    let raf = 0;
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        const y = stage.scrollTop;
        const delta = y - lastY;
        if (y < 40) setHidden(false);
        else if (delta > 4) setHidden(true);
        else if (delta < -4) setHidden(false);
        lastY = y;
      });
    };
    stage.addEventListener('scroll', onScroll, { passive: true });
    return () => { cancelAnimationFrame(raf); stage.removeEventListener('scroll', onScroll); };
  }, []);

  // Lock body scroll while the mobile menu is open
  React.useEffect(() => {
    const stage = document.querySelector('.c1-stage');
    if (!stage) return;
    if (menuOpen) stage.style.overflow = 'hidden';
    else stage.style.overflow = '';
    return () => { stage.style.overflow = ''; };
  }, [menuOpen]);

  const links = [
    {
      href: 'scope.html', label: 'Scope', cursor: 'Explore',
      children: [
        { href: 'scope.html?s=brands',    label: 'Brands' },
        { href: 'scope.html?s=concepts',  label: 'Concepts' },
        { href: 'scope.html?s=campaigns', label: 'Campaigns' },
      ],
    },
    { href: 'work.html', label: 'Stories', cursor: 'View' },
    { href: 'journal.html', label: 'Field Notes', cursor: 'Read' },
    { href: 'mailto:hello@chapter1.in', label: 'Say Hello', cursor: 'Write' },
  ];

  return (
    <>
      <nav style={{
        position: 'fixed', top: 0, left: 0, right: 0, zIndex: 50,
        display: 'grid', gridTemplateColumns: '1fr auto 1fr', alignItems: 'center',
        padding: '22px 28px', mixBlendMode: 'difference',
        transform: hidden ? 'translateY(-110%)' : 'translateY(0)',
        opacity: hidden ? 0 : 1,
        transition: 'transform .55s cubic-bezier(.7,0,.2,1), opacity .35s ease',
        willChange: 'transform, opacity',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', mixBlendMode: 'normal' }}>
          <a href="#" data-cursor="Home" style={{ display: 'inline-flex', alignItems: 'center', isolation: 'isolate' }}>
            <img src="uploads/Chapter One White.svg" alt="Chapter One"
              style={{ height: 32, width: 'auto', display: 'block', filter: 'brightness(0) invert(1)' }} />
          </a>
        </div>
        <div />
        <div className="c1-nav-right" style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 24 }}>
          <div className="noir-caps c1-nav-links" style={{ display: 'flex', justifyContent: 'flex-end', gap: 24, color: '#fff' }}>
            {links.map((l) => (
              l.children ? (
                <div key={l.label} className="c1-nav-dropdown" style={{ position: 'relative' }}>
                  <span className="noir-link c1-nav-dropdown-trigger" data-cursor={l.cursor} tabIndex={0} role="button" aria-haspopup="menu">{l.label}</span>
                  <div className="c1-nav-dropdown-panel" role="menu" aria-label={`${l.label} sub-menu`}>
                    {l.children.map((c) => (
                      <a key={c.label} role="menuitem" href={c.href} data-cursor="Open" className="noir-link">{c.label}</a>
                    ))}
                  </div>
                </div>
              ) : (
                <a key={l.label} className="noir-link" data-cursor={l.cursor} href={l.href}>{l.label}</a>
              )
            ))}
          </div>
          <button
            className="c1-nav-burger"
            onClick={() => setMenuOpen(!menuOpen)}
            aria-label={menuOpen ? 'Close menu' : 'Open menu'}
            aria-expanded={menuOpen}
            style={{
              display: 'none', /* shown on mobile via mobile.css */
              background: 'transparent', border: 0, padding: 8, cursor: 'pointer',
              color: '#fff', alignItems: 'center', justifyContent: 'center',
              width: 36, height: 36, mixBlendMode: 'normal',
            }}>
            <svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true">
              {menuOpen ? (
                <g stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
                  <line x1="4" y1="4" x2="18" y2="18" />
                  <line x1="18" y1="4" x2="4" y2="18" />
                </g>
              ) : (
                <g stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
                  <line x1="3" y1="7" x2="19" y2="7" />
                  <line x1="3" y1="15" x2="19" y2="15" />
                </g>
              )}
            </svg>
          </button>
        </div>
      </nav>

      {menuOpen && (
        <div className="c1-nav-overlay" style={{
          position: 'fixed', inset: 0, zIndex: 49,
          background: 'rgba(10,9,8,.96)', backdropFilter: 'blur(8px)',
          display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center',
          gap: 22, padding: 40, overflowY: 'auto',
        }}>
          {links.map((l, i) => (
            <React.Fragment key={l.label}>
              {l.children ? (
                <button
                  type="button"
                  onClick={() => setScopeOpen(!scopeOpen)}
                  aria-expanded={scopeOpen}
                  className="noir-caps"
                  style={{
                    background: 'transparent', border: 0, cursor: 'pointer',
                    display: 'inline-flex', alignItems: 'center', gap: 12,
                    fontSize: 16, letterSpacing: '.22em', textTransform: 'uppercase',
                    color: '#f0ebe2',
                    opacity: 0, animation: `c1-fade-in .5s cubic-bezier(.16,1,.3,1) ${80 + i * 70}ms forwards`,
                    padding: 0,
                  }}>
                  {l.label}
                  <span style={{
                    fontSize: 14,
                    color: 'var(--accent, #991539)',
                    transition: 'transform .25s ease',
                    transform: scopeOpen ? 'rotate(45deg)' : 'rotate(0)',
                    display: 'inline-block', lineHeight: 1,
                  }}>+</span>
                </button>
              ) : (
                <a href={l.href} onClick={() => setMenuOpen(false)}
                  className="noir-caps"
                  style={{
                    fontSize: 16, letterSpacing: '.22em', textTransform: 'uppercase',
                    color: '#f0ebe2',
                    opacity: 0, animation: `c1-fade-in .5s cubic-bezier(.16,1,.3,1) ${80 + i * 70}ms forwards`,
                  }}>
                  {l.label}
                </a>
              )}
              {l.children && scopeOpen && (
                <div style={{
                  display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12,
                  marginTop: -4,
                  animation: 'c1-fade-in .35s cubic-bezier(.16,1,.3,1) forwards',
                }}>
                  {l.children.map((c) => (
                    <a key={c.label} href={c.href} onClick={() => setMenuOpen(false)}
                      className="noir-caps"
                      style={{
                        fontSize: 11, letterSpacing: '.22em', textTransform: 'uppercase',
                        color: 'rgba(240,235,226,.6)',
                      }}>
                      {c.label}
                    </a>
                  ))}
                </div>
              )}
            </React.Fragment>
          ))}
          <style>{`@keyframes c1-fade-in { to { opacity: 1; transform: translateY(0); } from { opacity: 0; transform: translateY(8px); } }`}</style>
        </div>
      )}
    </>
  );
}

function NoirHeroShader({ accent }) {
  return (
    <header style={{ minHeight: '100vh', position: 'relative', overflow: 'hidden' }}>
      <NoirShader accent={accent} />
      <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse at bottom, rgba(0,0,0,.4), rgba(0,0,0,.1))' }} />

      <div style={{ position: 'relative', zIndex: 2, padding: '140px 40px 80px', minHeight: '100vh', display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 36 }}>
        <div className="noir-display" style={{
          fontSize: 'clamp(64px, 13vw, 220px)', lineHeight: .88, letterSpacing: '-.04em',
          maxWidth: '12ch',
        }}>
          <SplitText text="We Are" /><br />
          <SplitText text="What We" delay={180} /><br />
          <span style={{ fontStyle: 'italic', color: 'var(--accent)' }}><SplitText text="Dare" delay={360} /></span>{' '}<SplitText text="to" delay={460} /><br />
          <span style={{ fontStyle: 'italic' }}><SplitText text="Build." delay={560} /></span>
        </div>

        <Reveal delay={700}>
          <p style={{ fontSize: 18, maxWidth: '28ch', margin: 0, lineHeight: 1.5, color: 'rgba(240,235,226,.85)' }}>
            A new-age creative house for brands with something to say.
          </p>
        </Reveal>
      </div>
    </header>
  );
}

function NoirHeroMarquee({ accent }) {
  return (
    <header style={{ minHeight: '100vh', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
      <NoirShader accent={accent} />
      <div style={{ position: 'relative', zIndex: 2 }}>
        <Marquee speed={60}>
          <span className="noir-display" style={{ fontSize: '20vw', padding: '0 .25em', lineHeight: 1, color: '#fff' }}>
            Chapter&thinsp;<span style={{ fontStyle: 'italic', color: accent }}>One</span>&thinsp;—&thinsp;
          </span>
        </Marquee>
        <Marquee speed={80} reverse>
          <span className="noir-display" style={{ fontSize: '8vw', padding: '0 .5em', lineHeight: 1, color: 'var(--muted)', fontStyle: 'italic' }}>
            branding &amp; storytelling for night-minded brands —
          </span>
        </Marquee>
      </div>
    </header>
  );
}

function NoirHeroSplit({ accent }) {
  return (
    <header style={{ minHeight: '100vh', position: 'relative', display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
      <div style={{ position: 'relative', overflow: 'hidden' }}>
        <NoirShader accent={accent} />
      </div>
      <div style={{ padding: '140px 48px 48px', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
        <div className="noir-caps" style={{ color: 'var(--muted)' }}>Studio · Est. MMXVIII · Mumbai</div>
        <div>
          <div className="noir-display" style={{ fontSize: 'clamp(60px, 8vw, 140px)', lineHeight: .9, letterSpacing: '-.035em' }}>
            <SplitText text="Hôtels," /><br />
            <SplitText text="bars," delay={150} /><br />
            <span style={{ fontStyle: 'italic', color: 'var(--accent)' }}><SplitText text="appetites." delay={300} /></span>
          </div>
          <p style={{ fontSize: 18, maxWidth: '38ch', marginTop: 40, color: 'var(--muted)' }}>
            Branding and storytelling for luxury hospitality and craft F&amp;B —
            written with the seriousness of a wine list and the patience of a sauce.
          </p>
        </div>
        <a className="noir-link noir-caps" data-cursor="Enter">[ Enter the index ]</a>
      </div>
    </header>
  );
}

function NoirTicker() {
  return (
    <div style={{ borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)', padding: '20px 0' }}>
      <Marquee speed={90}>
        <span className="noir-caps" style={{ padding: '0 2rem', color: 'var(--muted)' }}>
          &nbsp;✦&nbsp; Brand DNA &nbsp;·&nbsp; Growth Blueprint &nbsp;·&nbsp; Experience &amp; Rituals &nbsp;·&nbsp; F&amp;B Concepts &nbsp;·&nbsp; Social Campaigns &nbsp;·&nbsp; Digital Presence &nbsp;·&nbsp; Content Creation &nbsp;·&nbsp;
        </span>
      </Marquee>
    </div>
  );
}

function NoirScope({ pad }) {
  // Snap-per-scroll: section is N×100vh tall so there's real scroll runway.
  // The sticky child stays pinned through the section. We hijack scroll inside
  // the section, advancing scrollTop by exactly one viewport per wheel/touch event.
  const N = SERVICES.length;
  const sectionRef = React.useRef(null);
  const [idx, setIdx] = React.useState(0);
  const idxRef = React.useRef(0);
  const lockRef = React.useRef(false);
  const touchY = React.useRef(0);

  React.useEffect(() => {
    const stage = document.querySelector('.c1-stage');
    const section = sectionRef.current;
    if (!stage || !section) return;

    // Disable snap-per-scroll on mobile — content gets clipped because
    // the touch-handler hijacks vertical scroll. Let the scope section
    // flow as a normal vertical stack on small viewports.
    const isMobile = () => window.matchMedia('(max-width: 768px)').matches;
    if (isMobile()) return;

    // Section pin range in stage coordinates
    const sectionTop = () => section.offsetTop;
    const sectionBottom = () => section.offsetTop + section.offsetHeight - stage.clientHeight;

    const isPinned = () => {
      const y = stage.scrollTop;
      return y >= sectionTop() - 1 && y <= sectionBottom() + 1;
    };

    const goTo = (next) => {
      next = Math.max(0, Math.min(N - 1, next));
      idxRef.current = next;
      setIdx(next);
      stage.scrollTo({
        top: sectionTop() + next * stage.clientHeight,
        behavior: 'smooth',
      });
      lockRef.current = true;
      setTimeout(() => { lockRef.current = false; }, 750);
    };

    const onWheel = (e) => {
      if (!isPinned()) return;
      const dy = e.deltaY;
      const atStart = idxRef.current === 0 && dy < 0;
      const atEnd = idxRef.current === N - 1 && dy > 0;
      if (atStart || atEnd) return; // let page scroll past
      e.preventDefault();
      if (lockRef.current) return;
      if (Math.abs(dy) < 6) return;
      goTo(idxRef.current + (dy > 0 ? 1 : -1));
    };

    const onTouchStart = (e) => { touchY.current = e.touches[0].clientY; };
    const onTouchMove = (e) => {
      if (!isPinned()) return;
      const dy = touchY.current - e.touches[0].clientY;
      const atStart = idxRef.current === 0 && dy < 0;
      const atEnd = idxRef.current === N - 1 && dy > 0;
      if (atStart || atEnd) return;
      if (Math.abs(dy) < 24) return;
      e.preventDefault();
      if (lockRef.current) return;
      goTo(idxRef.current + (dy > 0 ? 1 : -1));
      touchY.current = e.touches[0].clientY;
    };

    const onKey = (e) => {
      if (!isPinned()) return;
      if (e.key === 'ArrowRight' || e.key === 'PageDown') { e.preventDefault(); goTo(idxRef.current + 1); }
      if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); goTo(idxRef.current - 1); }
    };

    stage.addEventListener('wheel', onWheel, { passive: false });
    stage.addEventListener('touchstart', onTouchStart, { passive: true });
    stage.addEventListener('touchmove', onTouchMove, { passive: false });
    window.addEventListener('keydown', onKey);
    return () => {
      stage.removeEventListener('wheel', onWheel);
      stage.removeEventListener('touchstart', onTouchStart);
      stage.removeEventListener('touchmove', onTouchMove);
      window.removeEventListener('keydown', onKey);
    };
  }, [N]);

  return (
    <section id="scope" ref={sectionRef} style={{
      position: 'relative',
      height: `calc(${N} * 100vh)`,
    }}>
      <div style={{
        position: 'sticky', top: 0, height: '100vh',
        overflow: 'hidden',
        display: 'grid', gridTemplateRows: 'auto 1fr auto',
      }}>
        {/* Header */}
        <div style={{ padding: '110px 40px 0', display: 'grid', gridTemplateColumns: '1fr auto', alignItems: 'end', gap: 40 }}>
          <div>
            <div className="noir-caps" style={{ color: 'var(--muted)', marginBottom: 14 }}>[ 04 ] Scope</div>
            <div className="noir-display" style={{ fontSize: 'clamp(20px, 2.2vw, 30px)', fontStyle: 'italic', color: 'var(--accent)', letterSpacing: '-.01em' }}>What We Do</div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
            {SERVICES.map((s, i) => (
              <div key={s.n} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span className="noir-mono" style={{ fontSize: 10, letterSpacing: '.18em', color: i === idx ? 'var(--accent)' : 'rgba(240,235,226,.3)', transition: 'color .4s' }}>{s.n}</span>
                <span style={{ width: i === idx ? 44 : 14, height: 1, background: i <= idx ? 'var(--accent)' : 'rgba(240,235,226,.18)', transition: 'all .55s cubic-bezier(.16,1,.3,1)' }} />
              </div>
            ))}
          </div>
        </div>

        {/* Horizontal track — snaps one full viewport per index */}
        <div style={{ position: 'relative', overflow: 'hidden' }}>
          <div style={{
            display: 'flex',
            height: '100%',
            transform: `translate3d(${-idx * 100}vw, 0, 0)`,
            transition: 'transform .7s cubic-bezier(.7,0,.2,1)',
            willChange: 'transform',
          }}>
            {SERVICES.map((s) => (
              <article key={s.n} style={{
                flex: '0 0 100vw',
                height: '100%',
                padding: '40px 80px 40px',
                display: 'grid',
                gridTemplateColumns: '1fr 1fr',
                gap: '0 80px',
                alignItems: 'center',
              }}>
                <div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 20, marginBottom: 24 }}>
                    <span className="noir-mono noir-caps" style={{ fontSize: 11, color: 'var(--accent)' }}>{s.n} / {String(N).padStart(2, '0')}</span>
                    <span style={{ flex: 1, height: 1, background: 'rgba(240,235,226,.18)' }} />
                  </div>
                  <h3 className="noir-display" style={{ fontSize: 'clamp(80px, 11vw, 200px)', margin: 0, letterSpacing: '-.045em', lineHeight: .9 }}>
                    {s.title.slice(0, -1)}<span style={{ fontStyle: 'italic', color: 'var(--accent)' }}>{s.title.slice(-1)}</span>
                  </h3>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 32, maxWidth: 560 }}>
                  <p style={{ margin: 0, fontSize: 17, lineHeight: 1.7, color: 'rgba(240,235,226,.78)' }}>{s.body}</p>
                  <div>
                    <div className="noir-caps" style={{ fontSize: 9, color: 'rgba(240,235,226,.3)', marginBottom: 14, letterSpacing: '.22em' }}>Includes</div>
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
                      {s.tags.map((tag) => (
                        <span key={tag} style={{ padding: '6px 13px', border: '1px solid rgba(240,235,226,.18)', borderRadius: 999, fontSize: 10, letterSpacing: '.1em', textTransform: 'uppercase', fontFamily: 'ui-monospace, monospace', color: 'rgba(240,235,226,.55)' }}>{tag}</span>
                      ))}
                    </div>
                  </div>
                  <a href={`scope.html?s=${s.slug}`} data-cursor="Open →" className="noir-caps" style={{
                    display: 'inline-flex', alignItems: 'center', gap: 12,
                    padding: '14px 22px', border: '1px solid var(--accent)', color: 'var(--accent)',
                    fontSize: 11, alignSelf: 'flex-start',
                  }}>
                    Read more about {s.title.toLowerCase()} →
                  </a>
                </div>
              </article>
            ))}
          </div>
        </div>

        {/* Footer hint */}
        <div className="noir-caps" style={{ padding: '0 40px 32px', display: 'flex', justifyContent: 'space-between', color: 'rgba(240,235,226,.4)' }}>
          <span style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <span style={{ width: 28, height: 1, background: 'rgba(240,235,226,.4)' }} />
            Scroll to advance
          </span>
          <span>{String(idx + 1).padStart(2,'0')} / {String(N).padStart(2,'0')}</span>
        </div>
      </div>
    </section>
  );
}

function NoirManifesto({ pad }) {
  return (
    <section style={{ padding: `${pad}px 28px`, borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)' }}>
      <div style={{ maxWidth: 1200, margin: '0 auto', display: 'grid', gridTemplateColumns: '1fr 2fr', gap: 80, alignItems: 'start' }}>
        <div className="noir-caps" style={{ color: 'var(--muted)' }}>[ 02 ] Manifesto</div>
        <div>
          <p className="noir-display" style={{ fontSize: 'clamp(26px, 3vw, 44px)', lineHeight: 1.3, letterSpacing: '-.01em', margin: 0 }}>
            We don't make brands look better. We make them <span style={{ fontStyle: 'italic', color: 'var(--accent)' }}>mean more.</span> For those who know the right identity can outlive trends, markets, and moments.
          </p>
          <p style={{ fontSize: 17, color: 'var(--muted)', maxWidth: '56ch', marginTop: 40, lineHeight: 1.7 }}>
            At Chapter 1, we are a boutique agency working at the intersection of concept, culture, and creative strategy. We craft identities, campaigns, guest experiences, and ideas that make brands felt, remembered, and returned to. We believe the strongest brands live beyond logos — in moments, emotions, rituals, and stories people carry with them. We work with those building the exceptional, the unforgettable, and the next. <span style={{ fontStyle: 'italic', color: 'rgba(240,235,226,.8)' }}>We shape what the future remembers.</span>
          </p>
        </div>
      </div>
    </section>
  );
}

function NoirStories({ pad }) {
  const scrollRef = React.useRef(null);
  const scroll = (dir) => {
    const el = scrollRef.current;
    if (!el) return;
    el.scrollBy({ left: dir * (el.clientWidth * 0.85), behavior: 'smooth' });
  };
  return (
    <section id="stories" style={{ padding: `${pad}px 28px` }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr auto', alignItems: 'end', gap: 40, marginBottom: 60 }}>
        <div className="noir-caps" style={{ color: 'var(--muted)' }}>[ 01 ] Stories</div>
        <h2 className="noir-display" style={{ fontSize: 'clamp(56px, 9vw, 160px)', lineHeight: .9, letterSpacing: '-.035em', margin: 0 }}>
          Portfolio<br /><span style={{ fontStyle: 'italic', color: 'var(--accent)' }}>Highlights.</span>
        </h2>
        {/* Mobile-only carousel arrows (hidden on desktop via mobile.css) */}
        <div className="c1-stories-arrows" style={{ display: 'none', gap: 8, justifySelf: 'end' }}>
          <button onClick={() => scroll(-1)} aria-label="Previous"
            style={{ background: 'transparent', border: '1px solid var(--rule)', color: '#f0ebe2', width: 36, height: 36, borderRadius: 999, cursor: 'pointer', fontSize: 14 }}>←</button>
          <button onClick={() => scroll(1)} aria-label="Next"
            style={{ background: 'transparent', border: '1px solid var(--rule)', color: '#f0ebe2', width: 36, height: 36, borderRadius: 999, cursor: 'pointer', fontSize: 14 }}>→</button>
        </div>
      </div>

      <div ref={scrollRef} className="c1-stories-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 16 }}>
        {WORK.map((w, i) => {
          const layouts = [
            { col: 'span 4', h: 580 },
            { col: 'span 2', h: 580 },
            { col: 'span 2', h: 420 },
            { col: 'span 2', h: 420 },
            { col: 'span 2', h: 420 },
            { col: 'span 6', h: 480 },
          ];
          const L = layouts[i];
          return (
            <Reveal key={w.n} delay={i * 70} style={{ gridColumn: L.col }}>
              <a href={`project.html?id=${w.n}`} style={{ display: 'block', cursor: 'none' }}>
              <article data-cursor="Open →" style={{
                position: 'relative', overflow: 'hidden', height: L.h, cursor: 'none',
              }}>
                <PlaceholderImage palette={w.palette} style={{ height: '100%' }} />
                <div style={{
                  position: 'absolute', inset: 0, padding: 28,
                  display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
                  background: 'linear-gradient(to top, rgba(0,0,0,.6) 0%, transparent 50%)',
                }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                    <span className="noir-caps" style={{ color: 'rgba(240,235,226,.7)' }}>{w.n}</span>
                    <span className="noir-caps" style={{ color: 'rgba(240,235,226,.7)' }}>{w.year}</span>
                  </div>
                  <div>
                    <h3 className="noir-display" style={{
                      fontSize: i === 0 || i === 5 ? 'clamp(36px, 5vw, 80px)' : 'clamp(24px, 3vw, 44px)',
                      margin: '0 0 8px', letterSpacing: '-.025em', lineHeight: 1, color: '#f0ebe2',
                    }}>{w.client}</h3>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end' }}>
                      <span className="noir-caps" style={{ color: 'rgba(240,235,226,.65)' }}>{w.kind}</span>
                      <span className="noir-caps" style={{ color: 'var(--accent)', fontStyle: 'italic' }}>{w.tags[0]}</span>
                    </div>
                  </div>
                </div>
              </article>
              </a>
            </Reveal>
          );
        })}
      </div>
    </section>
  );
}

function NoirClients({ pad }) {
  return (
    <section style={{ padding: `${pad * .8}px 0`, borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)' }}>
      <div className="noir-caps" style={{ color: 'var(--muted)', padding: '0 28px 40px' }}>[ 03 ] Clients</div>
      <Marquee speed={70}>
        <span className="noir-display" style={{ fontSize: 64, padding: '0 1.5rem', letterSpacing: '-.02em' }}>
          {CLIENTS_TOP.map((c) => (
            <React.Fragment key={c}>
              {c}<span style={{ color: 'var(--accent)', fontStyle: 'italic', margin: '0 1.5rem' }}>✦</span>
            </React.Fragment>
          ))}
        </span>
      </Marquee>
      <Marquee speed={85} reverse>
        <span className="noir-display" style={{ fontSize: 48, padding: '20px 1.5rem 0', letterSpacing: '-.02em', color: 'var(--muted)', fontStyle: 'italic' }}>
          {CLIENTS_BOTTOM.map((c) => (
            <React.Fragment key={c}>
              {c}<span style={{ color: 'var(--accent)', margin: '0 1.5rem' }}>·</span>
            </React.Fragment>
          ))}
        </span>
      </Marquee>
    </section>
  );
}

function NoirJournal({ pad }) {
  return (
    <section id="journal" style={{ padding: `${pad}px 28px` }}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', alignItems: 'end', marginBottom: 60 }}>
        <div>
          <div className="noir-caps" style={{ color: 'var(--muted)', marginBottom: 16 }}>[ 05 ] Field Notes</div>
          <h2 className="noir-display" style={{ fontSize: 'clamp(48px, 7vw, 120px)', lineHeight: .9, letterSpacing: '-.03em', margin: 0 }}>
            Field <span style={{ fontStyle: 'italic', color: 'var(--accent)' }}>notes.</span>
          </h2>
        </div>
        <div />
      </div>
      <div style={{ borderTop: '1px solid var(--rule)' }}>
        {JOURNAL.map((j, i) => (
          <Reveal key={j.title} delay={i * 60}>
            <a href={`article.html?id=${slugify(j.title)}`} data-cursor="Open" style={{ display: 'block' }}>
              <article style={{
                display: 'grid', gridTemplateColumns: '140px 1fr 2fr 140px 40px',
                alignItems: 'center', gap: 30,
                padding: '32px 0', borderBottom: '1px solid var(--rule)',
              }}>
                <span className="noir-caps" style={{ color: 'var(--accent)' }}>{j.cat}</span>
                <span className="noir-caps" style={{ color: 'var(--muted)' }}>{j.date}</span>
                <h3 className="noir-display" style={{ fontSize: 32, margin: 0, letterSpacing: '-.02em' }}>{j.title}</h3>
                <span className="noir-caps" style={{ color: 'var(--muted)' }}>{j.read}</span>
                <span style={{ textAlign: 'right', color: 'var(--accent)' }}>↗</span>
              </article>
            </a>
          </Reveal>
        ))}
      </div>
    </section>
  );
}

function NoirContact({ pad, accent }) {
  const [form, setForm] = React.useState({ name: '', email: '', company: '', mobile: '', message: '' });
  const [sent, setSent] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const [submitError, setSubmitError] = React.useState(null);
  const set = (k) => (e) => setForm({ ...form, [k]: e.target.value });

  // Form submission: POSTs to a configurable endpoint (Google Apps Script
  // web app, Sheety, Formspree, Make/Zapier hook — anything that accepts
  // JSON). Configure once via DevTools console:
  //   localStorage.setItem('c1.form.endpoint', 'https://script.google.com/macros/s/.../exec');
  // See CONTACT-FORM-SETUP.md for the full Google Sheets recipe.
  // If no endpoint is set, falls back to opening a pre-filled mailto.
  const onSubmit = async (e) => {
    e.preventDefault();
    if (submitting) return;
    setSubmitError(null);
    setSubmitting(true);

    const payload = {
      ...form,
      submittedAt: new Date().toISOString(),
      source: 'chapter1.website',
      page: 'home',
    };
    const endpoint = (typeof localStorage !== 'undefined' && localStorage.getItem('c1.form.endpoint')) || '';

    if (endpoint) {
      try {
        // 'no-cors' + text/plain avoids the CORS preflight that Apps Script
        // doesn't support. The script still receives the body via e.postData.
        await fetch(endpoint, {
          method: 'POST',
          mode: 'no-cors',
          headers: { 'Content-Type': 'text/plain;charset=utf-8' },
          body: JSON.stringify(payload),
        });
        setSent(true);
      } catch (err) {
        console.warn('[contact] submit failed', err);
        setSubmitError('Could not send right now. Please email hello@chapter1.in directly.');
      } finally {
        setSubmitting(false);
      }
      return;
    }

    // Fallback: open the user's mail client with all fields pre-filled
    const subject = `New enquiry from ${form.name || 'a visitor'}`;
    const lines = [
      `Name: ${form.name}`,
      `Email: ${form.email}`,
      `Company: ${form.company || '—'}`,
      `Mobile: ${form.mobile || '—'}`,
      '',
      form.message || '',
    ];
    window.location.href = `mailto:hello@chapter1.in?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(lines.join('\n'))}`;
    setSent(true);
    setSubmitting(false);
  };

  const inputStyle = {
    background: 'transparent', border: 0, borderBottom: '1px solid var(--rule)',
    color: '#f0ebe2', fontFamily: 'Fraunces, Georgia, serif', fontWeight: 300,
    fontSize: 22, padding: '14px 0', outline: 'none', width: '100%',
    transition: 'border-color .3s',
  };
  const labelStyle = {
    display: 'block', fontFamily: 'JetBrains Mono, ui-monospace, monospace',
    fontSize: 10, letterSpacing: '.22em', textTransform: 'uppercase',
    color: 'var(--muted)', marginBottom: 6,
  };

  return (
    <section id="contact" style={{ padding: `${pad * 1.2}px 28px`, position: 'relative', overflow: 'hidden', borderTop: '1px solid var(--rule)' }}>
      <div style={{ position: 'absolute', inset: 0, opacity: .35 }}><NoirShader accent={accent} /></div>
      <div style={{ position: 'relative', zIndex: 2, maxWidth: 1240, margin: '0 auto' }}>
        {/* Headline — big & centered */}
        <div style={{ textAlign: 'center', marginBottom: 100 }}>
          <div className="noir-caps" style={{ color: 'var(--muted)', marginBottom: 40 }}>[ 06 ] Say Hello</div>
          <div className="noir-display" style={{ fontSize: 'clamp(56px, 10vw, 200px)', lineHeight: .92, letterSpacing: '-.04em' }}>
            <SplitText text="Let’s Craft" /><br />
            <span style={{ fontStyle: 'italic', color: 'var(--accent)' }}><SplitText text="The Next Chapter." delay={220} /></span>
          </div>
        </div>

        {/* Form — centered, narrower column below */}
        <div style={{ maxWidth: 760, margin: '0 auto' }}>
          {sent ? (
            <div style={{ padding: '60px 0' }}>
              <div className="noir-caps" style={{ color: accent, fontStyle: 'italic', marginBottom: 18 }}>Thanks — we’ll be in touch.</div>
              <p style={{ fontFamily: 'Fraunces, Georgia, serif', fontWeight: 300, fontSize: 22, lineHeight: 1.5, color: 'rgba(240,235,226,.85)', maxWidth: '34ch' }}>
                Your note has landed in the inbox of {form.name || 'one of us'}. Expect a reply within two working days.
              </p>
            </div>
          ) : (
            <form onSubmit={onSubmit} style={{ display: 'flex', flexDirection: 'column', gap: 28 }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 28 }}>
                <div>
                  <label style={labelStyle}>Name</label>
                  <input style={inputStyle} required type="text" value={form.name} onChange={set('name')} placeholder="Your name" />
                </div>
                <div>
                  <label style={labelStyle}>Email</label>
                  <input style={inputStyle} required type="email" value={form.email} onChange={set('email')} placeholder="you@studio.com" />
                </div>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 28 }}>
                <div>
                  <label style={labelStyle}>Company</label>
                  <input style={inputStyle} type="text" value={form.company} onChange={set('company')} placeholder="Brand or company" />
                </div>
                <div>
                  <label style={labelStyle}>Mobile number</label>
                  <input style={inputStyle} type="tel" inputMode="tel" pattern="[0-9+\\-\\s()]*" value={form.mobile} onChange={set('mobile')} placeholder="+91 98XXX XXXXX" />
                </div>
              </div>
              <div>
                <label style={labelStyle}>Tell us a bit more</label>
                <textarea style={{ ...inputStyle, minHeight: 110, resize: 'vertical', fontSize: 18, lineHeight: 1.55 }}
                  required value={form.message} onChange={set('message')}
                  placeholder="A few sentences about the brand, scope, timeline..." />
              </div>
              {submitError && (
                <div className="noir-caps" style={{ color: '#e07b8c', fontSize: 10, fontStyle: 'italic' }}>
                  {submitError}
                </div>
              )}
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 8 }}>
                <span className="noir-caps" style={{ color: 'var(--muted)', fontSize: 9 }}>We reply within 2 working days.</span>
                <button type="submit" data-cursor="Send" className="noir-caps" disabled={submitting} style={{
                  background: accent, color: '#fff', border: 0, padding: '14px 28px',
                  fontSize: 11, letterSpacing: '.22em', cursor: submitting ? 'wait' : 'pointer',
                  fontFamily: 'JetBrains Mono, ui-monospace, monospace',
                  opacity: submitting ? 0.65 : 1, transition: 'opacity .2s',
                }}>{submitting ? 'Sending…' : 'Send note →'}</button>
              </div>
            </form>
          )}
        </div>
      </div>
    </section>
  );
}

function NoirFooter() {
  const iconLinkStyle = {
    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
    width: 32, height: 32, color: 'rgba(240,235,226,.7)',
    transition: 'color .25s ease',
  };
  return (
    <footer style={{ borderTop: '1px solid var(--rule)', padding: '32px 28px', display: 'grid', gridTemplateColumns: '1fr auto 1fr', gap: 40, alignItems: 'center', fontSize: 11 }}>
      <div className="noir-caps" style={{ color: 'var(--muted)' }}>Chapter 1, Since 2023</div>
      <a href="mailto:hello@chapter1.in" data-cursor="Email" className="noir-link noir-caps" style={{ color: 'rgba(240,235,226,.85)' }}>hello@chapter1.in</a>
      <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, alignItems: 'center' }}>
        <a href="https://www.instagram.com/" target="_blank" rel="noopener noreferrer"
           data-cursor="Instagram" aria-label="Instagram" style={iconLinkStyle}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
            <rect x="3" y="3" width="18" height="18" rx="5" />
            <circle cx="12" cy="12" r="4" />
            <circle cx="17.5" cy="6.5" r="1" fill="currentColor" stroke="none" />
          </svg>
        </a>
        <a href="https://www.linkedin.com/" target="_blank" rel="noopener noreferrer"
           data-cursor="LinkedIn" aria-label="LinkedIn" style={iconLinkStyle}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
            <path d="M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zM3 9h4v12H3zM10 9h3.8v1.7h.05c.53-.95 1.83-1.95 3.77-1.95 4.03 0 4.78 2.65 4.78 6.1V21h-4v-5.5c0-1.31-.02-3-1.83-3-1.83 0-2.11 1.43-2.11 2.9V21h-4z" />
          </svg>
        </a>
        <a href="https://wa.me/919999999999" target="_blank" rel="noopener noreferrer"
           data-cursor="WhatsApp" aria-label="WhatsApp" style={iconLinkStyle}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
            <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.71.306 1.263.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.247-.694.247-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413" />
          </svg>
        </a>
      </div>
      <style>{`footer a[aria-label]:hover { color: var(--accent, #991539) !important; }`}</style>
    </footer>
  );
}

Object.assign(window, { DirNoir });
