// shared.jsx — cross-direction primitives (cursor, noise, observers, data)

// ── Content ────────────────────────────────────────────────────
const WORK = [
  {
    n: '01',
    client: 'Sleepy Owl',
    kind: 'F&B · India',
    year: '2025',
    tagline: 'Coffee that speaks before you do.',
    tags: ['Packaging', 'Identity', 'Retail'],
    palette: ['#1a0e08', '#c97a3c', '#f3e7d3', '#2a1810'],
  },
  {
    n: '02',
    client: 'Bombay Canteen',
    kind: 'Restaurant · Mumbai',
    year: '2025',
    tagline: 'The city on a plate.',
    tags: ['Identity', 'Menu design', 'Environmental'],
    palette: ['#1c1208', '#d4763c', '#f0e8d0', '#3a2010'],
  },
  {
    n: '03',
    client: 'Nicobar',
    kind: 'Lifestyle · India',
    year: '2024',
    tagline: 'Slow living, beautifully made.',
    tags: ['Brand strategy', 'Editorial', 'Web'],
    palette: ['#1e2018', '#8a9a6a', '#f0ede0', '#3a4028'],
  },
  {
    n: '04',
    client: 'Kama Ayurveda',
    kind: 'Beauty · India',
    year: '2024',
    tagline: 'Ancient wisdom, new shelf.',
    tags: ['Packaging', 'Art direction', 'Campaign'],
    palette: ['#2a1a10', '#c8a060', '#f5f0e8', '#8b4a20'],
  },
  {
    n: '05',
    client: 'Third Wave Coffee',
    kind: 'F&B · India',
    year: '2024',
    tagline: 'The pause between two ideas.',
    tags: ['Identity', 'Signage', 'Packaging'],
    palette: ['#0e0c08', '#b07845', '#ede4d0', '#2a1c10'],
  },
  {
    n: '06',
    client: 'Lune Hotels',
    kind: 'Hospitality · Goa',
    year: '2023',
    tagline: 'Where the sea forgets the time.',
    tags: ['Identity', 'Wayfinding', 'Web'],
    palette: ['#0e1820', '#3a6a8a', '#d8e8f0', '#1a3050'],
  },
];

