{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "backdrop",
  "title": "Backdrop",
  "description": "The config-driven decorative section backdrop: glow, texture (grid/dots/diagonal/crosshair/contours/topo), grain and a blueprint frame, with named presets and dark-aware ink.",
  "dependencies": [
    "next-themes@^0.4.6"
  ],
  "registryDependencies": [
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/marketing/backdrop-config.ts",
      "content": "/**\n * Backdrop configuration — the declarative surface for the `Backdrop` engine.\n *\n * A `BackdropConfig` fully describes one decorative section backdrop: which\n * texture to draw, how dense, whether to add a depth glow, aggregate grain and\n * a blueprint margin frame. `Backdrop` takes a `preset` name and/or a partial\n * `config` override, so call sites never touch a global store — everything is\n * props-driven.\n *\n * This is a plain (non-client) module: server components can import the presets\n * and looks directly.\n */\n\nexport type PatternKind =\n  | \"none\"\n  | \"grid\"\n  | \"setout\"\n  | \"contours\"\n  | \"dots\"\n  | \"diagonal\"\n  | \"topo\"\nexport type MaskKind =\n  | \"auto\"\n  | \"none\"\n  | \"soft\"\n  | \"topRight\"\n  | \"topLeft\"\n  | \"bottom\"\n  | \"right\"\n  | \"left\"\nexport type GlowColor = \"auto\" | \"accent\" | \"navy\"\n\nexport interface BackdropConfig {\n  /** Which texture to draw. */\n  pattern: PatternKind\n  /** Pattern cell / contour spacing, in px. */\n  scale: number\n  /** Pattern ink opacity on light surfaces (brand surfaces are auto-boosted). */\n  patternOpacity: number\n  /** Edge fade. `auto` lets each placement pick a sensible mask. */\n  mask: MaskKind\n  /** Blueprint margin guide-lines at the container edges. */\n  frame: boolean\n  /** Dashed (vs solid) guide-lines. */\n  frameDashed: boolean\n  /** Registration \"+\" crosshairs at the frame corners. */\n  ticks: boolean\n  /** Radial depth glow. */\n  glow: boolean\n  /** Glow hue. `auto` → navy on light, light-brand on dark, accent on brand. */\n  glowColor: GlowColor\n  /** Glow alpha. */\n  glowStrength: number\n  /** Aggregate grain (best on dark panels). */\n  grain: boolean\n  /** Grain alpha. */\n  grainStrength: number\n}\n\n/** Shipping defaults — a survey-crosshair look with a soft glow and grain. */\nexport const DEFAULT_CONFIG: BackdropConfig = {\n  pattern: \"setout\",\n  scale: 40,\n  patternOpacity: 0.11,\n  mask: \"auto\",\n  frame: true,\n  frameDashed: false,\n  ticks: true,\n  glow: true,\n  glowColor: \"auto\",\n  glowStrength: 0.07,\n  grain: true,\n  grainStrength: 0.12,\n}\n\n/** Named starting points. Pass a key to `Backdrop` via the `preset` prop. */\nexport const PRESETS = {\n  Off: {\n    pattern: \"none\",\n    scale: 56,\n    patternOpacity: 0.06,\n    mask: \"auto\",\n    frame: false,\n    frameDashed: false,\n    ticks: false,\n    glow: false,\n    glowColor: \"auto\",\n    glowStrength: 0.08,\n    grain: false,\n    grainStrength: 0.12,\n  },\n  Minimal: {\n    pattern: \"grid\",\n    scale: 80,\n    patternOpacity: 0.04,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: false,\n    ticks: true,\n    glow: false,\n    glowColor: \"auto\",\n    glowStrength: 0.07,\n    grain: false,\n    grainStrength: 0.1,\n  },\n  Survey: {\n    pattern: \"setout\",\n    scale: 40,\n    patternOpacity: 0.11,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: false,\n    ticks: true,\n    glow: true,\n    glowColor: \"auto\",\n    glowStrength: 0.07,\n    grain: true,\n    grainStrength: 0.12,\n  },\n  Blueprint: {\n    pattern: \"grid\",\n    scale: 44,\n    patternOpacity: 0.05,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: true,\n    ticks: true,\n    glow: false,\n    glowColor: \"navy\",\n    glowStrength: 0.07,\n    grain: false,\n    grainStrength: 0.1,\n  },\n  Earthworks: {\n    pattern: \"contours\",\n    scale: 30,\n    patternOpacity: 0.075,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: false,\n    ticks: true,\n    glow: true,\n    glowColor: \"auto\",\n    glowStrength: 0.1,\n    grain: true,\n    grainStrength: 0.18,\n  },\n  Relief: {\n    // Big, softly-spaced contours with heavy grain and a strong glow — reads\n    // like a shaded-relief / hill-shade map.\n    pattern: \"contours\",\n    scale: 52,\n    patternOpacity: 0.06,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: false,\n    ticks: true,\n    glow: true,\n    glowColor: \"auto\",\n    glowStrength: 0.13,\n    grain: true,\n    grainStrength: 0.24,\n  },\n  Topographic: {\n    // Pure contour map — no grid, medium spacing.\n    pattern: \"contours\",\n    scale: 44,\n    patternOpacity: 0.08,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: false,\n    ticks: true,\n    glow: true,\n    glowColor: \"auto\",\n    glowStrength: 0.08,\n    grain: true,\n    grainStrength: 0.12,\n  },\n  \"Site Plan\": {\n    // Tighter grid + contours — a denser survey/site-plan weave.\n    pattern: \"topo\",\n    scale: 40,\n    patternOpacity: 0.06,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: false,\n    ticks: true,\n    glow: true,\n    glowColor: \"auto\",\n    glowStrength: 0.07,\n    grain: true,\n    grainStrength: 0.1,\n  },\n  Dots: {\n    pattern: \"dots\",\n    scale: 26,\n    patternOpacity: 0.07,\n    mask: \"auto\",\n    frame: true,\n    frameDashed: false,\n    ticks: false,\n    glow: false,\n    glowColor: \"auto\",\n    glowStrength: 0.07,\n    grain: false,\n    grainStrength: 0.1,\n  },\n} satisfies Record<string, BackdropConfig>\n\nexport type PresetName = keyof typeof PRESETS\n\n/**\n * Baked, per-page looks. Wrap a page in `<BackdropLookProvider look={…}>` and\n * every `Backdrop` inside inherits it, so a whole landing page shares one\n * decorative treatment.\n */\n\n/** Earthworks contour map — a strong, textured hero/landing look. */\nexport const CONTOURS_LOOK: BackdropConfig = {\n  pattern: \"contours\",\n  scale: 62,\n  patternOpacity: 0.08,\n  mask: \"auto\",\n  frame: false,\n  frameDashed: false,\n  ticks: true,\n  glow: true,\n  glowColor: \"auto\",\n  glowStrength: 0.08,\n  grain: true,\n  grainStrength: 0.26,\n}\n\n/** Setting-out crosshairs — the calmer, precise survey look. */\nexport const SETOUT_LOOK: BackdropConfig = {\n  pattern: \"setout\",\n  scale: 40,\n  patternOpacity: 0.11,\n  mask: \"auto\",\n  frame: false,\n  frameDashed: false,\n  ticks: true,\n  glow: true,\n  glowColor: \"auto\",\n  glowStrength: 0.07,\n  grain: true,\n  grainStrength: 0.12,\n}\n",
      "type": "registry:component",
      "target": "components/blocks/marketing/backdrop-config.ts"
    },
    {
      "path": "components/blocks/marketing/backdrop.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { useTheme } from \"next-themes\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n  type BackdropConfig,\n  DEFAULT_CONFIG,\n  type MaskKind,\n  type PatternKind,\n  type PresetName,\n  PRESETS,\n} from \"@/components/blocks/marketing/backdrop-config\"\n\n/**\n * Config-driven decorative section backdrops.\n *\n * Drop `<Backdrop />` as the first child of a `relative isolate overflow-hidden`\n * section and it composes up to four `aria-hidden pointer-events-none -z-10`\n * layers from a {@link BackdropConfig}:\n *   1. an accent depth glow (radial gradient),\n *   2. a texture pattern (survey grid / earthworks contours / dots / …),\n *   3. aggregate grain (fractal-noise overlay), and\n *   4. a blueprint margin frame with corner registration crosshairs.\n *\n * The config comes from, in priority order: an explicit `config` prop override,\n * a named `preset`, the nearest {@link BackdropLookProvider}, or the shipped\n * `DEFAULT_CONFIG`.\n *\n * Ink is theme-aware. On `tone=\"brand\"` (navy in both themes) the ink stays\n * accent-yellow. On `tone=\"default\"` surfaces — which flip light↔dark — the ink\n * flips too: navy on light, a light-brand tint on dark, so the texture stays\n * visible on the near-black dark surface.\n */\n\ntype Tone = \"default\" | \"brand\"\ntype Placement = \"hero\" | \"panel\"\n\nconst baseLayer = \"pointer-events-none absolute inset-0 -z-10\"\n\nconst INK = {\n  navy: \"#121541\", // brand-600\n  accent: \"#f7ec33\", // accent-400\n  lightBrand: \"#b4b7d4\", // brand-200 — visible on the near-black dark surface\n  white: \"#ffffff\",\n} as const\n\nconst MASKS: Record<Exclude<MaskKind, \"auto\">, string | undefined> = {\n  none: undefined,\n  soft: \"radial-gradient(ellipse at center, rgba(0,0,0,0.85) 25%, transparent 78%)\",\n  topRight:\n    \"radial-gradient(ellipse 85% 80% at 80% 18%, rgba(0,0,0,0.92) 0%, transparent 72%)\",\n  topLeft:\n    \"radial-gradient(ellipse 80% 72% at 22% 16%, rgba(0,0,0,0.9) 0%, transparent 74%)\",\n  bottom: \"radial-gradient(ellipse at bottom, rgba(0,0,0,0.85) 30%, transparent 80%)\",\n  right:\n    \"radial-gradient(ellipse 72% 96% at 100% 42%, rgba(0,0,0,0.95) 0%, transparent 74%)\",\n  left: \"radial-gradient(ellipse 72% 96% at 0% 42%, rgba(0,0,0,0.95) 0%, transparent 74%)\",\n}\n\n/* ─────────────────────────── pattern construction ─────────────────────────── */\n\nconst svgUrl = (svg: string) =>\n  `url(\"data:image/svg+xml,${encodeURIComponent(svg)}\")`\n\n/** A \"+\" crosshair tile — the classic setting-out / coordinate-station mark. */\nconst crosshairTile = (size: number, hex: string) => {\n  const c = size / 2\n  const arm = Math.min(7, size / 5)\n  const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='${size}' height='${size}'><g stroke='${hex}' stroke-width='1.1'><line x1='${c}' y1='${c - arm}' x2='${c}' y2='${c + arm}'/><line x1='${c - arm}' y1='${c}' x2='${c + arm}' y2='${c}'/></g></svg>`\n  return svgUrl(svg)\n}\n\n/**\n * Three overlapping, off-centre ring stacks whose interference reads like a\n * merged earthworks/OS contour map rather than concentric bullseyes.\n */\nconst contourImage = (hex: string, gap: number, lineWidth = 1.8) => {\n  const feather = 0.5\n  const s0 = (gap - lineWidth - feather).toFixed(2)\n  const s1 = (gap - lineWidth).toFixed(2)\n  const s2 = (gap - feather).toFixed(2)\n  const rings = (size: string, pos: string) =>\n    `repeating-radial-gradient(ellipse ${size} at ${pos}, transparent 0, transparent ${s0}px, ${hex} ${s1}px, ${hex} ${s2}px, transparent ${gap}px)`\n  return [\n    rings(\"66% 56%\", \"72% 18%\"),\n    rings(\"58% 50%\", \"16% 86%\"),\n    rings(\"48% 62%\", \"98% 96%\"),\n  ].join(\", \")\n}\n\nconst patternStyle = (\n  pattern: Exclude<PatternKind, \"none\" | \"topo\">,\n  scale: number,\n  hex: string\n): React.CSSProperties => {\n  switch (pattern) {\n    case \"grid\":\n      return {\n        backgroundImage: `linear-gradient(to right, ${hex} 1px, transparent 1px), linear-gradient(to bottom, ${hex} 1px, transparent 1px)`,\n        backgroundSize: `${scale}px ${scale}px`,\n      }\n    case \"setout\":\n      return {\n        backgroundImage: crosshairTile(scale, hex),\n        backgroundSize: `${scale}px ${scale}px`,\n      }\n    case \"dots\":\n      return {\n        backgroundImage: `radial-gradient(circle, ${hex} 1.2px, transparent 1.6px)`,\n        backgroundSize: `${scale}px ${scale}px`,\n      }\n    case \"diagonal\":\n      return {\n        backgroundImage: `repeating-linear-gradient(45deg, ${hex} 0, ${hex} 1px, transparent 1px, transparent ${scale}px), repeating-linear-gradient(-45deg, ${hex} 0, ${hex} 1px, transparent 1px, transparent ${scale}px)`,\n      }\n    case \"contours\":\n      return { backgroundImage: contourImage(hex, scale) }\n    default:\n      return {}\n  }\n}\n\ninterface PatternFieldProps {\n  ink: string\n  pattern: Exclude<PatternKind, \"none\" | \"topo\">\n  scale: number\n  opacity: number\n  mask?: string\n  className?: string\n}\n\nfunction PatternField({\n  ink,\n  pattern,\n  scale,\n  opacity,\n  mask,\n  className,\n}: PatternFieldProps) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      className={cn(baseLayer, className)}\n      style={{\n        ...patternStyle(pattern, scale, ink),\n        opacity,\n        maskImage: mask,\n        WebkitMaskImage: mask,\n      }}\n    />\n  )\n}\n\n/* ─────────────────────────── blueprint margin frame ─────────────────────────── */\n\nfunction Tick({ className }: { className?: string }) {\n  return (\n    <span aria-hidden=\"true\" className={cn(\"absolute block size-2.5\", className)}>\n      <span className=\"absolute top-1/2 left-0 h-px w-full -translate-y-1/2 bg-current\" />\n      <span className=\"absolute top-0 left-1/2 h-full w-px -translate-x-1/2 bg-current\" />\n    </span>\n  )\n}\n\ninterface BlueprintFrameProps {\n  /** Guide-line colour (hex). */\n  lineInk?: string\n  /** Corner crosshair colour (hex). */\n  tickInk?: string\n  dashed?: boolean\n  ticks?: boolean\n  className?: string\n}\n\n/** The margin guide-lines + corner registration crosshairs, on their own. */\nfunction BlueprintFrame({\n  lineInk = INK.navy,\n  tickInk = INK.navy,\n  dashed = false,\n  ticks = true,\n  className,\n}: BlueprintFrameProps) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      className={cn(baseLayer, \"flex justify-center px-2 md:px-4\", className)}\n      style={{ color: tickInk }}\n    >\n      <div className=\"h-full w-full max-w-container\">\n        <div\n          className={cn(\"relative h-full border-x\", dashed && \"border-dashed\")}\n          style={{ borderColor: `${lineInk}24` }}\n        >\n          {ticks ? (\n            <>\n              <Tick className=\"-top-px -left-px -translate-x-1/2 -translate-y-1/2 opacity-60\" />\n              <Tick className=\"-top-px -right-px translate-x-1/2 -translate-y-1/2 opacity-60\" />\n              <Tick className=\"-bottom-px -left-px -translate-x-1/2 translate-y-1/2 opacity-60\" />\n              <Tick className=\"-right-px -bottom-px translate-x-1/2 translate-y-1/2 opacity-60\" />\n            </>\n          ) : null}\n        </div>\n      </div>\n    </div>\n  )\n}\n\n/* ─────────────────────────── aggregate grain ─────────────────────────── */\n\nconst grainImage =\n  \"url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E\\\")\"\n\n/** A fractal-noise grain overlay at `mix-blend-mode: overlay`, on its own. */\nfunction GrainOverlay({\n  opacity = 0.14,\n  className,\n}: {\n  opacity?: number\n  className?: string\n}) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      className={cn(baseLayer, className)}\n      style={{ backgroundImage: grainImage, opacity, mixBlendMode: \"overlay\" }}\n    />\n  )\n}\n\n/* ─────────────────────────── accent depth glow ─────────────────────────── */\n\ninterface AccentGlowProps {\n  color?: string\n  position?: string\n  size?: string\n  className?: string\n}\n\n/** A single radial depth glow, on its own. */\nfunction AccentGlow({\n  color = \"rgb(247 236 51 / 0.12)\",\n  position = \"top right\",\n  size = \"620px\",\n  className,\n}: AccentGlowProps) {\n  return (\n    <div\n      aria-hidden=\"true\"\n      className={cn(baseLayer, className)}\n      style={{\n        backgroundImage: `radial-gradient(${size} circle at ${position}, ${color}, transparent 70%)`,\n      }}\n    />\n  )\n}\n\n/* ─────────────────────────── composed backdrop ─────────────────────────── */\n\nconst placementGlow: Record<Placement, { position: string; size: string }> = {\n  hero: { position: \"82% 14%\", size: \"700px\" },\n  panel: { position: \"94% 8%\", size: \"560px\" },\n}\n\nconst placementMask: Record<Placement, Exclude<MaskKind, \"auto\">> = {\n  hero: \"soft\",\n  panel: \"topRight\",\n}\n\nconst glowRgb = (\n  color: BackdropConfig[\"glowColor\"],\n  onBrand: boolean,\n  isDark: boolean,\n  strength: number\n) => {\n  const resolved =\n    color === \"auto\" ? (onBrand ? \"accent\" : isDark ? \"lightBrand\" : \"navy\") : color\n  const channels =\n    resolved === \"accent\"\n      ? \"247 236 51\"\n      : resolved === \"lightBrand\"\n        ? \"180 183 212\"\n        : \"18 21 65\"\n  return `rgb(${channels} / ${strength})`\n}\n\n/**\n * Per-page look. Wrap a page's content in `<BackdropLookProvider look={…}>` and\n * every `Backdrop` inside inherits that config. Unwrapped sections fall back to\n * their own `preset`/`config` or `DEFAULT_CONFIG`.\n */\nconst BackdropLookContext = React.createContext<BackdropConfig | null>(null)\n\nfunction BackdropLookProvider({\n  look,\n  children,\n}: {\n  look: BackdropConfig\n  children: React.ReactNode\n}) {\n  return (\n    <BackdropLookContext.Provider value={look}>\n      {children}\n    </BackdropLookContext.Provider>\n  )\n}\n\ninterface BackdropProps {\n  /** Surface tone — `brand` for navy panels (accent ink), `default` otherwise. */\n  tone?: Tone\n  /** Preset placement — picks a sensible glow position and edge mask. */\n  placement?: Placement\n  /** Named preset to start from (overridden by a wrapping look or `config`). */\n  preset?: PresetName\n  /** Partial config override — wins over `preset` and any wrapping look. */\n  config?: Partial<BackdropConfig>\n  className?: string\n}\n\nfunction Backdrop({\n  tone = \"default\",\n  placement = \"panel\",\n  preset,\n  config,\n  className,\n}: BackdropProps) {\n  const pageLook = React.useContext(BackdropLookContext)\n  const { resolvedTheme } = useTheme()\n\n  // next-themes is undefined on the server / during hydration; render the light\n  // palette until mounted so SSR and first client paint match, then flip.\n  const [mounted, setMounted] = React.useState(false)\n  React.useEffect(() => setMounted(true), [])\n  const isDark = mounted && resolvedTheme === \"dark\"\n\n  // Resolution order: explicit config override > preset > wrapping look > default.\n  const base: BackdropConfig = config\n    ? { ...(preset ? PRESETS[preset] : (pageLook ?? DEFAULT_CONFIG)), ...config }\n    : preset\n      ? PRESETS[preset]\n      : (pageLook ?? DEFAULT_CONFIG)\n  const cfg = base\n\n  const onBrand = tone === \"brand\"\n\n  // Brand panels are navy in both themes → accent ink. Default surfaces flip.\n  const ink = onBrand ? INK.accent : isDark ? INK.lightBrand : INK.navy\n  const lineInk = onBrand ? INK.white : ink\n  const tickInk = onBrand ? INK.accent : ink\n\n  const maskName = cfg.mask === \"auto\" ? placementMask[placement] : cfg.mask\n  const mask = MASKS[maskName]\n  const patternOpacity = cfg.patternOpacity * (onBrand ? 1.7 : 1)\n  const glow = placementGlow[placement]\n\n  return (\n    <div aria-hidden=\"true\" className={cn(baseLayer, className)}>\n      {cfg.glow && cfg.glowStrength > 0 ? (\n        <AccentGlow\n          color={glowRgb(cfg.glowColor, onBrand, isDark, cfg.glowStrength)}\n          position={glow.position}\n          size={glow.size}\n        />\n      ) : null}\n\n      {cfg.pattern === \"topo\" ? (\n        <>\n          <PatternField\n            ink={ink}\n            pattern=\"grid\"\n            scale={cfg.scale}\n            opacity={patternOpacity * 0.85}\n            mask={mask}\n          />\n          <PatternField\n            ink={ink}\n            pattern=\"contours\"\n            scale={Math.round(cfg.scale * 0.6)}\n            opacity={patternOpacity}\n            mask={mask}\n          />\n        </>\n      ) : cfg.pattern !== \"none\" ? (\n        <PatternField\n          ink={ink}\n          pattern={cfg.pattern}\n          scale={cfg.scale}\n          opacity={patternOpacity}\n          mask={mask}\n        />\n      ) : null}\n\n      {cfg.grain && cfg.grainStrength > 0 ? (\n        <GrainOverlay opacity={cfg.grainStrength} />\n      ) : null}\n\n      {cfg.frame ? (\n        <BlueprintFrame\n          lineInk={lineInk}\n          tickInk={tickInk}\n          dashed={cfg.frameDashed}\n          ticks={cfg.ticks}\n        />\n      ) : null}\n    </div>\n  )\n}\n\nexport {\n  Backdrop,\n  BackdropLookProvider,\n  AccentGlow,\n  GrainOverlay,\n  BlueprintFrame,\n}\n",
      "type": "registry:component",
      "target": "components/blocks/marketing/backdrop.tsx"
    }
  ],
  "type": "registry:block"
}