// case-study.jsx — detail pages for individual projects

const CASES = {
  'township-launch': {
    slug: 'township-launch',
    image: 'https://images.unsplash.com/photo-WQ42ypHazNo?w=1200&q=80&auto=format&fit=crop',
    cat: 'Real Estate',
    title: 'Township pre-launch in a saturated suburb',
    titleAccent: 'pre-launch',
    deck: 'A 32-acre residential township in south Ludhiana — fourth new project in a 3km radius. We launched ahead of its more-funded neighbours by building a pre-launch engine that turned curiosity into site visits and site visits into bookings.',
    glyph: 'R',
    client: 'Aman Buildwell',
    year: '2025 — 2026',
    services: ['Branding', 'Performance', 'Field activation'],
    timeline: '14 weeks',
    challenge: 'Three competing townships in the same micro-market, two of them backed by larger brands with bigger ad budgets. Aman Buildwell had a better product but no awareness, no broker network, and a runway that did not include a six-month brand-building wait.',
    challenge2: 'The brief was simple and uncompromising: 50 bookings in the first 90 days, at a cost-per-booking under ₹65,000. Fail and the next phase of the township gets paused.',
    pull: 'Don\u2019t tell anyone we exist until the brand can answer their questions. So we built the brand and the funnel in parallel — and launched both on the same day.',
    pullWho: 'Satnam Jandu · Founder',
    approach: [
      { n: '01', t: 'Pre-launch teaser', d: 'Two weeks of curiosity creative on Meta + YouTube. No price, no name — just the question every Ludhiana family asks before buying.' },
      { n: '02', t: 'Site name reveal', d: 'A coordinated launch across hoardings, OOH, drone reels and broker WhatsApp. Lead capture funnel live on the same day.' },
      { n: '03', t: 'Broker enablement', d: 'Onboarded 240 brokers across 6 districts on a paid-per-qualified-lead model. Daily WhatsApp digests with new lead-quality data.' },
      { n: '04', t: 'Site activation', d: 'Site visit experience — drone reel screening, model-flat tour, refreshments, on-site closer team. Average visit length: 47 minutes.' },
    ],
    results: [
      { v: 94, suffix: '', k: 'Site visits month 1', note: 'Pre-launch goal was 60' },
      { v: 11, suffix: '', k: 'Bookings month 1', note: 'On the way to 50-by-day-90' },
      { v: 62, suffix: '%', k: 'Lead cost reduction', note: 'vs. their previous launch' },
      { v: 240, suffix: '', k: 'Brokers activated', note: 'Across 6 districts' },
    ],
    nextSlug: 'mla-campaign',
  },

  'mla-campaign': {
    slug: 'mla-campaign',
    image: 'https://images.unsplash.com/photo-AXGOhRx0Sko?w=1200&q=80&auto=format&fit=crop',
    cat: 'Political',
    title: 'Holding a seat against anti-incumbency',
    titleAccent: 'anti-incumbency',
    deck: 'A sitting MLA in central Punjab going into a tough re-election. Two years of bad press, a fragmented constituency, and a challenger with deep pockets. We ran the creative and outreach engine for the final 18 weeks.',
    glyph: 'V',
    client: 'Confidential · sitting MLA',
    year: 'Q4 2025',
    services: ['Strategy', 'Creative', 'IVR / Outreach', 'Field ops'],
    timeline: '18 weeks',
    challenge: 'Our candidate was widely seen as &ldquo;done with politics.&rdquo; The opposition\u2019s narrative had taken hold across WhatsApp and chai-stall conversations — and his own team didn\u2019t have a coherent counter-message to bring to door-to-door visits.',
    challenge2: 'We were brought in 18 weeks out. Too late for a brand rebuild, just enough time for a relentless, hyper-local campaign with a single, defensible message.',
    pull: 'The opposition was loudest on TV. We won every block by being loudest on the WhatsApp group that the block-incharge\u2019s wife runs.',
    pullWho: 'Anonymous · Campaign Manager',
    approach: [
      { n: '01', t: 'Constituency listening', d: 'Three weeks of structured interviews across all 84 polling booths. The opposition had a story; we needed to know which parts our voters actually believed.' },
      { n: '02', t: 'Single-message creative', d: 'One message, three formats, 14 weeks of repetition. Posters, reels, IVR scripts, door-to-door pamphlets — all carrying the same core sentence.' },
      { n: '03', t: 'IVR + WhatsApp engine', d: 'Twice-weekly IVR calls in the candidate\u2019s own voice. WhatsApp broadcast lists segmented by booth and household type. 4.2L verified touchpoints by polling day.' },
      { n: '04', t: 'Last-mile field kit', d: 'A printed playbook for every booth-incharge: what to say, when to say it, and what to do if asked the three hardest questions.' },
    ],
    results: [
      { v: 4.2, suffix: 'L', k: 'Voter touchpoints', note: 'Verified, deduped' },
      { v: 84, suffix: '/84', k: 'Booths covered', note: 'Field + WhatsApp' },
      { v: 18, suffix: '%', k: 'Vote-share gain', note: 'vs. previous election' },
      { v: 1, suffix: '', k: 'Seat held', note: 'Margin: 6,200 votes' },
    ],
    nextSlug: 'sona-foods',
  },

  'sona-foods': {
    slug: 'sona-foods',
    image: 'https://images.unsplash.com/photo-i0IvwAhhGZM?w=1200&q=80&auto=format&fit=crop',
    cat: 'D2C Brand',
    title: 'Repositioning a 40-year-old spice brand',
    titleAccent: '40-year-old',
    deck: 'Sona Foods has been in north-Indian kitchens since 1984. By 2024, their retail-shelf identity was losing the under-35 buyer to newer D2C brands with stronger Amazon storefronts. We rebuilt the identity, the packaging, and the online funnel.',
    glyph: 'S',
    client: 'Sona Foods Pvt. Ltd.',
    year: 'Q3 2025',
    services: ['Branding', 'Packaging', 'D2C Platform'],
    timeline: '11 weeks',
    challenge: 'The board didn\u2019t want a rebrand — they wanted &ldquo;better marketing.&rdquo; The data said otherwise. Their packaging hadn\u2019t been touched since 2007, their Amazon listings used phone photography, and they were paying premium for shelf space in stores their target buyers had stopped visiting.',
    challenge2: 'Our job was to convince a family-run business to evolve their identity without losing the 40-year recognition that older buyers (still 60% of revenue) actually relied on.',
    pull: 'We didn\u2019t throw the old logo in the bin. We let it become the seal of authenticity on a brand that\u2019s now built for the next 40 years too.',
    pullWho: 'Harpreet Kaur · Brand Director',
    approach: [
      { n: '01', t: 'Equity audit', d: 'What does the existing brand actually own in the buyer\u2019s mind? Hand-blended quality, north-Indian heritage, trust. We protected these. Everything else was on the table.' },
      { n: '02', t: 'Identity refresh', d: 'New wordmark, retained seal, modernised packaging system. Tighter typographic hierarchy across 64 SKUs.' },
      { n: '03', t: 'Photography rebuild', d: 'Three days in studio. Hero shots for every premium SKU, lifestyle imagery for the new D2C site, and Amazon-ready product gallery for all 64 listings.' },
      { n: '04', t: 'D2C launch', d: 'Shopify storefront with WhatsApp re-engagement, Amazon storefront overhaul, and a Meta campaign focused on the 28–45 home-cook audience.' },
    ],
    results: [
      { v: 38, suffix: '%', k: 'Repeat-buy rate', note: 'Year-on-year increase' },
      { v: 24, suffix: '%', k: 'Premium SKU AOV', note: 'Higher average order value' },
      { v: 64, suffix: '', k: 'SKUs redesigned', note: 'In a single sprint' },
      { v: 2.1, suffix: 'x', k: 'D2C revenue', note: 'In 90 days' },
    ],
    nextSlug: 'heritage-resorts',
  },

  'heritage-resorts': {
    slug: 'heritage-resorts',
    image: 'https://images.unsplash.com/photo-ou_9KZ6BJ-0?w=1200&q=80&auto=format&fit=crop',
    cat: 'Hospitality',
    title: 'Beating the OTAs at their own funnel',
    titleAccent: 'their own funnel',
    deck: 'Heritage Resorts has been operating in the Himalayas for over a decade — but for the last eight years, 80% of their bookings came through OTAs at 18–25% commission. We built a direct-booking funnel that overtook OTA volume in nine months.',
    glyph: 'H',
    client: 'Heritage Resorts (3 properties)',
    year: 'Q2 2025',
    services: ['Performance', 'Automation', 'Web'],
    timeline: '24 weeks',
    challenge: 'OTAs aren\u2019t the enemy — they\u2019re a tax. The challenge wasn\u2019t to leave Booking.com, it was to make Heritage&rsquo;s own channels measurably better at converting the same intent. That meant a faster site, a tighter funnel, and a follow-up engine that didn\u2019t feel like spam.',
    challenge2: 'It also meant convincing a 60-year-old GM that a WhatsApp concierge would not make the brand feel cheap.',
    pull: 'Guests trust an OTA because it asks for nothing upfront. We made our own funnel ask for less.',
    pullWho: 'Manjeet Sandhu · Performance Lead',
    approach: [
      { n: '01', t: 'Funnel teardown', d: 'Mapped every step of an OTA booking and every step of theirs. Their site was losing 41% of intent at the date-picker. We rebuilt it as a single conversational scroll.' },
      { n: '02', t: 'WhatsApp concierge', d: 'Real human concierge, real availability, real pricing — within 2 minutes of any enquiry, on the channel guests already use. Branded, scripted, but human.' },
      { n: '03', t: 'Direct-booking ads', d: 'Meta and Google campaigns segmented by route (Delhi-NCR → Himachal weekend, vs. Punjab → 3-night stays). Different creative, different audiences, same destination.' },
      { n: '04', t: 'Loyalty loop', d: 'Post-stay WhatsApp follow-up with a returning-guest rate that surprised everyone, including us. 28% of returning guests booked the next stay within 90 days.' },
    ],
    results: [
      { v: 52, suffix: '%', k: 'Direct vs OTA mix', note: 'First time direct led' },
      { v: 22, suffix: '%', k: 'Margin recovered', note: 'Net of all spend' },
      { v: 28, suffix: '%', k: 'Returning guest rate', note: '90-day rebook window' },
      { v: 9, suffix: 'mo', k: 'To break-even', note: 'On the full programme' },
    ],
    nextSlug: 'township-launch',
  },
};

