// Hero variants. Default: animated node graph. Also: minimal, code-block, scroll-pile.

function Hero({ variant = "node" }) {
  const N = window.NODESTONE;
  const h = N.hero;
  const go = (e, p) => {e.preventDefault();window.location.hash = p.slice(1);};

  return (
    <section className="hero" data-comment-anchor="9b5f5342cc-section-9-5">
      {variant === "node" && <NodeGraph intensity="subtle" />}
      {variant === "code" && <CodeHeroDecor />}

      <div className="wrap">
        <Reveal>
          <div className="eyebrow hero-eyebrow">{h.eyebrow}</div>
        </Reveal>
        <Reveal delay={80}>
          <h1 className="hero-title">{h.title}</h1>
        </Reveal>
        <Reveal delay={160}>
          <p className="lede hero-sub">{h.subtitle}</p>
        </Reveal>
        <Reveal delay={240}>
          <div className="hero-ctas">
            <a href={h.primaryCta.path} className="btn btn-primary" onClick={(e) => go(e, h.primaryCta.path)}>
              {h.primaryCta.label} <span className="arr">→</span>
            </a>
            <a href={h.secondaryCta.path} className="btn btn-ghost" onClick={(e) => go(e, h.secondaryCta.path)}>
              {h.secondaryCta.label}
            </a>
          </div>
        </Reveal>
        <Reveal delay={320}>
          <ul className="hero-bullets">
            {h.bullets.map((b, i) => <li key={i}>{b}</li>)}
          </ul>
        </Reveal>
      </div>
    </section>);

}

function CodeHeroDecor() {
  return (
    <div className="hero-canvas-wrap" aria-hidden="true" style={{ display: "flex", alignItems: "center", justifyContent: "flex-end", paddingRight: "8%" }}>
      <pre style={{
        fontFamily: "var(--font-mono)",
        fontSize: 13,
        color: "var(--fg-3)",
        background: "transparent",
        margin: 0,
        opacity: 0.7,
        lineHeight: 1.6
      }}>{`$ nodestone diagnose --target platform
✓ scanning cloud estate ............. 248 resources
✓ mapping dependencies .............. 31 services
✓ identifying drift ................. 12 deltas
✓ delivery pipeline review .......... 4 risks
✓ shipping the report.

→ next: stabilise, then build the right thing.`}</pre>
    </div>);

}

// ────────────────────────────────────────────────────────────────────────────
// Reveal — IntersectionObserver scroll-in
// ────────────────────────────────────────────────────────────────────────────
function Reveal({ children, delay = 0, stagger = false, as: Tag = "div", className = "", ...rest }) {
  const ref = React.useRef(null);
  const [seen, setSeen] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      ([e]) => {
        if (e.isIntersecting) {
          setSeen(true);
          io.disconnect();
        }
      },
      { rootMargin: "-40px 0px -10% 0px", threshold: 0.05 }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  const base = stagger ? "reveal-stagger" : "reveal";
  return (
    <Tag
      ref={ref}
      className={`${base} ${seen ? "in" : ""} ${className}`}
      style={{ transitionDelay: delay ? delay + "ms" : undefined }}
      {...rest}>
      
      {children}
    </Tag>);

}