const SERVICES = [
  {
    n: '01',
    slug: 'brands',
    title: 'Brands',
    body: 'A brand is the sum of what people believe about you. It is earned through every detail, every promise, and every experience. We build brands with depth and direction. From new ventures to evolving businesses, we shape identities that feel distinct in crowded markets. Our approach combines strategic thinking with creative instinct, ensuring every detail has purpose. The result is a brand people trust, desire, and return to.',
    tags: ['Market Research', 'Brand Strategy', 'Brand Audits', 'Nomenclature', 'Positioning', 'Visual Identity', 'Brand Guidelines', 'Rebranding', 'Consumer Insights', 'Menu Design', 'Graphic Design'],
    deliverables: [
      { name: 'Market Research', body: 'Field interviews, category mapping, and competitor teardowns that surface what your audience actually rewards — not what they say they want.' },
      { name: 'Brand Strategy', body: 'The argument that anchors every decision: positioning, audiences, brand promise, and the long-term direction of the company.' },
      { name: 'Brand Audits', body: 'A frank diagnostic of an existing brand — what’s working, what’s drifting, and what to keep, sharpen, or retire.' },
      { name: 'Nomenclature', body: 'Names for ventures, sub-brands, products, and rituals — each tested for sound, ownership, and shelf life.' },
      { name: 'Positioning', body: 'A single, defensible idea of where the brand sits in the market and the minds of its customers.' },
      { name: 'Visual Identity', body: 'Logo systems, type, colour, and motion built to flex across formats and survive a decade of use.' },
      { name: 'Brand Guidelines', body: 'Living documents written for the people who will actually use them — marketing, design, ops, and partners.' },
      { name: 'Rebranding', body: 'A considered move from where you are to where you need to be — with as much continuity as the business can carry.' },
      { name: 'Consumer Insights', body: 'Behavioural and cultural research that turns observations into decisions the brand can act on.' },
    ],
    dek: 'Identities with depth, distinction, and direction.',
    manifesto: 'A brand isn\u2019t a logo. It\u2019s a long argument with the market about who you are. We help businesses build that argument from first principles \u2014 starting with what you believe, why it matters, and how every surface of the company can carry that meaning forward. The work is part research, part instinct, and entirely practical: every system we build is meant to be used, not just admired.',
    process: [
      { n: '01', label: 'Discover', body: 'Stakeholder interviews, market research, audits of the existing brand and the field around it.' },
      { n: '02', label: 'Define', body: 'Positioning, narrative, naming, and the strategic backbone everything else hangs on.' },
      { n: '03', label: 'Design', body: 'Identity systems, typography, color, voice \u2014 built to flex across years, not weeks.' },
      { n: '04', label: 'Deploy', body: 'Guidelines, training, and the rollout craft to make sure the brand survives contact with reality.' },
    ],
    quote: { text: 'Chapter 1 didn\u2019t hand us a logo. They handed us a way of thinking about ourselves.', cite: 'Co-founder, F&B brand, 2025' },
    relatedWorkIds: ['01', '03', '04'],
  },
  {
    n: '02',
    slug: 'concepts',
    title: 'Concepts',
    body: 'Great concepts give people something to believe in, belong to, and talk about. We develop ideas that transform spaces, menus, experiences, and occasions into something people talk about. Whether it is a restaurant, bar, hotel experience, event, or launch, we build concepts that feel fresh, relevant, and commercially sharp. Every concept is designed to spark curiosity and loyalty. We create what people want to experience firsthand.',
    tags: ['F&B Concepts', 'Guest Experience Architecture', 'Brand Rituals', 'Event Ideation', 'Launch Concepts', 'Employer Branding'],
    deliverables: [
      { name: 'F&B Concepts', body: 'End-to-end concepts for restaurants, bars, and cafés — cuisine direction, menu architecture, service script, and the room itself.' },
      { name: 'Guest Experience Architecture', body: 'Mapping the complete arc of a visit, from the first message a guest reads to the moment they post about it.' },
      { name: 'Brand Rituals', body: 'Repeatable moments — a welcome, a closing, a signature dish, a Sunday — that turn a visit into a memory worth returning for.' },
      { name: 'Event Ideation', body: 'Launches, takeovers, and seasonal programming designed to land in culture, not just on the calendar.' },
      { name: 'Launch Concepts', body: 'The concept and choreography of opening week — soft launches, press, partnerships, and the first 90 days.' },
      { name: 'Employer Branding', body: 'How a venue or company shows up to the people who will actually run it — hiring, onboarding, and team culture.' },
    ],
    dek: 'Ideas that turn a venue into a verb.',
    manifesto: 'Hospitality is the new branding agency. A great concept is the difference between a restaurant people visit once and one they bring their parents to. We build the underlying idea \u2014 the cuisine, the rituals, the room, the playlist, the menu hierarchy \u2014 then translate that idea into every guest decision a venue will ever ask of itself. The work is rigorous, but it is meant to feel inevitable.',
    process: [
      { n: '01', label: 'Sharpen', body: 'Workshops with founders to crystallise the idea \u2014 what the place is, what it isn\u2019t, who it\u2019s for.' },
      { n: '02', label: 'Stage', body: 'Architectural cues, menu architecture, service rituals, sensory direction.' },
      { n: '03', label: 'Story', body: 'Naming, narrative, the language a venue uses with its guests and its team.' },
      { n: '04', label: 'Open', body: 'Pre-opening playbook, soft launch programming, the first 90 days of operations support.' },
    ],
    quote: { text: 'Half the menu was an argument we hadn\u2019t had yet. They made us have it.', cite: 'F&B Director, hotel group, 2024' },
    relatedWorkIds: ['02', '05', '06'],
  },
  {
    n: '03',
    slug: 'campaigns',
    title: 'Campaigns',
    body: 'Campaigns should do more than fill calendars. They should move culture and drive action. We create campaigns that build relevance, attract attention, and convert interest into momentum. From launches to festive moments to long-term brand building, we shape narratives that travel across platforms and audiences. Every campaign is rooted in strategy and brought to life with sharp creative execution. We make brands impossible to ignore.',
    tags: ['Campaign Strategy', 'Social Media Marketing', 'Performance Marketing', 'Content Creation', 'Influencer Strategy', 'Creative Production'],
    deliverables: [
      { name: 'Campaign Strategy', body: 'A clear business problem, a clear creative idea, and a clear plan for how to make one solve the other.' },
      { name: 'Social Media Marketing', body: 'Always-on channel strategy and content systems built for the platforms your audience actually lives on.' },
      { name: 'Performance Marketing', body: 'Paid creative, channel mix, and weekly optimisation against the metric that matters — not against vanity dashboards.' },
      { name: 'Content Creation', body: 'Photography, film, copy, and design produced in-house and with partners we trust — at the volume modern brands need.' },
      { name: 'Influencer Strategy', body: 'Selection, briefing, and measurement frameworks for working with creators in a way that actually moves the brand.' },
      { name: 'Creative Production', body: 'Casting, locations, post, and delivery — the unglamorous craft that decides whether the idea actually lands.' },
    ],
    dek: 'Stories that travel \u2014 across platforms, audiences, and quarters.',
    manifesto: 'A campaign is a brand making a claim out loud. We build campaigns the way we build brands \u2014 from the strategy down. The hook is the last thing we write, not the first. We start with what the business actually needs to move, then engineer the creative, the channel mix, and the production craft to get it there. Beautiful work that doesn\u2019t move the number isn\u2019t the work we want to do.',
    process: [
      { n: '01', label: 'Brief', body: 'A real brief: business problem, audience, behaviour we want to change, success metric.' },
      { n: '02', label: 'Idea', body: 'Big creative idea + the system it lives inside \u2014 hero, supporting acts, always-on.' },
      { n: '03', label: 'Make', body: 'Production, casting, copy, content, performance creative \u2014 in-house and with trusted partners.' },
      { n: '04', label: 'Move', body: 'Live in market, weekly read-outs, optimisation against the metric we agreed in week one.' },
    ],
    quote: { text: 'They wrote three lines that ended up on every wall in our office. Then they made the rest of the campaign earn them.', cite: 'CMO, lifestyle brand, 2025' },
    relatedWorkIds: ['01', '02', '04'],
  },
];

