// `|| {}` guards against _ds_bundle.js failing to load in production (a
// real network failure, not reproducible on localhost): destructuring
// straight off `undefined` would throw here and abort the rest of this
// file's top-level code (see Shell.jsx for the full mechanism this fixes).
const { Tabs, Tag, Kicker, Button, Rule } = window.INKStudioDesignSystem_0136e3 || {};

// Genuine portfolio work only — every entry below has real supplied photography or video.
// No fabricated clients, dates or quantities: fields are omitted where not actually known.
const PROJECTS = [
  // Collaborations — INK's participation in events run by others. Presented as
  // participation only; nothing here claims INK organised or managed the event.
  // `focal` (optional): a CSS object-position override consumed only by the
  // Home showcase carousel's fixed-frame cover-crop (see MASTER_CARD_RATIO
  // in Home.jsx) — for assets whose meaningful content sits off-center, so
  // the standardized card frame crops in on the right part of the image
  // instead of a plain center-crop. The Work grid page ignores it (it sizes
  // each Frame to the item's own `ratio` instead, uncropped).
  { title: "Event stage — partner recognition", cat: "Collab", tags: ["Collaborations", "Event Participation"], ratio: "981 / 1280", src: "assets/photography/collab-stage-recognition.jpg", focal: "50% 35%" },
  { title: "Partner wall — event collaboration", cat: "Collab", tags: ["Collaborations", "Event Participation"], ratio: "803 / 1280", src: "assets/photography/collab-partner-wall.jpg" },
  { title: "Event credits — collaboration", cat: "Collab", tags: ["Collaborations", "Event Participation"], ratio: "899 / 1305", src: "assets/photography/collab-event-credits.jpg", focal: "50% 32%" },
  // Print & Merchandise
  { title: "Sticker sheet no.4", cat: "Print", tags: ["Print & Merchandise", "Stickers"], ratio: "1 / 1", src: "assets/photography/sticker-sheets.jpg" },
  { title: "Sticker sheet — print run", cat: "Print", tags: ["Print & Merchandise", "Stickers"], ratio: "3 / 4", src: "assets/photography/print-process.jpg", focal: "50% 68%" },
  { title: "SEED COFFEE poster", cat: "Print", tags: ["Print & Merchandise", "Poster design"], ratio: "1080 / 1350", src: "assets/photography/seed-coffee-poster.jpg", focal: "50% 6%" },
  { title: "Cultural sticker packs", cat: "Print", tags: ["Print & Merchandise", "Stickers"], ratio: "9 / 16", src: "assets/photography/sticker-cultural-kfupm.jpg" },
  { title: "Graduation sticker sheet", cat: "Print", tags: ["Print & Merchandise", "Stickers"], ratio: "9 / 16", src: "assets/photography/sticker-graduation-sheet.jpg", zoom: 0.78 },
  { title: "FABLAB sticker sheet", cat: "Print", tags: ["Print & Merchandise", "Stickers"], ratio: "9 / 16", src: "assets/photography/sticker-fablab.jpg", focal: "50% 72%" },
  // 3D Printing & Custom Objects
  { title: "Award set — 3D printed", cat: "3D", tags: ["3D Printing & Custom Objects", "Awards"], ratio: "3 / 4", src: "assets/photography/award-3d-print.jpg", zoom: 0.72 },
  { title: "Class of 2026 award", cat: "3D", tags: ["3D Printing & Custom Objects", "Awards"], ratio: "3 / 4", src: "assets/photography/award-2026-grad.jpg", zoom: 0.72 },
  { title: "Graduation cap figurines", cat: "3D", tags: ["3D Printing & Custom Objects", "Custom models"], ratio: "1 / 1", src: "assets/photography/custom-grad-figurines.jpg" },
  { title: "KSA plate keychains", cat: "3D", tags: ["3D Printing & Custom Objects", "Keychains"], ratio: "3 / 4", src: "assets/photography/custom-keychains.jpg" },
  { title: "Custom plate medals", cat: "3D", tags: ["3D Printing & Custom Objects", "Medals"], ratio: "3 / 4", src: "assets/photography/custom-plate-medals.jpg" },
  { title: "أرشدني wayfinding pins", cat: "3D", tags: ["3D Printing & Custom Objects", "Pins & badges"], ratio: "1 / 1", src: "assets/photography/custom-pins-badges.jpg" },
  // Media Production
  { title: "Product photography — drink series", cat: "Media", tags: ["Media Production", "Product Photography"], ratio: "1600 / 898", src: "assets/photography/product-drink-03.jpg" },
  { title: "Product photography — drink series II", cat: "Media", tags: ["Media Production", "Product Photography"], ratio: "2000 / 1333", src: "assets/photography/product-drink-02.jpg" },
  { title: "Product photography — packaged snack", cat: "Media", tags: ["Media Production", "Product Photography"], ratio: "2000 / 1333", src: "assets/photography/product-snack-packaging.jpg" },
  { title: "Automotive photography — night shot", cat: "Media", tags: ["Media Production", "Photography"], ratio: "2000 / 1333", src: "assets/photography/automotive-night.jpg" },
  { title: "Studio setup — behind the scenes", cat: "Media", tags: ["Media Production", "Behind the scenes"], ratio: "2000 / 1333", src: "assets/photography/studio-setup-bts.jpg" },
  // Motion & Animation
  { title: "Good vibes — motion loop", cat: "Motion", tags: ["Motion & Animation", "Motion graphics"], ratio: "2736 / 2050", video: "assets/motion/good-vibes-loop.mp4" },
  { title: "Coffee beans — title reveal", cat: "Motion", tags: ["Motion & Animation", "Animation"], ratio: "2736 / 2050", video: "assets/motion/coffee-beans-reveal.mp4" },
  { title: "Animated mascot loop", cat: "Motion", tags: ["Motion & Animation", "Animation"], ratio: "2304 / 3004", video: "assets/motion/glow-mascot-loop.mp4", zoom: 0.6 },
  { title: "Gradient loader animation", cat: "Motion", tags: ["Motion & Animation", "Animation"], ratio: "1280 / 958", video: "assets/motion/gradient-loader-loop.mp4" },
];

// Canonical family order for the grouped "All" view — matches the tab order.
const FAMILY_ORDER = ["Collab", "Media", "Motion", "Print", "3D"];