// ────────────────────────────────────────────────────────────────────────────
// HOME
// ────────────────────────────────────────────────────────────────────────────
function HomePage({ heroVariant }) {
  const N = window.NODESTONE;
  return (
    <>
      <Hero variant={heroVariant} />

      {/* Problems we fix — pastel 1 */}
      <section className="block pastel-1" data-comment-anchor="72ed5e5073-section-110-7">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>{N.problems.eyebrow}</div>
            <h2 style={{ maxWidth: "20ch", marginBottom: 24 }}>{N.problems.title}</h2>
            {N.problems.intro && <p className="lede" style={{ marginBottom: 64 }}>{N.problems.intro}</p>}
          </Reveal>
          <Reveal stagger className="grid-3">
            {N.problems.items.map((p) =>
            <div className="card" key={p.n}>
                <div className="card-icon"><Icon name={p.icon} /></div>
                <h3>{p.title}</h3>
                <p>{p.body}</p>
              </div>
            )}
          </Reveal>
        </div>
      </section>

      {/* Services overview — neutral */}
      <section className="block">
        <div className="wrap">
          <Reveal>
            <h2 style={{ maxWidth: "20ch", marginBottom: 24 }}>What we specialise in.</h2>
            <p className="lede" style={{ marginBottom: 56 }}>
              From the infrastructure nobody sees, to the software people use every day — and everything that holds them together.
            </p>
          </Reveal>
          <Reveal>
            <div>
              {(() => {
                const FEATURED = ["infrastructure-automation", "saas-product", "application-development"];
                return FEATURED.map((slug, i) => {
                  const s = N.services.find((x) => x.slug === slug);
                  if (!s) return null;
                  return (
                    <a key={s.slug} href={"#/services/" + s.slug} className="service-row"
                    onClick={(e) => {e.preventDefault();window.location.hash = "/services/" + s.slug;}}>
                      <span className="mono">{String(i + 1).padStart(2, "0")}</span>
                      <div>
                        <h3 style={{ marginBottom: 6 }}>{s.label}</h3>
                        <p>{s.short}</p>
                      </div>
                      <span className="arr">→</span>
                    </a>);

                });
              })()}
            </div>
          </Reveal>
          <Reveal>
            <div style={{ marginTop: 32, display: "flex", gap: 12, flexWrap: "wrap", alignItems: "center" }}>
              <a href="#/services" className="btn btn-ghost"
              onClick={(e) => {e.preventDefault();window.location.hash = "/services";}}>
                See all services <span className="arr">→</span>
              </a>
            </div>
          </Reveal>
        </div>
      </section>

      {/* How we work — pastel 3 (lavender) */}
      <section className="block pastel-3">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>{N.process.eyebrow}</div>
            <h2 style={{ maxWidth: "22ch", marginBottom: 64 }}>{N.process.title}</h2>
          </Reveal>
          <Reveal stagger className="grid-4">
            {N.process.steps.map((s) =>
            <div className="card" key={s.n}>
                <div className="card-icon"><Icon name={s.icon} /></div>
                <h3>{s.title}</h3>
                <p>{s.body}</p>
              </div>
            )}
          </Reveal>
        </div>
      </section>

      {/* Flexible / modes — neutral */}
      <section className="block">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>{N.modes.eyebrow}</div>
            <h2 style={{ maxWidth: "22ch", marginBottom: 64 }}>{N.modes.title}</h2>
          </Reveal>
          <Reveal stagger className="grid-3">
            {N.modes.items.map((m) =>
            <div className="card" key={m.kind}>
                <div className="card-icon"><Icon name={m.icon} /></div>
                <h3 style={{ fontSize: 20 }}>{m.kind}</h3>
                <p style={{ color: "var(--fg-3)", fontSize: 15, lineHeight: 1.55 }}>{m.body}</p>
              </div>
            )}
          </Reveal>
        </div>
      </section>

      {/* Products preview — pastel 2 (sage) */}
      <section className="block pastel-2">
        <div className="wrap">
          <Reveal>
            <h2 style={{ maxWidth: "22ch", marginBottom: 24 }}>{N.products.title}</h2>
            <p className="lede" style={{ marginBottom: 56 }}>{N.products.intro}</p>
          </Reveal>
          <Reveal stagger className="grid-2">
            {N.products.items.map((p) =>
            <div className="workshop-card" key={p.name}>
                <span className="status">{p.status}</span>
                <h3 style={{ fontSize: 26, letterSpacing: "-0.024em" }}>{p.name}</h3>
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                  {p.kind.map((k) => <span key={k} className="label-pill">{k}</span>)}
                </div>
                <p style={{ color: "var(--fg-3)", fontSize: 15 }}>{p.body}</p>
              </div>
            )}
          </Reveal>
          <Reveal>
            <div style={{ marginTop: 40 }}>
              <a href="#/workshop" className="btn btn-ghost"
              onClick={(e) => {e.preventDefault();window.location.hash = "/workshop";}}>
                See the workshop <span className="arr">→</span>
              </a>
            </div>
          </Reveal>
        </div>
      </section>

      <ClosingCta />
    </>);

}

// ────────────────────────────────────────────────────────────────────────────
// SERVICES OVERVIEW
// ────────────────────────────────────────────────────────────────────────────
function ServicesPage() {
  const N = window.NODESTONE;
  return (
    <>
      <section className="page-hero">
        <div className="wrap">
          <Reveal>
            <h1>Cloud, platforms, applications and integration. Done properly.</h1>
            <p className="lede">
              We help businesses untangle the parts of their technology that matter most. Platforms, infrastructure, delivery pipelines, applications, integrations — and the operating habits around them.
            </p>
          </Reveal>
        </div>
      </section>

      <section className="block-tight">
        <div className="wrap">
          <Reveal>
            <div className="divider-mono">What we can do</div>
          </Reveal>
          <Reveal>
            <div>
              {N.services.map((s, i) =>
              <a key={s.slug} href={"#/services/" + s.slug} className="service-row"
              onClick={(e) => {e.preventDefault();window.location.hash = "/services/" + s.slug;}}>
                  <span className="mono">{String(i + 1).padStart(2, "0")}</span>
                  <div>
                    <h3 style={{ marginBottom: 6 }}>{s.label}</h3>
                    <p>{s.long}</p>
                  </div>
                  <span className="arr">→</span>
                </a>
              )}
            </div>
          </Reveal>

          <Reveal>
            <a href="#/contact" className="service-cta"
            onClick={(e) => {e.preventDefault();sessionStorage.setItem('contactPreselect','other');window.location.hash = "/contact";}}>
              <div className="service-cta-body" data-comment-anchor="31897ee9f7-div-292-15">
                <h3 className="service-cta-title">Don't see what you need?<br />Let's chat anyway.</h3>
                <p className="service-cta-copy">
                  If you need something that isn't shown above, start a conversation with us anyway. We're happy to listen, give an honest first read, and point you in the right direction — even if that direction isn't us.
                </p>
              </div>
              <div className="service-cta-action">
                <span>Start a conversation</span>
                <span className="arr">→</span>
              </div>
            </a>
          </Reveal>
        </div>
      </section>

      {/* problems framing */}
      <section className="block pastel-4">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>How we can help</div>
            <h2 style={{ maxWidth: "22ch", marginBottom: 48 }}>Most conversations start with one of these problems.</h2>
          </Reveal>
          <Reveal stagger className="grid-3">
            {N.problems.items.map((p) =>
            <div className="card" key={p.n}>
                <div className="card-icon"><Icon name={p.icon} /></div>
                <h3>{p.title}</h3>
                <p>{p.body}</p>
              </div>
            )}
          </Reveal>
        </div>
      </section>

      {/* engagement modes */}
      <section className="block">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>Flexible by design</div>
            <h2 style={{ maxWidth: "22ch", marginBottom: 48 }}>Shaped around what you need.</h2>
          </Reveal>
          <Reveal stagger className="grid-3">
            {N.modes.items.map((m) =>
            <div className="card" key={m.kind}>
                <div className="card-icon"><Icon name={m.icon} /></div>
                <h3 style={{ fontSize: 20 }}>{m.kind}</h3>
                <p style={{ color: "var(--fg-3)", fontSize: 15, lineHeight: 1.55 }}>{m.body}</p>
              </div>
            )}
          </Reveal>
        </div>
      </section>

      <ClosingCta />
    </>);

}