const CLIENTS_TOP = [
  'Hilton', 'Grand Mercure', 'Hyatt Regency', 'Hilton Garden Inn',
  'Novotel', 'House of Commons', 'Caspian Techparks',
  'Ran Baas, The Palace', 'ibis hotels', 'CGH Earth', 'Poshace.com',
];

const CLIENTS_BOTTOM = [
  'Nora\u2019s Cantina', 'NVeil', 'Box Bite', 'The HQ',
  'Orah Brew Garden', '188 DownTown', 'Kahani Sutra',
  'The Flying Trunk', 'Stolen Heaven', 'Terrace Bar and Kitchen',
  'Meraki Shimla', 'The Patiala', '1131 Bar + Kitchen', 'Streats', 'Hrada Spa',
];

const CLIENTS = [...CLIENTS_TOP, ...CLIENTS_BOTTOM];

const JOURNAL = [
  { cat: 'Essay', title: 'Why Indian brands fear the serif', read: '7 min', date: 'Mar \'26' },
  { cat: 'Field notes', title: 'Designing for the second city', read: '5 min', date: 'Feb \'26' },
  { cat: 'Interview', title: 'On packaging as a first impression', read: '9 min', date: 'Jan \'26' },
  { cat: 'Essay', title: 'The logo is the last thing we make', read: '6 min', date: 'Dec \'25' },
];