const ORDER = ['township-launch', 'mla-campaign', 'sona-foods', 'heritage-resorts'];

function CaseStudyPage({ slug, setRoute }) {
  const cs = CASES[slug];
  if (!cs) {
    return (
      <div className="page">
        <div className="page-head">
          <div className="wrap">
            <h1>Case study not found</h1>
            <button className="btn btn-ghost btn-large" style={{ marginTop: 24 }} onClick={() => setRoute('work')}>
              ← Back to all work
            </button>
          </div>
        </div>
      </div>
    );
  }

  const next = CASES[cs.nextSlug];

  // Reveal-with-html helper — for fields that include &rdquo; etc.
  const html = (s) => ({ dangerouslySetInnerHTML: { __html: s } });

  return (
    <div className="page">
      {/* Hero */}
      <section className="cs-hero">
        <div className="cs-hero-grid" aria-hidden="true" />
        <div className="wrap">
          <div className="cs-breadcrumb">
            <a onClick={() => setRoute('work')}>Work</a>
            <span className="sep" />
            <span>{cs.cat}</span>
            <span className="sep" />
            <span className="cur">Case · {cs.client}</span>
          </div>

          <CaseStudyTitle text={cs.title} accent={cs.titleAccent} />

          <LineReveal delay={400}>
            <p className="cs-deck">{cs.deck}</p>
          </LineReveal>

          <div className="cs-meta">
            <Reveal className="cell" delay={100}>
              <span className="k">Client</span>
              <span className="v">{cs.client}</span>
            </Reveal>
            <Reveal className="cell" delay={180}>
              <span className="k">Year</span>
              <span className="v">{cs.year}</span>
            </Reveal>
            <Reveal className="cell" delay={260}>
              <span className="k">Services</span>
              <span className="v">{cs.services.join(' · ')}</span>
            </Reveal>
            <Reveal className="cell" delay={340}>
              <span className="k">Timeline</span>
              <span className="v">{cs.timeline}</span>
            </Reveal>
          </div>

          <Parallax speed={-0.12}>
            <Reveal className="cs-hero-image">
              {cs.image ? (
                <img src={cs.image} alt={cs.title} style={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: 'var(--r-lg)' }} />
              ) : (
                <div className="inner">
                  <span className="glyph">{cs.glyph}</span>
                  <span className="lbl">Hero image · 16:9 · {cs.cat}</span>
                </div>
              )}
            </Reveal>
          </Parallax>
        </div>
      </section>

      {/* Challenge */}
      <section className="cs-section">
        <div className="wrap">
          <div className="cs-section-head">
            <div className="kicker"><span className="num">01</span>Challenge</div>
            <div className="cs-section-body">
              <Reveal>
                <h2>The <span className="it">problem</span> on the table.</h2>
              </Reveal>
              <LineReveal delay={120}>
                <p {...html(cs.challenge)} />
              </LineReveal>
              <LineReveal delay={240}>
                <p {...html(cs.challenge2)} />
              </LineReveal>
              <Reveal className="cs-pullquote" delay={400}>
                <div className="q" {...html(cs.pull)} />
                <div className="who">{cs.pullWho}</div>
              </Reveal>
            </div>
          </div>
        </div>
      </section>

      {/* Approach + Gallery */}
      <section className="cs-section">
        <div className="wrap">
          <div className="cs-section-head">
            <div className="kicker"><span className="num">02</span>Approach</div>
            <div className="cs-section-body">
              <Reveal>
                <h2>How we <span className="it">shipped it.</span></h2>
                <p>Four workstreams running in parallel, calibrated weekly against the only metric that mattered.</p>
              </Reveal>

              <div className="cs-approach">
                {cs.approach.map((s, i) => (
                  <Reveal key={s.n} className="cs-approach-step" delay={i * 80}>
                    <div className="step-num">{s.n}</div>
                    <h4>{s.t}</h4>
                    <p>{s.d}</p>
                  </Reveal>
                ))}
              </div>
            </div>
          </div>

          {/* Gallery — varies by case */}
          <div className="cs-gallery">
            <Reveal className="slot a-1-7" delay={50}>
              <span className="num">{cs.glyph}/01</span>
              <span className="slot-lbl">Process shot · 4:3</span>
            </Reveal>
            <Reveal className="slot a-8-12" delay={120}>
              <span className="num">{cs.glyph}/02</span>
              <span className="slot-lbl">Detail · 4:3</span>
            </Reveal>
            <Reveal className="slot a-1-5" delay={180}>
              <span className="num">{cs.glyph}/03</span>
              <span className="slot-lbl">Vertical · 3:4</span>
            </Reveal>
            <Reveal className="slot a-6-12" delay={240}>
              <span className="num">{cs.glyph}/04</span>
              <span className="slot-lbl">Wide · 4:3</span>
            </Reveal>
            <Reveal className="slot a-1-12" delay={300}>
              <span className="num">{cs.glyph}/05</span>
              <span className="slot-lbl">Full-bleed · 16:9</span>
            </Reveal>
          </div>
        </div>
      </section>

      {/* Results */}
      <section className="cs-section">
        <div className="wrap">
          <div className="cs-section-head">
            <div className="kicker"><span className="num">03</span>Results</div>
            <div className="cs-section-body">
              <Reveal>
                <h2>What <span className="it">moved.</span></h2>
                <p>Measured weekly. Reported monthly. Audit-trail kept end-to-end.</p>
              </Reveal>

              <div className="cs-results">
                {cs.results.map((r, i) => (
                  <Reveal key={r.k} className="cs-result" delay={i * 80}>
                    <div className="v">
                      <CountUp to={r.v}
                               duration={1600}
                               format={(n) => {
                                 if (Number.isInteger(r.v)) return Math.round(n);
                                 return n.toFixed(1);
                               }}
                               suffix={r.suffix} />
                    </div>
                    <div className="k">{r.k}</div>
                    {r.note && <div className="note">{r.note}</div>}
                  </Reveal>
                ))}
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* Next case */}
      <section style={{ paddingBottom: 80 }}>
        <div className="wrap">
          <div className="cs-next">
            <div>
              <div className="label">Next case study</div>
              <div className="cs-next-card" onClick={() => setRoute('case/' + next.slug)}>
                <h3>{next.title}</h3>
                <div className="arr">View case <Arrow size={14} /></div>
              </div>
            </div>
            <div className="cs-next-card" onClick={() => setRoute('case/' + next.slug)}>
              <div className="cs-next-thumb">
                <div className="inner">
                  <span className="glyph">{next.glyph}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </section>

      <CTA setRoute={setRoute} />
    </div>
  );
}