// ────────────────────────────────────────────────────────────────────────────
// SERVICE DETAIL (deep page — for cloud-migration and devops-platform)
// ────────────────────────────────────────────────────────────────────────────
const SERVICE_DETAILS = {
  "cloud-migration": {
    eyebrow: "Service · 01",
    title: "Cloud migration built for the long run.",
    lede: "Most migrations finish, then quietly start to drift. We plan for running it, not just moving it.",
    sections: [
    {
      h: "What we do",
      body:
      "Assess the infrastructure. Map dependencies — including the awkward ones. Plan the move with visible risk and a clear order of operations. Build cloud platforms that are secure, easy to monitor and manageable after the project closes."
    },
    {
      h: "How it usually starts",
      body:
      "An ageing on-premise setup, a half-finished migration, or a cloud bill that nobody fully understands. Sometimes the migration plan exists; it just hasn't survived contact with the business."
    },
    {
      h: "What you get",
      body:
      "A migration roadmap your engineers actually believe. A secure, well-structured cloud environment that holds up under audit. A delivery cadence that doesn't pause the rest of the business."
    },
    {
      h: "What we don't do",
      body:
      "Eight-week discovery phases that produce a deck. Migrations that finish on a slide and break in production. Cloud architectures that need a team of specialists just to keep the lights on."
    }],

    deliverables: [
    "Infrastructure assessment & dependency map",
    "Target platform architecture",
    "Cloud environment setup (AWS / Azure / GCP)",
    "Migration waves & risk register",
    "Monitoring, alerting & cost management baseline",
    "Operations guide & handover"],

    stack: ["AWS", "Azure", "GCP", "Terraform", "Kubernetes", "Docker", "Ansible", "Helm", "Prometheus", "Datadog", "GitHub Actions"]
  },
  "devops-platform": {
    eyebrow: "Service · 02",
    title: "Delivery pipelines and platform engineering, without the complexity.",
    lede: "We treat the platform as a product, with internal users and a roadmap of its own. The point is calmer releases and quicker recovery — not a wall of dashboards.",
    sections: [
    {
      h: "What we do",
      body:
      "Build delivery pipelines, environments and platform patterns that help teams release calmly and recover quickly. Codify the path from idea to production, so the next change doesn't need a working group to ship."
    },
    {
      h: "How it usually starts",
      body:
      "Deployments that turn into a whole-team event. Environments that drift between deployments. A backlog of platform tickets that everyone routes around. We pick the highest-leverage thread and pull."
    },
    {
      h: "What you get",
      body:
      "Pipelines your team owns. Environments that match production within tolerance. Monitoring that surfaces the right alerts, not all of them. Documented patterns the next hire can read on day one."
    },
    {
      h: "What we don't do",
      body:
      "Platform rebuilds that take a year before anyone benefits. Tooling collections without an operating model. Maturity assessments without a route forward."
    }],

    deliverables: [
    "Automated pipeline design & implementation",
    "Environment & release pattern reset",
    "Internal developer platform baseline",
    "Observability & on-call practices",
    "Service catalogue & standard deployment patterns",
    "Team enablement & ownership transfer"],

    stack: ["GitHub Actions", "GitLab", "Jenkins", "CircleCI", "Terraform", "Kubernetes", "Docker", "Helm", "ArgoCD", "Prometheus", "Grafana", "Datadog", "Elasticsearch", "HashiCorp Vault", "Nginx", "Backstage", "Azure DevOps"]
  },
  "infrastructure-automation": {
    eyebrow: "Service · 03",
    title: "Infrastructure that runs itself. Not you.",
    lede: "Manual steps create risk. We replace them with repeatable, documented automation that holds up when the person who built it isn't around.",
    sections: [
    {
      h: "What we do",
      body: "Audit the current infrastructure, identify fragile manual processes and build automation that's properly documented and maintainable. Provisioning, configuration, patching, deployments — made repeatable."
    },
    {
      h: "How it usually starts",
      body: "A new joiner asking how to set up the environment. A deployment that only one person knows how to run. An audit that surfaces inconsistencies nobody can explain."
    },
    {
      h: "What you get",
      body: "Infrastructure as code across the environment. Drift detection so you know when things wander. Runbooks your team can actually follow — not just the person who wrote them."
    },
    {
      h: "What we don't do",
      body: "Automation for the sake of it. Toolchains that need a specialist to operate. Scripts that work once and break silently."
    }],
    deliverables: [
    "Infrastructure audit & gap analysis",
    "Infrastructure as code (Terraform / Ansible)",
    "Automated provisioning pipelines",
    "Drift detection setup",
    "Runbooks & operating documentation",
    "Team handover & enablement"],
    stack: ["Terraform", "Ansible", "Puppet", "GitHub Actions", "AWS", "Azure", "GCP", "Kubernetes", "Docker", "Python", "PowerShell", "Prometheus", "Datadog", "HashiCorp Vault", "Packer"]
  },
  "digital-transformation": {
    eyebrow: "Service · 04",
    title: "Better work. Not louder language.",
    lede: "Transformation that produces a strategy deck and calls it done isn't transformation. We focus on how work actually gets done — and make targeted improvements that hold.",
    sections: [
    {
      h: "What we do",
      body: "Assess how work moves through the business, identify where technology helps and where it gets in the way, and make changes the team can sustain. Platforms, processes and people — considered together."
    },
    {
      h: "How it usually starts",
      body: "A business that's changed but whose tools haven't kept up. Processes built around workarounds. Technology that drives the team, rather than the other way around."
    },
    {
      h: "What you get",
      body: "A clear picture of where change will have the most impact. A practical roadmap — phased, prioritised and honest about effort. Improvements the team understands and can own."
    },
    {
      h: "What we don't do",
      body: "Multi-year programmes with uncertain outcomes. Change for the sake of modernisation. Recommendations that ignore the people who'll live with them."
    }],
    deliverables: [
    "Current-state process assessment",
    "Opportunity & priority map",
    "Phased transformation roadmap",
    "Platform selection & vendor guidance",
    "Adoption approach & change support",
    "Progress reviews & course correction"],
    stack: ["Confluence", "Jira", "Notion", "Slack", "SharePoint", "Microsoft 365", "Power Platform", "Dynamics 365", "Salesforce", "HubSpot", "ServiceNow"]
  },
  "application-development": {
    eyebrow: "Service · 05",
    title: "Applications built around how the business actually works.",
    lede: "Off-the-shelf doesn't always fit. We design and build applications around the real workflow — with the people who'll use them in mind from the start.",
    sections: [
    {
      h: "What we do",
      body: "Design, build and iterate on business applications — from internal tools to customer-facing products. We start with the workflow the business actually runs, not the idealised version in the requirements doc."
    },
    {
      h: "How it usually starts",
      body: "A spreadsheet that's become load-bearing. An off-the-shelf tool that doesn't quite fit. An application that needs rebuilding because the original is no longer maintainable."
    },
    {
      h: "What you get",
      body: "An application that fits the workflow, not the other way around. Clean, maintainable code with documentation that doesn't go stale. A team that understands what they're running — not just how to use it."
    },
    {
      h: "What we don't do",
      body: "Feature factories without a clear outcome. Applications that can only be maintained by whoever built them. Over-engineered solutions to straightforward problems."
    }],
    deliverables: [
    "Requirements & workflow mapping",
    "Application architecture & design",
    "Frontend & backend development",
    "Integration with existing systems",
    "Testing & quality assurance",
    "Documentation & handover"],
    stack: ["React", "Next.js", "Node.js", "Python", "Django", "TypeScript", "PostgreSQL", "MySQL", "MongoDB", "Redis", "REST APIs", "GraphQL", "Docker", "AWS", "GitHub Actions", "Flutter", "React Native", "Swift", "Kotlin"]
  },
  "saas-product": {
    eyebrow: "Service · 06",
    title: "Software products built to ship and scale.",
    lede: "We've built our own. We know where the scope creeps, where the architecture decisions bite back, and where cutting corners costs twice as much later.",
    sections: [
    {
      h: "What we do",
      body: "Shape, build and operate software products from the ground up — end to end. Product thinking, architecture, delivery and the operating habits that keep it running well after launch."
    },
    {
      h: "How it usually starts",
      body: "A validated idea that hasn't been built yet. A prototype that needs to become a real product. Something that was built fast and now needs to be built properly."
    },
    {
      h: "What you get",
      body: "A product with sensible architecture and a delivery process to match. Decisions documented so the next person isn't starting from scratch. A roadmap that lives somewhere other than a slide."
    },
    {
      h: "What we don't do",
      body: "Endless discovery phases. Scope that grows without a conversation about trade-offs. Products handed over without the team knowing how to run them."
    }],
    deliverables: [
    "Product scoping & architecture",
    "Frontend & backend development",
    "Infrastructure & deployment setup",
    "Billing & subscription integration",
    "Observability & alerting baseline",
    "Launch support & roadmap handover"],
    stack: ["React", "Next.js", "Node.js", "TypeScript", "PostgreSQL", "Redis", "Stripe", "Auth0", "AWS", "Terraform", "Docker", "Kubernetes", "GitHub Actions", "Datadog"]
  },
  "systems-integration": {
    eyebrow: "Service · 07",
    title: "Connections that actually hold.",
    lede: "When systems don't talk to each other, people fill the gap — with exports, copy-paste and inboxes. We remove the gap.",
    sections: [
    {
      h: "What we do",
      body: "Design and build integrations between platforms, data sources and processes. Clean connections, reliable event flows and documented patterns — so it holds up, not just on day one."
    },
    {
      h: "How it usually starts",
      body: "Data that lives in two places and needs to be in one. A report someone manually compiles every week. A new platform that needs to work with the five that came before it."
    },
    {
      h: "What you get",
      body: "Integrations that are reliable, monitored and documented. Data that flows without manual intervention. A clear picture of what connects to what — and what to do when something breaks."
    },
    {
      h: "What we don't do",
      body: "Point-to-point connections that become unmaintainable. Integrations without error handling or monitoring. Solutions that only the person who built them understands."
    }],
    deliverables: [
    "Integration mapping & dependency review",
    "API design & implementation",
    "Event-driven patterns where appropriate",
    "Error handling & retry logic",
    "Monitoring & alerting setup",
    "Integration documentation & runbook"],
    stack: ["REST APIs", "GraphQL", "Webhooks", "ETL", "AWS EventBridge", "AWS SNS", "AWS Lambda", "Node.js", "PostgreSQL", "MySQL", "Kafka", "RabbitMQ", "Azure Service Bus", "MuleSoft", "Apache Camel", "Zapier", "Make", "Datadog"]
  }
};