// ── Utilities ──────────────────────────────────────────────────
function slugify(s) {
  return (s || '')
    .toString()
    .toLowerCase()
    .replace(/['']/g, '')
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

function getQueryParam(name) {
  if (typeof window === 'undefined') return null;
  const p = new URLSearchParams(window.location.search);
  return p.get(name);
}

// ── Hooks ──────────────────────────────────────────────────────
function useMousePos() {
  const [p, setP] = React.useState({ x: 0.5, y: 0.5, raw: { x: 0, y: 0 } });
  React.useEffect(() => {
    const onMove = (e) => setP({
      x: e.clientX / window.innerWidth,
      y: e.clientY / window.innerHeight,
      raw: { x: e.clientX, y: e.clientY },
    });
    window.addEventListener('mousemove', onMove);
    return () => window.removeEventListener('mousemove', onMove);
  }, []);
  return p;
}

function useScrollY(ref) {
  const [y, setY] = React.useState(0);
  React.useEffect(() => {
    const el = ref?.current || window;
    const get = () => (el === window ? window.scrollY : el.scrollTop);
    const onScroll = () => setY(get());
    el.addEventListener('scroll', onScroll, { passive: true });
    return () => el.removeEventListener('scroll', onScroll);
  }, [ref]);
  return y;
}

function useInView(options = { threshold: 0.12 }) {
  const ref = React.useRef(null);
  const [inView, setInView] = React.useState(false);
  React.useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setInView(true); io.disconnect(); }
    }, options);
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

// ── Reusable visuals ───────────────────────────────────────────

function GrainOverlay({ opacity = 0.08, blend = 'overlay' }) {
  return (
    <div style={{
      position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 100,
      mixBlendMode: blend, opacity,
      backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.8'/></svg>")`,
      backgroundSize: '200px 200px',
    }} />
  );
}

function PlaceholderImage({ palette = ['#222', '#888', '#ddd', '#666'], label, n, style, ratio }) {
  const [a, b, c, d] = palette;
  return (
    <div style={{
      position: 'relative', overflow: 'hidden',
      background: `
        radial-gradient(120% 80% at 20% 10%, ${a} 0%, transparent 60%),
        radial-gradient(90% 90% at 90% 90%, ${d} 0%, transparent 55%),
        linear-gradient(200deg, ${b} 0%, ${c} 100%)
      `,
      aspectRatio: ratio,
      ...style,
    }}>
      <div style={{ position: 'absolute', inset: 0, boxShadow: 'inset 0 0 120px rgba(0,0,0,.25)' }} />
      {(label || n) && (
        <div style={{
          position: 'absolute', bottom: 12, left: 14, right: 14,
          display: 'flex', justifyContent: 'space-between',
          color: c, mixBlendMode: 'difference', opacity: .9,
          fontSize: 10, letterSpacing: '.14em', textTransform: 'uppercase',
          fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
        }}>
          <span>{label}</span>
          <span>{n}</span>
        </div>
      )}
    </div>
  );
}