// Replace the placeholder WordReveal title render with proper italic-aware version.
// We render an invisible aria title for screen readers, the WordReveal handles the
// visible animated text — but to support the italic accent we need a smarter render.
// Simpler approach: render title directly with WordReveal split words; italicize
// matching word inline via post-process.
function CaseStudyTitle({ text, accent }) {
  const ref = React.useRef(null);
  const [seen, setSeen] = React.useState(false);
  React.useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          setSeen(true);
          io.disconnect();
        }
      });
    }, { threshold: 0.1 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);

  const words = text.split(' ');
  const accentWords = accent.split(' ');
  const accentIndex = words.findIndex((w, i) =>
    accentWords.every((aw, j) => words[i + j] && words[i + j].toLowerCase() === aw.toLowerCase())
  );
  return (
    <h1 ref={ref} className="cs-title word-reveal">
      {words.map((w, i) => {
        const isAccent = accentIndex >= 0 && i >= accentIndex && i < accentIndex + accentWords.length;
        return (
          <span key={i} className="wr-word">
            <span className={"wr-inner" + (isAccent ? " it" : "")} style={{
              transitionDelay: seen ? `${i * 50}ms` : '0ms',
              transform: seen ? 'translateY(0)' : 'translateY(110%)',
              opacity: seen ? 1 : 0.001,
            }}>{w}</span>
            {i < words.length - 1 && '\u00A0'}
          </span>
        );
      })}
    </h1>
  );
}

Object.assign(window, { CaseStudyPage, CaseStudyTitle, CASES, CASE_ORDER: ORDER });