function ServiceDetailPage({ slug }) {
  const N = window.NODESTONE;
  const service = N.services.find((s) => s.slug === slug);
  const detail = SERVICE_DETAILS[slug];

  if (!service) {
    return (
      <section className="page-hero">
        <div className="wrap">
          <div className="eyebrow" style={{ marginBottom: 24 }}>Service not found</div>
          <h1>That service doesn't exist (yet).</h1>
          <p className="lede">Have a look at <a href="#/services" style={{ borderBottom: "1px solid var(--line-strong)" }}>everything we do</a>.</p>
        </div>
      </section>);

  }

  // If no deep detail authored, fall back to short page
  if (!detail) {
    return (
      <>
        <section className="page-hero">
          <div className="wrap">
            <Reveal>
              <div className="eyebrow" style={{ marginBottom: 24 }}>Service</div>
              <h1>{service.label}.</h1>
              <p className="lede">{service.long}</p>
            </Reveal>
          </div>
        </section>
        <section className="block pastel-5">
          <div className="wrap">
            <Reveal>
              <h2 style={{ maxWidth: "20ch", marginBottom: 24 }}>Want the deep version?</h2>
              <p className="lede" style={{ marginBottom: 32 }}>
                Detailed scope, deliverables and engagement shape — sent across the same afternoon you ask.
              </p>
              <a href="#/contact" className="btn btn-primary"
              onClick={(e) => {e.preventDefault();window.location.hash = "/contact";}}>
                Get the deep brief <span className="arr">→</span>
              </a>
            </Reveal>
          </div>
        </section>
        <ClosingCta serviceSlug={slug} />
      </>);

  }

  return (
    <>
      <section className="page-hero">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 24 }}>{detail.eyebrow}</div>
            <h1>{detail.title}</h1>
            <p className="lede">{detail.lede}</p>
          </Reveal>
        </div>
      </section>

      <section className="block-tight">
        <div className="wrap">
          <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 64 }} className="detail-grid">
            <Reveal>
              <div style={{ position: "sticky", top: 120 }}>
                <div className="mono" style={{ color: "var(--fg-4)", fontSize: 11, letterSpacing: ".08em", textTransform: "uppercase", marginBottom: 12 }}>
                  At a glance
                </div>
                <h3 style={{ fontSize: 20, fontWeight: 500, marginBottom: 12 }}>{service.label}</h3>
                <p style={{ color: "var(--fg-3)", fontSize: 14.5 }}>{service.short}</p>
                <div style={{ marginTop: 24, display: "flex", flexWrap: "wrap", gap: 6 }}>
                  {detail.stack.map((t) =>
                  <span key={t} className="kbd" style={{ fontSize: 11 }}>{t}</span>
                  )}
                </div>
              </div>
            </Reveal>

            <div style={{ display: "flex", flexDirection: "column", gap: 48 }}>
              {detail.sections.map((sec, i) =>
              <Reveal key={i}>
                  <div>
                    <h3 style={{ fontSize: 26, fontWeight: 500, letterSpacing: "-0.024em", marginBottom: 16 }}>{sec.h}</h3>
                    <p style={{ color: "var(--fg-2)", fontSize: 17, lineHeight: 1.6, maxWidth: "62ch" }}>{sec.body}</p>
                  </div>
                </Reveal>
              )}
            </div>
          </div>

          <style>{`
            @media (max-width: 820px) {
              .detail-grid { grid-template-columns: 1fr !important; gap: 32px !important; }
            }
          `}</style>
        </div>
      </section>

      <section className="block pastel-3">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>Deliverables</div>
            <h2 style={{ maxWidth: "20ch", marginBottom: 56 }}>What ends up in your hands.</h2>
          </Reveal>
          <Reveal stagger style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 16 }}>
            {detail.deliverables.map((d, i) =>
            <div key={i} style={{
              display: "flex", gap: 16, alignItems: "baseline",
              padding: "18px 20px", borderRadius: "var(--radius)",
              background: "color-mix(in oklab, var(--bg-elev) 80%, transparent)",
              border: "1px solid var(--line)"
            }}>
                <span className="mono" style={{ color: "var(--fg-4)", fontSize: 12 }}>
                  {String(i + 1).padStart(2, "0")}
                </span>
                <span style={{ fontSize: 15.5 }}>{d}</span>
              </div>
            )}
          </Reveal>
          <style>{`
            @media (max-width: 720px) {
              .pastel-3 .reveal-stagger { grid-template-columns: 1fr !important; }
            }
          `}</style>
        </div>
      </section>

      {/* Other services nav */}
      <section className="block-tight">
        <div className="wrap">
          <Reveal>
            <div className="divider-mono">Other services</div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 12 }}>
              {N.services.filter((s) => s.slug !== slug).slice(0, 4).map((s) =>
              <a key={s.slug} href={"#/services/" + s.slug}
              onClick={(e) => {e.preventDefault();window.location.hash = "/services/" + s.slug;}}
              className="card" style={{ padding: 20, gap: 6 }}>
                  <h3 style={{ fontSize: 17 }}>{s.label}</h3>
                  <p style={{ fontSize: 13.5, color: "var(--fg-3)" }}>{s.short}</p>
                  <span className="mono" style={{ marginTop: 8, fontSize: 11, color: "var(--fg-4)" }}>Read more →</span>
                </a>
              )}
            </div>
          </Reveal>
        </div>
      </section>

      <ClosingCta serviceSlug={slug} />
    </>);

}