function CustomCursor({ color = '#111', bg = 'transparent' }) {
  const dotRef = React.useRef(null);
  const ringRef = React.useRef(null);
  const [label, setLabel] = React.useState('');
  React.useEffect(() => {
    let dx = 0, dy = 0, rx = 0, ry = 0, tx = 0, ty = 0;
    const onMove = (e) => { tx = e.clientX; ty = e.clientY; };
    const tick = () => {
      dx += (tx - dx) * 0.5;
      dy += (ty - dy) * 0.5;
      rx += (tx - rx) * 0.14;
      ry += (ty - ry) * 0.14;
      if (dotRef.current) dotRef.current.style.transform = `translate(${dx - 3}px, ${dy - 3}px)`;
      if (ringRef.current) ringRef.current.style.transform = `translate(${rx - 16}px, ${ry - 16}px)`;
      raf = requestAnimationFrame(tick);
    };
    let raf = requestAnimationFrame(tick);
    window.addEventListener('mousemove', onMove);

    const onOver = (e) => {
      const el = e.target.closest('[data-cursor]');
      setLabel(el ? el.getAttribute('data-cursor') : '');
    };
    document.addEventListener('mouseover', onOver);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseover', onOver);
    };
  }, []);
  return (
    <>
      <div ref={dotRef} style={{
        position: 'fixed', top: 0, left: 0, width: 6, height: 6, borderRadius: '50%',
        background: color, pointerEvents: 'none', zIndex: 9999,
        mixBlendMode: 'difference',
      }} />
      <div ref={ringRef} style={{
        position: 'fixed', top: 0, left: 0, width: 32, height: 32,
        borderRadius: '50%', border: `1px solid ${color}`, background: bg,
        pointerEvents: 'none', zIndex: 9998, opacity: .55,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: label ? 14 : 0,
        transition: 'padding .25s, background .2s',
      }}>
        {label && (
          <span style={{
            position: 'absolute', whiteSpace: 'nowrap',
            fontSize: 10, letterSpacing: '.12em', textTransform: 'uppercase',
            fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
            color,
          }}>{label}</span>
        )}
      </div>
    </>
  );
}

function Reveal({ children, delay = 0, y = 30, as: Tag = 'div', style = {} }) {
  const [ref, inView] = useInView({ threshold: 0.14 });
  return (
    <Tag ref={ref} style={{
      transform: inView ? 'translate3d(0,0,0)' : `translate3d(0,${y}px,0)`,
      opacity: inView ? 1 : 0,
      transition: `transform 1.1s cubic-bezier(.2,.7,.1,1) ${delay}ms, opacity .9s ease ${delay}ms`,
      willChange: 'transform, opacity',
      ...style,
    }}>{children}</Tag>
  );
}

function SplitText({ text, delay = 0, stagger = 30, style = {}, as: Tag = 'span' }) {
  const [ref, inView] = useInView({ threshold: 0.2 });
  const chars = Array.from(text);
  return (
    <Tag ref={ref} style={{ display: 'inline-block', ...style }}>
      {chars.map((ch, i) => (
        <span key={i} style={{
          display: 'inline-block',
          transform: inView ? 'translateY(0)' : 'translateY(110%)',
          opacity: inView ? 1 : 0,
          transition: `transform .9s cubic-bezier(.2,.8,.1,1) ${delay + i * stagger}ms, opacity .6s ease ${delay + i * stagger}ms`,
          whiteSpace: ch === ' ' ? 'pre' : 'normal',
        }}>{ch}</span>
      ))}
    </Tag>
  );
}

function Marquee({ children, speed = 60, reverse = false, style = {} }) {
  return (
    <div style={{ overflow: 'hidden', display: 'flex', whiteSpace: 'nowrap', ...style }}>
      <style>{`
        @keyframes marquee-l { from { transform: translateX(0) } to { transform: translateX(-50%) } }
        @keyframes marquee-r { from { transform: translateX(-50%) } to { transform: translateX(0) } }
      `}</style>
      <div style={{
        display: 'flex', flexShrink: 0,
        animation: `${reverse ? 'marquee-r' : 'marquee-l'} ${speed}s linear infinite`,
      }}>
        {children}
        {children}
      </div>
    </div>
  );
}

Object.assign(window, {
  WORK, SERVICES, CLIENTS, CLIENTS_TOP, CLIENTS_BOTTOM, JOURNAL,
  slugify, getQueryParam,
  useMousePos, useScrollY, useInView,
  GrainOverlay, PlaceholderImage, CustomCursor, Reveal, SplitText, Marquee,
});