const AR_DIGITS = ["٠", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"];
function arDigits(n) {
  return String(n).replace(/[0-9]/g, (d) => AR_DIGITS[+d]);
}

// Arabic count-noun agreement for "piece(s)" — plain standard-Arabic rule,
// not a single noun mechanically reused after every number: 1 takes the
// singular, 2 the dual, 3-10 the plural ("قطع"), 11+ reverts to the
// singular tamyiz ("قطعة") used after a number. Every count this page
// actually shows (3, 4, 5, 6, 24) is covered by this.
function arPieceWord(n) {
  if (n === 1) return "قطعة";
  if (n === 2) return "قطعتان";
  if (n >= 3 && n <= 10) return "قطع";
  return "قطعة";
}

// Animates between arbitrary from/to values whenever `to` changes (counts up or
// down), reusing the same cubic ease-out curve as the homepage's CountUp — just
// tuned faster for instant filter-switch feedback rather than a scroll reveal.
function FilterCountUp({ to, duration = 480, format }) {
  const [n, setN] = React.useState(to);
  const prevRef = React.useRef(to);
  React.useEffect(() => {
    const from = prevRef.current;
    if (from === to) return;
    let raf, start;
    const step = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setN(Math.round(from + (to - from) * eased));
      if (p < 1) raf = requestAnimationFrame(step);
      else prevRef.current = to;
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [to]);
  return <>{format ? format(n) : n}</>;
}

function ProjectCard({ p, i }) {
  return (
    <Reveal delay={(i % 6) * 70} as="article" style={{ display: "flex", flexDirection: "column", gap: "var(--space-4)" }}>
      <Frame ratio={p.ratio} label={p.tags[0]} src={p.src} video={p.video} />
      <div style={{ fontWeight: "var(--weight-title)", fontStretch: "var(--stretch-title)" }}>{p.title}</div>
      <div style={{ display: "flex", gap: "var(--space-2)", flexWrap: "wrap" }}>{p.tags.map((t) => <Tag key={t}>{t}</Tag>)}</div>
    </Reveal>
  );
}

// A living background for the whole Work page — the homepage hero's own
// soft colored forms (.ink-blob, the same vermilion/cobalt/saffron/pine
// family) recomposed at a different scale, crop and spread than the hero:
// larger, a couple cropped by the viewport edge, distributed down the
// entire scrollable page instead of held inside one hero band, so the
// canvas keeps going as the grid is browsed rather than stopping after
// the first screen. A scroll-linked parallax wrapper (separate from each
// blob's own continuous morph/drift loop, so the two transforms never
// fight over the same element) gives the forms a small, slow sense of
// depth as the page scrolls. Two small monochrome production marks — crop
// marks and one registration mark, the same shapes QuadScenePrint already
// draws on the Services page — add a touch of studio texture without
// turning into decoration. Pure background: aria-hidden, pointer-events
// none, zIndex 0 behind the actual content (zIndex 1), never inside a
// project card, and never the visual priority over the photography.
// A compact vocabulary of the studio's own recurring motifs — reused
// exactly as they read elsewhere on the site (yellow print/merch, blue
// media, green 3D, red motion) rather than inventing new shapes — each
// rendered small enough to read as a background accent, never a
// foreground illustration.
function BgTote({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 60 60" width="100%" height="100%">
      <path d="M14,20 L14,50 Q14,54 18,54 L42,54 Q46,54 46,50 L46,20 Z" fill="none" stroke={color} strokeWidth="2.2" />
      <path d="M21,20 V13 Q21,6 30,6 Q39,6 39,13 V20" fill="none" stroke={color} strokeWidth="2.2" />
    </svg>
  );
}
function BgTag({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 40 28" width="100%" height="100%">
      <path d="M4,4 L28,4 L36,14 L28,24 L4,24 Z" fill="none" stroke={color} strokeWidth="2" />
      <circle cx="13" cy="14" r="3.4" fill="none" stroke={color} strokeWidth="1.6" />
    </svg>
  );
}
function BgCard({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 54 36" width="100%" height="100%">
      <rect x="3" y="3" width="48" height="30" rx="4" fill="none" stroke={color} strokeWidth="2" />
      <line x1="12" y1="15" x2="34" y2="15" stroke={color} strokeWidth="1.6" />
      <line x1="12" y1="23" x2="26" y2="23" stroke={color} strokeWidth="1.6" />
    </svg>
  );
}
function BgCamera({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 54 42" width="100%" height="100%">
      <rect x="2" y="11" width="50" height="28" rx="4" fill="none" stroke={color} strokeWidth="2.2" />
      <rect x="17" y="2" width="15" height="10" rx="2" fill="none" stroke={color} strokeWidth="2.2" />
      <circle cx="27" cy="25" r="10" fill="none" stroke={color} strokeWidth="2.2" />
    </svg>
  );
}
function BgMic({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 20 40" width="100%" height="100%">
      <rect x="6" y="2" width="8" height="16" rx="4" fill="none" stroke={color} strokeWidth="1.8" />
      <path d="M3,16 a7,7 0 0 0 14,0" fill="none" stroke={color} strokeWidth="1.6" />
      <line x1="10" y1="23" x2="10" y2="34" stroke={color} strokeWidth="1.6" />
      <line x1="4" y1="34" x2="16" y2="34" stroke={color} strokeWidth="1.6" />
    </svg>
  );
}
function BgCube({ color }) {
  const s = 13;
  const top = `M${s * 1.5},0 L${s * 2.9},${s * 0.8} L${s * 1.5},${s * 1.6} L${s * 0.1},${s * 0.8} Z`;
  const left = `M${s * 0.1},${s * 0.8} L${s * 1.5},${s * 1.6} L${s * 1.5},${s * 3.2} L${s * 0.1},${s * 2.4} Z`;
  const right = `M${s * 2.9},${s * 0.8} L${s * 1.5},${s * 1.6} L${s * 1.5},${s * 3.2} L${s * 2.9},${s * 2.4} Z`;
  return (
    <svg aria-hidden="true" viewBox={`0 0 ${s * 3} ${s * 3.2}`} width="100%" height="100%">
      {[top, left, right].map((d, i) => <path key={i} d={d} fill="none" stroke={color} strokeWidth="1.8" />)}
    </svg>
  );
}
function BgMotionCircle({ color }) {
  return <svg aria-hidden="true" viewBox="0 0 30 30" width="100%" height="100%"><circle cx="15" cy="15" r="11" fill="none" stroke={color} strokeWidth="2.4" /></svg>;
}
function BgMotionSquare({ color }) {
  return <svg aria-hidden="true" viewBox="0 0 30 30" width="100%" height="100%"><rect x="5" y="5" width="20" height="20" fill="none" stroke={color} strokeWidth="2.4" /></svg>;
}
function BgMotionTriangle({ color }) {
  return <svg aria-hidden="true" viewBox="0 0 30 30" width="100%" height="100%"><path d="M15,4 L27,26 L3,26 Z" fill="none" stroke={color} strokeWidth="2.4" /></svg>;
}
function BgCropMarks({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 56 56" width="100%" height="100%">
      <path d="M12,12 l12,0 M12,12 l0,12" stroke={color} strokeWidth="1.4" fill="none" />
      <path d="M44,12 l-12,0 M44,12 l0,12" stroke={color} strokeWidth="1.4" fill="none" />
      <path d="M12,44 l12,0 M12,44 l0,-12" stroke={color} strokeWidth="1.4" fill="none" />
      <path d="M44,44 l-12,0 M44,44 l0,-12" stroke={color} strokeWidth="1.4" fill="none" />
    </svg>
  );
}
function BgRegMark({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 26 26" width="100%" height="100%">
      <circle cx="13" cy="13" r="7.5" fill="none" stroke={color} strokeWidth="1.2" />
      <line x1="2" y1="13" x2="24" y2="13" stroke={color} strokeWidth="1" />
      <line x1="13" y1="2" x2="13" y2="24" stroke={color} strokeWidth="1" />
    </svg>
  );
}
function BgSticker({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 36 36" width="100%" height="100%">
      <rect x="3" y="3" width="30" height="30" rx="9" fill="none" stroke={color} strokeWidth="2" />
      {[11, 18, 25].map((x) => [11, 18, 25].map((y) => <circle key={`${x}-${y}`} cx={x} cy={y} r="1.4" fill={color} />))}
    </svg>
  );
}
// The same production-light glyph as the homepage Package cards' own
// ProductionLayer (identical path data) — reused, not reinvented.
function BgLight({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 30 30" width="100%" height="100%">
      <path d="M8,4 L22,4 L26,16 L4,16 Z" fill="none" stroke={color} strokeWidth="1.6" />
      <line x1="15" y1="16" x2="15" y2="24" stroke={color} strokeWidth="1.4" />
      <line x1="9" y1="24" x2="21" y2="24" stroke={color} strokeWidth="1.4" />
    </svg>
  );
}

// Additive vocabulary — new individual objects for the Work background's
// second pass, in the same stroke-only line-art language as the shapes
// above (no fills but small dot/accent details, same weight range).
function BgNotebook({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 40 52" width="100%" height="100%">
      <rect x="8" y="4" width="28" height="44" rx="3" fill="none" stroke={color} strokeWidth="1.8" />
      {[10, 18, 26, 34, 42].map((y) => <circle key={y} cx="4" cy={y} r="1.6" fill="none" stroke={color} strokeWidth="1.2" />)}
      <line x1="14" y1="18" x2="30" y2="18" stroke={color} strokeWidth="1.2" />
      <line x1="14" y1="24" x2="30" y2="24" stroke={color} strokeWidth="1.2" />
      <line x1="14" y1="30" x2="26" y2="30" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}
function BgBrochure({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 50 36" width="100%" height="100%">
      <rect x="3" y="3" width="44" height="30" rx="3" fill="none" stroke={color} strokeWidth="1.8" />
      <line x1="18" y1="3" x2="18" y2="33" stroke={color} strokeWidth="1.2" />
      <line x1="33" y1="3" x2="33" y2="33" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}
function BgPrinter({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 46 36" width="100%" height="100%">
      <rect x="6" y="14" width="34" height="16" rx="3" fill="none" stroke={color} strokeWidth="1.8" />
      <rect x="12" y="4" width="22" height="12" fill="none" stroke={color} strokeWidth="1.6" />
      <rect x="14" y="26" width="18" height="8" fill="none" stroke={color} strokeWidth="1.4" />
      <circle cx="34" cy="22" r="1.4" fill={color} />
    </svg>
  );
}
function BgPrintSheet({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 40 52" width="100%" height="100%">
      <rect x="4" y="3" width="32" height="46" rx="2" fill="none" stroke={color} strokeWidth="1.8" />
      <line x1="10" y1="14" x2="30" y2="14" stroke={color} strokeWidth="1.2" />
      <line x1="10" y1="20" x2="30" y2="20" stroke={color} strokeWidth="1.2" />
      <line x1="10" y1="26" x2="24" y2="26" stroke={color} strokeWidth="1.2" />
      <line x1="10" y1="36" x2="28" y2="36" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}
function BgAirplane({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 50 50" width="100%" height="100%">
      <path d="M6,26 L44,8 L30,44 L24,30 L6,26 Z" fill="none" stroke={color} strokeWidth="1.8" strokeLinejoin="round" />
      <line x1="24" y1="30" x2="44" y2="8" stroke={color} strokeWidth="1.4" />
    </svg>
  );
}
function BgTrain({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 56 34" width="100%" height="100%">
      <rect x="4" y="8" width="34" height="16" rx="3" fill="none" stroke={color} strokeWidth="1.8" />
      <rect x="10" y="12" width="8" height="6" fill="none" stroke={color} strokeWidth="1.2" />
      <rect x="22" y="12" width="8" height="6" fill="none" stroke={color} strokeWidth="1.2" />
      <circle cx="12" cy="28" r="3" fill="none" stroke={color} strokeWidth="1.6" />
      <circle cx="30" cy="28" r="3" fill="none" stroke={color} strokeWidth="1.6" />
      <line x1="38" y1="18" x2="50" y2="18" stroke={color} strokeWidth="1.4" />
    </svg>
  );
}
function BgTripod({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 40 50" width="100%" height="100%">
      <path d="M20,10 L8,46 M20,10 L20,46 M20,10 L32,46" fill="none" stroke={color} strokeWidth="1.6" />
      <rect x="12" y="2" width="16" height="12" rx="2" fill="none" stroke={color} strokeWidth="1.8" />
      <circle cx="20" cy="8" r="3" fill="none" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}
function BgClapperboard({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 48 40" width="100%" height="100%">
      <rect x="4" y="14" width="40" height="22" rx="2" fill="none" stroke={color} strokeWidth="1.8" />
      <path d="M4,14 L10,4 L46,4 L40,14 Z" fill="none" stroke={color} strokeWidth="1.8" strokeLinejoin="round" />
      <line x1="14" y1="4" x2="8" y2="14" stroke={color} strokeWidth="1.2" />
      <line x1="24" y1="4" x2="18" y2="14" stroke={color} strokeWidth="1.2" />
      <line x1="34" y1="4" x2="28" y2="14" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}
function BgFilmFrame({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 40 54" width="100%" height="100%">
      <rect x="8" y="2" width="24" height="50" rx="2" fill="none" stroke={color} strokeWidth="1.8" />
      {[6, 14, 22, 30, 38, 46].map((y) => (
        <React.Fragment key={y}>
          <rect x="3" y={y} width="4" height="4" fill="none" stroke={color} strokeWidth="1" />
          <rect x="33" y={y} width="4" height="4" fill="none" stroke={color} strokeWidth="1" />
        </React.Fragment>
      ))}
    </svg>
  );
}
function BgTrophy({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 34 44" width="100%" height="100%">
      <path d="M11,4 H23 V16 Q23,24 17,24 Q11,24 11,16 Z" fill="none" stroke={color} strokeWidth="1.8" />
      <path d="M11,6 Q3,6 3,14 Q3,20 11,20" fill="none" stroke={color} strokeWidth="1.4" />
      <path d="M23,6 Q31,6 31,14 Q31,20 23,20" fill="none" stroke={color} strokeWidth="1.4" />
      <line x1="17" y1="24" x2="17" y2="32" stroke={color} strokeWidth="1.6" />
      <rect x="10" y="32" width="14" height="4" rx="1" fill="none" stroke={color} strokeWidth="1.4" />
      <rect x="7" y="36" width="20" height="4" rx="1" fill="none" stroke={color} strokeWidth="1.4" />
    </svg>
  );
}
function BgShieldAward({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 34 40" width="100%" height="100%">
      <path d="M17,2 L31,8 V18 Q31,32 17,38 Q3,32 3,18 V8 Z" fill="none" stroke={color} strokeWidth="1.8" strokeLinejoin="round" />
      <path d="M11,19 L15,23 L23,13" fill="none" stroke={color} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}
function BgMedal({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 32 46" width="100%" height="100%">
      <path d="M10,2 L4,20 L16,16 L28,20 L22,2" fill="none" stroke={color} strokeWidth="1.2" strokeLinejoin="round" />
      <path d="M10,2 L16,16 L22,2" fill="none" stroke={color} strokeWidth="1.6" strokeLinejoin="round" />
      <circle cx="16" cy="32" r="12" fill="none" stroke={color} strokeWidth="1.8" />
      <circle cx="16" cy="32" r="6" fill="none" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}
function BgBadge({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 30 34" width="100%" height="100%">
      <circle cx="15" cy="14" r="12" fill="none" stroke={color} strokeWidth="1.8" />
      <circle cx="15" cy="14" r="4" fill="none" stroke={color} strokeWidth="1.2" />
      <path d="M11,26 L15,34 L19,26 Z" fill="none" stroke={color} strokeWidth="1.4" strokeLinejoin="round" />
    </svg>
  );
}
function BgGeo({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 36 36" width="100%" height="100%">
      <path d="M18,2 L34,13 L18,34 L2,13 Z" fill="none" stroke={color} strokeWidth="1.8" strokeLinejoin="round" />
      <path d="M2,13 L34,13 M11,13 L18,34 M25,13 L18,34 M18,2 L11,13 M18,2 L25,13" fill="none" stroke={color} strokeWidth="1" opacity="0.6" />
    </svg>
  );
}
function BgBox({ color }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 44 40" width="100%" height="100%">
      <path d="M22,4 L40,12 V30 L22,38 L4,30 V12 Z" fill="none" stroke={color} strokeWidth="1.8" strokeLinejoin="round" />
      <path d="M4,12 L22,20 L40,12 M22,20 V38" fill="none" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}

const BG_SHAPES = {
  tote: BgTote, tag: BgTag, card: BgCard, camera: BgCamera, mic: BgMic, light: BgLight, cube: BgCube, mCircle: BgMotionCircle, mSquare: BgMotionSquare, mTriangle: BgMotionTriangle, crop: BgCropMarks, reg: BgRegMark, sticker: BgSticker,
  notebook: BgNotebook, brochure: BgBrochure, printer: BgPrinter, printSheet: BgPrintSheet, airplane: BgAirplane, train: BgTrain, tripod: BgTripod, clapperboard: BgClapperboard, filmFrame: BgFilmFrame, trophy: BgTrophy, shieldAward: BgShieldAward, medal: BgMedal, badge: BgBadge, geo: BgGeo, box: BgBox,
};
// Fixed discipline colors — the same vocabulary reused sitewide: yellow
// for print/merch, blue for media, green for 3D, red/orange for motion.
const BG_COLOR = {
  tote: "var(--saffron-500)", tag: "var(--saffron-500)", card: "var(--saffron-500)", sticker: "var(--saffron-500)",
  notebook: "var(--saffron-500)", brochure: "var(--saffron-500)", printer: "var(--saffron-500)", printSheet: "var(--saffron-500)", box: "var(--saffron-500)",
  camera: "var(--cobalt-600)", mic: "var(--cobalt-600)", light: "var(--cobalt-600)", tripod: "var(--cobalt-600)", clapperboard: "var(--cobalt-600)", filmFrame: "var(--cobalt-600)",
  cube: "var(--pine-500)", trophy: "var(--pine-500)", shieldAward: "var(--pine-500)", medal: "var(--pine-500)", badge: "var(--pine-500)", geo: "var(--pine-500)",
  mCircle: "var(--vermilion-500)", mSquare: "var(--vermilion-500)", mTriangle: "var(--vermilion-500)", airplane: "var(--vermilion-500)", train: "var(--vermilion-500)",
};
// A large library of individually-placed, individually-scaled objects,
// each with its own position — NOT grouped into repeated icon clusters.
// Left/right sides and vertical spacing are deliberately staggered
// (never paired at the same height) so the page reads as continuously,
// organically populated rather than "cluster — gap — cluster — gap".
// Sizes vary deliberately: mostly medium/medium-large, a few small
// accents, and a handful of larger feature graphics that read as real
// illustrations rather than tiny icons. `dx` is each object's own
// individual inset from its edge (also varied, for the "generous but
// intentional spacing" look) — re-clamped against the real grid gutter
// at render time so ordinary objects still never sit on top of a card;
// the handful of large feature objects are allowed the same occasional
// soft edge-bleed the atmospheric blobs already use.
const MOTIF_LIBRARY = [
  { type: "tote", side: "left", top: "2%", dx: 18, size: 86 },
  { type: "camera", side: "right", top: "5%", dx: 24, size: 46 },
  { type: "cube", side: "left", top: "9%", dx: 30, size: 42 },
  { type: "mCircle", side: "right", top: "8%", dx: 60, size: 26 },
  { type: "tag", side: "right", top: "12%", dx: 20, size: 28 },
  { type: "card", side: "left", top: "15%", dx: 22, size: 58 },
  { type: "mic", side: "right", top: "18%", dx: 30, size: 36 },
  { type: "tag", side: "left", top: "21%", dx: 50, size: 30 },
  { type: "mSquare", side: "right", top: "25%", dx: 22, size: 28 },
  { type: "camera", side: "left", top: "26%", dx: 66, size: 32 },
  { type: "sticker", side: "left", top: "28%", dx: 36, size: 46 },
  { type: "light", side: "right", top: "32%", dx: 26, size: 38 },
  { type: "mTriangle", side: "left", top: "36%", dx: 60, size: 28 },
  { type: "cube", side: "right", top: "39%", dx: 20, size: 92 },
  { type: "sticker", side: "right", top: "41%", dx: 68, size: 24 },
  { type: "tote", side: "left", top: "43%", dx: 24, size: 54 },
  { type: "camera", side: "right", top: "46%", dx: 40, size: 42 },
  { type: "mSquare", side: "left", top: "48%", dx: 22, size: 26 },
  { type: "card", side: "left", top: "50%", dx: 60, size: 36 },
  { type: "mCircle", side: "right", top: "53%", dx: 24, size: 30 },
  { type: "tag", side: "left", top: "57%", dx: 20, size: 66 },
  { type: "mic", side: "right", top: "60%", dx: 44, size: 40 },
  { type: "cube", side: "right", top: "62%", dx: 22, size: 50 },
  { type: "sticker", side: "left", top: "64%", dx: 64, size: 26 },
  { type: "mSquare", side: "right", top: "67%", dx: 22, size: 56 },
  { type: "light", side: "left", top: "71%", dx: 30, size: 44 },
  { type: "mTriangle", side: "right", top: "74%", dx: 56, size: 30 },
  { type: "card", side: "right", top: "76%", dx: 20, size: 34 },
  { type: "cube", side: "left", top: "78%", dx: 26, size: 60 },
  { type: "tote", side: "right", top: "81%", dx: 18, size: 88 },
  { type: "camera", side: "left", top: "85%", dx: 46, size: 36 },
  { type: "card", side: "right", top: "88%", dx: 24, size: 50 },
  { type: "mTriangle", side: "left", top: "90%", dx: 62, size: 24 },
  { type: "mCircle", side: "left", top: "91%", dx: 56, size: 26 },
  { type: "tag", side: "right", top: "94%", dx: 30, size: 40 },
  { type: "cube", side: "left", top: "95%", dx: 26, size: 36 },
  { type: "mic", side: "left", top: "97%", dx: 22, size: 32 },
  { type: "sticker", side: "right", top: "99%", dx: 40, size: 28 },

  // --- Additive pass: new production/creative vocabulary, individually
  // placed in the gaps between the entries above (never at the same
  // top as an existing one) — same free-distribution/depth-via-scale
  // approach, not a second system layered on top.
  { type: "notebook", side: "right", top: "3.5%", dx: 22, size: 50 },
  { type: "trophy", side: "left", top: "6.5%", dx: 44, size: 34 },
  { type: "airplane", side: "right", top: "10.5%", dx: 40, size: 30 },
  { type: "badge", side: "left", top: "13.5%", dx: 60, size: 24 },
  { type: "brochure", side: "right", top: "19.5%", dx: 24, size: 40 },
  { type: "filmFrame", side: "left", top: "23%", dx: 30, size: 46 },
  { type: "printer", side: "right", top: "30%", dx: 46, size: 36 },
  { type: "geo", side: "left", top: "34%", dx: 50, size: 28 },
  { type: "medal", side: "right", top: "37.5%", dx: 26, size: 32 },
  { type: "tripod", side: "left", top: "44.5%", dx: 22, size: 38 },
  { type: "shieldAward", side: "right", top: "54.5%", dx: 40, size: 30 },
  { type: "printSheet", side: "left", top: "58.5%", dx: 24, size: 44 },
  { type: "train", side: "right", top: "68.5%", dx: 20, size: 34 },
  { type: "clapperboard", side: "left", top: "72.5%", dx: 36, size: 40 },
  { type: "notebook", side: "right", top: "82.5%", dx: 50, size: 26 },
  { type: "airplane", side: "left", top: "86.5%", dx: 42, size: 22 },
  { type: "box", side: "right", top: "92.5%", dx: 20, size: 48 },
];
const MOTIF_FLOATS = ["work-bg-float-a", "work-bg-float-b", "work-bg-float-c"];

// Evenly-spaced selection of n items from a library array (preserving
// order), used to thin a fixed spread-across-the-page library down to
// fewer, still-evenly-distributed instances for shorter filtered pages.
function pickEven(arr, n) {
  if (n >= arr.length) return arr;
  if (n <= 1) return [arr[Math.floor((arr.length - 1) / 2)]];
  const out = [];
  for (let i = 0; i < n; i++) out.push(arr[Math.round((i * (arr.length - 1)) / (n - 1))]);
  return out;
}

// Large, slow-moving atmospheric forms — the site's own colored blobs —
// a fixed library spread the entire way down the page; thinned by count.
const FORM_LIBRARY = [
  { size: 460, left: "-12%", top: "1%", color: "var(--vermilion-500)", op: 0.07, driftDur: 17, driftDelay: -2, k: 0.025 },
  { size: 300, right: "-7%", top: "13%", color: "var(--saffron-500)", op: 0.085, driftDur: 13, driftDelay: -6, k: -0.035 },
  { size: 360, left: "3%", top: "25%", color: "var(--cobalt-600)", op: 0.06, driftDur: 19, driftDelay: -4, k: 0.03 },
  { size: 260, right: "-9%", top: "37%", color: "var(--pine-500)", op: 0.075, driftDur: 15, driftDelay: -1, k: -0.03 },
  { size: 240, left: "40%", top: "49%", color: "var(--vermilion-500)", op: 0.055, driftDur: 21, driftDelay: -8, k: 0.028 },
  { size: 340, left: "-10%", top: "61%", color: "var(--saffron-500)", op: 0.07, driftDur: 18, driftDelay: -3, k: -0.032 },
  { size: 280, right: "-6%", top: "73%", color: "var(--cobalt-600)", op: 0.065, driftDur: 16, driftDelay: -7, k: 0.034 },
  { size: 300, left: "6%", top: "85%", color: "var(--vermilion-500)", op: 0.06, driftDur: 20, driftDelay: -5, k: -0.026 },
  { size: 240, right: "2%", top: "95%", color: "var(--pine-500)", op: 0.07, driftDur: 14, driftDelay: -2, k: 0.03 },
];

// Small print/production accents — a fixed library, thinned by count.
const MARK_LIBRARY = [
  { type: "crop", size: 50, left: "5%", top: "9%" },
  { type: "reg", size: 24, right: "10%", top: "20%" },
  { type: "crop", size: 40, right: "6%", top: "38%" },
  { type: "reg", size: 22, left: "7%", top: "50%" },
  { type: "crop", size: 44, left: "4%", top: "68%" },
  { type: "reg", size: 24, right: "9%", top: "80%" },
  { type: "crop", size: 36, right: "5%", top: "91%" },
];

// A continuous, freely-distributed background system running the full
// length of the (long, filterable) Work page: each studio object —
// print/merch in yellow, media in blue, 3D in green, motion in red —
// has its own individual position, never grouped into repeated icon
// packs, plus a handful of large soft atmospheric forms as a secondary
// layer underneath it all. Quantity (how many individual objects, forms
// and marks appear) scales proportionally with `count`, the number of
// project pieces the current filter is showing, against MOTIF_LIBRARY's
// own 24-piece "All" baseline — so a short filtered page still gets a
// genuinely populated spread sized to its own (shorter) page rather
// than one token cluster, while "All" gets the full, richest spread.
// Percentages are relative to the whole scrollable page height, so the
// composition adapts automatically to each filter's actual page length.
// Independent per-item drift/rotation/timing and a slow scroll parallax
// keep it alive without ever tracking the cursor or moving as a group.
// The real usable gutter beside the (max-width, centered) project grid —
// (viewportWidth - containerMax) / 2, minus a small safety buffer — so
// ordinary objects can be clamped to never reach past the grid's own
// edge into a card, at any viewport width, not just the mobile
// breakpoint. container-max is 1280px sitewide (tokens/spacing.css);
// floors at 26px (the page-margin gutter mobile/medium widths fall back
// to) and caps at 140px on wide screens — generous enough that the
// library's larger feature-scale objects mostly clear it too, with only
// the occasional soft edge-bleed at narrower desktop widths, which is
// the same treatment the atmospheric forms already use.
function useEdgeGutter() {
  const compute = () => (typeof window === "undefined" ? 140 : Math.max(26, Math.min(140, (window.innerWidth - 1280) / 2 - 8)));
  const [gutter, setGutter] = React.useState(compute);
  React.useEffect(() => {
    const onResize = () => setGutter(compute());
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, []);
  return gutter;
}

function WorkBackdrop({ reduced, count }) {
  const rootRef = React.useRef(null);
  const narrow = useIsNarrow();
  const edgeGutter = useEdgeGutter();

  React.useEffect(() => {
    if (reduced) return;
    let raf = null;
    const update = () => {
      raf = null;
      if (rootRef.current) rootRef.current.style.setProperty("--workScrollY", `${window.scrollY}px`);
    };
    const onScroll = () => { if (raf == null) raf = requestAnimationFrame(update); };
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); if (raf != null) cancelAnimationFrame(raf); };
  }, [reduced]);

  // Proportional to the "All" (24-piece) baseline the libraries are
  // authored for, so a shorter filtered page gets fewer objects but at
  // the same visual density per page-length, never a bare minimum.
  const ratio = Math.max(0.28, Math.min(1, (count || 24) / 24));
  const motifs = pickEven(MOTIF_LIBRARY, Math.max(10, Math.round(MOTIF_LIBRARY.length * ratio)));
  const forms = pickEven(FORM_LIBRARY, Math.max(3, Math.round(FORM_LIBRARY.length * ratio)));
  const marks = pickEven(MARK_LIBRARY, Math.max(2, Math.round(MARK_LIBRARY.length * ratio)));

  return (
    <div ref={rootRef} aria-hidden="true" style={{ position: "absolute", inset: 0, overflow: "hidden", pointerEvents: "none", zIndex: 0 }}>
      <style>{`
        @keyframes work-bg-float-a { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-10px) rotate(-4deg); } }
        @keyframes work-bg-float-b { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(8px) rotate(5deg); } }
        @keyframes work-bg-float-c { 0%, 100% { transform: translateY(0) scale(1); } 50% { transform: translateY(-6px) scale(1.06); } }
      `}</style>
      {forms.map((f, i) => {
        // On narrow viewports the large atmospheric sizes and off-edge
        // negative left/right offsets are clamped so they never extend
        // past the page edge — `right`/`left` position the box edge
        // independently of its size, so shrinking size alone can't fix
        // horizontal overflow; the offset itself has to be non-negative.
        // (inset, not 0%, to leave room for the ink-drift keyframe's own
        // 14px horizontal wobble without poking back past the edge)
        const size = narrow ? Math.min(f.size, 190) : f.size;
        const left = narrow && f.left != null && parseFloat(f.left) < 0 ? "4%" : f.left;
        const right = narrow && f.right != null && parseFloat(f.right) < 0 ? "4%" : f.right;
        return (
          <div
            key={`form-${i}`}
            style={{
              position: "absolute", width: size, height: size,
              left, right, top: f.top,
              transform: reduced ? "none" : `translateY(calc(var(--workScrollY, 0px) * ${f.k}))`,
              willChange: reduced ? "auto" : "transform",
            }}
          >
            <div
              className="ink-blob"
              style={{
                width: "100%", height: "100%", background: f.color, opacity: f.op,
                animation: reduced ? "none" : `ink-blob-morph 9s var(--ease-standard) infinite, ink-drift ${f.driftDur}s var(--ease-standard) infinite ${f.driftDelay}s`,
              }}
            />
          </div>
        );
      })}
      {motifs.map((m, i) => {
        const Shape = BG_SHAPES[m.type];
        // Each object is individually sized and inset — not identical
        // across a group — and re-clamped against the real grid gutter
        // so ordinary (non-feature) objects stay clear of the cards;
        // the library's few large feature objects (size > gutter) are
        // allowed the same soft edge-bleed the atmospheric forms use.
        const size = narrow ? Math.min(m.size, m.size > 70 ? 56 : 34) : m.size;
        const inset = Math.min(m.dx, Math.max(6, edgeGutter - size));
        const op = size >= 70 ? 0.22 : size >= 44 ? 0.3 : size >= 32 ? 0.34 : 0.3;
        const floatAnim = MOTIF_FLOATS[i % MOTIF_FLOATS.length];
        const dur = 6400 + (i * 337) % 3600 + size * 20;
        const edgeProp = m.side === "left" ? "left" : "right";
        const parallaxK = m.side === "left" ? 0.045 : -0.045;
        return (
          <div
            key={`motif-${i}`}
            style={{
              position: "absolute", width: size, height: size,
              [edgeProp]: `${inset}px`, top: m.top,
              opacity: op,
              transform: reduced ? "none" : `translateY(calc(var(--workScrollY, 0px) * ${parallaxK}))`,
              willChange: reduced ? "auto" : "transform",
            }}
          >
            <div style={{ width: "100%", height: "100%", animation: reduced ? "none" : `${floatAnim} ${dur}ms ease-in-out infinite ${-(i * 260) % 4000}ms` }}>
              <Shape color={BG_COLOR[m.type]} />
            </div>
          </div>
        );
      })}
      {marks.map((mk, i) => {
        const Shape = BG_SHAPES[mk.type];
        return (
          <div key={`mark-${i}`} style={{ position: "absolute", width: mk.size, height: mk.size, left: mk.left, right: mk.right, top: mk.top, opacity: 0.18 }}>
            <Shape color="var(--ink-400)" />
          </div>
        );
      })}
    </div>
  );
}

function Work({ lang }) {
  const ar = lang === "ar";
  const reduced = useReducedMotion();
  const narrow = useIsNarrow();
  const [cat, setCat] = React.useState("All");
  const count = cat === "All" ? PROJECTS.length : PROJECTS.filter((p) => p.cat === cat).length;
  return (
    <main dir={ar ? "rtl" : "ltr"} style={{ position: "relative", overflowX: "hidden" }}>
      <WorkBackdrop reduced={reduced} count={count} />
      <div style={{ position: "relative", zIndex: 1 }}>
        <section style={{ padding: "var(--space-9) var(--page-margin) var(--space-7)" }}>
          <div style={{ maxWidth: "var(--container-max)", margin: "0 auto" }}>
            <Kicker>{ar ? "الأعمال" : "Work"}</Kicker>
            <div style={{ marginTop: "var(--space-5)" }}>
              <div className="ink-label" style={{ color: "var(--text-muted)" }}>{ar ? "أعمال مختارة" : "Selected work"}</div>
              <h1 className="ink-display" style={{ fontSize: "var(--size-display-md)", marginTop: "var(--space-2)" }}>
                <FilterCountUp to={count} format={ar ? arDigits : undefined} /> {ar ? arPieceWord(count) : "pieces"}
              </h1>
            </div>
            <div style={{ marginTop: "var(--space-7)" }}>
              <Tabs tabs={["All", { value: "Collab", label: "Collaborations" }, "Media", "Motion", "Print", "3D"]} value={cat} onChange={setCat} />
            </div>
          </div>
        </section>
        <section style={{ padding: "0 var(--page-margin) var(--space-10)" }}>
          <div style={{ maxWidth: "var(--container-max)", margin: "0 auto" }}>
            {cat === "All" ? (
              FAMILY_ORDER.map((key) => {
                const items = PROJECTS.filter((p) => p.cat === key);
                if (!items.length) return null;
                return (
                  <div key={key} style={{ marginBottom: "var(--space-9)" }}>
                    <Rule weight="hairline" tone="hairline" label={items[0].tags[0]} style={{ marginBottom: "var(--space-6)" }} />
                    <div style={{ display: "grid", gridTemplateColumns: narrow ? "1fr" : "repeat(3, 1fr)", gap: "var(--space-6) var(--space-5)" }}>
                      {items.map((p, i) => <ProjectCard key={p.title} p={p} i={i} />)}
                    </div>
                  </div>
                );
              })
            ) : (
              <div style={{ display: "grid", gridTemplateColumns: narrow ? "1fr" : "repeat(3, 1fr)", gap: "var(--space-6) var(--space-5)" }}>
                {PROJECTS.filter((p) => p.cat === cat).map((p, i) => <ProjectCard key={p.title} p={p} i={i} />)}
              </div>
            )}
          </div>
        </section>
      </div>
    </main>
  );
}

// ---------------------------------------------------------------------------
// Services page — the deeper "what can INK actually make" exploration linked
// from the nav (Home stays the quick introduction, Contact is where a project
// starts). The four-discipline section below is one immersive 2x2 quadrant
// canvas (not four cards): hovering/focusing/tapping a quadrant pulls it
// forward with its own dark field, accent color and a large scene-scale
// animation, while the other three recede quietly. Packages further down
// reuses Home's own Packages component verbatim: packagesData, PackageCard,
// Section, Reveal, useIsNarrow, useReducedMotion and useInView are all
// already global (Shell.jsx and Home.jsx load before this file), so that
// section matches the homepage's already-approved hover-expand interaction
// instead of inventing a second visual language for the same idea. Home
// itself is untouched throughout — this only reuses its component.
// ---------------------------------------------------------------------------

// Content + per-discipline identity as plain data. row/col place each
// service in the 2x2 canvas (0,0 top-left … 1,1 bottom-right); accent/glow/bg
// give each an distinct black-field mood using colors already in the token
// set (vermilion=red, cobalt=electric blue, saffron=warm orange, pine=green)
// rather than introducing new ones.
function quadrantServicesData(ar) {
  return [
    {
      id: "media", slug: "media-production", n: "01", row: 0, col: 0,
      accent: "var(--cobalt-600)", glow: "27,54,204", bg: "linear-gradient(160deg, #1d1211 0%, #100b0a 100%)",
      title: ar ? "إنتاج إعلامي" : "Media Production",
      tagline: ar ? "قصص موثقة بالشكل الصحيح." : "Stories captured properly.",
      items: ar
        ? ["تغطية الفعاليات", "تصوير فوتوغرافي", "تصوير فيديو", "تصوير المنتجات", "أفلام ترويجية", "مقابلات", "المونتاج والمعالجة النهائية"]
        : ["Event coverage", "Photography", "Videography", "Product photography", "Promotional films", "Interviews", "Editing & post-production"],
    },
    {
      id: "motion", slug: "motion-animation", n: "02", row: 0, col: 1,
      accent: "var(--vermilion-500)", glow: "226,58,24", bg: "linear-gradient(160deg, #10131f 0%, #0a0c14 100%)",
      title: ar ? "الموشن والأنيميشن" : "Motion & Animation",
      tagline: ar ? "نمنح الأفكار حركة." : "Making ideas move.",
      items: ar
        ? ["موشن جرافيك", "أنيميشن الشعارات", "محتوى متحرك", "شاشات الفعاليات", "موشن لمنصات التواصل", "المؤثرات البصرية"]
        : ["Motion graphics", "Logo animation", "Animated content", "Event screens", "Social media motion", "Visual effects"],
    },
    {
      id: "print", slug: "print-merchandise", n: "03", row: 1, col: 0,
      accent: "var(--saffron-500)", glow: "240,179,44", bg: "linear-gradient(160deg, #1d1710 0%, #110d09 100%)",
      title: ar ? "الطباعة والمنتجات" : "Print & Merchandise",
      tagline: ar ? "أشياء يمكن حملها واستخدامها وأخذها إلى المنزل." : "Things people can hold, use and take home.",
      items: ar
        ? ["حقائب قماشية", "دفاتر وقرطاسية", "ملصقات وشيتات ملصقات", "بطاقات عمل", "بوسترات ومواد الفعاليات", "لوحات PVC / فوركس ولافتات", "هدايا الفعاليات", "مطبوعات مخصصة"]
        : ["Tote bags", "Notebooks & stationery", "Stickers & sticker sheets", "Business cards", "Posters & event materials", "PVC / Forex boards & signage", "Event giveaways", "Custom printed pieces"],
    },
    {
      id: "custom3d", slug: "3d-custom", n: "04", row: 1, col: 1,
      accent: "var(--pine-500)", glow: "30,122,85", bg: "linear-gradient(160deg, #0e1a15 0%, #08110e 100%)",
      title: ar ? "التصميم الثلاثي الأبعاد والقطع المخصصة" : "3D & Custom Objects",
      tagline: ar ? "إبداع يتجاوز السطح المسطح." : "Made beyond the flat surface.",
      items: ar
        ? ["جوائز وكؤوس", "ميداليات ودروع", "دبابيس وشارات", "مجسمات مخصصة", "ستاندات وواجهات عرض", "قطع بعلامة تجارية", "قطع حسب الطلب"]
        : ["Awards & trophies", "Medals & plaques", "Pins & badges", "Custom models", "Stands & displays", "Branded objects", "Made-to-order pieces"],
    },
  ];
}

// ---------------------------------------------------------------------------
// Quadrant scenes — large, full-bleed art direction for the active state,
// not a small decorative icon. Each is built the same way: elements that
// draw/scale/slide into place once (a CSS transition keyed off `active`),
// nested inside a wrapper that then carries the slow ambient loop (a
// separate CSS `animation` on a *different* element/property than the
// entrance transition, so the two never fight over the same property).
// Ambient loops are skipped outright under prefers-reduced-motion; the
// one-time entrance transitions still play since they communicate state.
// ---------------------------------------------------------------------------

// Media Production — a cinematic viewfinder: large corner frame marks, a
// diagonal exposure sweep that passes through once, and a focus box that
// pulls into the frame and then holds a slow breathing pulse.
function QuadSceneMedia({ active, reduced, accent }) {
  const corners = [
    "M46,42 L104,42 M46,42 L46,96",
    "M354,42 L296,42 M354,42 L354,96",
    "M46,258 L104,258 M46,258 L46,204",
    "M354,258 L296,258 M354,258 L354,204",
  ];
  return (
    <svg aria-hidden="true" viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}>
      {corners.map((d, i) => (
        <path key={i} d={d} fill="none" stroke={accent} strokeWidth="2.4" opacity={active ? 0.85 : 0} style={{ transition: `opacity 550ms var(--ease-standard) ${i * 70}ms` }} />
      ))}
      <rect x="-130" y="-50" width="150" height="400" fill={accent} opacity={active ? 0.16 : 0}
        style={{ transform: active ? "translateX(600px) rotate(-12deg)" : "translateX(0) rotate(-12deg)", transformOrigin: "center", transition: "transform 1150ms var(--ease-standard) 60ms, opacity 260ms ease" }} />
      <rect x={active ? 150 : 46} y={active ? 92 : 224} width="100" height="76" fill="none" stroke={accent} strokeWidth="1.8" opacity={active ? 0.9 : 0}
        style={{ transition: "x 950ms var(--ease-standard) 140ms, y 950ms var(--ease-standard) 140ms, opacity 420ms ease 140ms" }} />
      <circle cx={active ? 200 : 96} cy={active ? 130 : 262} r={active ? 3.6 : 0} fill={accent} opacity={active ? 1 : 0}
        style={{ transition: "cx 950ms var(--ease-standard) 140ms, cy 950ms var(--ease-standard) 140ms, opacity 420ms ease 140ms" }}>
        {active && !reduced ? <animate attributeName="r" values="3.6;6.2;3.6" dur="3.2s" repeatCount="indefinite" /> : null}
      </circle>
      {[0, 1, 2, 3, 4, 5, 6].map((i) => (
        <line key={i} x1={64 + i * 46} x2={64 + i * 46} y1="278" y2="290" stroke={accent} strokeWidth="1.4" opacity={active ? 0.45 : 0} style={{ transition: `opacity 420ms var(--ease-standard) ${300 + i * 35}ms` }} />
      ))}
      {/* Camera HUD — the same timecode/REC/HD/battery readout as the
          homepage's own Media Production card, now anchored to all four
          camera corners like a real viewfinder overlay. This quadrant's
          own title/tagline/list are left-aligned and run the panel's
          full height, so 00:00:00 and HD sit behind/around that copy as
          a background HUD layer (low opacity, never competing with the
          text) rather than being pushed to the empty right side. REC and
          the battery stay on the right, clear of the text either way. */}
      <g style={{ opacity: active ? 0.85 : 0, transition: "opacity 550ms var(--ease-standard) 220ms" }}>
        <text x="326" y="62" fill="rgba(246,243,237,0.75)" fontSize="12" letterSpacing="0.12em" textAnchor="end">REC</text>
        <circle cx="340" cy="58" r="4" fill="var(--vermilion-500)">
          {active && !reduced ? <animate attributeName="opacity" values="1;0.35;1" dur="1.6s" repeatCount="indefinite" /> : null}
        </circle>
      </g>
      {/* y<~38 falls inside the band this "slice"-scaled viewBox crops off
          the top at this panel's wide aspect ratio — confirmed clipped
          invisible — so this sits at the same height as REC instead. */}
      <text x="58" y="62" fill="rgba(246,243,237,0.4)" fontSize="12" letterSpacing="0.06em" style={{ fontVariantNumeric: "tabular-nums", opacity: active ? 1 : 0, transition: "opacity 550ms var(--ease-standard) 220ms" }}>00:00:00</text>
      {/* Bottom-left bracket runs y204-258 — HD sits low in that range,
          near the bracket's bottom edge, mirroring how REC sits close to
          the top edge of the top-right bracket (42-96). */}
      <text x="58" y="242" fill="rgba(246,243,237,0.4)" fontSize="13" fontWeight="600" letterSpacing="0.05em" style={{ opacity: active ? 1 : 0, transition: "opacity 550ms var(--ease-standard) 220ms" }}>HD</text>
      <g style={{ opacity: active ? 0.8 : 0, transition: "opacity 550ms var(--ease-standard) 220ms" }}>
        <rect x="304" y="224" width="30" height="16" rx="2.5" fill="none" stroke={accent} strokeWidth="1.8" />
        <rect x="334" y="228" width="3.2" height="8" rx="1.2" fill={accent} />
        <rect x="307" y="227" width="21" height="10" rx="1.2" fill={accent} opacity="0.7" />
      </g>
    </svg>
  );
}

// Motion & Animation — three bold outline forms fly into a composition and
// settle into slow, independent drift; a trajectory line self-draws behind
// them, and one dot keeps travelling it.
function QuadSceneMotion({ active, reduced, accent }) {
  const d = "M50,240 C130,240 120,96 220,96 C290,96 276,190 350,158";
  return (
    <svg aria-hidden="true" viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}>
      <path d={d} fill="none" stroke={accent} strokeWidth="1.8" pathLength="1" strokeDasharray="1" strokeDashoffset={active ? 0 : 1} opacity={active ? 0.55 : 0} style={{ transition: "stroke-dashoffset 1000ms var(--ease-standard) 60ms, opacity 420ms ease 60ms" }} />
      {active && !reduced ? (
        <circle r="3.4" fill={accent} opacity="0.95">
          <animateMotion dur="4.2s" repeatCount="indefinite" path={d} />
        </circle>
      ) : null}
      <g style={{ animation: active && !reduced ? "ink-quad-drift-a 6200ms ease-in-out infinite" : "none" }}>
        <circle cx="50" cy="240" r={active ? 20 : 0} fill="none" stroke={accent} strokeWidth="2.2" opacity={active ? 0.9 : 0} style={{ transition: "r 750ms var(--ease-standard) 120ms, opacity 400ms ease 120ms" }} />
      </g>
      <g style={{ transformBox: "fill-box", transformOrigin: "center", animation: active && !reduced ? "ink-quad-drift-b 7400ms ease-in-out infinite" : "none" }}>
        <rect x="196" y="72" width="48" height="48" fill="none" stroke={accent} strokeWidth="2.2" opacity={active ? 0.9 : 0}
          style={{ transform: active ? "scale(1) rotate(0deg)" : "scale(0) rotate(-30deg)", transformBox: "fill-box", transformOrigin: "center", transition: "transform 750ms var(--ease-standard) 220ms, opacity 400ms ease 220ms" }} />
      </g>
      <g style={{ transformBox: "fill-box", transformOrigin: "center", animation: active && !reduced ? "ink-quad-drift-c 5400ms ease-in-out infinite" : "none" }}>
        <path d="M350,134 L376,180 L324,180 Z" fill="none" stroke={accent} strokeWidth="2.2" opacity={active ? 0.9 : 0}
          style={{ transform: active ? "scale(1)" : "scale(0)", transformBox: "fill-box", transformOrigin: "center", transition: "transform 750ms var(--ease-standard) 320ms, opacity 400ms ease 320ms" }} />
      </g>
    </svg>
  );
}

// Print & Merchandise — a small set of physical pieces (a card, a poster
// sheet, a sticker with a confined halftone patch, a tag, a tote-bag
// outline) slide in from different edges once, overlap loosely like items
// arranged on a studio table, and settle; the poster and tote then hold a
// slow independent sway. Crop marks and one registration mark on the
// poster, plus a small dot cluster on the sticker, carry the "physical
// print" read without covering the quadrant in dots.
function QuadScenePrint({ active, ar, reduced, accent }) {
  return (
    <svg aria-hidden="true" viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}>
      <g transform={ar ? "translate(400,0) scale(-1,1)" : undefined}>
        {/* business card — enters from the left */}
        <g style={{ transform: active ? "translate(96px,160px) rotate(-8deg)" : "translate(-140px,160px) rotate(-8deg)", transition: "transform 780ms var(--ease-standard) 60ms" }}>
          <rect x="-37" y="-23" width="74" height="46" rx="4" fill="none" stroke={accent} strokeWidth="1.6" opacity={active ? 0.85 : 0} style={{ transition: "opacity 380ms ease 60ms" }} />
          <line x1="-22" y1="4" x2="18" y2="4" stroke={accent} strokeWidth="1.1" opacity={active ? 0.5 : 0} style={{ transition: "opacity 380ms ease 200ms" }} />
        </g>

        {/* printed sheet / small poster — enters from the top, then a slow sway */}
        <g style={{ animation: active && !reduced ? "ink-quad-drift-c 9200ms ease-in-out infinite" : "none" }}>
          <g style={{ transform: active ? "translate(205px,140px) rotate(4deg)" : "translate(205px,-140px) rotate(4deg)", transition: "transform 820ms var(--ease-standard) 160ms" }}>
            <rect x="-30" y="-42" width="60" height="84" rx="3" fill="none" stroke={accent} strokeWidth="1.6" opacity={active ? 0.9 : 0} style={{ transition: "opacity 400ms ease 160ms" }} />
            <line x1="-18" y1="-16" x2="18" y2="-16" stroke={accent} strokeWidth="1" opacity={active ? 0.5 : 0} style={{ transition: "opacity 400ms ease 320ms" }} />
            <line x1="-18" y1="-4" x2="10" y2="-4" stroke={accent} strokeWidth="1" opacity={active ? 0.5 : 0} style={{ transition: "opacity 400ms ease 360ms" }} />
            {[[-30, -42, -1, -1], [30, -42, 1, -1], [-30, 42, -1, 1], [30, 42, 1, 1]].map(([cx, cy, dx, dy], i) => (
              <path key={i} d={`M${cx},${cy} l${dx * 9},0 M${cx},${cy} l0,${dy * 9}`} stroke={accent} strokeWidth="1" opacity={active ? 0.55 : 0} style={{ transition: `opacity 400ms ease ${420 + i * 40}ms` }} />
            ))}
            <g opacity={active ? 0.6 : 0} style={{ transition: "opacity 400ms ease 500ms" }}>
              <circle cx="30" cy="-42" r="7" fill="none" stroke={accent} strokeWidth="1" />
              <line x1="23" y1="-42" x2="37" y2="-42" stroke={accent} strokeWidth="0.8" />
              <line x1="30" y1="-49" x2="30" y2="-35" stroke={accent} strokeWidth="0.8" />
            </g>
          </g>
        </g>

        {/* sticker — enters from the bottom, small confined dot cluster only */}
        <g style={{ transform: active ? "translate(276px,172px) rotate(-6deg)" : "translate(276px,340px) rotate(-6deg)", transition: "transform 780ms var(--ease-standard) 260ms" }}>
          <rect x="-27" y="-27" width="54" height="54" rx="10" fill="none" stroke={accent} strokeWidth="1.6" opacity={active ? 0.88 : 0} style={{ transition: "opacity 380ms ease 260ms" }} />
          {[-14, -2, 10].map((dx, i) => [-14, -2, 10].map((dy, j) => (
            <circle key={`${i}-${j}`} cx={dx} cy={dy} r="1.6" fill={accent} opacity={active ? 0.5 : 0} style={{ transition: `opacity 380ms ease ${380 + (i * 3 + j) * 25}ms` }} />
          )))}
        </g>

        {/* tag — enters from the right */}
        <g style={{ transform: active ? "translate(334px,108px) rotate(14deg)" : "translate(470px,108px) rotate(14deg)", transition: "transform 780ms var(--ease-standard) 340ms" }}>
          <path d="M-20,-14 L14,-14 L22,0 L14,14 L-20,14 Z" fill="none" stroke={accent} strokeWidth="1.6" opacity={active ? 0.85 : 0} style={{ transition: "opacity 380ms ease 340ms" }} />
          <circle cx="-10" cy="0" r="3" fill="none" stroke={accent} strokeWidth="1.2" opacity={active ? 0.8 : 0} style={{ transition: "opacity 380ms ease 420ms" }} />
        </g>

        {/* tote-bag outline — enters from the top, then a slow sway */}
        <g style={{ animation: active && !reduced ? "ink-quad-drift-a 8600ms ease-in-out infinite" : "none" }}>
          <g style={{ transform: active ? "translate(122px,212px) rotate(-3deg)" : "translate(122px,-60px) rotate(-3deg)", transition: "transform 840ms var(--ease-standard) 120ms" }}>
            <path d="M-24,-14 L-24,20 Q-24,26 -18,26 L18,26 Q24,26 24,20 L24,-14 Z" fill="none" stroke={accent} strokeWidth="1.6" opacity={active ? 0.85 : 0} style={{ transition: "opacity 400ms ease 120ms" }} />
            <path d="M-13,-14 V-24 Q-13,-33 0,-33 Q13,-33 13,-24 V-14" fill="none" stroke={accent} strokeWidth="1.6" opacity={active ? 0.85 : 0} style={{ transition: "opacity 400ms ease 120ms" }} />
          </g>
        </g>
      </g>
    </svg>
  );
}

// 3D & Custom Objects — a converging perspective grid fades in behind a
// faceted wireframe solid (not a plain cube) that unflattens from a
// squashed silhouette into full depth, then holds a slow restrained sway.
function QuadScene3D({ active, reduced, accent }) {
  const cx = 210, cy = 150;
  const depthLines = [0, 1, 2, 3, 4].map((i) => {
    const t = i / 4;
    const y = 40 + t * 220;
    const spread = 40 + t * 260;
    return <line key={i} x1={cx - spread / 2} x2={cx + spread / 2} y1={y} y2={y} stroke={accent} strokeWidth="1" opacity={active ? 0.14 + t * 0.1 : 0} style={{ transition: `opacity 500ms var(--ease-standard) ${i * 60}ms` }} />;
  });
  const converge = [-160, -80, 0, 80, 160].map((x, i) => (
    <line key={i} x1={cx} y1="40" x2={cx + x} y2="260" stroke={accent} strokeWidth="1" opacity={active ? 0.16 : 0} style={{ transition: `opacity 500ms var(--ease-standard) ${i * 50}ms` }} />
  ));
  const s = 46;
  const top = `M${cx},${cy - s} L${cx + s * 0.86},${cy - s * 0.5} L${cx},${cy} L${cx - s * 0.86},${cy - s * 0.5} Z`;
  const left = `M${cx - s * 0.86},${cy - s * 0.5} L${cx},${cy} L${cx},${cy + s} L${cx - s * 0.86},${cy + s * 0.5} Z`;
  const right = `M${cx + s * 0.86},${cy - s * 0.5} L${cx},${cy} L${cx},${cy + s} L${cx + s * 0.86},${cy + s * 0.5} Z`;
  return (
    <svg aria-hidden="true" viewBox="0 0 420 300" preserveAspectRatio="xMidYMid slice" style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}>
      {converge}
      {depthLines}
      <g style={{ transformBox: "fill-box", transformOrigin: "center", transform: active ? "scaleY(1)" : "scaleY(0.4)", transition: "transform 680ms var(--ease-standard) 100ms" }}>
        <g style={{ transformBox: "fill-box", transformOrigin: "center", animation: active && !reduced ? "ink-quad-sway 8200ms ease-in-out infinite" : "none" }}>
          {[top, left, right].map((d, i) => (
            <path key={i} d={d} fill="none" stroke={accent} strokeWidth="1.6" pathLength="1" strokeDasharray="1" strokeDashoffset={active ? 0 : 1} opacity={active ? 0.9 : 0} style={{ transition: `stroke-dashoffset 700ms var(--ease-standard) ${180 + i * 100}ms, opacity 480ms var(--ease-standard) ${180 + i * 100}ms` }} />
          ))}
        </g>
      </g>
    </svg>
  );
}

const QUADRANT_SCENES = { media: QuadSceneMedia, motion: QuadSceneMotion, print: QuadScenePrint, custom3d: QuadScene3D };

function ServiceQuadrant({ svc, ar, reduced, corner, active, onEnter, onFocus, onBlur, onToggle, setPage }) {
  const Scene = QUADRANT_SCENES[svc.id];
  const align = corner === "tr" || corner === "br" ? "flex-end" : "flex-start";
  return (
    <div
      id={svc.slug}
      className="ink-quad"
      role="group"
      tabIndex={0}
      aria-expanded={active}
      aria-label={svc.title}
      onMouseEnter={onEnter}
      onFocus={onFocus}
      onBlur={onBlur}
      onClick={onToggle}
      onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onToggle(); } }}
      style={{
        position: "relative", overflow: "hidden", cursor: "pointer", height: "100%", width: "100%",
        padding: "var(--space-6)",
        background: active ? svc.bg : "transparent",
        transition: "background 580ms var(--ease-standard)",
      }}
    >
      <span aria-hidden="true" style={{
        position: "absolute", inset: 0, zIndex: 0, opacity: active ? 1 : 0, transition: "opacity 520ms var(--ease-standard)",
        background: `radial-gradient(closest-side at ${corner === "tl" ? "18% 20%" : corner === "tr" ? "82% 20%" : corner === "bl" ? "18% 80%" : "82% 80%"}, rgba(${svc.glow},0.30), rgba(${svc.glow},0) 68%)`,
      }} />
      <span aria-hidden="true" style={{ position: "absolute", inset: 0, zIndex: 0 }}>
        <Scene active={active} reduced={reduced} accent={svc.accent} ar={ar} />
      </span>
      <div style={{ position: "relative", zIndex: 1, display: "flex", flexDirection: "column", justifyContent: "space-between", alignItems: align, height: "100%", width: "100%" }}>
        <div style={{ textAlign: align === "flex-end" ? "end" : "start" }}>
          <div className="ink-label" style={{ color: active ? svc.accent : "var(--vermilion-500)", transition: "color 420ms ease" }}>{svc.n}</div>
          <div style={{
            fontFamily: ar ? "var(--font-arabic)" : "var(--font-display)", fontWeight: ar ? 800 : "var(--weight-title)", fontStretch: ar ? "normal" : "var(--stretch-title)", maxWidth: "17ch",
            fontSize: active ? "clamp(22px, 2.4vw, 34px)" : "clamp(18px, 1.9vw, 25px)",
            color: active ? "var(--paper-050)" : "var(--ink-900)", lineHeight: 1.15,
            marginTop: "var(--space-2)", transition: "font-size 520ms var(--ease-standard), color 460ms ease",
          }}>{svc.title}</div>
          <p style={{ fontSize: "var(--size-body-sm)", color: active ? "rgba(246,243,237,0.75)" : "var(--text-muted)", marginTop: "var(--space-2)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)", transition: "color 460ms ease" }}>{svc.tagline}</p>
        </div>
        <div style={{ display: "grid", gridTemplateRows: active ? "1fr" : "0fr", transition: "grid-template-rows 560ms var(--ease-standard)", width: "100%" }}>
          <div style={{ overflow: "hidden" }}>
            <ul style={{ listStyle: "none", padding: 0, margin: "var(--space-4) 0 0", display: "flex", flexDirection: "column", gap: "var(--space-2)", alignItems: align }}>
              {svc.items.map((it, i) => (
                <li key={it} style={{
                  display: "flex", alignItems: "baseline", gap: "var(--space-3)", fontSize: "var(--size-body-sm)", color: "rgba(246,243,237,0.82)",
                  fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)",
                  opacity: active ? 1 : 0, transform: active ? "translateY(0)" : "translateY(8px)",
                  transition: `opacity 420ms var(--ease-standard) ${140 + i * 45}ms, transform 420ms var(--ease-standard) ${140 + i * 45}ms`,
                }}>
                  <span aria-hidden="true" style={{ color: svc.accent }}>—</span>
                  <span>{it}</span>
                </li>
              ))}
            </ul>
            <button
              onClick={(e) => { e.stopPropagation(); setPage("Contact", { service: svc.id }); }}
              style={{
                marginTop: "var(--space-4)", background: "none", border: `1.5px solid ${svc.accent}`, borderRadius: "var(--radius-pill)",
                padding: "6px 16px", color: "var(--paper-050)", font: "inherit", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)",
                fontSize: "var(--size-body-sm)", cursor: "pointer",
                opacity: active ? 1 : 0, transform: active ? "translateY(0)" : "translateY(8px)",
                transition: `opacity 420ms var(--ease-standard) ${140 + svc.items.length * 45}ms, transform 420ms var(--ease-standard) ${140 + svc.items.length * 45}ms, border-color 300ms ease`,
              }}
            >
              {ar ? "ابدأ مشروعًا ↗" : "Start a project ↗"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

// Mobile/tablet equivalent of the same identity: a compact 2x2 tile grid for
// selection (numeral + title only, no hover available) with the exact same
// rich scene/tagline/list revealing in a full-width panel directly below —
// avoids clipping a tall detail list inside a small fixed-aspect tile.
function ServiceQuadrantTile({ svc, ar, active, onToggle }) {
  return (
    <button
      id={svc.slug}
      onClick={onToggle}
      aria-expanded={active}
      className="ink-quad"
      style={{
        position: "relative", width: "100%", height: "100%", padding: "var(--space-5) var(--space-4)",
        background: "none", border: "none", cursor: "pointer", textAlign: "start",
        display: "flex", flexDirection: "column",
        // English titles are short enough to stay on one or two lines at
        // this tile size, so bottom-anchoring them (weight resting near
        // the accent border) is a deliberate look worth keeping exactly
        // as-is. Arabic titles vary from one line ("إنتاج إعلامي") to
        // three ("التصميم الثلاثي الأبعاد والقطع المخصصة") in this same
        // narrow cell, and bottom-anchoring pushed the numeral above each
        // title to a DIFFERENT height depending on how many lines that
        // title happened to wrap to — reading as unbalanced against the
        // other three cells rather than one consistent grid. Centering
        // is Arabic-only, applied to all four tiles together, so the
        // fix stays scoped to the reported language and every cell moves
        // in step rather than singling one out.
        justifyContent: ar ? "center" : "flex-end",
        borderBottom: active ? `3px solid ${svc.accent}` : "3px solid transparent",
        transition: "border-color 380ms var(--ease-standard)",
      }}
    >
      <div className="ink-label" style={{ color: active ? svc.accent : "var(--vermilion-500)", transition: "color 380ms ease" }}>{svc.n}</div>
      <div style={{
        fontFamily: ar ? "var(--font-arabic)" : "var(--font-display)", fontWeight: ar ? 800 : "var(--weight-title)", fontStretch: ar ? "normal" : "var(--stretch-title)", fontSize: "clamp(15px, 4.2vw, 18px)", color: "var(--ink-900)", marginTop: "var(--space-1)",
        // 1.2 reads fine for English's usual one/two lines, but the
        // Arabic 3D/Custom Objects title wraps to three at this weight
        // and cell width, and 1.2 was tight enough there to read as
        // crowded/near-overlapping — see the justifyContent comment
        // above for why the fix is Arabic-only and applies to all four.
        lineHeight: ar ? 1.4 : 1.2,
      }}>{svc.title}</div>
    </button>
  );
}

function ServiceQuadrantPanel({ svc, ar, reduced, setPage }) {
  const Scene = QUADRANT_SCENES[svc.id];
  return (
    <div style={{ position: "relative", overflow: "hidden", borderRadius: "var(--radius-lg)", padding: "var(--space-6)", background: svc.bg, marginTop: "var(--space-5)" }}>
      <span aria-hidden="true" style={{ position: "absolute", inset: 0, zIndex: 0, background: `radial-gradient(closest-side at 30% 20%, rgba(${svc.glow},0.30), rgba(${svc.glow},0) 68%)` }} />
      <span aria-hidden="true" style={{ position: "absolute", inset: 0, zIndex: 0 }}>
        <Scene active reduced={reduced} accent={svc.accent} ar={ar} />
      </span>
      <div style={{ position: "relative", zIndex: 1 }}>
        <p style={{ fontSize: "var(--size-body-sm)", color: "rgba(246,243,237,0.8)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)" }}>{svc.tagline}</p>
        <ul style={{ listStyle: "none", padding: 0, margin: "var(--space-4) 0 0", display: "flex", flexDirection: "column", gap: "var(--space-2)" }}>
          {svc.items.map((it) => (
            <li key={it} style={{ display: "flex", alignItems: "baseline", gap: "var(--space-3)", fontSize: "var(--size-body-sm)", color: "rgba(246,243,237,0.9)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)" }}>
              <span aria-hidden="true" style={{ color: svc.accent }}>—</span>
              <span>{it}</span>
            </li>
          ))}
        </ul>
        <button
          onClick={() => setPage("Contact", { service: svc.id })}
          style={{
            marginTop: "var(--space-5)", background: "none", border: `1.5px solid ${svc.accent}`, borderRadius: "var(--radius-pill)",
            padding: "8px 18px", color: "var(--paper-050)", font: "inherit", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)",
            fontSize: "var(--size-body-sm)", cursor: "pointer",
          }}
        >
          {ar ? "ابدأ مشروعًا ↗" : "Start a project ↗"}
        </button>
      </div>
    </div>
  );
}

// The four-discipline section — one 2x2 canvas divided by a single hairline
// cross rather than four bordered cards. Desktop: hover/focus on a quadrant
// grows its row+column via CSS Grid track sizing (the other three compress
// in response) and turns it into a dark, accented, animated scene; a short
// editorial statement sits at the canvas center, fading back once a
// quadrant is active so it never competes or blocks interaction (it is
// pointer-events:none throughout). Mobile: the same four disciplines as a
// compact tappable tile grid with the identical rich content expanding in
// a panel underneath.
// The exact same glyph WhatWeMake's own card already uses for each
// discipline (Home.jsx) — reused so the mobile ambient motif is a familiar
// "existing visual motif from that service card," never a new symbol.
const SERVICE_AMBIENT_ICON = { media: "camera", motion: "clapperboard", print: "printer", custom3d: "box" };

function ServiceQuadrantGrid({ ar, setPage, target }) {
  const narrow = useIsNarrow();
  const reduced = useReducedMotion();
  const [activeId, setActiveId] = React.useState(null);
  const SERVICES = quadrantServicesData(ar);
  const activeSvc = SERVICES.find((s) => s.id === activeId) || null;
  const corners = ["tl", "tr", "bl", "br"];
  const sectionRef = React.useRef(null);

  // Landing here from a Home service card or a footer service link: open
  // that exact quadrant and scroll it into view, the same as if the user
  // had hovered/tapped it directly.
  React.useEffect(() => {
    if (!target || !SERVICES.some((s) => s.id === target)) return;
    setActiveId(target);
    const raf = requestAnimationFrame(() => {
      sectionRef.current && sectionRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
    });
    return () => cancelAnimationFrame(raf);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [target]);

  const colTemplate = !activeSvc ? "1fr 1fr" : activeSvc.col === 0 ? "1.32fr 0.68fr" : "0.68fr 1.32fr";
  const rowTemplate = !activeSvc ? "1fr 1fr" : activeSvc.row === 0 ? "1.32fr 0.68fr" : "0.68fr 1.32fr";

  return (
    <section ref={sectionRef} style={{ position: "relative", padding: "var(--space-8) var(--page-margin) var(--space-9)" }}>
      <style>{`
        .ink-quad:focus { outline: none; }
        .ink-quad:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; z-index: 2; position: relative; }
        @keyframes ink-quad-drift-a { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }
        @keyframes ink-quad-drift-b { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(7px) rotate(5deg); } }
        @keyframes ink-quad-drift-c { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
        @keyframes ink-quad-sway { 0%, 100% { transform: rotate(-3deg); } 50% { transform: rotate(3deg); } }
      `}</style>
      {/* The section's own ambient environment, reacting to the selected
          service rather than only its detail panel (which already has its
          own rich dark/glow treatment — see ServiceQuadrantPanel). Reuses
          that exact same service's `glow` RGB triplet already authored in
          quadrantServicesData, as a soft, restrained radial wash behind
          the whole selector + panel rather than a flat background swap —
          low enough opacity that the kicker/heading text above it and the
          panel's own darker card both stay perfectly readable throughout
          the 420ms crossfade between services.
          On mobile the eyebrow ("SERVICES") and the restored "One idea,
          four ways..." heading sit well above the radial's own center
          (62% down the section), so a lone radial barely reached them —
          the whole point of this task's "entire feature region" ask. A
          second, full-bleed top-to-bottom tint layered underneath it
          brings that same service's atmosphere all the way up to the very
          top of the section (and down past the panel), while the radial
          on top still gives the grid/panel area its own extra emphasis —
          together the whole region visibly shifts, not just its lower
          half. Desktop keeps the original single radial exactly as before
          (its kicker/heading sit inline with the grid, not stacked above
          it, so the same reach problem doesn't exist there).
          This round doubled the mobile opacities outright (the previous
          pass's version read as "barely perceptible" per this task's own
          complaint) and added two large, service-colored ambient blobs
          plus that service's own line icon (the same glyph WhatWeMake's
          card already uses for it — camera/clapperboard/printer/box, not
          a new symbol) rendered huge and faint near the top and bottom of
          the region, so the transformation reads as the whole area's
          atmosphere changing, not just a background tint. */}
      <div
        aria-hidden="true"
        style={{
          position: "absolute", inset: 0, zIndex: 0, pointerEvents: "none", overflow: "hidden",
          background: activeSvc
            ? narrow
              ? `linear-gradient(180deg, rgba(${activeSvc.glow},0.22) 0%, rgba(${activeSvc.glow},0.10) 22%, rgba(${activeSvc.glow},0.10) 55%, rgba(${activeSvc.glow},0.18) 100%), radial-gradient(ellipse 78% 60% at 50% 62%, rgba(${activeSvc.glow},0.30), rgba(${activeSvc.glow},0) 74%)`
              : `radial-gradient(ellipse 70% 55% at 50% 62%, rgba(${activeSvc.glow},0.16), rgba(${activeSvc.glow},0) 72%)`
            : "transparent",
          opacity: activeSvc ? 1 : 0,
          transition: "opacity 420ms ease, background 420ms ease",
        }}
      />
      {narrow && activeSvc ? (
        <div aria-hidden="true" style={{ position: "absolute", inset: 0, zIndex: 0, pointerEvents: "none", overflow: "hidden", opacity: 1, transition: "opacity 420ms ease" }}>
          <div style={{ position: "absolute", left: "-16%", top: "-6%", width: "68%", height: "26%", borderRadius: "50%", background: `radial-gradient(circle, rgba(${activeSvc.glow},0.34), rgba(${activeSvc.glow},0) 70%)`, transition: "background 420ms ease" }} />
          <div style={{ position: "absolute", right: "-18%", bottom: "6%", width: "78%", height: "32%", borderRadius: "50%", background: `radial-gradient(circle, rgba(${activeSvc.glow},0.28), rgba(${activeSvc.glow},0) 72%)`, transition: "background 420ms ease" }} />
          <Icon name={SERVICE_AMBIENT_ICON[activeSvc.id] || "sparkles"} size={130} color={activeSvc.accent} style={{ position: "absolute", left: "-6%", top: "2%", opacity: 0.16, transform: "rotate(-8deg)" }} />
          <Icon name={SERVICE_AMBIENT_ICON[activeSvc.id] || "sparkles"} size={100} color={activeSvc.accent} style={{ position: "absolute", right: "-4%", bottom: "10%", opacity: 0.14, transform: "rotate(10deg)" }} />
        </div>
      ) : null}
      <div style={{ position: "relative", zIndex: 1, maxWidth: "var(--container-max)", margin: "0 auto" }}>
        <Reveal><Kicker>{ar ? "الخدمات" : "Services"}</Kicker></Reveal>

        {/* On mobile the grid's own centered "One idea. Four ways..."
            overlay (below) never renders — it's sized/positioned for
            resting inside the desktop grid's empty center cross and reads
            as a tiny illegible label at phone width — so the page opened
            with nothing but the bare "Services" kicker. Restoring the
            phrase as a real heading here, at full mobile display size,
            gives the page the same intentional opening beat every other
            page has before its content starts. */}
        {narrow ? (
          <Reveal delay={60} as="h1" style={{
            marginTop: "var(--space-5)",
            fontFamily: ar ? "var(--font-arabic-display)" : "var(--font-display)",
            fontWeight: ar ? 800 : "var(--weight-display)",
            fontStretch: ar ? "normal" : "var(--stretch-display)",
            fontSize: ar ? "clamp(30px, 9vw, 44px)" : "clamp(30px, 9.5vw, 46px)",
            lineHeight: ar ? 1.15 : 1.05,
            letterSpacing: ar ? 0 : "var(--tracking-display)",
            textTransform: ar ? "none" : "uppercase",
            maxWidth: "16ch",
          }}>
            {ar ? <>فكرة واحدة،<br />أربع طرق لتحقيقها.</> : <>One idea,<br />four ways to make it.</>}
          </Reveal>
        ) : null}

        <div
          onMouseLeave={!narrow ? () => setActiveId(null) : undefined}
          style={{
            position: "relative", marginTop: "var(--space-6)",
            display: "grid",
            gridTemplateColumns: narrow ? "1fr 1fr" : colTemplate,
            gridTemplateRows: narrow ? "auto" : rowTemplate,
            aspectRatio: narrow ? "2 / 1.1" : "16 / 9",
            border: "var(--rule-weight-strong) solid var(--border-strong)",
            borderRadius: "var(--radius-lg)",
            overflow: "hidden",
            transition: reduced || narrow ? "none" : "grid-template-columns 680ms var(--ease-standard), grid-template-rows 680ms var(--ease-standard)",
          }}
        >
          {SERVICES.map((svc, i) => (
            <div key={svc.id} style={{
              position: "relative", overflow: "hidden",
              borderInlineEnd: i % 2 === 0 ? "var(--rule-weight) solid var(--border-hairline)" : "none",
              borderBottom: i < 2 ? "var(--rule-weight) solid var(--border-hairline)" : "none",
            }}>
              {narrow ? (
                <ServiceQuadrantTile
                  svc={svc} ar={ar}
                  active={activeId === svc.id}
                  onToggle={() => setActiveId((cur) => (cur === svc.id ? null : svc.id))}
                />
              ) : (
                <ServiceQuadrant
                  svc={svc} ar={ar} reduced={reduced} corner={corners[i]}
                  active={activeId === svc.id}
                  onEnter={() => setActiveId(svc.id)}
                  onFocus={() => setActiveId(svc.id)}
                  onBlur={() => setActiveId((cur) => (cur === svc.id ? null : cur))}
                  onToggle={() => setActiveId((cur) => (cur === svc.id ? null : svc.id))}
                  setPage={setPage}
                />
              )}
            </div>
          ))}

          {/* Central editorial statement — decorative only, never intercepts
              a hover/click, and recedes once a quadrant takes over so it
              never competes with the active scene. */}
          {!narrow ? (
            <div aria-hidden="true" style={{
              position: "absolute", left: "50%", top: "50%", transform: "translate(-50%, -50%)",
              zIndex: 3, pointerEvents: "none", textAlign: "center",
              opacity: activeId ? 0.05 : 1, transition: "opacity 520ms var(--ease-standard)",
              background: "var(--surface-page)", padding: "var(--space-3) var(--space-5)", borderRadius: "var(--radius-sm)",
            }}>
              <div style={{
                fontFamily: ar ? "var(--font-arabic-display)" : "var(--font-display)", fontWeight: ar ? 800 : "var(--weight-title)", fontStretch: ar ? "normal" : "var(--stretch-title)",
                fontSize: "clamp(14px, 1.5vw, 20px)", lineHeight: 1.3,
                textTransform: ar ? "none" : "uppercase", letterSpacing: ar ? 0 : "var(--tracking-heading)",
                color: "var(--ink-900)",
              }}>
                {ar ? <>فكرة واحدة.<br />أربع طرق<br />لتحقيقها.</> : <>One idea.<br />Four ways<br />to make it real.</>}
              </div>
            </div>
          ) : null}
        </div>

        {narrow ? (
          <div style={{ display: "grid", gridTemplateRows: activeSvc ? "1fr" : "0fr", transition: "grid-template-rows 500ms var(--ease-standard)" }}>
            <div style={{ overflow: "hidden" }}>
              {activeSvc ? <ServiceQuadrantPanel svc={activeSvc} ar={ar} reduced={reduced} setPage={setPage} /> : null}
            </div>
          </div>
        ) : null}

        <Reveal delay={320} style={{ marginTop: "var(--space-6)", textAlign: "end" }}>
          <span className="ink-label" style={{ color: "var(--text-muted)" }}>
            {ar ? "شيء مختلف؟ " : "Something different? "}
            <button
              onClick={() => setPage("Contact")}
              className="ink-label"
              style={{ background: "none", border: "none", padding: 0, margin: 0, font: "inherit", color: "var(--vermilion-500)", textDecoration: "underline", textUnderlineOffset: 3, cursor: "pointer" }}
            >
              {ar ? "تواصل معنا." : "Ask us."}
            </button>
          </span>
        </Reveal>
      </div>
    </section>
  );
}

// Quieter refinement of Home's own "How we work" idea, scoped to Services —
// same four steps, same concept, but a plain horizontal editorial rail
// instead of the homepage's stat row. A thin red accent line fills in once
// the rail scrolls into view; skipped under prefers-reduced-motion.
// ---------------------------------------------------------------------------
// "Build the Experience" — the Services-page treatment of the four packages.
// Home's own Packages/PackageCard section (four hover-expand cards) stays the
// homepage's quick introduction; this is a completely different interactive
// piece for the same four tiers, reusing only the CONTENT (packagesData —
// already global via Home.jsx's script-load order) so names/copy/includes
// stay consistent with what's already established, never Home's card layout
// or components. The four tiers are framed as one project that visibly
// builds — each stage's layer stays on the canvas when a later stage is
// selected, and gracefully exits again when stepping back — rather than
// four separate panels.
// ---------------------------------------------------------------------------

// The abstract "project" composition. Four cumulative layers keyed purely by
// `level` (0-3): every shape's own visibility is `level >= N`, so stepping
// forward always leaves earlier layers untouched while later ones fade in,
// and stepping back simply reverses the same opacity/transform transitions —
// no separate enter/exit logic needed. Ambient motion (REC pulse, the
// traveling dot) is skipped outright under prefers-reduced-motion; the
// one-time entrance transitions still play since they communicate state.
function BuildCanvas({ level, reduced, ar }) {
  const NEUTRAL = "rgba(246,243,237,0.6)";
  const MERCH = "var(--saffron-500)";
  const EVENT = "var(--cobalt-600)";
  const MOTION = "var(--vermilion-500)";
  const THREED = "var(--pine-500)";
  const at = (n) => level >= n;
  const motionD = "M110,270 C170,230 190,150 246,150 C300,150 330,90 400,56";
  return (
    <svg aria-hidden="true" viewBox="0 0 480 380" preserveAspectRatio="xMidYMid slice" style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}>
      <g transform={ar ? "translate(480,0) scale(-1,1)" : undefined}>
        {/* subtle background grid — the "construction table" */}
        {[0, 1, 2, 3, 4, 5, 6].map((i) => (
          <line key={`v${i}`} x1={40 + i * 66} x2={40 + i * 66} y1="20" y2="360" stroke="rgba(246,243,237,0.06)" strokeWidth="1" />
        ))}
        {[0, 1, 2, 3, 4].map((i) => (
          <line key={`h${i}`} x1="20" x2="460" y1={40 + i * 80} y2={40 + i * 80} stroke="rgba(246,243,237,0.06)" strokeWidth="1" />
        ))}

        {/* Layer 3 bloom — sits behind everything, richer multi-accent glow */}
        <circle cx="240" cy="190" r="170" fill="url(#ink-build-bloom)" opacity={at(3) ? 1 : 0} style={{ transition: "opacity 700ms var(--ease-standard)" }} />
        <defs>
          <radialGradient id="ink-build-bloom" cx="50%" cy="50%" r="60%">
            <stop offset="0%" stopColor="rgba(226,58,24,0.16)" />
            <stop offset="55%" stopColor="rgba(27,54,204,0.10)" />
            <stop offset="100%" stopColor="rgba(226,58,24,0)" />
          </radialGradient>
        </defs>

        {/* Layer 2 — event framing brackets around the whole canvas */}
        {[
          "M28,28 L64,28 M28,28 L28,64",
          "M452,28 L416,28 M452,28 L452,64",
          "M28,352 L64,352 M28,352 L28,316",
          "M452,352 L416,352 M452,352 L452,316",
        ].map((d, i) => (
          <path key={i} d={d} fill="none" stroke={EVENT} strokeWidth="2" opacity={at(2) ? 0.75 : 0} style={{ transition: `opacity 550ms var(--ease-standard) ${i * 60}ms` }} />
        ))}
        <g opacity={at(2) ? 0.85 : 0} style={{ transition: "opacity 450ms ease 200ms" }}>
          <circle cx="46" cy="336" r="4" fill={EVENT}>
            {at(2) && !reduced ? <animate attributeName="opacity" values="0.9;0.35;0.9" dur="2.4s" repeatCount="indefinite" /> : null}
          </circle>
          {/* the outer scene mirrors position for RTL via scale(-1,1); pivot
              this text back upright in place so the label stays legible
              rather than rendering its glyphs backwards */}
          <g transform={ar ? "translate(58,0) scale(-1,1) translate(-58,0)" : undefined}>
            <text x="58" y="340" fontSize="10" letterSpacing="1.5" fill="rgba(246,243,237,0.75)">REC</text>
          </g>
        </g>

        {/* Layer 0 — foundation: printed collateral. A key-visual frame plus
            a small offset stack of card/brochure silhouettes and a sticker
            sheet — reads as printed matter, not an unexplained glyph.
            Scales up a hair and settles once Full Experience locks in. */}
        <g style={{ transform: at(3) ? "translate(240px,152px) scale(1.035)" : "translate(240px,150px) scale(1)", transformOrigin: "center", transition: "transform 620ms var(--ease-standard) 260ms" }}>
          <rect x="-70" y="-90" width="140" height="180" rx="6" fill="none" stroke={NEUTRAL} strokeWidth="1.6" opacity={at(0) ? 0.85 : 0} style={{ transition: "opacity 600ms var(--ease-standard)" }} />
          {/* a small, varied assortment of sticker silhouettes so the sheet
              reads as printed stickers, not a blank rectangle */}
          <path d="M-35,-58 L-26,-42 L-44,-42 Z" fill="none" stroke={NEUTRAL} strokeWidth="1.2" strokeLinejoin="round" opacity={at(0) ? 0.6 : 0} style={{ transition: "opacity 600ms var(--ease-standard) 140ms" }} />
          <path d="M32,-51 C34,-44 34,-44 41,-42 C34,-40 34,-40 32,-33 C30,-40 30,-40 23,-42 C30,-44 30,-44 32,-51 Z" fill="none" stroke={NEUTRAL} strokeWidth="1.2" opacity={at(0) ? 0.6 : 0} style={{ transition: "opacity 600ms var(--ease-standard) 200ms" }} />
          <path d="M-42,4 C-45,-3 -36,-8 -28,-4 C-20,0 -19,10 -26,16 C-33,22 -44,18 -45,10 C-46,7 -45,5 -42,4 Z" fill="none" stroke={NEUTRAL} strokeWidth="1.2" opacity={at(0) ? 0.6 : 0} style={{ transition: "opacity 600ms var(--ease-standard) 260ms" }} />
          <path d="M32,7 L40,15 L32,23 L24,15 Z" fill="none" stroke={NEUTRAL} strokeWidth="1.2" opacity={at(0) ? 0.6 : 0} style={{ transition: "opacity 600ms var(--ease-standard) 320ms" }} />
          <path d="M0,50 C5,56 5,64 0,69 C-5,64 -5,56 0,50 Z" fill="none" stroke={NEUTRAL} strokeWidth="1.2" opacity={at(0) ? 0.6 : 0} style={{ transition: "opacity 600ms var(--ease-standard) 380ms" }} />
        </g>
        <g style={{ transform: "translate(88px,86px) rotate(-7deg)", opacity: at(0) ? 0.85 : 0, transition: "opacity 600ms var(--ease-standard) 80ms" }}>
          <rect x="-22" y="-15" width="44" height="30" rx="3" fill="none" stroke={NEUTRAL} strokeWidth="1.4" />
        </g>
        <g style={{ transform: "translate(100px,96px) rotate(5deg)", opacity: at(0) ? 0.85 : 0, transition: "opacity 600ms var(--ease-standard) 150ms" }}>
          <rect x="-22" y="-15" width="44" height="30" rx="3" fill="#141019" stroke={NEUTRAL} strokeWidth="1.4" />
          <line x1="-12" y1="0" x2="12" y2="0" stroke={NEUTRAL} strokeWidth="1" />
        </g>

        {/* Layer 1 — merch pieces sliding in from the left/top/right, plus
            the custom/3D discipline arriving here too: a small dimensional
            object presented on its own stand, not a floating cube. */}
        <g style={{ transform: at(1) ? "translate(78px,300px) rotate(-4deg)" : "translate(-100px,300px) rotate(-4deg)", transition: "transform 760ms var(--ease-standard) 60ms" }}>
          <path d="M-24,-14 L-24,20 Q-24,26 -18,26 L18,26 Q24,26 24,20 L24,-14 Z" fill="none" stroke={MERCH} strokeWidth="1.6" opacity={at(1) ? 0.9 : 0} style={{ transition: "opacity 420ms ease 60ms" }} />
          <path d="M-13,-14 V-24 Q-13,-33 0,-33 Q13,-33 13,-24 V-14" fill="none" stroke={MERCH} strokeWidth="1.6" opacity={at(1) ? 0.9 : 0} style={{ transition: "opacity 420ms ease 60ms" }} />
        </g>
        <g style={{ transform: at(1) ? "translate(392px,84px) rotate(6deg)" : "translate(392px,-80px) rotate(6deg)", transition: "transform 760ms var(--ease-standard) 180ms" }}>
          <rect x="-24" y="-24" width="48" height="48" rx="9" fill="none" stroke={MERCH} strokeWidth="1.6" opacity={at(1) ? 0.9 : 0} style={{ transition: "opacity 420ms ease 180ms" }} />
          {[-10, 2].map((dx, i) => [-10, 2].map((dy, j) => (
            <circle key={`${i}-${j}`} cx={dx} cy={dy} r="1.4" fill={MERCH} opacity={at(1) ? 0.7 : 0} style={{ transition: `opacity 420ms ease ${240 + (i * 2 + j) * 30}ms` }} />
          )))}
        </g>
        <g style={{ transform: at(1) ? "translate(388px,300px) rotate(12deg)" : "translate(560px,300px) rotate(12deg)", transition: "transform 760ms var(--ease-standard) 260ms" }}>
          <path d="M-17,-12 L12,-12 L19,0 L12,12 L-17,12 Z" fill="none" stroke={MERCH} strokeWidth="1.6" opacity={at(1) ? 0.9 : 0} style={{ transition: "opacity 420ms ease 260ms" }} />
          <circle cx="-8" cy="0" r="2.6" fill="none" stroke={MERCH} strokeWidth="1.1" opacity={at(1) ? 0.85 : 0} style={{ transition: "opacity 420ms ease 320ms" }} />
        </g>
        <g style={{ transform: at(1) ? "translate(240px,300px) scale(1)" : "translate(240px,338px) scale(0.7)", transformOrigin: "center", transition: "transform 720ms var(--ease-standard) 320ms" }}>
          <g style={{ transformBox: "fill-box", transformOrigin: "center", animation: at(1) && !reduced ? "ink-build-sway 8600ms ease-in-out infinite" : "none" }}>
            <path d="M0,-20 L17,-10 L0,0 L-17,-10 Z" fill="none" stroke={THREED} strokeWidth="1.5" opacity={at(1) ? 0.85 : 0} style={{ transition: "opacity 420ms ease 320ms" }} />
            <path d="M-17,-10 L0,0 L0,20 L-17,10 Z" fill="none" stroke={THREED} strokeWidth="1.5" opacity={at(1) ? 0.85 : 0} style={{ transition: "opacity 420ms ease 380ms" }} />
            <path d="M17,-10 L0,0 L0,20 L17,10 Z" fill="none" stroke={THREED} strokeWidth="1.5" opacity={at(1) ? 0.85 : 0} style={{ transition: "opacity 420ms ease 380ms" }} />
          </g>
          <line x1="-22" y1="24" x2="22" y2="24" stroke={THREED} strokeWidth="1.4" opacity={at(1) ? 0.6 : 0} style={{ transition: "opacity 420ms ease 440ms" }} />
        </g>

        {/* Layer 3 — Motion & Animation, echoing the approved quadrant's own
            composition (circle, a drawn curve, square, curve, triangle) at
            a scale that weaves through everything already on the table —
            not a new symbol, the same visual grammar redrawn to fit here. */}
        <path
          d={motionD}
          fill="none" stroke={MOTION} strokeWidth="1.8" pathLength="1" strokeDasharray="1"
          strokeDashoffset={at(3) ? 0 : 1} opacity={at(3) ? 0.65 : 0}
          style={{ transition: "stroke-dashoffset 1050ms var(--ease-standard) 260ms, opacity 420ms ease 260ms" }}
        />
        {at(3) && !reduced ? (
          <circle r="3.2" fill={MOTION} opacity="0.95">
            <animateMotion dur="4.4s" repeatCount="indefinite" path={motionD} />
          </circle>
        ) : null}
        <g style={{ animation: at(3) && !reduced ? "ink-build-drift-a 6200ms ease-in-out infinite" : "none" }}>
          <circle cx="110" cy="270" r={at(3) ? 15 : 0} fill="none" stroke={MOTION} strokeWidth="2" opacity={at(3) ? 0.9 : 0} style={{ transition: "r 700ms var(--ease-standard) 120ms, opacity 380ms ease 120ms" }} />
        </g>
        <g style={{ transformBox: "fill-box", transformOrigin: "center", animation: at(3) && !reduced ? "ink-build-drift-b 7400ms ease-in-out infinite" : "none" }}>
          <rect x="228" y="132" width="36" height="36" fill="none" stroke={MOTION} strokeWidth="2"
            opacity={at(3) ? 0.9 : 0}
            style={{ transform: at(3) ? "scale(1) rotate(0deg)" : "scale(0) rotate(-30deg)", transformBox: "fill-box", transformOrigin: "center", transition: "transform 700ms var(--ease-standard) 380ms, opacity 380ms ease 380ms" }} />
        </g>
        <g style={{ transformBox: "fill-box", transformOrigin: "center", animation: at(3) && !reduced ? "ink-build-drift-c 5400ms ease-in-out infinite" : "none" }}>
          <path d="M400,38 L420,72 L380,72 Z" fill="none" stroke={MOTION} strokeWidth="2"
            opacity={at(3) ? 0.9 : 0}
            style={{ transform: at(3) ? "scale(1)" : "scale(0)", transformBox: "fill-box", transformOrigin: "center", transition: "transform 700ms var(--ease-standard) 460ms, opacity 380ms ease 460ms" }} />
        </g>
      </g>
    </svg>
  );
}

// Horizontal stage rail — numbered selectors, not tabs or cards. Hover/focus
// previews a stage (desktop only); clicking locks it. A red progress line
// fills to the active stage, echoing the rail already used in How We Work.
function PackageStepper({ stages, activeIndex, narrow, onHover, onSelect }) {
  return (
    <div
      onMouseLeave={!narrow ? () => onHover(null) : undefined}
      style={{ position: "relative", display: "flex", justifyContent: "space-between", gap: "var(--space-3)" }}
    >
      <div aria-hidden="true" style={{ position: "absolute", insetInlineStart: 9, insetInlineEnd: 9, top: 9, height: 2, background: "var(--ink-700)" }} />
      <div aria-hidden="true" style={{ position: "absolute", insetInlineStart: 9, top: 9, height: 2, width: `calc(${(activeIndex / (stages.length - 1)) * 100}% - ${(activeIndex / (stages.length - 1)) * 18}px)`, background: "var(--vermilion-500)", transition: "width 520ms var(--ease-standard)" }} />
      {stages.map((s, i) => (
        <button
          key={s.id}
          className="ink-build-step"
          onMouseEnter={!narrow ? () => onHover(i) : undefined}
          onFocus={() => onHover(i)}
          onBlur={() => onHover(null)}
          onClick={() => onSelect(i)}
          aria-current={i === activeIndex}
          style={{
            position: "relative", zIndex: 1, background: "none", border: "none", cursor: "pointer", padding: 0,
            display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "var(--space-2)",
            flex: "1 1 0%", minWidth: 0, textAlign: "start",
          }}
        >
          <span aria-hidden="true" style={{
            width: 18, height: 18, borderRadius: "50%",
            border: `2px solid ${i <= activeIndex ? "var(--vermilion-500)" : "var(--ink-700)"}`,
            background: i <= activeIndex ? "var(--vermilion-500)" : "var(--surface-page)",
            transition: "background 420ms ease, border-color 420ms ease",
          }} />
          <span className="ink-label" style={{ color: i === activeIndex ? "var(--paper-050)" : "var(--ink-400)", transition: "color 300ms ease" }}>{`0${i + 1}`}</span>
          <span style={{ fontSize: narrow ? "var(--size-caption)" : "var(--size-body-sm)", fontWeight: "var(--weight-button)", fontStretch: "var(--stretch-button)", color: i === activeIndex ? "var(--paper-050)" : "var(--ink-400)", transition: "color 300ms ease", overflow: "hidden", textOverflow: narrow ? "clip" : "ellipsis", whiteSpace: narrow ? "normal" : "nowrap", maxWidth: "100%", lineHeight: 1.25 }}>{s.name}</span>
        </button>
      ))}
    </div>
  );
}

function PackageInfoPanel({ pkg, index, ar, setPage }) {
  const leadIn = index === 0
    ? null
    : ar
    ? `كل ما في ${["", "حزمة البداية", "حزمة المنتجات", "حضور الفعالية"][index]}، بالإضافة إلى —`
    : `Everything in ${["", "Starter", "Merch", "Event Presence"][index]}, plus —`;
  return (
    <div>
      <div className="ink-label" style={{ color: "var(--vermilion-500)" }}>{`0${index + 1}`}</div>
      <div style={{ fontFamily: ar ? "var(--font-arabic)" : "var(--font-display)", fontWeight: ar ? 800 : "var(--weight-title)", fontStretch: ar ? "normal" : "var(--stretch-title)", fontSize: "clamp(22px, 2.4vw, 30px)", color: "var(--paper-050)", marginTop: "var(--space-2)", lineHeight: 1.15 }}>{pkg.name}</div>
      <p style={{ fontSize: "var(--size-body-sm)", color: "rgba(246,243,237,0.75)", marginTop: "var(--space-3)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)" }}>{pkg.tagline}</p>

      {leadIn ? <p style={{ fontSize: "var(--size-body-sm)", color: "var(--saffron-500)", marginTop: "var(--space-5)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)" }}>{leadIn}</p> : null}

      <div className="ink-label" style={{ color: "var(--ink-400)", marginTop: leadIn ? "var(--space-3)" : "var(--space-5)" }}>{ar ? "يشمل" : "Includes"}</div>
      <ul style={{ listStyle: "none", padding: 0, margin: "var(--space-3) 0 0", display: "flex", flexDirection: "column", gap: "var(--space-2)" }}>
        {pkg.includes.map((it) => (
          <li key={it} style={{ display: "flex", alignItems: "baseline", gap: "var(--space-3)", fontSize: "var(--size-body-sm)", color: "rgba(246,243,237,0.9)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)" }}>
            <span aria-hidden="true" style={{ color: "var(--vermilion-500)" }}>—</span>
            <span>{it}</span>
          </li>
        ))}
      </ul>

      <div className="ink-label" style={{ color: "var(--ink-400)", marginTop: "var(--space-5)" }}>{ar ? "الأنسب لـ" : "Best for"}</div>
      <p style={{ fontSize: "var(--size-body-sm)", color: "rgba(246,243,237,0.85)", marginTop: "var(--space-2)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)" }}>{pkg.bestFor.replace(/^Best for:\s*/i, "").replace(/^الأنسب لـ:\s*/, "")}</p>

      <div style={{ marginTop: "var(--space-6)", paddingTop: "var(--space-5)", borderTop: "1px solid var(--ink-700)" }}>
        <div className="ink-spec" style={{ color: "var(--ink-400)" }}>{ar ? "يبدأ من" : "Starting from"}</div>
        <div style={{ fontFamily: ar ? "var(--font-arabic)" : "var(--font-display)", fontWeight: ar ? 700 : "var(--weight-title)", fontStretch: ar ? "normal" : "var(--stretch-title)", fontSize: "var(--size-h3)", color: "var(--paper-050)", marginTop: "var(--space-1)" }}>{formatPrice(pkg.price, ar)}</div>
        <Button variant="accent" size="sm" iconRight="arrow-up-right" onClick={() => setPage("Contact")} style={{ marginTop: "var(--space-4)" }}>
          {ar ? "اطلب هذه الحزمة" : "Request this package"}
        </Button>
      </div>
    </div>
  );
}

// The right-hand details panel's own per-tier "material" — the exact
// same glow language the Home Packages cards already use (MerchLayer's
// gold sweep + glints, ProductionLayer's blue sweep + crystalline white
// glints, ScenePkgFull's radial multicolor bloom — same colors, same
// gradients, same keyframe), reused here rather than invented fresh, so
// the two interfaces read as the same four package identities. Starter
// gets no layer at all — the plain dark panel already is "the base
// level." All three colored layers stay mounted at all times and only
// cross-fade their own opacity (the same active/inactive pattern used
// throughout this file), so switching tiers is one smooth material
// change with no flash — never a hard swap between DOM layers. Applied
// only to this details panel, never the illustration canvas beside it.
function PackageGlowPanel({ index, reduced, children }) {
  const merch = index === 1;
  const event = index === 2;
  const full = index === 3;
  return (
    <div style={{ position: "relative" }}>
      {/* Local keyframes — Home's own WhatWeMake component defines the
          same-named ink-svc-sweep animation, but that CSS only exists in
          the DOM while that component is mounted, so it's redeclared
          identically here for this page. ink-svc-sweep-b is this page's
          own — a wider excursion for the Event Presence blue streak, so
          it never locks perfectly in step with the white sweep's
          ink-svc-sweep timing. */}
      <style>{`
        @keyframes ink-svc-sweep { 0% { transform: translateX(-24%); } 50% { transform: translateX(24%); } 100% { transform: translateX(-24%); } }
        @keyframes ink-svc-sweep-b { 0% { transform: translateX(28%); } 50% { transform: translateX(-30%); } 100% { transform: translateX(28%); } }
      `}</style>
      {/* Merch — warm golden sheen + two gold glints. */}
      <span aria-hidden="true" style={{ position: "absolute", inset: 0, overflow: "hidden", borderRadius: "inherit", opacity: merch ? 1 : 0, transition: "opacity 650ms var(--ease-standard)" }}>
        <span style={{ position: "absolute", inset: "-20% -10%", background: "linear-gradient(100deg, transparent 42%, rgba(240,179,44,0.32) 50%, transparent 58%)", animation: merch && !reduced ? "ink-svc-sweep 8000ms ease-in-out infinite" : "none" }} />
        {[{ x: "insetInlineStart", xv: 22, y: "top", yv: 18, d: "0s" }, { x: "insetInlineEnd", xv: 26, y: "bottom", yv: 32, d: "1.4s" }].map((g, i) => (
          <svg key={i} aria-hidden="true" viewBox="0 0 16 16" style={{ position: "absolute", [g.x]: g.xv, [g.y]: g.yv, width: 10, height: 10, opacity: merch ? 0.85 : 0, transition: "opacity 450ms ease" }}>
            <path d="M8,0 L9.4,6.6 L16,8 L9.4,9.4 L8,16 L6.6,9.4 L0,8 L6.6,6.6 Z" fill="var(--saffron-500)">
              {merch && !reduced ? <animate attributeName="opacity" values="0.3;1;0.3" dur="3.2s" begin={g.d} repeatCount="indefinite" /> : null}
            </path>
          </svg>
        ))}
      </span>
      {/* Event Presence — a very subtle static cobalt base underneath
          (Home's ProductionLayer tint, kept faint rather than removed),
          plus TWO independently-traveling streaks: a soft blue one and
          the brighter white sheen Home's own Event Presence card already
          carries (it composes PrintLayer underneath ProductionLayer, so
          that "traveling" light is literally PrintLayer's sweep, reused
          verbatim). The blue streak runs a different width/duration/
          phase than the white one (own keyframe, own timing) so the two
          drift in and out of alignment instead of locking together. */}
      <span aria-hidden="true" style={{ position: "absolute", inset: 0, overflow: "hidden", borderRadius: "inherit", opacity: event ? 1 : 0, transition: "opacity 650ms var(--ease-standard)" }}>
        <span style={{ position: "absolute", inset: "-20% -10%", background: "linear-gradient(100deg, transparent 40%, rgba(27,54,204,0.07) 50%, transparent 60%)" }} />
        <span style={{ position: "absolute", inset: "-20% -14%", background: "linear-gradient(100deg, transparent 34%, rgba(70,110,240,0.32) 50%, transparent 66%)", animation: event && !reduced ? "ink-svc-sweep-b 7200ms ease-in-out infinite -1800ms" : "none" }} />
        <span style={{ position: "absolute", inset: "-20% -10%", background: "linear-gradient(100deg, transparent 42%, rgba(255,255,255,0.45) 50%, transparent 58%)", animation: event && !reduced ? "ink-svc-sweep 9000ms ease-in-out infinite" : "none" }} />
        {[{ x: "insetInlineStart", xv: 24, y: "top", yv: 22, d: "0s" }, { x: "insetInlineEnd", xv: 26, y: "bottom", yv: 36, d: "1.6s" }].map((g, i) => (
          <svg key={i} aria-hidden="true" viewBox="0 0 16 16" style={{ position: "absolute", [g.x]: g.xv, [g.y]: g.yv, width: 9, height: 9, opacity: event ? 0.9 : 0, transition: "opacity 450ms ease" }}>
            <path d="M8,0 L9.4,6.6 L16,8 L9.4,9.4 L8,16 L6.6,9.4 L0,8 L6.6,6.6 Z" fill="var(--paper-000)">
              {event && !reduced ? <animate attributeName="opacity" values="0.25;0.95;0.25" dur="3s" begin={g.d} repeatCount="indefinite" /> : null}
            </path>
          </svg>
        ))}
      </span>
      {/* Full Experience — the same dark-cosmic radial multicolor bloom
          ScenePkgFull washes its whole card with, PLUS the moving gold
          and white sweeps Full Experience already inherits on Home (it
          composes MerchLayer and PrintLayer underneath its own bloom and
          MotionLayer) — both reused verbatim, layered on top of the
          bloom rather than replacing it. */}
      <span aria-hidden="true" style={{
        position: "absolute", inset: 0, overflow: "hidden", borderRadius: "inherit",
        opacity: full ? 1 : 0, transition: "opacity 650ms var(--ease-standard)",
        background: "radial-gradient(60% 65% at 50% 42%, rgba(240,179,44,0.24) 0%, rgba(27,54,204,0.20) 55%, rgba(240,179,44,0) 100%)",
      }}>
        <span style={{ position: "absolute", inset: "-20% -10%", background: "linear-gradient(100deg, transparent 42%, rgba(240,179,44,0.28) 50%, transparent 58%)", animation: full && !reduced ? "ink-svc-sweep 8000ms ease-in-out infinite" : "none" }} />
        <span style={{ position: "absolute", inset: "-20% -10%", background: "linear-gradient(100deg, transparent 42%, rgba(255,255,255,0.38) 50%, transparent 58%)", animation: full && !reduced ? "ink-svc-sweep 9000ms ease-in-out infinite" : "none" }} />
      </span>
      <div style={{ position: "relative", zIndex: 1 }}>{children}</div>
    </div>
  );
}

// The section itself — an editorial construction table, not a card grid.
// lockedIndex is the committed selection (click/tap); hoverIndex is a
// transient desktop-only preview that falls back to lockedIndex the moment
// the pointer leaves the rail. Both the canvas and the info panel read the
// same resolved index, so "preview" and "select" always stay in sync.
// The exact same glyph each package's own PackageCard/PackageInfoPanel icon
// already uses (see packagesData in Home.jsx) — reused for the mobile
// ambient motif rather than invented fresh.
const PACKAGE_AMBIENT_ICON = { starter: "package", merch: "shopping-bag", event: "camera", full: "sparkles" };

function BuildExperience({ ar, setPage }) {
  const narrow = useIsNarrow();
  const reduced = useReducedMotion();
  const [lockedIndex, setLockedIndex] = React.useState(0);
  const [hoverIndex, setHoverIndex] = React.useState(null);
  const [dragging, setDragging] = React.useState(false);
  const [dragX, setDragX] = React.useState(0);
  const dragRef = React.useRef({ startX: 0, startY: 0, active: false, horizontal: false });
  const PACKAGES = packagesData(ar);
  const activeIndex = hoverIndex ?? lockedIndex;
  const activePkg = PACKAGES[activeIndex];
  const lastIndex = PACKAGES.length - 1;

  const goTo = (i) => {
    setLockedIndex(Math.max(0, Math.min(lastIndex, i)));
    setHoverIndex(null);
  };

  // Direct-manipulation drag/swipe on the canvas itself, resolving into the
  // exact same setLockedIndex the timeline and arrows use — one state
  // machine, so all four navigation methods can never disagree. The canvas
  // only nudges a few px while dragging (tactile feedback, not a slide
  // between "pages"); the actual stage change — and its layer transitions —
  // only commits once the pointer is released past the threshold.
  // touchAction:"pan-y" lets the browser keep native vertical scrolling
  // while this handles the horizontal axis, and the up-front |dx| vs |dy|
  // check bails out of the drag entirely for a mostly-vertical gesture.
  const onCanvasPointerDown = (e) => {
    if (e.pointerType === "mouse" && e.button !== 0) return;
    dragRef.current = { startX: e.clientX, startY: e.clientY, active: true, horizontal: false };
    setDragging(true);
  };
  const onCanvasPointerMove = (e) => {
    const st = dragRef.current;
    if (!st.active) return;
    const dx = e.clientX - st.startX;
    const dy = e.clientY - st.startY;
    if (!st.horizontal) {
      if (Math.abs(dx) < 6 && Math.abs(dy) < 6) return;
      if (Math.abs(dy) > Math.abs(dx)) { st.active = false; return; }
      st.horizontal = true;
      try { e.currentTarget.setPointerCapture(e.pointerId); } catch (err) {}
    }
    setDragX(Math.max(-56, Math.min(56, dx * 0.35)));
  };
  const endDrag = (e) => {
    const st = dragRef.current;
    if (st.active && st.horizontal) {
      const dx = e.clientX - st.startX;
      const THRESHOLD = 64;
      if (dx <= -THRESHOLD) goTo(lockedIndex + 1);
      else if (dx >= THRESHOLD) goTo(lockedIndex - 1);
    }
    dragRef.current.active = false;
    setDragging(false);
    setDragX(0);
  };

  const arrowStyle = {
    position: "absolute", top: "50%", transform: "translateY(-50%)", zIndex: 2,
    width: 34, height: 34, borderRadius: "50%",
    border: "1px solid rgba(246,243,237,0.35)", background: "rgba(20,17,22,0.55)",
    color: "rgba(246,243,237,0.85)", display: "flex", alignItems: "center", justifyContent: "center",
    transition: "opacity 300ms ease, border-color 220ms ease, background 220ms ease",
  };

  const activeMood = PACKAGE_MOODS[activePkg.id];
  // Full Experience is the premium/final tier and its ambient shouldn't
  // reuse the generic per-package glow RGB below — that gold (240,179,44)
  // sat one shade off Merch's own gold (230,163,44), so selecting either
  // tier washed this whole region in nearly the same amber. A flat white/
  // charcoal pass at this fixed it but read as plain grey rather than the
  // premium final tier, so this is now a genuinely deep-space direction
  // instead: near-black carrying a whisper of midnight blue-violet (never
  // enough to read as "purple", just enough to feel like atmosphere
  // rather than flat grey) plus one soft violet-toned haze standing in
  // for a distant nebula. Gold stays concentrated in the sparkle icons
  // themselves (below), which get their own richer, warmer, faintly
  // luminous tone instead of the flat generic accent — so this is the
  // only place in the whole region gold actually shows. The other three
  // packages are untouched — same single-color glow treatment as before.
  const isFullMood = activePkg.id === "full";
  const FULL_COSMIC_GOLD = "#ffd479";
  const packageWash = isFullMood
    ? "linear-gradient(180deg, rgba(10,9,20,0.40) 0%, rgba(14,12,24,0.16) 30%, rgba(20,15,32,0.18) 60%, rgba(8,7,15,0.36) 100%)"
    : `linear-gradient(180deg, rgba(${activeMood.glow},0.16) 0%, rgba(${activeMood.glow},0.06) 30%, rgba(${activeMood.glow},0.06) 60%, rgba(${activeMood.glow},0.14) 100%)`;
  const packageBlobA = isFullMood
    ? "radial-gradient(circle, rgba(112,92,196,0.24), rgba(112,92,196,0) 70%)"
    : `radial-gradient(circle, rgba(${activeMood.glow},0.46), rgba(${activeMood.glow},0) 70%)`;
  const packageBlobB = isFullMood
    ? "radial-gradient(circle, rgba(7,6,14,0.66), rgba(7,6,14,0) 72%)"
    : `radial-gradient(circle, rgba(${activeMood.glow},0.36), rgba(${activeMood.glow},0) 72%)`;

  return (
    <Section
      index="02"
      title={ar ? "الحزم" : "Packages"}
      decor={narrow ? (
        // The existing per-tier "material" (PackageGlowPanel, below) only
        // ever lit the right-hand details panel, never the heading, the
        // stage progress line, or the space around the canvas — exactly
        // what this task calls out. This round significantly increased
        // that reach again (the previous version was still judged "too
        // subtle"): a full-bleed tinted wash behind the ENTIRE section
        // first, THEN two much stronger, larger glow blobs bleeding past
        // the section's own left/right edges, plus the active package's
        // own icon (the exact glyph its PackageCard already uses —
        // package/shopping-bag/camera/sparkles, not a new symbol) rendered
        // huge and faint near the top and bottom — so the active tier
        // genuinely feels like it "takes over" the whole region, not just
        // tints one corner of it. Mobile-only; desktop's own
        // PackageGlowPanel treatment (the right-hand details panel) is
        // untouched.
        <>
          <div
            aria-hidden="true"
            style={{
              position: "absolute", inset: 0,
              background: packageWash,
              transition: reduced ? "none" : "background 420ms ease",
            }}
          />
          <div
            aria-hidden="true"
            style={{
              position: "absolute", left: "-20%", top: "-10%", width: "85%", height: "38%", borderRadius: "50%",
              background: packageBlobA,
              transition: reduced ? "none" : "background 420ms ease",
            }}
          />
          <div
            aria-hidden="true"
            style={{
              position: "absolute", right: "-22%", top: "32%", width: "95%", height: "54%", borderRadius: "50%",
              background: packageBlobB,
              transition: reduced ? "none" : "background 420ms ease",
            }}
          />
          {/* Full Experience's sparkles get their own richer, warmer gold
              plus a faint glow (a drop-shadow, not an animated filter —
              cheap to paint since it never changes) so they read as small
              luminous light sources against the cosmic wash above, rather
              than the same flat accent color every other tier's ambient
              icon uses. */}
          <Icon name={PACKAGE_AMBIENT_ICON[activePkg.id] || "package"} size={120} color={isFullMood ? FULL_COSMIC_GOLD : activeMood.accent} style={{ position: "absolute", right: "-4%", top: "1%", opacity: isFullMood ? 0.24 : 0.16, transform: "rotate(9deg)", filter: isFullMood ? "drop-shadow(0 0 16px rgba(255,212,121,0.5))" : undefined }} />
          <Icon name={PACKAGE_AMBIENT_ICON[activePkg.id] || "package"} size={100} color={isFullMood ? FULL_COSMIC_GOLD : activeMood.accent} style={{ position: "absolute", left: "-5%", bottom: "3%", opacity: isFullMood ? 0.22 : 0.14, transform: "rotate(-11deg)", filter: isFullMood ? "drop-shadow(0 0 14px rgba(255,212,121,0.45))" : undefined }} />
        </>
      ) : null}
    >
      <style>{`
        .ink-build-step:focus { outline: none; }
        .ink-build-step:focus-visible .ink-label { outline: 2px solid var(--focus-ring); outline-offset: 3px; border-radius: 2px; }
        .ink-build-arrow:focus { outline: none; }
        .ink-build-arrow:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
        .ink-build-arrow:not(:disabled):hover { border-color: rgba(246,243,237,0.7); background: rgba(20,17,22,0.8); }
        .ink-build-arrow:disabled { cursor: default; }
        @keyframes ink-build-sway { 0%, 100% { transform: rotate(-2.5deg); } 50% { transform: rotate(2.5deg); } }
        @keyframes ink-build-drift-a { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
        @keyframes ink-build-drift-b { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(6px) rotate(4deg); } }
        @keyframes ink-build-drift-c { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
      `}</style>
      <Reveal as="h2" style={{
        fontFamily: ar ? "var(--font-arabic-display)" : "var(--font-display)", fontWeight: ar ? 800 : "var(--weight-display)", fontStretch: ar ? "normal" : "var(--stretch-display)",
        fontSize: ar ? (narrow ? 26 : 34) : "clamp(26px, 3.4vw, 42px)",
        lineHeight: ar ? 1.3 : "var(--leading-heading)",
        textTransform: ar ? "none" : "uppercase", letterSpacing: ar ? 0 : "var(--tracking-heading)",
        maxWidth: "20ch",
      }}>
        {ar ? "ابنِ التجربة" : "Build the experience"}
      </Reveal>
      <Reveal delay={70} as="p" style={{ marginTop: "var(--space-3)", color: "var(--text-muted)", fontSize: "var(--size-body-lg)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)", maxWidth: "48ch" }}>
        {ar ? "أربع مراحل لمشروع واحد — كل مرحلة تُبنى فوق التي قبلها." : "Four stages of one project — each one builds on the last."}
      </Reveal>

      <Reveal delay={120} style={{ marginTop: "var(--space-8)" }}>
        <PackageStepper stages={PACKAGES} activeIndex={activeIndex} narrow={narrow} onHover={setHoverIndex} onSelect={goTo} />
      </Reveal>

      <Reveal delay={160} style={{ marginTop: "var(--space-6)" }}>
        <div style={{
          display: "grid", gridTemplateColumns: narrow ? "1fr" : "1.4fr 1fr",
          border: "var(--rule-weight-strong) solid var(--border-strong)", borderRadius: "var(--radius-lg)", overflow: "hidden",
          background: "linear-gradient(160deg, #17141a 0%, #0c0a0e 100%)",
        }}>
          <div
            onPointerDown={onCanvasPointerDown}
            onPointerMove={onCanvasPointerMove}
            onPointerUp={endDrag}
            onPointerCancel={endDrag}
            style={{
              position: "relative", aspectRatio: narrow ? "4 / 3" : "auto", minHeight: narrow ? undefined : 420,
              touchAction: "pan-y", cursor: dragging ? "grabbing" : "grab", userSelect: "none",
            }}
          >
            <div style={{ position: "absolute", inset: 0, transform: `translateX(${dragX}px)`, transition: dragging || reduced ? "none" : "transform 420ms var(--ease-standard)" }}>
              <BuildCanvas level={activeIndex} reduced={reduced} ar={ar} />
            </div>

            <button
              type="button"
              aria-label={ar ? "الحزمة السابقة" : "Previous package"}
              className="ink-build-arrow"
              onPointerDown={(e) => e.stopPropagation()}
              onClick={() => goTo(lockedIndex - 1)}
              disabled={lockedIndex === 0}
              style={{ ...arrowStyle, insetInlineStart: 10, opacity: lockedIndex === 0 ? 0.25 : 0.85 }}
            >
              <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path d={ar ? "M9 6l6 6-6 6" : "M15 6l-6 6 6 6"} fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" /></svg>
            </button>
            <button
              type="button"
              aria-label={ar ? "الحزمة التالية" : "Next package"}
              className="ink-build-arrow"
              onPointerDown={(e) => e.stopPropagation()}
              onClick={() => goTo(lockedIndex + 1)}
              disabled={lockedIndex === lastIndex}
              style={{ ...arrowStyle, insetInlineEnd: 10, opacity: lockedIndex === lastIndex ? 0.25 : 0.85 }}
            >
              <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path d={ar ? "M15 6l-6 6 6 6" : "M9 6l6 6-6 6"} fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" /></svg>
            </button>
          </div>
          <PackageGlowPanel index={activeIndex} reduced={reduced}>
            <div style={{
              padding: "var(--space-7) var(--space-6)",
              borderTop: narrow ? "1px solid var(--ink-700)" : "none",
              borderInlineStart: narrow ? "none" : "1px solid var(--ink-700)",
            }}>
              <PackageInfoPanel pkg={activePkg} index={activeIndex} ar={ar} setPage={setPage} />
            </div>
          </PackageGlowPanel>
        </div>
      </Reveal>
      {/* Subtle, secondary pricing note — never competes visually with the
          package canvas above it (small label + muted caption only). */}
      <div style={{ marginTop: "var(--space-6)", maxWidth: "56ch" }}>
        <div className="ink-label" style={{ color: "var(--text-muted)" }}>{ar ? "تسعير أفضل للمشاريع المجمّعة" : "Bundled project pricing"}</div>
        <p style={{ fontSize: "var(--size-caption)", color: "var(--text-faint)", marginTop: "var(--space-2)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)", lineHeight: ar ? "var(--leading-arabic)" : "var(--leading-body)" }}>
          {ar ? "عند جمع عدة خدمات في مشروع واحد، يمكن أن تكون التكلفة الإجمالية أفضل من طلب كل خدمة بشكل منفصل." : "Combining services can reduce the total project cost compared with ordering each separately."}
        </p>
      </div>
    </Section>
  );
}

function ServicesProcess({ ar }) {
  const narrow = useIsNarrow();
  const reduced = useReducedMotion();
  const [railRef, inView] = useInView(0.35);
  const STEPS = processSteps(ar);
  return (
    <Section index="03" title={ar ? "طريقة العمل" : "How we work"} style={{ background: "var(--sand-200)" }}>
      {!narrow ? (
        <div ref={railRef} aria-hidden="true" style={{ position: "relative", height: 2, background: "var(--border-hairline)", marginBottom: "var(--space-7)" }}>
          <div style={{ position: "absolute", insetInlineStart: 0, top: 0, bottom: 0, width: inView ? "100%" : "0%", background: "var(--vermilion-500)", transition: reduced ? "none" : "width 1100ms var(--ease-standard)" }} />
        </div>
      ) : null}
      <div style={{ display: "grid", gridTemplateColumns: narrow ? "1fr" : "repeat(4, 1fr)", gap: "var(--space-6)" }}>
        {STEPS.map(([t, d], i) => (
          <Reveal key={t} delay={i * 90}>
            <div className="ink-label" style={{ color: "var(--vermilion-500)" }}>{`0${i + 1}`}</div>
            <div className="ink-h3" style={{ marginTop: "var(--space-2)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-display)" }}>{t}</div>
            <p style={{ fontSize: "var(--size-body-sm)", color: "var(--ink-700)", marginTop: "var(--space-3)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)", lineHeight: ar ? "var(--leading-arabic)" : "var(--leading-body)" }}>{d}</p>
          </Reveal>
        ))}
      </div>
    </Section>
  );
}

function ServicesFinalCTA({ ar, setPage }) {
  return (
    <section style={{ padding: "var(--space-9) var(--page-margin) var(--space-10)", borderTop: "var(--rule-weight-strong) solid var(--border-strong)" }}>
      <div style={{ maxWidth: "var(--container-max)", margin: "0 auto", display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: "var(--space-6)" }}>
        <div>
          <Reveal as="h2" style={{ fontFamily: ar ? "var(--font-arabic-display)" : "var(--font-display)", fontWeight: ar ? 800 : "var(--weight-display)", fontStretch: ar ? "normal" : "var(--stretch-display)", fontSize: "var(--size-h1)", textTransform: ar ? "none" : "uppercase", letterSpacing: ar ? 0 : "var(--tracking-heading)" }}>
            {ar ? "لديك فكرة؟" : "Have something in mind?"}
          </Reveal>
          <Reveal delay={80} as="p" style={{ marginTop: "var(--space-3)", color: "var(--text-muted)", fontSize: "var(--size-body-lg)", fontFamily: ar ? "var(--font-arabic)" : "var(--font-body)" }}>
            {ar ? "أخبرنا عنها." : "Tell us about it."}
          </Reveal>
        </div>
        <Reveal delay={150}>
          <Button variant="accent" size="lg" iconRight="arrow-up-right" onClick={() => setPage("Contact")}>
            {ar ? "ابدأ مشروع" : "Start a project"}
          </Button>
        </Reveal>
      </div>
    </section>
  );
}

function Services({ lang, setPage, target }) {
  const ar = lang === "ar";
  return (
    <main dir={ar ? "rtl" : "ltr"}>
      <ServiceQuadrantGrid ar={ar} setPage={setPage} target={target} />

      <BuildExperience ar={ar} setPage={setPage} />

      <ServicesProcess ar={ar} />

      <ServicesFinalCTA ar={ar} setPage={setPage} />
    </main>
  );
}

Object.assign(window, { Work, Services, PROJECTS, BG_SHAPES, BG_COLOR });