// ────────────────────────────────────────────────────────────────────────────
// ABOUT
// ────────────────────────────────────────────────────────────────────────────
function AboutPage() {
  const N = window.NODESTONE;
  const a = N.about;
  return (
    <>
      <section className="page-hero" style={{ paddingBottom: 40 }}>
        <div className="wrap">
          <Reveal>
            <h1>{a.title}</h1>
          </Reveal>
        </div>
      </section>

      <section className="block-tight">
        <div className="wrap">
          <Reveal>
            <div style={{ display: "flex", flexDirection: "column", gap: 28, maxWidth: "62ch", fontSize: 18, lineHeight: 1.6, color: "var(--fg-2)" }}>
              {a.paragraphs.map((p, i) => <p key={i}>{p}</p>)}
            </div>
          </Reveal>
        </div>
      </section>

      <section className="block pastel-5">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>What we believe</div>
            <h2 style={{ maxWidth: "22ch", marginBottom: 56 }}>Four habits we won't compromise on.</h2>
          </Reveal>
          <Reveal stagger className="grid-4">
            {a.pillars.map((p) =>
            <div className="card" key={p.label}>
                <div className="card-icon"><Icon name={p.icon} /></div>
                <h3 style={{ fontSize: 19, fontWeight: 500 }}>{p.label}</h3>
                <p style={{ fontSize: 14.5 }}>{p.body}</p>
              </div>
            )}
          </Reveal>
        </div>
      </section>

      {/* photos-coming-later placeholder area */}
      <section className="block">
        <div className="wrap">
          <Reveal>
            <div className="divider-mono">Team · photos coming soon</div>
          </Reveal>
          <Reveal stagger style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 20 }}>
            {[1, 2, 3].map((i) =>
            <div className="img-placeholder" key={i} style={{ aspectRatio: "3 / 4" }}>
                portrait · slot {i}
              </div>
            )}
          </Reveal>
          <style>{`@media (max-width:720px){.reveal-stagger{grid-template-columns:1fr 1fr !important}}`}</style>
        </div>
      </section>

      <ClosingCta />
    </>);

}

// ────────────────────────────────────────────────────────────────────────────
// PRODUCTS
// ────────────────────────────────────────────────────────────────────────────
function ProductsPage() {
  const N = window.NODESTONE;
  return (
    <>
      <section className="page-hero">
        <div className="wrap">
          <Reveal>
            <h1>{N.products.title}</h1>
            <p className="lede">{N.products.intro}</p>
          </Reveal>
        </div>
      </section>

      <section className="block-tight">
        <div className="wrap">
          <Reveal stagger className="grid-2">
            {N.products.items.map((p) =>
            <div className="workshop-card" key={p.name} style={{ padding: 40 }}>
                <span className="status">{p.status}</span>
                <h3 style={{ fontSize: 32, letterSpacing: "-0.028em", lineHeight: 1.1 }}>{p.name}</h3>
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                  {p.kind.map((k) => <span key={k} className="label-pill">{k}</span>)}
                </div>
                <p style={{ color: "var(--fg-2)", fontSize: 16, lineHeight: 1.55 }}>{p.body}</p>
                <div className="img-placeholder" style={{ aspectRatio: "16 / 9", marginTop: 16 }}>
                  product visual · coming
                </div>
              </div>
            )}
          </Reveal>
        </div>
      </section>

      <section className="block pastel-6">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>Other things on the bench</div>
            <h2 style={{ maxWidth: "22ch", marginBottom: 32 }}>Quiet experiments. Some will become products. Some won't.</h2>
            <p className="lede" style={{ marginBottom: 48 }}>
              When we keep seeing the same gap in client work, we tend to prototype the thing we wish existed. Most stay internal tools. A few earn a name and a roadmap.
            </p>
          </Reveal>
          <Reveal stagger style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 16 }}>
            {[
            { k: ["Plugin", "Idea"], icon: "calendarClock", t: "Trello Calendar Plugin", b: "A plugin that rethinks Trello's calendar view — cleaner layout, less noise, easier to see what actually needs attention and when." },
            { k: ["API", "Idea"], icon: "route", t: "Working Days API", b: "A lightweight API for UK working day and bank holiday lookups — the kind of utility that shouldn't need a third-party subscription or a scraping script." },
            { k: ["App", "Idea"], icon: "book", t: "Brief builder", b: "A simple web app for shaping project briefs. Structured questions, clean output, less back-and-forth before work begins." }].
            map((x) =>
            <div className="card" key={x.t}>
                <div className="card-icon"><Icon name={x.icon} /></div>
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                  {x.k.map((k) => <span key={k} className="label-pill">{k}</span>)}
                </div>
                <h3 style={{ fontSize: 19 }}>{x.t}</h3>
                <p style={{ fontSize: 14.5 }}>{x.b}</p>
              </div>
            )}
          </Reveal>
          <style>{`@media (max-width:820px){.pastel-6 .reveal-stagger{grid-template-columns:1fr 1fr !important}} @media (max-width:520px){.pastel-6 .reveal-stagger{grid-template-columns:1fr !important}}`}</style>
        </div>
      </section>

      <ClosingCta />
    </>);

}

// ────────────────────────────────────────────────────────────────────────────
// CONTACT
// ────────────────────────────────────────────────────────────────────────────
function ContactPage() {
  const N = window.NODESTONE;
  const c = N.contact;
  const [sent, setSent] = React.useState(false);
  const [selectedServices, setSelectedServices] = React.useState([]);
  const formRef = React.useRef(null);
  React.useEffect(() => {
    const pre = sessionStorage.getItem('contactPreselect');
    if (pre) { setSelectedServices([pre]); sessionStorage.removeItem('contactPreselect'); }
  }, []);
  const toggleService = (slug) =>
    setSelectedServices((prev) => prev.includes(slug) ? prev.filter((s) => s !== slug) : [...prev, slug]);
  const dismissSuccess = () => {
    formRef.current?.reset();
    setSelectedServices([]);
    setSent(false);
  };

  return (
    <>
      <section className="page-hero">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 24 }}>{c.eyebrow}</div>
            <h1>{c.title}</h1>
            <p className="lede">{c.body}</p>
          </Reveal>
        </div>
      </section>

      <section className="block-tight">
        <div className="wrap">
          <Reveal>
            <div className="contact-card" data-comment-anchor="73af8044c1-div-741-13">
              {sent && (
                <div className="contact-success">
                  <button className="contact-success-dismiss" onClick={dismissSuccess} aria-label="Dismiss">
                    <Icon name="x" size={18} />
                  </button>
                  <div className="contact-success-check">
                    <svg viewBox="0 0 52 52">
                      <circle cx="26" cy="26" r="23" pathLength="1" />
                      <path d="M14 26l9 9 16-16" pathLength="1" />
                    </svg>
                  </div>
                  <h3 className="contact-success-title">Message received.</h3>
                  <p className="contact-success-body">We'll be in touch soon. A confirmation email is on its way so you know we've got it.</p>
                </div>
              )}
              <form ref={formRef} className="contact-form" onSubmit={(e) => {e.preventDefault();setSent(true);}}>
                <div>
                  <label htmlFor="cf-name">Your name</label>
                  <input id="cf-name" type="text" required placeholder="Jane Doe" />
                </div>
                <div>
                  <label htmlFor="cf-email">Email</label>
                  <input id="cf-email" type="email" required placeholder="jane@company.com" />
                </div>
                <div>
                  <label htmlFor="cf-co">Company (optional)</label>
                  <input id="cf-co" type="text" placeholder="Acme Ltd" />
                </div>
                <div>
                  <label>What are you looking for help with?</label>
                  <div className="service-chips">
                    {N.services.map((s) =>
                    <button type="button" key={s.slug}
                      className={"service-chip" + (selectedServices.includes(s.slug) ? " selected" : "")}
                      onClick={() => toggleService(s.slug)}>{s.label}</button>
                    )}
                    <button type="button"
                      className={"service-chip" + (selectedServices.includes("other") ? " selected" : "")}
                      onClick={() => toggleService("other")}>Something else</button>
                  </div>
                </div>
                <div>
                  <label htmlFor="cf-msg">More context</label>
                  <textarea id="cf-msg" required placeholder="The platform, the pressure, the outcome you need. Short is fine." rows={5} />
                </div>
                <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, marginTop: 8 }}>
                  <button type="submit" className="btn btn-primary">
                    Send it <span className="arr">→</span>
                  </button>
                  <span className="mono" style={{ fontSize: 11, color: "var(--fg-4)" }}>
                    or email <a href={"mailto:" + c.email} style={{ borderBottom: "1px solid var(--line-strong)" }}>{c.email}</a>
                  </span>
                </div>
              </form>
              <dl className="contact-meta">
                <div>
                  <dt>Email</dt>
                  <dd><a href={"mailto:" + c.email}>{c.email}</a></dd>
                </div>
                <div>
                  <dt>Response</dt>
                  <dd data-comment-anchor="43d50b9fc9-dd-775-19">{c.response}</dd>
                </div>
                <div>
                  <dt>Hours</dt>
                  <dd data-comment-anchor="a927195845-dd-779-19">{c.hours}</dd>
                </div>
                <div>
                  <dt>Location</dt>
                  <dd>{N.brand.location}</dd>
                </div>
              </dl>
            </div>
          </Reveal>
        </div>
      </section>

      {/* FAQ-ish strip */}
      <section className="block pastel-2">
        <div className="wrap">
          <Reveal>
            <div className="eyebrow" style={{ marginBottom: 16 }}>What to expect</div>
            <h2 style={{ maxWidth: "20ch", marginBottom: 48 }}>What happens after you hit send.</h2>
          </Reveal>
          <Reveal stagger className="grid-3">
            {[
            { n: "01", icon: "send", t: "A short reply, quickly", b: "Usually within one working day. We confirm we've understood the problem and the pressure." },
            { n: "02", icon: "chat", t: "A 30-minute conversation", b: "No deck, no questionnaire. We listen, ask the awkward questions, share an honest first read." },
            { n: "03", icon: "route", t: "A sensible next move", b: "Diagnostic, delivery or leadership — whichever fits. We'll be specific about scope and shape." }].
            map((s) =>
            <div className="card" key={s.n}>
                <div className="card-icon"><Icon name={s.icon} /></div>
                <h3>{s.t}</h3>
                <p>{s.b}</p>
              </div>
            )}
          </Reveal>
        </div>
      </section>
    </>);

}

// ────────────────────────────────────────────────────────────────────────────
// CLOSING CTA + FOOTER
// ────────────────────────────────────────────────────────────────────────────
function ClosingCta({ serviceSlug } = {}) {
  const N = window.NODESTONE;
  const c = N.cta;
  return (
    <section className="block-tight">
      <div className="wrap">
        <Reveal>
          <div style={{
            background: "var(--bg-elev)",
            border: "1px solid var(--line)",
            borderRadius: "var(--radius-lg)",
            padding: "64px 56px",
            display: "grid",
            gridTemplateColumns: "1.4fr 1fr",
            gap: 48,
            alignItems: "end",
            position: "relative",
            overflow: "hidden"
          }} className="closing-cta">
            <div style={{
              position: "absolute",
              inset: 0,
              background: "radial-gradient(circle at 90% 10%, var(--pastel-3) 0%, transparent 45%), radial-gradient(circle at 0% 100%, var(--pastel-1) 0%, transparent 50%)",
              opacity: 0.5,
              pointerEvents: "none"
            }} />
            <div style={{ position: "relative" }}>
              {c.eyebrow && <div className="eyebrow" style={{ marginBottom: 16 }}>{c.eyebrow}</div>}
              <h2 style={{ marginBottom: 16, maxWidth: "16ch" }}>{c.title}</h2>
              <p className="lede" style={{ marginBottom: 0 }}>{c.body}</p>
            </div>
            <div style={{ position: "relative", display: "flex", justifyContent: "flex-end", alignItems: "end", gap: 12 }}>
              <a href={c.button.path} className="btn btn-primary"
              onClick={(e) => {e.preventDefault();if(serviceSlug){sessionStorage.setItem('contactPreselect',serviceSlug);}window.location.hash = c.button.path.slice(1);}}>
                {c.button.label} <span className="arr">→</span>
              </a>
            </div>
            <style>{`@media (max-width:720px){.closing-cta{grid-template-columns:1fr !important; padding: 40px 28px !important;}}`}</style>
          </div>
        </Reveal>
      </div>
    </section>);

}

function Footer() {
  const N = window.NODESTONE;
  const go = (e, p) => {e.preventDefault();window.location.hash = p.slice(1);};
  return (
    <footer className="footer">
      <div className="wrap">
        <div className="footer-grid">
          <div>
            <div style={{ display: "inline-flex", alignItems: "center", gap: 8, fontFamily: "var(--font-display)", fontWeight: 600, fontSize: 19, letterSpacing: "-0.03em", color: "var(--fg)", marginBottom: 16 }}>
              <span className="nav-brand-dot" />
              Nodestone
            </div>
            <p style={{ maxWidth: "34ch", fontSize: 13.5, color: "var(--fg-3)" }}>
              Cloud, infrastructure, automation, applications, integrations and software delivery.
            </p>
          </div>
          <div>
            <h5>Services</h5>
            <div className="footer-links">
              {N.services.slice(0, 4).map((s) =>
              <a key={s.slug} href={"#/services/" + s.slug} onClick={(e) => go(e, "#/services/" + s.slug)}>{s.label}</a>
              )}
              <a href="#/services" onClick={(e) => go(e, "#/services")} style={{ color: "var(--fg-4)" }}>See all →</a>
            </div>
          </div>
          <div>
            <h5>Company</h5>
            <div className="footer-links">
              <a href="#/about" onClick={(e) => go(e, "#/about")}>Who we are</a>
              <a href="#/workshop" onClick={(e) => go(e, "#/workshop")}>Workshop</a>
              <a href="#/contact" onClick={(e) => go(e, "#/contact")}>Contact</a>
            </div>
          </div>
          <div>
            <h5>Get in touch</h5>
            <div className="footer-links">
              <a href={"mailto:" + N.brand.contactEmail}>{N.brand.contactEmail}</a>
              <span style={{ color: "var(--fg-4)" }}>{N.brand.location}</span>
            </div>
          </div>
        </div>

        <div className="footer-bottom">
          <span>© {new Date().getFullYear()} {N.brand.company}</span>
          <span className="mono">v0.6 · In development</span>
        </div>
      </div>
    </footer>);

}

window.HomePage = HomePage;
window.ServicesPage = ServicesPage;
window.ServiceDetailPage = ServiceDetailPage;
window.AboutPage = AboutPage;
window.ProductsPage = ProductsPage;
window.ContactPage = ContactPage;
window.Footer = Footer;
window.Hero = Hero;
window.Reveal = Reveal;
window.ClosingCta = ClosingCta;